From c5c69067d34d7600bde569c8879a9201d7aec20a Mon Sep 17 00:00:00 2001 From: umairjavaid Date: Mon, 28 Aug 2023 22:13:34 +0000 Subject: [PATCH 001/117] added sklearn classes --- ivy/functional/frontends/sklearn/tree/Dec | 0 .../frontends/sklearn/tree/__init__.py | 0 .../frontends/sklearn/tree/_classes.py | 740 ++++++++++++++++++ 3 files changed, 740 insertions(+) create mode 100644 ivy/functional/frontends/sklearn/tree/Dec create mode 100644 ivy/functional/frontends/sklearn/tree/__init__.py create mode 100644 ivy/functional/frontends/sklearn/tree/_classes.py diff --git a/ivy/functional/frontends/sklearn/tree/Dec b/ivy/functional/frontends/sklearn/tree/Dec new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/ivy/functional/frontends/sklearn/tree/__init__.py b/ivy/functional/frontends/sklearn/tree/__init__.py new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/ivy/functional/frontends/sklearn/tree/_classes.py b/ivy/functional/frontends/sklearn/tree/_classes.py new file mode 100644 index 0000000000000..8028b17e4c33c --- /dev/null +++ b/ivy/functional/frontends/sklearn/tree/_classes.py @@ -0,0 +1,740 @@ +import copy +import numbers +import warnings +from abc import ABCMeta, abstractmethod +from math import ceil +from numbers import Integral, Real + +import numpy as np #to be replaced by ivy +from scipy.sparse import issparse #to be replaced by ivy + +from ..base import ( + BaseEstimator, + ClassifierMixin, + MultiOutputMixin, + RegressorMixin, + _fit_context, + clone, + is_classifier, +) +from ..utils import Bunch, check_random_state, compute_sample_weight +from ..utils._param_validation import Hidden, Interval, RealNotInt, StrOptions +from ..utils.multiclass import check_classification_targets +from ..utils.validation import ( + _assert_all_finite_element_wise, + _check_sample_weight, + assert_all_finite, + check_is_fitted, +) +from . import _criterion, _splitter, _tree, +from ._criterion import Criterion +from ._splitter import Splitter + +from ._tree import ( + BestFirstTreeBuilder, + DepthFirstTreeBuilder, + Tree, + _build_pruned_tree_ccp, + ccp_pruning_path, +) + + +# In essence, the code iterates through the input array column-wise and checks for the presence of NaN values in each column. +# If it finds any NaN in a column, it marks that column as containing NaN and moves on to the next column. +# The final result is an array indicating whether each column contains at least one NaN value. +from ._utils import _any_isnan_axis0 #Cython code + +__all__ = [ + "DecisionTreeClassifier", + "DecisionTreeRegressor", + "ExtraTreeClassifier", + "ExtraTreeRegressor", +] + +DTYPE = _tree.DTYPE +DOUBLE = _tree.DOUBLE + +CRITERIA_CLF = { + "gini": _criterion.Gini, + "log_loss": _criterion.Entropy, + "entropy": _criterion.Entropy, +} + +CRITERIA_REG = { + "squared_error": _criterion.MSE, + "friedman_mse": _criterion.FriedmanMSE, + "absolute_error": _criterion.MAE, + "poisson": _criterion.Poisson, +} + +DENSE_SPLITTERS = {"best": _splitter.BestSplitter, "random": _splitter.RandomSplitter} + +SPARSE_SPLITTERS = { + "best": _splitter.BestSparseSplitter, + "random": _splitter.RandomSparseSplitter, +} + +class BaseDecisionTree(MultiOutputMixin, BaseEstimator, metaclass=ABCMeta): + """Base class for decision trees. + + Warning: This class should not be used directly. + Use derived classes instead. + """ + + _parameter_constraints: dict = { + "splitter": [StrOptions({"best", "random"})], + "max_depth": [Interval(Integral, 1, None, closed="left"), None], + "min_samples_split": [ + Interval(Integral, 2, None, closed="left"), + Interval(RealNotInt, 0.0, 1.0, closed="right"), + ], + "min_samples_leaf": [ + Interval(Integral, 1, None, closed="left"), + Interval(RealNotInt, 0.0, 1.0, closed="neither"), + ], + "min_weight_fraction_leaf": [Interval(Real, 0.0, 0.5, closed="both")], + "max_features": [ + Interval(Integral, 1, None, closed="left"), + Interval(RealNotInt, 0.0, 1.0, closed="right"), + StrOptions({"sqrt", "log2"}), + None, + ], + "random_state": ["random_state"], + "max_leaf_nodes": [Interval(Integral, 2, None, closed="left"), None], + "min_impurity_decrease": [Interval(Real, 0.0, None, closed="left")], + "ccp_alpha": [Interval(Real, 0.0, None, closed="left")], + } + + @abstractmethod + def __init__( + self, + *, + criterion, + splitter, + max_depth, + min_samples_split, + min_samples_leaf, + min_weight_fraction_leaf, + max_features, + max_leaf_nodes, + random_state, + min_impurity_decrease, + class_weight=None, + ccp_alpha=0.0, + ): + self.criterion = criterion + self.splitter = splitter + self.max_depth = max_depth + self.min_samples_split = min_samples_split + self.min_samples_leaf = min_samples_leaf + self.min_weight_fraction_leaf = min_weight_fraction_leaf + self.max_features = max_features + self.max_leaf_nodes = max_leaf_nodes + self.random_state = random_state + self.min_impurity_decrease = min_impurity_decrease + self.class_weight = class_weight + self.ccp_alpha = ccp_alpha + + def get_depth(self): + """Return the depth of the decision tree. + + The depth of a tree is the maximum distance between the root + and any leaf. + + Returns + ------- + self.tree_.max_depth : int + The maximum depth of the tree. + """ + check_is_fitted(self) + return self.tree_.max_depth + + def get_n_leaves(self): + """Return the number of leaves of the decision tree. + + Returns + ------- + self.tree_.n_leaves : int + Number of leaves. + """ + check_is_fitted(self) + return self.tree_.n_leaves + + def _support_missing_values(self, X): + return not issparse(X) and self._get_tags()["allow_nan"] + + def _compute_missing_values_in_feature_mask(self, X): + """Return boolean mask denoting if there are missing values for each feature. + + This method also ensures that X is finite. + + Parameter + --------- + X : array-like of shape (n_samples, n_features), dtype=DOUBLE + Input data. + + Returns + ------- + missing_values_in_feature_mask : ndarray of shape (n_features,), or None + Missing value mask. If missing values are not supported or there + are no missing values, return None. + """ + common_kwargs = dict(estimator_name=self.__class__.__name__, input_name="X") + + if not self._support_missing_values(X): + assert_all_finite(X, **common_kwargs) + return None + + with np.errstate(over="ignore"): + overall_sum = np.sum(X) + + if not np.isfinite(overall_sum): + # Raise a ValueError in case of the presence of an infinite element. + _assert_all_finite_element_wise(X, xp=np, allow_nan=True, **common_kwargs) + + # If the sum is not nan, then there are no missing values + if not np.isnan(overall_sum): + return None + + missing_values_in_feature_mask = _any_isnan_axis0(X) + return missing_values_in_feature_mask + + def _fit( + self, + X, + y, + sample_weight=None, + check_input=True, + missing_values_in_feature_mask=None, + ): + random_state = check_random_state(self.random_state) + + if check_input: + # Need to validate separately here. + # We can't pass multi_output=True because that would allow y to be + # csr. + + # _compute_missing_values_in_feature_mask will check for finite values and + # compute the missing mask if the tree supports missing values + check_X_params = dict( + dtype=DTYPE, accept_sparse="csc", force_all_finite=False + ) + check_y_params = dict(ensure_2d=False, dtype=None) + X, y = self._validate_data( + X, y, validate_separately=(check_X_params, check_y_params) + ) + + missing_values_in_feature_mask = ( + self._compute_missing_values_in_feature_mask(X) + ) + if issparse(X): + X.sort_indices() + + if X.indices.dtype != np.intc or X.indptr.dtype != np.intc: + raise ValueError( + "No support for np.int64 index based sparse matrices" + ) + + if self.criterion == "poisson": + if np.any(y < 0): + raise ValueError( + "Some value(s) of y are negative which is" + " not allowed for Poisson regression." + ) + if np.sum(y) <= 0: + raise ValueError( + "Sum of y is not positive which is " + "necessary for Poisson regression." + ) + + # Determine output settings + n_samples, self.n_features_in_ = X.shape + is_classification = is_classifier(self) + + y = np.atleast_1d(y) + expanded_class_weight = None + + if y.ndim == 1: + # reshape is necessary to preserve the data contiguity against vs + # [:, np.newaxis] that does not. + y = np.reshape(y, (-1, 1)) + + self.n_outputs_ = y.shape[1] + + if is_classification: + check_classification_targets(y) + y = np.copy(y) + + self.classes_ = [] + self.n_classes_ = [] + + if self.class_weight is not None: + y_original = np.copy(y) + + y_encoded = np.zeros(y.shape, dtype=int) + for k in range(self.n_outputs_): + classes_k, y_encoded[:, k] = np.unique(y[:, k], return_inverse=True) + self.classes_.append(classes_k) + self.n_classes_.append(classes_k.shape[0]) + y = y_encoded + + if self.class_weight is not None: + expanded_class_weight = compute_sample_weight( + self.class_weight, y_original + ) + + self.n_classes_ = np.array(self.n_classes_, dtype=np.intp) + + if getattr(y, "dtype", None) != DOUBLE or not y.flags.contiguous: + y = np.ascontiguousarray(y, dtype=DOUBLE) + + max_depth = np.iinfo(np.int32).max if self.max_depth is None else self.max_depth + + if isinstance(self.min_samples_leaf, numbers.Integral): + min_samples_leaf = self.min_samples_leaf + else: # float + min_samples_leaf = int(ceil(self.min_samples_leaf * n_samples)) + + if isinstance(self.min_samples_split, numbers.Integral): + min_samples_split = self.min_samples_split + else: # float + min_samples_split = int(ceil(self.min_samples_split * n_samples)) + min_samples_split = max(2, min_samples_split) + + min_samples_split = max(min_samples_split, 2 * min_samples_leaf) + + if isinstance(self.max_features, str): + if self.max_features == "auto": + if is_classification: + max_features = max(1, int(np.sqrt(self.n_features_in_))) + warnings.warn( + ( + "`max_features='auto'` has been deprecated in 1.1 " + "and will be removed in 1.3. To keep the past behaviour, " + "explicitly set `max_features='sqrt'`." + ), + FutureWarning, + ) + else: + max_features = self.n_features_in_ + warnings.warn( + ( + "`max_features='auto'` has been deprecated in 1.1 " + "and will be removed in 1.3. To keep the past behaviour, " + "explicitly set `max_features=1.0'`." + ), + FutureWarning, + ) + elif self.max_features == "sqrt": + max_features = max(1, int(np.sqrt(self.n_features_in_))) + elif self.max_features == "log2": + max_features = max(1, int(np.log2(self.n_features_in_))) + elif self.max_features is None: + max_features = self.n_features_in_ + elif isinstance(self.max_features, numbers.Integral): + max_features = self.max_features + else: # float + if self.max_features > 0.0: + max_features = max(1, int(self.max_features * self.n_features_in_)) + else: + max_features = 0 + + self.max_features_ = max_features + + max_leaf_nodes = -1 if self.max_leaf_nodes is None else self.max_leaf_nodes + + if len(y) != n_samples: + raise ValueError( + "Number of labels=%d does not match number of samples=%d" + % (len(y), n_samples) + ) + + if sample_weight is not None: + sample_weight = _check_sample_weight(sample_weight, X, DOUBLE) + + if expanded_class_weight is not None: + if sample_weight is not None: + sample_weight = sample_weight * expanded_class_weight + else: + sample_weight = expanded_class_weight + + # Set min_weight_leaf from min_weight_fraction_leaf + if sample_weight is None: + min_weight_leaf = self.min_weight_fraction_leaf * n_samples + else: + min_weight_leaf = self.min_weight_fraction_leaf * np.sum(sample_weight) + + # Build tree + criterion = self.criterion + if not isinstance(criterion, Criterion): + if is_classification: + criterion = CRITERIA_CLF[self.criterion]( + self.n_outputs_, self.n_classes_ + ) + else: + criterion = CRITERIA_REG[self.criterion](self.n_outputs_, n_samples) + else: + # Make a deepcopy in case the criterion has mutable attributes that + # might be shared and modified concurrently during parallel fitting + criterion = copy.deepcopy(criterion) + + SPLITTERS = SPARSE_SPLITTERS if issparse(X) else DENSE_SPLITTERS + + splitter = self.splitter + if not isinstance(self.splitter, Splitter): + splitter = SPLITTERS[self.splitter]( + criterion, + self.max_features_, + min_samples_leaf, + min_weight_leaf, + random_state, + ) + + if is_classifier(self): + self.tree_ = Tree(self.n_features_in_, self.n_classes_, self.n_outputs_) + else: + self.tree_ = Tree( + self.n_features_in_, + # TODO: tree shouldn't need this in this case + np.array([1] * self.n_outputs_, dtype=np.intp), + self.n_outputs_, + ) + + # Use BestFirst if max_leaf_nodes given; use DepthFirst otherwise + if max_leaf_nodes < 0: + builder = DepthFirstTreeBuilder( + splitter, + min_samples_split, + min_samples_leaf, + min_weight_leaf, + max_depth, + self.min_impurity_decrease, + ) + else: + builder = BestFirstTreeBuilder( + splitter, + min_samples_split, + min_samples_leaf, + min_weight_leaf, + max_depth, + max_leaf_nodes, + self.min_impurity_decrease, + ) + + builder.build(self.tree_, X, y, sample_weight, missing_values_in_feature_mask) + + if self.n_outputs_ == 1 and is_classifier(self): + self.n_classes_ = self.n_classes_[0] + self.classes_ = self.classes_[0] + + self._prune_tree() + + return self + + def _validate_X_predict(self, X, check_input): + """Validate the training data on predict (probabilities).""" + if check_input: + if self._support_missing_values(X): + force_all_finite = "allow-nan" + else: + force_all_finite = True + X = self._validate_data( + X, + dtype=DTYPE, + accept_sparse="csr", + reset=False, + force_all_finite=force_all_finite, + ) + if issparse(X) and ( + X.indices.dtype != np.intc or X.indptr.dtype != np.intc + ): + raise ValueError("No support for np.int64 index based sparse matrices") + else: + # The number of features is checked regardless of `check_input` + self._check_n_features(X, reset=False) + return X + + def predict(self, X, check_input=True): + """Predict class or regression value for X. + + For a classification model, the predicted class for each sample in X is + returned. For a regression model, the predicted value based on X is + returned. + + Parameters + ---------- + X : {array-like, sparse matrix} of shape (n_samples, n_features) + The input samples. Internally, it will be converted to + ``dtype=np.float32`` and if a sparse matrix is provided + to a sparse ``csr_matrix``. + + check_input : bool, default=True + Allow to bypass several input checking. + Don't use this parameter unless you know what you're doing. + + Returns + ------- + y : array-like of shape (n_samples,) or (n_samples, n_outputs) + The predicted classes, or the predict values. + """ + check_is_fitted(self) + X = self._validate_X_predict(X, check_input) + proba = self.tree_.predict(X) + n_samples = X.shape[0] + + # Classification + if is_classifier(self): + if self.n_outputs_ == 1: + return self.classes_.take(np.argmax(proba, axis=1), axis=0) + + else: + class_type = self.classes_[0].dtype + predictions = np.zeros((n_samples, self.n_outputs_), dtype=class_type) + for k in range(self.n_outputs_): + predictions[:, k] = self.classes_[k].take( + np.argmax(proba[:, k], axis=1), axis=0 + ) + + return predictions + + # Regression + else: + if self.n_outputs_ == 1: + return proba[:, 0] + + else: + return proba[:, :, 0] + + def apply(self, X, check_input=True): + """Return the index of the leaf that each sample is predicted as. + + .. versionadded:: 0.17 + + Parameters + ---------- + X : {array-like, sparse matrix} of shape (n_samples, n_features) + The input samples. Internally, it will be converted to + ``dtype=np.float32`` and if a sparse matrix is provided + to a sparse ``csr_matrix``. + + check_input : bool, default=True + Allow to bypass several input checking. + Don't use this parameter unless you know what you're doing. + + Returns + ------- + X_leaves : array-like of shape (n_samples,) + For each datapoint x in X, return the index of the leaf x + ends up in. Leaves are numbered within + ``[0; self.tree_.node_count)``, possibly with gaps in the + numbering. + """ + check_is_fitted(self) + X = self._validate_X_predict(X, check_input) + return self.tree_.apply(X) + + def decision_path(self, X, check_input=True): + """Return the decision path in the tree. + + .. versionadded:: 0.18 + + Parameters + ---------- + X : {array-like, sparse matrix} of shape (n_samples, n_features) + The input samples. Internally, it will be converted to + ``dtype=np.float32`` and if a sparse matrix is provided + to a sparse ``csr_matrix``. + + check_input : bool, default=True + Allow to bypass several input checking. + Don't use this parameter unless you know what you're doing. + + Returns + ------- + indicator : sparse matrix of shape (n_samples, n_nodes) + Return a node indicator CSR matrix where non zero elements + indicates that the samples goes through the nodes. + """ + X = self._validate_X_predict(X, check_input) + return self.tree_.decision_path(X) + + def _prune_tree(self): + """Prune tree using Minimal Cost-Complexity Pruning.""" + check_is_fitted(self) + + if self.ccp_alpha == 0.0: + return + + # build pruned tree + if is_classifier(self): + n_classes = np.atleast_1d(self.n_classes_) + pruned_tree = Tree(self.n_features_in_, n_classes, self.n_outputs_) + else: + pruned_tree = Tree( + self.n_features_in_, + # TODO: the tree shouldn't need this param + np.array([1] * self.n_outputs_, dtype=np.intp), + self.n_outputs_, + ) + _build_pruned_tree_ccp(pruned_tree, self.tree_, self.ccp_alpha) + + self.tree_ = pruned_tree + + def cost_complexity_pruning_path(self, X, y, sample_weight=None): + """Compute the pruning path during Minimal Cost-Complexity Pruning. + + See :ref:`minimal_cost_complexity_pruning` for details on the pruning + process. + + Parameters + ---------- + X : {array-like, sparse matrix} of shape (n_samples, n_features) + The training input samples. Internally, it will be converted to + ``dtype=np.float32`` and if a sparse matrix is provided + to a sparse ``csc_matrix``. + + y : array-like of shape (n_samples,) or (n_samples, n_outputs) + The target values (class labels) as integers or strings. + + sample_weight : array-like of shape (n_samples,), default=None + Sample weights. If None, then samples are equally weighted. Splits + that would create child nodes with net zero or negative weight are + ignored while searching for a split in each node. Splits are also + ignored if they would result in any single class carrying a + negative weight in either child node. + + Returns + ------- + ccp_path : :class:`~sklearn.utils.Bunch` + Dictionary-like object, with the following attributes. + + ccp_alphas : ndarray + Effective alphas of subtree during pruning. + + impurities : ndarray + Sum of the impurities of the subtree leaves for the + corresponding alpha value in ``ccp_alphas``. + """ + est = clone(self).set_params(ccp_alpha=0.0) + est.fit(X, y, sample_weight=sample_weight) + return Bunch(**ccp_pruning_path(est.tree_)) + + @property + def feature_importances_(self): + """Return the feature importances. + + The importance of a feature is computed as the (normalized) total + reduction of the criterion brought by that feature. + It is also known as the Gini importance. + + Warning: impurity-based feature importances can be misleading for + high cardinality features (many unique values). See + :func:`sklearn.inspection.permutation_importance` as an alternative. + + Returns + ------- + feature_importances_ : ndarray of shape (n_features,) + Normalized total reduction of criteria by feature + (Gini importance). + """ + check_is_fitted(self) + + return self.tree_.compute_feature_importances() + +class DecisionTreeClassifier(ClassifierMixin, BaseDecisionTree): + _parameter_constraints: dict = { + **BaseDecisionTree._parameter_constraints, + "criterion": [StrOptions({"gini", "entropy", "log_loss"}), Hidden(Criterion)], + "class_weight": [dict, list, StrOptions({"balanced"}), None], + } + + def __init__( + self, + *, + criterion="gini", + splitter="best", + max_depth=None, + min_samples_split=2, + min_samples_leaf=1, + min_weight_fraction_leaf=0.0, + max_features=None, + random_state=None, + max_leaf_nodes=None, + min_impurity_decrease=0.0, + class_weight=None, + ccp_alpha=0.0, + ): + super().__init__( + criterion=criterion, + splitter=splitter, + max_depth=max_depth, + min_samples_split=min_samples_split, + min_samples_leaf=min_samples_leaf, + min_weight_fraction_leaf=min_weight_fraction_leaf, + max_features=max_features, + max_leaf_nodes=max_leaf_nodes, + class_weight=class_weight, + random_state=random_state, + min_impurity_decrease=min_impurity_decrease, + ccp_alpha=ccp_alpha, + ) + + @_fit_context(prefer_skip_nested_validation=True) + def fit(self, X, y, sample_weight=None, check_input=True): + + super()._fit( + X, + y, + sample_weight=sample_weight, + check_input=check_input, + ) + return self + + def predict_proba(self, X, check_input=True): + + check_is_fitted(self) + X = self._validate_X_predict(X, check_input) + proba = self.tree_.predict(X) + + if self.n_outputs_ == 1: + proba = proba[:, : self.n_classes_] + normalizer = proba.sum(axis=1)[:, np.newaxis] + normalizer[normalizer == 0.0] = 1.0 + proba /= normalizer + + return proba + + else: + all_proba = [] + + for k in range(self.n_outputs_): + proba_k = proba[:, k, : self.n_classes_[k]] + normalizer = proba_k.sum(axis=1)[:, np.newaxis] + normalizer[normalizer == 0.0] = 1.0 + proba_k /= normalizer + all_proba.append(proba_k) + + return all_proba + + def predict_log_proba(self, X): + + proba = self.predict_proba(X) + + if self.n_outputs_ == 1: + return np.log(proba) + + else: + for k in range(self.n_outputs_): + proba[k] = np.log(proba[k]) + + return proba + + def _more_tags(self): + # XXX: nan is only support for dense arrays, but we set this for common test to + # pass, specifically: check_estimators_nan_inf + allow_nan = self.splitter == "best" and self.criterion in { + "gini", + "log_loss", + "entropy", + } + return {"multilabel": True, "allow_nan": allow_nan} \ No newline at end of file From ab4442f811ec71c9c7b0930f6986125351dc7be2 Mon Sep 17 00:00:00 2001 From: umairjavaid Date: Tue, 29 Aug 2023 01:28:43 +0000 Subject: [PATCH 002/117] added frontend support for DecisionTrees scikit-learn --- ivy/functional/frontends/sklearn/base.py | 5 + ivy/functional/frontends/sklearn/tree/Dec | 0 .../frontends/sklearn/tree/__init__.py | 0 .../frontends/sklearn/tree/_classes.py | 628 +----------------- 4 files changed, 24 insertions(+), 609 deletions(-) delete mode 100644 ivy/functional/frontends/sklearn/tree/Dec delete mode 100644 ivy/functional/frontends/sklearn/tree/__init__.py diff --git a/ivy/functional/frontends/sklearn/base.py b/ivy/functional/frontends/sklearn/base.py index 2e8efd6078ee3..1db8a5caf63e5 100644 --- a/ivy/functional/frontends/sklearn/base.py +++ b/ivy/functional/frontends/sklearn/base.py @@ -26,3 +26,8 @@ def fit(self, X, y, **kwargs): def predict(self, X): raise NotImplementedError + + +class MultiOutputMixin: + def _more_tags(self): + return {"multioutput": True} \ No newline at end of file diff --git a/ivy/functional/frontends/sklearn/tree/Dec b/ivy/functional/frontends/sklearn/tree/Dec deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/ivy/functional/frontends/sklearn/tree/__init__.py b/ivy/functional/frontends/sklearn/tree/__init__.py deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/ivy/functional/frontends/sklearn/tree/_classes.py b/ivy/functional/frontends/sklearn/tree/_classes.py index 8028b17e4c33c..c8352f80b8250 100644 --- a/ivy/functional/frontends/sklearn/tree/_classes.py +++ b/ivy/functional/frontends/sklearn/tree/_classes.py @@ -1,109 +1,11 @@ -import copy -import numbers -import warnings from abc import ABCMeta, abstractmethod -from math import ceil -from numbers import Integral, Real - -import numpy as np #to be replaced by ivy -from scipy.sparse import issparse #to be replaced by ivy - from ..base import ( BaseEstimator, ClassifierMixin, MultiOutputMixin, - RegressorMixin, - _fit_context, - clone, - is_classifier, ) -from ..utils import Bunch, check_random_state, compute_sample_weight -from ..utils._param_validation import Hidden, Interval, RealNotInt, StrOptions -from ..utils.multiclass import check_classification_targets -from ..utils.validation import ( - _assert_all_finite_element_wise, - _check_sample_weight, - assert_all_finite, - check_is_fitted, -) -from . import _criterion, _splitter, _tree, -from ._criterion import Criterion -from ._splitter import Splitter - -from ._tree import ( - BestFirstTreeBuilder, - DepthFirstTreeBuilder, - Tree, - _build_pruned_tree_ccp, - ccp_pruning_path, -) - - -# In essence, the code iterates through the input array column-wise and checks for the presence of NaN values in each column. -# If it finds any NaN in a column, it marks that column as containing NaN and moves on to the next column. -# The final result is an array indicating whether each column contains at least one NaN value. -from ._utils import _any_isnan_axis0 #Cython code - -__all__ = [ - "DecisionTreeClassifier", - "DecisionTreeRegressor", - "ExtraTreeClassifier", - "ExtraTreeRegressor", -] - -DTYPE = _tree.DTYPE -DOUBLE = _tree.DOUBLE - -CRITERIA_CLF = { - "gini": _criterion.Gini, - "log_loss": _criterion.Entropy, - "entropy": _criterion.Entropy, -} - -CRITERIA_REG = { - "squared_error": _criterion.MSE, - "friedman_mse": _criterion.FriedmanMSE, - "absolute_error": _criterion.MAE, - "poisson": _criterion.Poisson, -} - -DENSE_SPLITTERS = {"best": _splitter.BestSplitter, "random": _splitter.RandomSplitter} - -SPARSE_SPLITTERS = { - "best": _splitter.BestSparseSplitter, - "random": _splitter.RandomSparseSplitter, -} class BaseDecisionTree(MultiOutputMixin, BaseEstimator, metaclass=ABCMeta): - """Base class for decision trees. - - Warning: This class should not be used directly. - Use derived classes instead. - """ - - _parameter_constraints: dict = { - "splitter": [StrOptions({"best", "random"})], - "max_depth": [Interval(Integral, 1, None, closed="left"), None], - "min_samples_split": [ - Interval(Integral, 2, None, closed="left"), - Interval(RealNotInt, 0.0, 1.0, closed="right"), - ], - "min_samples_leaf": [ - Interval(Integral, 1, None, closed="left"), - Interval(RealNotInt, 0.0, 1.0, closed="neither"), - ], - "min_weight_fraction_leaf": [Interval(Real, 0.0, 0.5, closed="both")], - "max_features": [ - Interval(Integral, 1, None, closed="left"), - Interval(RealNotInt, 0.0, 1.0, closed="right"), - StrOptions({"sqrt", "log2"}), - None, - ], - "random_state": ["random_state"], - "max_leaf_nodes": [Interval(Integral, 2, None, closed="left"), None], - "min_impurity_decrease": [Interval(Real, 0.0, None, closed="left")], - "ccp_alpha": [Interval(Real, 0.0, None, closed="left")], - } @abstractmethod def __init__( @@ -136,68 +38,20 @@ def __init__( self.ccp_alpha = ccp_alpha def get_depth(self): - """Return the depth of the decision tree. - - The depth of a tree is the maximum distance between the root - and any leaf. - - Returns - ------- - self.tree_.max_depth : int - The maximum depth of the tree. - """ - check_is_fitted(self) - return self.tree_.max_depth + raise NotImplementedError + def get_n_leaves(self): - """Return the number of leaves of the decision tree. + raise NotImplementedError - Returns - ------- - self.tree_.n_leaves : int - Number of leaves. - """ - check_is_fitted(self) - return self.tree_.n_leaves def _support_missing_values(self, X): - return not issparse(X) and self._get_tags()["allow_nan"] - - def _compute_missing_values_in_feature_mask(self, X): - """Return boolean mask denoting if there are missing values for each feature. + raise NotImplementedError - This method also ensures that X is finite. - Parameter - --------- - X : array-like of shape (n_samples, n_features), dtype=DOUBLE - Input data. - - Returns - ------- - missing_values_in_feature_mask : ndarray of shape (n_features,), or None - Missing value mask. If missing values are not supported or there - are no missing values, return None. - """ - common_kwargs = dict(estimator_name=self.__class__.__name__, input_name="X") - - if not self._support_missing_values(X): - assert_all_finite(X, **common_kwargs) - return None - - with np.errstate(over="ignore"): - overall_sum = np.sum(X) - - if not np.isfinite(overall_sum): - # Raise a ValueError in case of the presence of an infinite element. - _assert_all_finite_element_wise(X, xp=np, allow_nan=True, **common_kwargs) - - # If the sum is not nan, then there are no missing values - if not np.isnan(overall_sum): - return None + def _compute_missing_values_in_feature_mask(self, X): + raise NotImplementedError - missing_values_in_feature_mask = _any_isnan_axis0(X) - return missing_values_in_feature_mask def _fit( self, @@ -207,446 +61,38 @@ def _fit( check_input=True, missing_values_in_feature_mask=None, ): - random_state = check_random_state(self.random_state) - - if check_input: - # Need to validate separately here. - # We can't pass multi_output=True because that would allow y to be - # csr. - - # _compute_missing_values_in_feature_mask will check for finite values and - # compute the missing mask if the tree supports missing values - check_X_params = dict( - dtype=DTYPE, accept_sparse="csc", force_all_finite=False - ) - check_y_params = dict(ensure_2d=False, dtype=None) - X, y = self._validate_data( - X, y, validate_separately=(check_X_params, check_y_params) - ) - - missing_values_in_feature_mask = ( - self._compute_missing_values_in_feature_mask(X) - ) - if issparse(X): - X.sort_indices() - - if X.indices.dtype != np.intc or X.indptr.dtype != np.intc: - raise ValueError( - "No support for np.int64 index based sparse matrices" - ) - - if self.criterion == "poisson": - if np.any(y < 0): - raise ValueError( - "Some value(s) of y are negative which is" - " not allowed for Poisson regression." - ) - if np.sum(y) <= 0: - raise ValueError( - "Sum of y is not positive which is " - "necessary for Poisson regression." - ) - - # Determine output settings - n_samples, self.n_features_in_ = X.shape - is_classification = is_classifier(self) - - y = np.atleast_1d(y) - expanded_class_weight = None - - if y.ndim == 1: - # reshape is necessary to preserve the data contiguity against vs - # [:, np.newaxis] that does not. - y = np.reshape(y, (-1, 1)) - - self.n_outputs_ = y.shape[1] - - if is_classification: - check_classification_targets(y) - y = np.copy(y) - - self.classes_ = [] - self.n_classes_ = [] - - if self.class_weight is not None: - y_original = np.copy(y) - - y_encoded = np.zeros(y.shape, dtype=int) - for k in range(self.n_outputs_): - classes_k, y_encoded[:, k] = np.unique(y[:, k], return_inverse=True) - self.classes_.append(classes_k) - self.n_classes_.append(classes_k.shape[0]) - y = y_encoded - - if self.class_weight is not None: - expanded_class_weight = compute_sample_weight( - self.class_weight, y_original - ) - - self.n_classes_ = np.array(self.n_classes_, dtype=np.intp) - - if getattr(y, "dtype", None) != DOUBLE or not y.flags.contiguous: - y = np.ascontiguousarray(y, dtype=DOUBLE) - - max_depth = np.iinfo(np.int32).max if self.max_depth is None else self.max_depth - - if isinstance(self.min_samples_leaf, numbers.Integral): - min_samples_leaf = self.min_samples_leaf - else: # float - min_samples_leaf = int(ceil(self.min_samples_leaf * n_samples)) - - if isinstance(self.min_samples_split, numbers.Integral): - min_samples_split = self.min_samples_split - else: # float - min_samples_split = int(ceil(self.min_samples_split * n_samples)) - min_samples_split = max(2, min_samples_split) - - min_samples_split = max(min_samples_split, 2 * min_samples_leaf) - - if isinstance(self.max_features, str): - if self.max_features == "auto": - if is_classification: - max_features = max(1, int(np.sqrt(self.n_features_in_))) - warnings.warn( - ( - "`max_features='auto'` has been deprecated in 1.1 " - "and will be removed in 1.3. To keep the past behaviour, " - "explicitly set `max_features='sqrt'`." - ), - FutureWarning, - ) - else: - max_features = self.n_features_in_ - warnings.warn( - ( - "`max_features='auto'` has been deprecated in 1.1 " - "and will be removed in 1.3. To keep the past behaviour, " - "explicitly set `max_features=1.0'`." - ), - FutureWarning, - ) - elif self.max_features == "sqrt": - max_features = max(1, int(np.sqrt(self.n_features_in_))) - elif self.max_features == "log2": - max_features = max(1, int(np.log2(self.n_features_in_))) - elif self.max_features is None: - max_features = self.n_features_in_ - elif isinstance(self.max_features, numbers.Integral): - max_features = self.max_features - else: # float - if self.max_features > 0.0: - max_features = max(1, int(self.max_features * self.n_features_in_)) - else: - max_features = 0 - - self.max_features_ = max_features - - max_leaf_nodes = -1 if self.max_leaf_nodes is None else self.max_leaf_nodes - - if len(y) != n_samples: - raise ValueError( - "Number of labels=%d does not match number of samples=%d" - % (len(y), n_samples) - ) - - if sample_weight is not None: - sample_weight = _check_sample_weight(sample_weight, X, DOUBLE) + raise NotImplementedError - if expanded_class_weight is not None: - if sample_weight is not None: - sample_weight = sample_weight * expanded_class_weight - else: - sample_weight = expanded_class_weight - - # Set min_weight_leaf from min_weight_fraction_leaf - if sample_weight is None: - min_weight_leaf = self.min_weight_fraction_leaf * n_samples - else: - min_weight_leaf = self.min_weight_fraction_leaf * np.sum(sample_weight) - - # Build tree - criterion = self.criterion - if not isinstance(criterion, Criterion): - if is_classification: - criterion = CRITERIA_CLF[self.criterion]( - self.n_outputs_, self.n_classes_ - ) - else: - criterion = CRITERIA_REG[self.criterion](self.n_outputs_, n_samples) - else: - # Make a deepcopy in case the criterion has mutable attributes that - # might be shared and modified concurrently during parallel fitting - criterion = copy.deepcopy(criterion) - - SPLITTERS = SPARSE_SPLITTERS if issparse(X) else DENSE_SPLITTERS - - splitter = self.splitter - if not isinstance(self.splitter, Splitter): - splitter = SPLITTERS[self.splitter]( - criterion, - self.max_features_, - min_samples_leaf, - min_weight_leaf, - random_state, - ) - - if is_classifier(self): - self.tree_ = Tree(self.n_features_in_, self.n_classes_, self.n_outputs_) - else: - self.tree_ = Tree( - self.n_features_in_, - # TODO: tree shouldn't need this in this case - np.array([1] * self.n_outputs_, dtype=np.intp), - self.n_outputs_, - ) - - # Use BestFirst if max_leaf_nodes given; use DepthFirst otherwise - if max_leaf_nodes < 0: - builder = DepthFirstTreeBuilder( - splitter, - min_samples_split, - min_samples_leaf, - min_weight_leaf, - max_depth, - self.min_impurity_decrease, - ) - else: - builder = BestFirstTreeBuilder( - splitter, - min_samples_split, - min_samples_leaf, - min_weight_leaf, - max_depth, - max_leaf_nodes, - self.min_impurity_decrease, - ) - - builder.build(self.tree_, X, y, sample_weight, missing_values_in_feature_mask) - - if self.n_outputs_ == 1 and is_classifier(self): - self.n_classes_ = self.n_classes_[0] - self.classes_ = self.classes_[0] - - self._prune_tree() - - return self def _validate_X_predict(self, X, check_input): - """Validate the training data on predict (probabilities).""" - if check_input: - if self._support_missing_values(X): - force_all_finite = "allow-nan" - else: - force_all_finite = True - X = self._validate_data( - X, - dtype=DTYPE, - accept_sparse="csr", - reset=False, - force_all_finite=force_all_finite, - ) - if issparse(X) and ( - X.indices.dtype != np.intc or X.indptr.dtype != np.intc - ): - raise ValueError("No support for np.int64 index based sparse matrices") - else: - # The number of features is checked regardless of `check_input` - self._check_n_features(X, reset=False) - return X - - def predict(self, X, check_input=True): - """Predict class or regression value for X. + raise NotImplementedError - For a classification model, the predicted class for each sample in X is - returned. For a regression model, the predicted value based on X is - returned. - - Parameters - ---------- - X : {array-like, sparse matrix} of shape (n_samples, n_features) - The input samples. Internally, it will be converted to - ``dtype=np.float32`` and if a sparse matrix is provided - to a sparse ``csr_matrix``. - - check_input : bool, default=True - Allow to bypass several input checking. - Don't use this parameter unless you know what you're doing. - - Returns - ------- - y : array-like of shape (n_samples,) or (n_samples, n_outputs) - The predicted classes, or the predict values. - """ - check_is_fitted(self) - X = self._validate_X_predict(X, check_input) - proba = self.tree_.predict(X) - n_samples = X.shape[0] - - # Classification - if is_classifier(self): - if self.n_outputs_ == 1: - return self.classes_.take(np.argmax(proba, axis=1), axis=0) - - else: - class_type = self.classes_[0].dtype - predictions = np.zeros((n_samples, self.n_outputs_), dtype=class_type) - for k in range(self.n_outputs_): - predictions[:, k] = self.classes_[k].take( - np.argmax(proba[:, k], axis=1), axis=0 - ) - - return predictions - - # Regression - else: - if self.n_outputs_ == 1: - return proba[:, 0] - - else: - return proba[:, :, 0] + def predict(self, X, check_input=True): + raise NotImplementedError + def apply(self, X, check_input=True): - """Return the index of the leaf that each sample is predicted as. - - .. versionadded:: 0.17 - - Parameters - ---------- - X : {array-like, sparse matrix} of shape (n_samples, n_features) - The input samples. Internally, it will be converted to - ``dtype=np.float32`` and if a sparse matrix is provided - to a sparse ``csr_matrix``. - - check_input : bool, default=True - Allow to bypass several input checking. - Don't use this parameter unless you know what you're doing. - - Returns - ------- - X_leaves : array-like of shape (n_samples,) - For each datapoint x in X, return the index of the leaf x - ends up in. Leaves are numbered within - ``[0; self.tree_.node_count)``, possibly with gaps in the - numbering. - """ - check_is_fitted(self) - X = self._validate_X_predict(X, check_input) - return self.tree_.apply(X) + raise NotImplementedError + def decision_path(self, X, check_input=True): - """Return the decision path in the tree. - - .. versionadded:: 0.18 - - Parameters - ---------- - X : {array-like, sparse matrix} of shape (n_samples, n_features) - The input samples. Internally, it will be converted to - ``dtype=np.float32`` and if a sparse matrix is provided - to a sparse ``csr_matrix``. - - check_input : bool, default=True - Allow to bypass several input checking. - Don't use this parameter unless you know what you're doing. + raise NotImplementedError - Returns - ------- - indicator : sparse matrix of shape (n_samples, n_nodes) - Return a node indicator CSR matrix where non zero elements - indicates that the samples goes through the nodes. - """ - X = self._validate_X_predict(X, check_input) - return self.tree_.decision_path(X) def _prune_tree(self): - """Prune tree using Minimal Cost-Complexity Pruning.""" - check_is_fitted(self) + raise NotImplementedError - if self.ccp_alpha == 0.0: - return - - # build pruned tree - if is_classifier(self): - n_classes = np.atleast_1d(self.n_classes_) - pruned_tree = Tree(self.n_features_in_, n_classes, self.n_outputs_) - else: - pruned_tree = Tree( - self.n_features_in_, - # TODO: the tree shouldn't need this param - np.array([1] * self.n_outputs_, dtype=np.intp), - self.n_outputs_, - ) - _build_pruned_tree_ccp(pruned_tree, self.tree_, self.ccp_alpha) - - self.tree_ = pruned_tree def cost_complexity_pruning_path(self, X, y, sample_weight=None): - """Compute the pruning path during Minimal Cost-Complexity Pruning. - - See :ref:`minimal_cost_complexity_pruning` for details on the pruning - process. + raise NotImplementedError - Parameters - ---------- - X : {array-like, sparse matrix} of shape (n_samples, n_features) - The training input samples. Internally, it will be converted to - ``dtype=np.float32`` and if a sparse matrix is provided - to a sparse ``csc_matrix``. - - y : array-like of shape (n_samples,) or (n_samples, n_outputs) - The target values (class labels) as integers or strings. - - sample_weight : array-like of shape (n_samples,), default=None - Sample weights. If None, then samples are equally weighted. Splits - that would create child nodes with net zero or negative weight are - ignored while searching for a split in each node. Splits are also - ignored if they would result in any single class carrying a - negative weight in either child node. - - Returns - ------- - ccp_path : :class:`~sklearn.utils.Bunch` - Dictionary-like object, with the following attributes. - - ccp_alphas : ndarray - Effective alphas of subtree during pruning. - - impurities : ndarray - Sum of the impurities of the subtree leaves for the - corresponding alpha value in ``ccp_alphas``. - """ - est = clone(self).set_params(ccp_alpha=0.0) - est.fit(X, y, sample_weight=sample_weight) - return Bunch(**ccp_pruning_path(est.tree_)) @property def feature_importances_(self): - """Return the feature importances. - - The importance of a feature is computed as the (normalized) total - reduction of the criterion brought by that feature. - It is also known as the Gini importance. + raise NotImplementedError - Warning: impurity-based feature importances can be misleading for - high cardinality features (many unique values). See - :func:`sklearn.inspection.permutation_importance` as an alternative. - - Returns - ------- - feature_importances_ : ndarray of shape (n_features,) - Normalized total reduction of criteria by feature - (Gini importance). - """ - check_is_fitted(self) - - return self.tree_.compute_feature_importances() class DecisionTreeClassifier(ClassifierMixin, BaseDecisionTree): - _parameter_constraints: dict = { - **BaseDecisionTree._parameter_constraints, - "criterion": [StrOptions({"gini", "entropy", "log_loss"}), Hidden(Criterion)], - "class_weight": [dict, list, StrOptions({"balanced"}), None], - } def __init__( self, @@ -679,7 +125,6 @@ def __init__( ccp_alpha=ccp_alpha, ) - @_fit_context(prefer_skip_nested_validation=True) def fit(self, X, y, sample_weight=None, check_input=True): super()._fit( @@ -691,47 +136,12 @@ def fit(self, X, y, sample_weight=None, check_input=True): return self def predict_proba(self, X, check_input=True): - - check_is_fitted(self) - X = self._validate_X_predict(X, check_input) - proba = self.tree_.predict(X) - - if self.n_outputs_ == 1: - proba = proba[:, : self.n_classes_] - normalizer = proba.sum(axis=1)[:, np.newaxis] - normalizer[normalizer == 0.0] = 1.0 - proba /= normalizer - - return proba - - else: - all_proba = [] - - for k in range(self.n_outputs_): - proba_k = proba[:, k, : self.n_classes_[k]] - normalizer = proba_k.sum(axis=1)[:, np.newaxis] - normalizer[normalizer == 0.0] = 1.0 - proba_k /= normalizer - all_proba.append(proba_k) - - return all_proba + raise NotImplementedError def predict_log_proba(self, X): - - proba = self.predict_proba(X) - - if self.n_outputs_ == 1: - return np.log(proba) - - else: - for k in range(self.n_outputs_): - proba[k] = np.log(proba[k]) - - return proba + raise NotImplementedError def _more_tags(self): - # XXX: nan is only support for dense arrays, but we set this for common test to - # pass, specifically: check_estimators_nan_inf allow_nan = self.splitter == "best" and self.criterion in { "gini", "log_loss", From 5b8cc5ab51170127a545156c7dda1192481502c3 Mon Sep 17 00:00:00 2001 From: umairjavaid Date: Sun, 3 Sep 2023 03:33:27 +0000 Subject: [PATCH 003/117] added sklearn decision tree extracted code --- .../frontends/sklearn/_criterion copy.pyx | 1548 ++++++++++++++ .../frontends/sklearn/_criterion.pyx | 1548 ++++++++++++++ .../frontends/sklearn/_splitter copy.pyx | 1531 ++++++++++++++ .../frontends/sklearn/_splitter.pyx | 1531 ++++++++++++++ .../frontends/sklearn/_tree copy.pyx | 1833 +++++++++++++++++ ivy/functional/frontends/sklearn/_tree.pyx | 1833 +++++++++++++++++ .../frontends/sklearn/_tree_ivy copy.pyx | 1833 +++++++++++++++++ .../frontends/sklearn/_tree_ivy.pyx | 1833 +++++++++++++++++ ivy/functional/frontends/sklearn/tree copy.py | 822 ++++++++ .../frontends/sklearn/tree ivy copy.py | 840 ++++++++ ivy/functional/frontends/sklearn/tree ivy.py | 840 ++++++++ ivy/functional/frontends/sklearn/tree.py | 822 ++++++++ .../frontends/sklearn/tree/_criterion.pyx | 1548 ++++++++++++++ .../frontends/sklearn/tree/_splitter.pyx | 1531 ++++++++++++++ .../frontends/sklearn/tree/_tree.py | 822 ++++++++ .../frontends/sklearn/tree/_tree.pyx | 1833 +++++++++++++++++ .../frontends/sklearn/tree/_tree_ivy.pyx | 1833 +++++++++++++++++ .../frontends/sklearn/tree/tree ivy.py | 840 ++++++++ ivy/functional/frontends/sklearn/tree/tree.py | 822 ++++++++ ivy/functional/frontends/sklearn/tree/try.py | 5 + 20 files changed, 26048 insertions(+) create mode 100644 ivy/functional/frontends/sklearn/_criterion copy.pyx create mode 100644 ivy/functional/frontends/sklearn/_criterion.pyx create mode 100644 ivy/functional/frontends/sklearn/_splitter copy.pyx create mode 100644 ivy/functional/frontends/sklearn/_splitter.pyx create mode 100644 ivy/functional/frontends/sklearn/_tree copy.pyx create mode 100644 ivy/functional/frontends/sklearn/_tree.pyx create mode 100644 ivy/functional/frontends/sklearn/_tree_ivy copy.pyx create mode 100644 ivy/functional/frontends/sklearn/_tree_ivy.pyx create mode 100644 ivy/functional/frontends/sklearn/tree copy.py create mode 100644 ivy/functional/frontends/sklearn/tree ivy copy.py create mode 100644 ivy/functional/frontends/sklearn/tree ivy.py create mode 100644 ivy/functional/frontends/sklearn/tree.py create mode 100644 ivy/functional/frontends/sklearn/tree/_criterion.pyx create mode 100644 ivy/functional/frontends/sklearn/tree/_splitter.pyx create mode 100644 ivy/functional/frontends/sklearn/tree/_tree.py create mode 100644 ivy/functional/frontends/sklearn/tree/_tree.pyx create mode 100644 ivy/functional/frontends/sklearn/tree/_tree_ivy.pyx create mode 100644 ivy/functional/frontends/sklearn/tree/tree ivy.py create mode 100644 ivy/functional/frontends/sklearn/tree/tree.py create mode 100644 ivy/functional/frontends/sklearn/tree/try.py diff --git a/ivy/functional/frontends/sklearn/_criterion copy.pyx b/ivy/functional/frontends/sklearn/_criterion copy.pyx new file mode 100644 index 0000000000000..df2da34a5303d --- /dev/null +++ b/ivy/functional/frontends/sklearn/_criterion copy.pyx @@ -0,0 +1,1548 @@ +# Authors: Gilles Louppe +# Peter Prettenhofer +# Brian Holt +# Noel Dawe +# Satrajit Gosh +# Lars Buitinck +# Arnaud Joly +# Joel Nothman +# Fares Hedayati +# Jacob Schreiber +# Nelson Liu +# +# License: BSD 3 clause + +from libc.string cimport memcpy +from libc.string cimport memset +from libc.math cimport fabs, INFINITY + +import numpy as np +cimport numpy as cnp +cnp.import_array() + +from scipy.special.cython_special cimport xlogy + +from ._utils cimport log +from ._utils cimport WeightedMedianCalculator + +# EPSILON is used in the Poisson criterion +cdef double EPSILON = 10 * np.finfo('double').eps + +cdef class Criterion: + """Interface for impurity criteria. + + This object stores methods on how to calculate how good a split is using + different metrics. + """ + def __getstate__(self): + return {} + + def __setstate__(self, d): + pass + + cdef int init( + self, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight, + double weighted_n_samples, + const SIZE_t[:] sample_indices, + SIZE_t start, + SIZE_t end, + ) except -1 nogil: + """Placeholder for a method which will initialize the criterion. + + Returns -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + + Parameters + ---------- + y : ndarray, dtype=DOUBLE_t + y is a buffer that can store values for n_outputs target variables + stored as a Cython memoryview. + sample_weight : ndarray, dtype=DOUBLE_t + The weight of each sample stored as a Cython memoryview. + weighted_n_samples : double + The total weight of the samples being considered + sample_indices : ndarray, dtype=SIZE_t + A mask on the samples. Indices of the samples in X and y we want to use, + where sample_indices[start:end] correspond to the samples in this node. + start : SIZE_t + The first sample to be used on this node + end : SIZE_t + The last sample used on this node + + """ + pass + + cdef void init_missing(self, SIZE_t n_missing) noexcept nogil: + """Initialize sum_missing if there are missing values. + + This method assumes that caller placed the missing samples in + self.sample_indices[-n_missing:] + + Parameters + ---------- + n_missing: SIZE_t + Number of missing values for specific feature. + """ + pass + + cdef int reset(self) except -1 nogil: + """Reset the criterion at pos=start. + + This method must be implemented by the subclass. + """ + pass + + cdef int reverse_reset(self) except -1 nogil: + """Reset the criterion at pos=end. + + This method must be implemented by the subclass. + """ + pass + + cdef int update(self, SIZE_t new_pos) except -1 nogil: + """Updated statistics by moving sample_indices[pos:new_pos] to the left child. + + This updates the collected statistics by moving sample_indices[pos:new_pos] + from the right child to the left child. It must be implemented by + the subclass. + + Parameters + ---------- + new_pos : SIZE_t + New starting index position of the sample_indices in the right child + """ + pass + + cdef double node_impurity(self) noexcept nogil: + """Placeholder for calculating the impurity of the node. + + Placeholder for a method which will evaluate the impurity of + the current node, i.e. the impurity of sample_indices[start:end]. This is the + primary function of the criterion class. The smaller the impurity the + better. + """ + pass + + cdef void children_impurity(self, double* impurity_left, + double* impurity_right) noexcept nogil: + """Placeholder for calculating the impurity of children. + + Placeholder for a method which evaluates the impurity in + children nodes, i.e. the impurity of sample_indices[start:pos] + the impurity + of sample_indices[pos:end]. + + Parameters + ---------- + impurity_left : double pointer + The memory address where the impurity of the left child should be + stored. + impurity_right : double pointer + The memory address where the impurity of the right child should be + stored + """ + pass + + cdef void node_value(self, double* dest) noexcept nogil: + """Placeholder for storing the node value. + + Placeholder for a method which will compute the node value + of sample_indices[start:end] and save the value into dest. + + Parameters + ---------- + dest : double pointer + The memory address where the node value should be stored. + """ + pass + + cdef double proxy_impurity_improvement(self) noexcept nogil: + """Compute a proxy of the impurity reduction. + + This method is used to speed up the search for the best split. + It is a proxy quantity such that the split that maximizes this value + also maximizes the impurity improvement. It neglects all constant terms + of the impurity decrease for a given split. + + The absolute impurity improvement is only computed by the + impurity_improvement method once the best split has been found. + """ + cdef double impurity_left + cdef double impurity_right + self.children_impurity(&impurity_left, &impurity_right) + + return (- self.weighted_n_right * impurity_right + - self.weighted_n_left * impurity_left) + + cdef double impurity_improvement(self, double impurity_parent, + double impurity_left, + double impurity_right) noexcept nogil: + """Compute the improvement in impurity. + + This method computes the improvement in impurity when a split occurs. + The weighted impurity improvement equation is the following: + + N_t / N * (impurity - N_t_R / N_t * right_impurity + - N_t_L / N_t * left_impurity) + + where N is the total number of samples, N_t is the number of samples + at the current node, N_t_L is the number of samples in the left child, + and N_t_R is the number of samples in the right child, + + Parameters + ---------- + impurity_parent : double + The initial impurity of the parent node before the split + + impurity_left : double + The impurity of the left child + + impurity_right : double + The impurity of the right child + + Return + ------ + double : improvement in impurity after the split occurs + """ + return ((self.weighted_n_node_samples / self.weighted_n_samples) * + (impurity_parent - (self.weighted_n_right / + self.weighted_n_node_samples * impurity_right) + - (self.weighted_n_left / + self.weighted_n_node_samples * impurity_left))) + + cdef void init_sum_missing(self): + """Init sum_missing to hold sums for missing values.""" + +cdef inline void _move_sums_classification( + ClassificationCriterion criterion, + double[:, ::1] sum_1, + double[:, ::1] sum_2, + double* weighted_n_1, + double* weighted_n_2, + bint put_missing_in_1, +) noexcept nogil: + """Distribute sum_total and sum_missing into sum_1 and sum_2. + + If there are missing values and: + - put_missing_in_1 is True, then missing values to go sum_1. Specifically: + sum_1 = sum_missing + sum_2 = sum_total - sum_missing + + - put_missing_in_1 is False, then missing values go to sum_2. Specifically: + sum_1 = 0 + sum_2 = sum_total + """ + cdef SIZE_t k, c, n_bytes + if criterion.n_missing != 0 and put_missing_in_1: + for k in range(criterion.n_outputs): + n_bytes = criterion.n_classes[k] * sizeof(double) + memcpy(&sum_1[k, 0], &criterion.sum_missing[k, 0], n_bytes) + + for k in range(criterion.n_outputs): + for c in range(criterion.n_classes[k]): + sum_2[k, c] = criterion.sum_total[k, c] - criterion.sum_missing[k, c] + + weighted_n_1[0] = criterion.weighted_n_missing + weighted_n_2[0] = criterion.weighted_n_node_samples - criterion.weighted_n_missing + else: + # Assigning sum_2 = sum_total for all outputs. + for k in range(criterion.n_outputs): + n_bytes = criterion.n_classes[k] * sizeof(double) + memset(&sum_1[k, 0], 0, n_bytes) + memcpy(&sum_2[k, 0], &criterion.sum_total[k, 0], n_bytes) + + weighted_n_1[0] = 0.0 + weighted_n_2[0] = criterion.weighted_n_node_samples + + +cdef class ClassificationCriterion(Criterion): + """Abstract criterion for classification.""" + + def __cinit__(self, SIZE_t n_outputs, + cnp.ndarray[SIZE_t, ndim=1] n_classes): + """Initialize attributes for this criterion. + + Parameters + ---------- + n_outputs : SIZE_t + The number of targets, the dimensionality of the prediction + n_classes : numpy.ndarray, dtype=SIZE_t + The number of unique classes in each target + """ + self.start = 0 + self.pos = 0 + self.end = 0 + self.missing_go_to_left = 0 + + self.n_outputs = n_outputs + self.n_samples = 0 + self.n_node_samples = 0 + self.weighted_n_node_samples = 0.0 + self.weighted_n_left = 0.0 + self.weighted_n_right = 0.0 + self.weighted_n_missing = 0.0 + + self.n_classes = np.empty(n_outputs, dtype=np.intp) + + cdef SIZE_t k = 0 + cdef SIZE_t max_n_classes = 0 + + # For each target, set the number of unique classes in that target, + # and also compute the maximal stride of all targets + for k in range(n_outputs): + self.n_classes[k] = n_classes[k] + + if n_classes[k] > max_n_classes: + max_n_classes = n_classes[k] + + self.max_n_classes = max_n_classes + + # Count labels for each output + self.sum_total = np.zeros((n_outputs, max_n_classes), dtype=np.float64) + self.sum_left = np.zeros((n_outputs, max_n_classes), dtype=np.float64) + self.sum_right = np.zeros((n_outputs, max_n_classes), dtype=np.float64) + + def __reduce__(self): + return (type(self), + (self.n_outputs, np.asarray(self.n_classes)), self.__getstate__()) + + cdef int init( + self, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight, + double weighted_n_samples, + const SIZE_t[:] sample_indices, + SIZE_t start, + SIZE_t end + ) except -1 nogil: + """Initialize the criterion. + + This initializes the criterion at node sample_indices[start:end] and children + sample_indices[start:start] and sample_indices[start:end]. + + Returns -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + + Parameters + ---------- + y : ndarray, dtype=DOUBLE_t + The target stored as a buffer for memory efficiency. + sample_weight : ndarray, dtype=DOUBLE_t + The weight of each sample stored as a Cython memoryview. + weighted_n_samples : double + The total weight of all samples + sample_indices : ndarray, dtype=SIZE_t + A mask on the samples. Indices of the samples in X and y we want to use, + where sample_indices[start:end] correspond to the samples in this node. + start : SIZE_t + The first sample to use in the mask + end : SIZE_t + The last sample to use in the mask + """ + self.y = y + self.sample_weight = sample_weight + self.sample_indices = sample_indices + self.start = start + self.end = end + self.n_node_samples = end - start + self.weighted_n_samples = weighted_n_samples + self.weighted_n_node_samples = 0.0 + + cdef SIZE_t i + cdef SIZE_t p + cdef SIZE_t k + cdef SIZE_t c + cdef DOUBLE_t w = 1.0 + + for k in range(self.n_outputs): + memset(&self.sum_total[k, 0], 0, self.n_classes[k] * sizeof(double)) + + for p in range(start, end): + i = sample_indices[p] + + # w is originally set to be 1.0, meaning that if no sample weights + # are given, the default weight of each sample is 1.0. + if sample_weight is not None: + w = sample_weight[i] + + # Count weighted class frequency for each target + for k in range(self.n_outputs): + c = self.y[i, k] + self.sum_total[k, c] += w + + self.weighted_n_node_samples += w + + # Reset to pos=start + self.reset() + return 0 + + cdef void init_sum_missing(self): + """Init sum_missing to hold sums for missing values.""" + self.sum_missing = np.zeros((self.n_outputs, self.max_n_classes), dtype=np.float64) + + cdef void init_missing(self, SIZE_t n_missing) noexcept nogil: + """Initialize sum_missing if there are missing values. + + This method assumes that caller placed the missing samples in + self.sample_indices[-n_missing:] + """ + cdef SIZE_t i, p, k, c + cdef DOUBLE_t w = 1.0 + + self.n_missing = n_missing + if n_missing == 0: + return + + memset(&self.sum_missing[0, 0], 0, self.max_n_classes * self.n_outputs * sizeof(double)) + + self.weighted_n_missing = 0.0 + + # The missing samples are assumed to be in self.sample_indices[-n_missing:] + for p in range(self.end - n_missing, self.end): + i = self.sample_indices[p] + if self.sample_weight is not None: + w = self.sample_weight[i] + + for k in range(self.n_outputs): + c = self.y[i, k] + self.sum_missing[k, c] += w + + self.weighted_n_missing += w + + cdef int reset(self) except -1 nogil: + """Reset the criterion at pos=start. + + Returns -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + """ + self.pos = self.start + _move_sums_classification( + self, + self.sum_left, + self.sum_right, + &self.weighted_n_left, + &self.weighted_n_right, + self.missing_go_to_left, + ) + return 0 + + cdef int reverse_reset(self) except -1 nogil: + """Reset the criterion at pos=end. + + Returns -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + """ + self.pos = self.end + _move_sums_classification( + self, + self.sum_right, + self.sum_left, + &self.weighted_n_right, + &self.weighted_n_left, + not self.missing_go_to_left + ) + return 0 + + cdef int update(self, SIZE_t new_pos) except -1 nogil: + """Updated statistics by moving sample_indices[pos:new_pos] to the left child. + + Returns -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + + Parameters + ---------- + new_pos : SIZE_t + The new ending position for which to move sample_indices from the right + child to the left child. + """ + cdef SIZE_t pos = self.pos + # The missing samples are assumed to be in + # self.sample_indices[-self.n_missing:] that is + # self.sample_indices[end_non_missing:self.end]. + cdef SIZE_t end_non_missing = self.end - self.n_missing + + cdef const SIZE_t[:] sample_indices = self.sample_indices + cdef const DOUBLE_t[:] sample_weight = self.sample_weight + + cdef SIZE_t i + cdef SIZE_t p + cdef SIZE_t k + cdef SIZE_t c + cdef DOUBLE_t w = 1.0 + + # Update statistics up to new_pos + # + # Given that + # sum_left[x] + sum_right[x] = sum_total[x] + # and that sum_total is known, we are going to update + # sum_left from the direction that require the least amount + # of computations, i.e. from pos to new_pos or from end to new_po. + if (new_pos - pos) <= (end_non_missing - new_pos): + for p in range(pos, new_pos): + i = sample_indices[p] + + if sample_weight is not None: + w = sample_weight[i] + + for k in range(self.n_outputs): + self.sum_left[k, self.y[i, k]] += w + + self.weighted_n_left += w + + else: + self.reverse_reset() + + for p in range(end_non_missing - 1, new_pos - 1, -1): + i = sample_indices[p] + + if sample_weight is not None: + w = sample_weight[i] + + for k in range(self.n_outputs): + self.sum_left[k, self.y[i, k]] -= w + + self.weighted_n_left -= w + + # Update right part statistics + self.weighted_n_right = self.weighted_n_node_samples - self.weighted_n_left + for k in range(self.n_outputs): + for c in range(self.n_classes[k]): + self.sum_right[k, c] = self.sum_total[k, c] - self.sum_left[k, c] + + self.pos = new_pos + return 0 + + cdef double node_impurity(self) noexcept nogil: + pass + + cdef void children_impurity(self, double* impurity_left, + double* impurity_right) noexcept nogil: + pass + + cdef void node_value(self, double* dest) noexcept nogil: + """Compute the node value of sample_indices[start:end] and save it into dest. + + Parameters + ---------- + dest : double pointer + The memory address which we will save the node value into. + """ + cdef SIZE_t k + + for k in range(self.n_outputs): + memcpy(dest, &self.sum_total[k, 0], self.n_classes[k] * sizeof(double)) + dest += self.max_n_classes + + +cdef class Entropy(ClassificationCriterion): + r"""Cross Entropy impurity criterion. + + This handles cases where the target is a classification taking values + 0, 1, ... K-2, K-1. If node m represents a region Rm with Nm observations, + then let + + count_k = 1 / Nm \sum_{x_i in Rm} I(yi = k) + + be the proportion of class k observations in node m. + + The cross-entropy is then defined as + + cross-entropy = -\sum_{k=0}^{K-1} count_k log(count_k) + """ + + cdef double node_impurity(self) noexcept nogil: + """Evaluate the impurity of the current node. + + Evaluate the cross-entropy criterion as impurity of the current node, + i.e. the impurity of sample_indices[start:end]. The smaller the impurity the + better. + """ + cdef double entropy = 0.0 + cdef double count_k + cdef SIZE_t k + cdef SIZE_t c + + for k in range(self.n_outputs): + for c in range(self.n_classes[k]): + count_k = self.sum_total[k, c] + if count_k > 0.0: + count_k /= self.weighted_n_node_samples + entropy -= count_k * log(count_k) + + return entropy / self.n_outputs + + cdef void children_impurity(self, double* impurity_left, + double* impurity_right) noexcept nogil: + """Evaluate the impurity in children nodes. + + i.e. the impurity of the left child (sample_indices[start:pos]) and the + impurity the right child (sample_indices[pos:end]). + + Parameters + ---------- + impurity_left : double pointer + The memory address to save the impurity of the left node + impurity_right : double pointer + The memory address to save the impurity of the right node + """ + cdef double entropy_left = 0.0 + cdef double entropy_right = 0.0 + cdef double count_k + cdef SIZE_t k + cdef SIZE_t c + + for k in range(self.n_outputs): + for c in range(self.n_classes[k]): + count_k = self.sum_left[k, c] + if count_k > 0.0: + count_k /= self.weighted_n_left + entropy_left -= count_k * log(count_k) + + count_k = self.sum_right[k, c] + if count_k > 0.0: + count_k /= self.weighted_n_right + entropy_right -= count_k * log(count_k) + + impurity_left[0] = entropy_left / self.n_outputs + impurity_right[0] = entropy_right / self.n_outputs + + +cdef class Gini(ClassificationCriterion): + r"""Gini Index impurity criterion. + + This handles cases where the target is a classification taking values + 0, 1, ... K-2, K-1. If node m represents a region Rm with Nm observations, + then let + + count_k = 1/ Nm \sum_{x_i in Rm} I(yi = k) + + be the proportion of class k observations in node m. + + The Gini Index is then defined as: + + index = \sum_{k=0}^{K-1} count_k (1 - count_k) + = 1 - \sum_{k=0}^{K-1} count_k ** 2 + """ + + cdef double node_impurity(self) noexcept nogil: + """Evaluate the impurity of the current node. + + Evaluate the Gini criterion as impurity of the current node, + i.e. the impurity of sample_indices[start:end]. The smaller the impurity the + better. + """ + cdef double gini = 0.0 + cdef double sq_count + cdef double count_k + cdef SIZE_t k + cdef SIZE_t c + + for k in range(self.n_outputs): + sq_count = 0.0 + + for c in range(self.n_classes[k]): + count_k = self.sum_total[k, c] + sq_count += count_k * count_k + + gini += 1.0 - sq_count / (self.weighted_n_node_samples * + self.weighted_n_node_samples) + + return gini / self.n_outputs + + cdef void children_impurity(self, double* impurity_left, + double* impurity_right) noexcept nogil: + """Evaluate the impurity in children nodes. + + i.e. the impurity of the left child (sample_indices[start:pos]) and the + impurity the right child (sample_indices[pos:end]) using the Gini index. + + Parameters + ---------- + impurity_left : double pointer + The memory address to save the impurity of the left node to + impurity_right : double pointer + The memory address to save the impurity of the right node to + """ + cdef double gini_left = 0.0 + cdef double gini_right = 0.0 + cdef double sq_count_left + cdef double sq_count_right + cdef double count_k + cdef SIZE_t k + cdef SIZE_t c + + for k in range(self.n_outputs): + sq_count_left = 0.0 + sq_count_right = 0.0 + + for c in range(self.n_classes[k]): + count_k = self.sum_left[k, c] + sq_count_left += count_k * count_k + + count_k = self.sum_right[k, c] + sq_count_right += count_k * count_k + + gini_left += 1.0 - sq_count_left / (self.weighted_n_left * + self.weighted_n_left) + + gini_right += 1.0 - sq_count_right / (self.weighted_n_right * + self.weighted_n_right) + + impurity_left[0] = gini_left / self.n_outputs + impurity_right[0] = gini_right / self.n_outputs + + +cdef inline void _move_sums_regression( + RegressionCriterion criterion, + double[::1] sum_1, + double[::1] sum_2, + double* weighted_n_1, + double* weighted_n_2, + bint put_missing_in_1, +) noexcept nogil: + """Distribute sum_total and sum_missing into sum_1 and sum_2. + + If there are missing values and: + - put_missing_in_1 is True, then missing values to go sum_1. Specifically: + sum_1 = sum_missing + sum_2 = sum_total - sum_missing + + - put_missing_in_1 is False, then missing values go to sum_2. Specifically: + sum_1 = 0 + sum_2 = sum_total + """ + cdef: + SIZE_t i + SIZE_t n_bytes = criterion.n_outputs * sizeof(double) + bint has_missing = criterion.n_missing != 0 + + if has_missing and put_missing_in_1: + memcpy(&sum_1[0], &criterion.sum_missing[0], n_bytes) + for i in range(criterion.n_outputs): + sum_2[i] = criterion.sum_total[i] - criterion.sum_missing[i] + weighted_n_1[0] = criterion.weighted_n_missing + weighted_n_2[0] = criterion.weighted_n_node_samples - criterion.weighted_n_missing + else: + memset(&sum_1[0], 0, n_bytes) + # Assigning sum_2 = sum_total for all outputs. + memcpy(&sum_2[0], &criterion.sum_total[0], n_bytes) + weighted_n_1[0] = 0.0 + weighted_n_2[0] = criterion.weighted_n_node_samples + + +cdef class RegressionCriterion(Criterion): + r"""Abstract regression criterion. + + This handles cases where the target is a continuous value, and is + evaluated by computing the variance of the target values left and right + of the split point. The computation takes linear time with `n_samples` + by using :: + + var = \sum_i^n (y_i - y_bar) ** 2 + = (\sum_i^n y_i ** 2) - n_samples * y_bar ** 2 + """ + + def __cinit__(self, SIZE_t n_outputs, SIZE_t n_samples): + """Initialize parameters for this criterion. + + Parameters + ---------- + n_outputs : SIZE_t + The number of targets to be predicted + + n_samples : SIZE_t + The total number of samples to fit on + """ + # Default values + self.start = 0 + self.pos = 0 + self.end = 0 + + self.n_outputs = n_outputs + self.n_samples = n_samples + self.n_node_samples = 0 + self.weighted_n_node_samples = 0.0 + self.weighted_n_left = 0.0 + self.weighted_n_right = 0.0 + self.weighted_n_missing = 0.0 + + self.sq_sum_total = 0.0 + + self.sum_total = np.zeros(n_outputs, dtype=np.float64) + self.sum_left = np.zeros(n_outputs, dtype=np.float64) + self.sum_right = np.zeros(n_outputs, dtype=np.float64) + + def __reduce__(self): + return (type(self), (self.n_outputs, self.n_samples), self.__getstate__()) + + cdef int init( + self, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight, + double weighted_n_samples, + const SIZE_t[:] sample_indices, + SIZE_t start, + SIZE_t end, + ) except -1 nogil: + """Initialize the criterion. + + This initializes the criterion at node sample_indices[start:end] and children + sample_indices[start:start] and sample_indices[start:end]. + """ + # Initialize fields + self.y = y + self.sample_weight = sample_weight + self.sample_indices = sample_indices + self.start = start + self.end = end + self.n_node_samples = end - start + self.weighted_n_samples = weighted_n_samples + self.weighted_n_node_samples = 0. + + cdef SIZE_t i + cdef SIZE_t p + cdef SIZE_t k + cdef DOUBLE_t y_ik + cdef DOUBLE_t w_y_ik + cdef DOUBLE_t w = 1.0 + self.sq_sum_total = 0.0 + memset(&self.sum_total[0], 0, self.n_outputs * sizeof(double)) + + for p in range(start, end): + i = sample_indices[p] + + if sample_weight is not None: + w = sample_weight[i] + + for k in range(self.n_outputs): + y_ik = self.y[i, k] + w_y_ik = w * y_ik + self.sum_total[k] += w_y_ik + self.sq_sum_total += w_y_ik * y_ik + + self.weighted_n_node_samples += w + + # Reset to pos=start + self.reset() + return 0 + + cdef void init_sum_missing(self): + """Init sum_missing to hold sums for missing values.""" + self.sum_missing = np.zeros(self.n_outputs, dtype=np.float64) + + cdef void init_missing(self, SIZE_t n_missing) noexcept nogil: + """Initialize sum_missing if there are missing values. + + This method assumes that caller placed the missing samples in + self.sample_indices[-n_missing:] + """ + cdef SIZE_t i, p, k + cdef DOUBLE_t y_ik + cdef DOUBLE_t w_y_ik + cdef DOUBLE_t w = 1.0 + + self.n_missing = n_missing + if n_missing == 0: + return + + memset(&self.sum_missing[0], 0, self.n_outputs * sizeof(double)) + + self.weighted_n_missing = 0.0 + + # The missing samples are assumed to be in self.sample_indices[-n_missing:] + for p in range(self.end - n_missing, self.end): + i = self.sample_indices[p] + if self.sample_weight is not None: + w = self.sample_weight[i] + + for k in range(self.n_outputs): + y_ik = self.y[i, k] + w_y_ik = w * y_ik + self.sum_missing[k] += w_y_ik + + self.weighted_n_missing += w + + cdef int reset(self) except -1 nogil: + """Reset the criterion at pos=start.""" + self.pos = self.start + _move_sums_regression( + self, + self.sum_left, + self.sum_right, + &self.weighted_n_left, + &self.weighted_n_right, + self.missing_go_to_left + ) + return 0 + + cdef int reverse_reset(self) except -1 nogil: + """Reset the criterion at pos=end.""" + self.pos = self.end + _move_sums_regression( + self, + self.sum_right, + self.sum_left, + &self.weighted_n_right, + &self.weighted_n_left, + not self.missing_go_to_left + ) + return 0 + + cdef int update(self, SIZE_t new_pos) except -1 nogil: + """Updated statistics by moving sample_indices[pos:new_pos] to the left.""" + cdef const DOUBLE_t[:] sample_weight = self.sample_weight + cdef const SIZE_t[:] sample_indices = self.sample_indices + + cdef SIZE_t pos = self.pos + + # The missing samples are assumed to be in + # self.sample_indices[-self.n_missing:] that is + # self.sample_indices[end_non_missing:self.end]. + cdef SIZE_t end_non_missing = self.end - self.n_missing + cdef SIZE_t i + cdef SIZE_t p + cdef SIZE_t k + cdef DOUBLE_t w = 1.0 + + # Update statistics up to new_pos + # + # Given that + # sum_left[x] + sum_right[x] = sum_total[x] + # and that sum_total is known, we are going to update + # sum_left from the direction that require the least amount + # of computations, i.e. from pos to new_pos or from end to new_pos. + if (new_pos - pos) <= (end_non_missing - new_pos): + for p in range(pos, new_pos): + i = sample_indices[p] + + if sample_weight is not None: + w = sample_weight[i] + + for k in range(self.n_outputs): + self.sum_left[k] += w * self.y[i, k] + + self.weighted_n_left += w + else: + self.reverse_reset() + + for p in range(end_non_missing - 1, new_pos - 1, -1): + i = sample_indices[p] + + if sample_weight is not None: + w = sample_weight[i] + + for k in range(self.n_outputs): + self.sum_left[k] -= w * self.y[i, k] + + self.weighted_n_left -= w + + self.weighted_n_right = (self.weighted_n_node_samples - + self.weighted_n_left) + for k in range(self.n_outputs): + self.sum_right[k] = self.sum_total[k] - self.sum_left[k] + + self.pos = new_pos + return 0 + + cdef double node_impurity(self) noexcept nogil: + pass + + cdef void children_impurity(self, double* impurity_left, + double* impurity_right) noexcept nogil: + pass + + cdef void node_value(self, double* dest) noexcept nogil: + """Compute the node value of sample_indices[start:end] into dest.""" + cdef SIZE_t k + + for k in range(self.n_outputs): + dest[k] = self.sum_total[k] / self.weighted_n_node_samples + + +cdef class MSE(RegressionCriterion): + """Mean squared error impurity criterion. + + MSE = var_left + var_right + """ + + cdef double node_impurity(self) noexcept nogil: + """Evaluate the impurity of the current node. + + Evaluate the MSE criterion as impurity of the current node, + i.e. the impurity of sample_indices[start:end]. The smaller the impurity the + better. + """ + cdef double impurity + cdef SIZE_t k + + impurity = self.sq_sum_total / self.weighted_n_node_samples + for k in range(self.n_outputs): + impurity -= (self.sum_total[k] / self.weighted_n_node_samples)**2.0 + + return impurity / self.n_outputs + + cdef double proxy_impurity_improvement(self) noexcept nogil: + """Compute a proxy of the impurity reduction. + + This method is used to speed up the search for the best split. + It is a proxy quantity such that the split that maximizes this value + also maximizes the impurity improvement. It neglects all constant terms + of the impurity decrease for a given split. + + The absolute impurity improvement is only computed by the + impurity_improvement method once the best split has been found. + + The MSE proxy is derived from + + sum_{i left}(y_i - y_pred_L)^2 + sum_{i right}(y_i - y_pred_R)^2 + = sum(y_i^2) - n_L * mean_{i left}(y_i)^2 - n_R * mean_{i right}(y_i)^2 + + Neglecting constant terms, this gives: + + - 1/n_L * sum_{i left}(y_i)^2 - 1/n_R * sum_{i right}(y_i)^2 + """ + cdef SIZE_t k + cdef double proxy_impurity_left = 0.0 + cdef double proxy_impurity_right = 0.0 + + for k in range(self.n_outputs): + proxy_impurity_left += self.sum_left[k] * self.sum_left[k] + proxy_impurity_right += self.sum_right[k] * self.sum_right[k] + + return (proxy_impurity_left / self.weighted_n_left + + proxy_impurity_right / self.weighted_n_right) + + cdef void children_impurity(self, double* impurity_left, + double* impurity_right) noexcept nogil: + """Evaluate the impurity in children nodes. + + i.e. the impurity of the left child (sample_indices[start:pos]) and the + impurity the right child (sample_indices[pos:end]). + """ + cdef const DOUBLE_t[:] sample_weight = self.sample_weight + cdef const SIZE_t[:] sample_indices = self.sample_indices + cdef SIZE_t pos = self.pos + cdef SIZE_t start = self.start + + cdef DOUBLE_t y_ik + + cdef double sq_sum_left = 0.0 + cdef double sq_sum_right + + cdef SIZE_t i + cdef SIZE_t p + cdef SIZE_t k + cdef DOUBLE_t w = 1.0 + + for p in range(start, pos): + i = sample_indices[p] + + if sample_weight is not None: + w = sample_weight[i] + + for k in range(self.n_outputs): + y_ik = self.y[i, k] + sq_sum_left += w * y_ik * y_ik + + sq_sum_right = self.sq_sum_total - sq_sum_left + + impurity_left[0] = sq_sum_left / self.weighted_n_left + impurity_right[0] = sq_sum_right / self.weighted_n_right + + for k in range(self.n_outputs): + impurity_left[0] -= (self.sum_left[k] / self.weighted_n_left) ** 2.0 + impurity_right[0] -= (self.sum_right[k] / self.weighted_n_right) ** 2.0 + + impurity_left[0] /= self.n_outputs + impurity_right[0] /= self.n_outputs + + +cdef class MAE(RegressionCriterion): + r"""Mean absolute error impurity criterion. + + MAE = (1 / n)*(\sum_i |y_i - f_i|), where y_i is the true + value and f_i is the predicted value.""" + + cdef cnp.ndarray left_child + cdef cnp.ndarray right_child + cdef void** left_child_ptr + cdef void** right_child_ptr + cdef DOUBLE_t[::1] node_medians + + def __cinit__(self, SIZE_t n_outputs, SIZE_t n_samples): + """Initialize parameters for this criterion. + + Parameters + ---------- + n_outputs : SIZE_t + The number of targets to be predicted + + n_samples : SIZE_t + The total number of samples to fit on + """ + # Default values + self.start = 0 + self.pos = 0 + self.end = 0 + + self.n_outputs = n_outputs + self.n_samples = n_samples + self.n_node_samples = 0 + self.weighted_n_node_samples = 0.0 + self.weighted_n_left = 0.0 + self.weighted_n_right = 0.0 + + self.node_medians = np.zeros(n_outputs, dtype=np.float64) + + self.left_child = np.empty(n_outputs, dtype='object') + self.right_child = np.empty(n_outputs, dtype='object') + # initialize WeightedMedianCalculators + for k in range(n_outputs): + self.left_child[k] = WeightedMedianCalculator(n_samples) + self.right_child[k] = WeightedMedianCalculator(n_samples) + + self.left_child_ptr = cnp.PyArray_DATA(self.left_child) + self.right_child_ptr = cnp.PyArray_DATA(self.right_child) + + cdef int init( + self, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight, + double weighted_n_samples, + const SIZE_t[:] sample_indices, + SIZE_t start, + SIZE_t end, + ) except -1 nogil: + """Initialize the criterion. + + This initializes the criterion at node sample_indices[start:end] and children + sample_indices[start:start] and sample_indices[start:end]. + """ + cdef SIZE_t i, p, k + cdef DOUBLE_t w = 1.0 + + # Initialize fields + self.y = y + self.sample_weight = sample_weight + self.sample_indices = sample_indices + self.start = start + self.end = end + self.n_node_samples = end - start + self.weighted_n_samples = weighted_n_samples + self.weighted_n_node_samples = 0. + + cdef void** left_child = self.left_child_ptr + cdef void** right_child = self.right_child_ptr + + for k in range(self.n_outputs): + ( left_child[k]).reset() + ( right_child[k]).reset() + + for p in range(start, end): + i = sample_indices[p] + + if sample_weight is not None: + w = sample_weight[i] + + for k in range(self.n_outputs): + # push method ends up calling safe_realloc, hence `except -1` + # push all values to the right side, + # since pos = start initially anyway + ( right_child[k]).push(self.y[i, k], w) + + self.weighted_n_node_samples += w + # calculate the node medians + for k in range(self.n_outputs): + self.node_medians[k] = ( right_child[k]).get_median() + + # Reset to pos=start + self.reset() + return 0 + + cdef void init_missing(self, SIZE_t n_missing) noexcept nogil: + """Raise error if n_missing != 0.""" + if n_missing == 0: + return + with gil: + raise ValueError("missing values is not supported for MAE.") + + cdef int reset(self) except -1 nogil: + """Reset the criterion at pos=start. + + Returns -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + """ + cdef SIZE_t i, k + cdef DOUBLE_t value + cdef DOUBLE_t weight + + cdef void** left_child = self.left_child_ptr + cdef void** right_child = self.right_child_ptr + + self.weighted_n_left = 0.0 + self.weighted_n_right = self.weighted_n_node_samples + self.pos = self.start + + # reset the WeightedMedianCalculators, left should have no + # elements and right should have all elements. + + for k in range(self.n_outputs): + # if left has no elements, it's already reset + for i in range(( left_child[k]).size()): + # remove everything from left and put it into right + ( left_child[k]).pop(&value, + &weight) + # push method ends up calling safe_realloc, hence `except -1` + ( right_child[k]).push(value, + weight) + return 0 + + cdef int reverse_reset(self) except -1 nogil: + """Reset the criterion at pos=end. + + Returns -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + """ + self.weighted_n_right = 0.0 + self.weighted_n_left = self.weighted_n_node_samples + self.pos = self.end + + cdef DOUBLE_t value + cdef DOUBLE_t weight + cdef void** left_child = self.left_child_ptr + cdef void** right_child = self.right_child_ptr + + # reverse reset the WeightedMedianCalculators, right should have no + # elements and left should have all elements. + for k in range(self.n_outputs): + # if right has no elements, it's already reset + for i in range(( right_child[k]).size()): + # remove everything from right and put it into left + ( right_child[k]).pop(&value, + &weight) + # push method ends up calling safe_realloc, hence `except -1` + ( left_child[k]).push(value, + weight) + return 0 + + cdef int update(self, SIZE_t new_pos) except -1 nogil: + """Updated statistics by moving sample_indices[pos:new_pos] to the left. + + Returns -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + """ + cdef const DOUBLE_t[:] sample_weight = self.sample_weight + cdef const SIZE_t[:] sample_indices = self.sample_indices + + cdef void** left_child = self.left_child_ptr + cdef void** right_child = self.right_child_ptr + + cdef SIZE_t pos = self.pos + cdef SIZE_t end = self.end + cdef SIZE_t i, p, k + cdef DOUBLE_t w = 1.0 + + # Update statistics up to new_pos + # + # We are going to update right_child and left_child + # from the direction that require the least amount of + # computations, i.e. from pos to new_pos or from end to new_pos. + if (new_pos - pos) <= (end - new_pos): + for p in range(pos, new_pos): + i = sample_indices[p] + + if sample_weight is not None: + w = sample_weight[i] + + for k in range(self.n_outputs): + # remove y_ik and its weight w from right and add to left + ( right_child[k]).remove(self.y[i, k], w) + # push method ends up calling safe_realloc, hence except -1 + ( left_child[k]).push(self.y[i, k], w) + + self.weighted_n_left += w + else: + self.reverse_reset() + + for p in range(end - 1, new_pos - 1, -1): + i = sample_indices[p] + + if sample_weight is not None: + w = sample_weight[i] + + for k in range(self.n_outputs): + # remove y_ik and its weight w from left and add to right + ( left_child[k]).remove(self.y[i, k], w) + ( right_child[k]).push(self.y[i, k], w) + + self.weighted_n_left -= w + + self.weighted_n_right = (self.weighted_n_node_samples - + self.weighted_n_left) + self.pos = new_pos + return 0 + + cdef void node_value(self, double* dest) noexcept nogil: + """Computes the node value of sample_indices[start:end] into dest.""" + cdef SIZE_t k + for k in range(self.n_outputs): + dest[k] = self.node_medians[k] + + cdef double node_impurity(self) noexcept nogil: + """Evaluate the impurity of the current node. + + Evaluate the MAE criterion as impurity of the current node, + i.e. the impurity of sample_indices[start:end]. The smaller the impurity the + better. + """ + cdef const DOUBLE_t[:] sample_weight = self.sample_weight + cdef const SIZE_t[:] sample_indices = self.sample_indices + cdef SIZE_t i, p, k + cdef DOUBLE_t w = 1.0 + cdef DOUBLE_t impurity = 0.0 + + for k in range(self.n_outputs): + for p in range(self.start, self.end): + i = sample_indices[p] + + if sample_weight is not None: + w = sample_weight[i] + + impurity += fabs(self.y[i, k] - self.node_medians[k]) * w + + return impurity / (self.weighted_n_node_samples * self.n_outputs) + + cdef void children_impurity(self, double* p_impurity_left, + double* p_impurity_right) noexcept nogil: + """Evaluate the impurity in children nodes. + + i.e. the impurity of the left child (sample_indices[start:pos]) and the + impurity the right child (sample_indices[pos:end]). + """ + cdef const DOUBLE_t[:] sample_weight = self.sample_weight + cdef const SIZE_t[:] sample_indices = self.sample_indices + + cdef SIZE_t start = self.start + cdef SIZE_t pos = self.pos + cdef SIZE_t end = self.end + + cdef SIZE_t i, p, k + cdef DOUBLE_t median + cdef DOUBLE_t w = 1.0 + cdef DOUBLE_t impurity_left = 0.0 + cdef DOUBLE_t impurity_right = 0.0 + + cdef void** left_child = self.left_child_ptr + cdef void** right_child = self.right_child_ptr + + for k in range(self.n_outputs): + median = ( left_child[k]).get_median() + for p in range(start, pos): + i = sample_indices[p] + + if sample_weight is not None: + w = sample_weight[i] + + impurity_left += fabs(self.y[i, k] - median) * w + p_impurity_left[0] = impurity_left / (self.weighted_n_left * + self.n_outputs) + + for k in range(self.n_outputs): + median = ( right_child[k]).get_median() + for p in range(pos, end): + i = sample_indices[p] + + if sample_weight is not None: + w = sample_weight[i] + + impurity_right += fabs(self.y[i, k] - median) * w + p_impurity_right[0] = impurity_right / (self.weighted_n_right * + self.n_outputs) + + +cdef class FriedmanMSE(MSE): + """Mean squared error impurity criterion with improvement score by Friedman. + + Uses the formula (35) in Friedman's original Gradient Boosting paper: + + diff = mean_left - mean_right + improvement = n_left * n_right * diff^2 / (n_left + n_right) + """ + + cdef double proxy_impurity_improvement(self) noexcept nogil: + """Compute a proxy of the impurity reduction. + + This method is used to speed up the search for the best split. + It is a proxy quantity such that the split that maximizes this value + also maximizes the impurity improvement. It neglects all constant terms + of the impurity decrease for a given split. + + The absolute impurity improvement is only computed by the + impurity_improvement method once the best split has been found. + """ + cdef double total_sum_left = 0.0 + cdef double total_sum_right = 0.0 + + cdef SIZE_t k + cdef double diff = 0.0 + + for k in range(self.n_outputs): + total_sum_left += self.sum_left[k] + total_sum_right += self.sum_right[k] + + diff = (self.weighted_n_right * total_sum_left - + self.weighted_n_left * total_sum_right) + + return diff * diff / (self.weighted_n_left * self.weighted_n_right) + + cdef double impurity_improvement(self, double impurity_parent, double + impurity_left, double impurity_right) noexcept nogil: + # Note: none of the arguments are used here + cdef double total_sum_left = 0.0 + cdef double total_sum_right = 0.0 + + cdef SIZE_t k + cdef double diff = 0.0 + + for k in range(self.n_outputs): + total_sum_left += self.sum_left[k] + total_sum_right += self.sum_right[k] + + diff = (self.weighted_n_right * total_sum_left - + self.weighted_n_left * total_sum_right) / self.n_outputs + + return (diff * diff / (self.weighted_n_left * self.weighted_n_right * + self.weighted_n_node_samples)) + + +cdef class Poisson(RegressionCriterion): + """Half Poisson deviance as impurity criterion. + + Poisson deviance = 2/n * sum(y_true * log(y_true/y_pred) + y_pred - y_true) + + Note that the deviance is >= 0, and since we have `y_pred = mean(y_true)` + at the leaves, one always has `sum(y_pred - y_true) = 0`. It remains the + implemented impurity (factor 2 is skipped): + 1/n * sum(y_true * log(y_true/y_pred) + """ + # FIXME in 1.0: + # min_impurity_split with default = 0 forces us to use a non-negative + # impurity like the Poisson deviance. Without this restriction, one could + # throw away the 'constant' term sum(y_true * log(y_true)) and just use + # Poisson loss = - 1/n * sum(y_true * log(y_pred)) + # = - 1/n * sum(y_true * log(mean(y_true)) + # = - mean(y_true) * log(mean(y_true)) + # With this trick (used in proxy_impurity_improvement()), as for MSE, + # children_impurity would only need to go over left xor right split, not + # both. This could be faster. + + cdef double node_impurity(self) noexcept nogil: + """Evaluate the impurity of the current node. + + Evaluate the Poisson criterion as impurity of the current node, + i.e. the impurity of sample_indices[start:end]. The smaller the impurity the + better. + """ + return self.poisson_loss(self.start, self.end, self.sum_total, + self.weighted_n_node_samples) + + cdef double proxy_impurity_improvement(self) noexcept nogil: + """Compute a proxy of the impurity reduction. + + This method is used to speed up the search for the best split. + It is a proxy quantity such that the split that maximizes this value + also maximizes the impurity improvement. It neglects all constant terms + of the impurity decrease for a given split. + + The absolute impurity improvement is only computed by the + impurity_improvement method once the best split has been found. + + The Poisson proxy is derived from: + + sum_{i left }(y_i * log(y_i / y_pred_L)) + + sum_{i right}(y_i * log(y_i / y_pred_R)) + = sum(y_i * log(y_i) - n_L * mean_{i left}(y_i) * log(mean_{i left}(y_i)) + - n_R * mean_{i right}(y_i) * log(mean_{i right}(y_i)) + + Neglecting constant terms, this gives + + - sum{i left }(y_i) * log(mean{i left}(y_i)) + - sum{i right}(y_i) * log(mean{i right}(y_i)) + """ + cdef SIZE_t k + cdef double proxy_impurity_left = 0.0 + cdef double proxy_impurity_right = 0.0 + cdef double y_mean_left = 0. + cdef double y_mean_right = 0. + + for k in range(self.n_outputs): + if (self.sum_left[k] <= EPSILON) or (self.sum_right[k] <= EPSILON): + # Poisson loss does not allow non-positive predictions. We + # therefore forbid splits that have child nodes with + # sum(y_i) <= 0. + # Since sum_right = sum_total - sum_left, it can lead to + # floating point rounding error and will not give zero. Thus, + # we relax the above comparison to sum(y_i) <= EPSILON. + return -INFINITY + else: + y_mean_left = self.sum_left[k] / self.weighted_n_left + y_mean_right = self.sum_right[k] / self.weighted_n_right + proxy_impurity_left -= self.sum_left[k] * log(y_mean_left) + proxy_impurity_right -= self.sum_right[k] * log(y_mean_right) + + return - proxy_impurity_left - proxy_impurity_right + + cdef void children_impurity(self, double* impurity_left, + double* impurity_right) noexcept nogil: + """Evaluate the impurity in children nodes. + + i.e. the impurity of the left child (sample_indices[start:pos]) and the + impurity of the right child (sample_indices[pos:end]) for Poisson. + """ + cdef SIZE_t start = self.start + cdef SIZE_t pos = self.pos + cdef SIZE_t end = self.end + + impurity_left[0] = self.poisson_loss(start, pos, self.sum_left, + self.weighted_n_left) + + impurity_right[0] = self.poisson_loss(pos, end, self.sum_right, + self.weighted_n_right) + + cdef inline DOUBLE_t poisson_loss(self, + SIZE_t start, + SIZE_t end, + const double[::1] y_sum, + DOUBLE_t weight_sum) noexcept nogil: + """Helper function to compute Poisson loss (~deviance) of a given node. + """ + cdef const DOUBLE_t[:, ::1] y = self.y + cdef const DOUBLE_t[:] sample_weight = self.sample_weight + cdef const SIZE_t[:] sample_indices = self.sample_indices + + cdef DOUBLE_t y_mean = 0. + cdef DOUBLE_t poisson_loss = 0. + cdef DOUBLE_t w = 1.0 + cdef SIZE_t i, k, p + cdef SIZE_t n_outputs = self.n_outputs + + for k in range(n_outputs): + if y_sum[k] <= EPSILON: + # y_sum could be computed from the subtraction + # sum_right = sum_total - sum_left leading to a potential + # floating point rounding error. + # Thus, we relax the comparison y_sum <= 0 to + # y_sum <= EPSILON. + return INFINITY + + y_mean = y_sum[k] / weight_sum + + for p in range(start, end): + i = sample_indices[p] + + if sample_weight is not None: + w = sample_weight[i] + + poisson_loss += w * xlogy(y[i, k], y[i, k] / y_mean) + return poisson_loss / (weight_sum * n_outputs) \ No newline at end of file diff --git a/ivy/functional/frontends/sklearn/_criterion.pyx b/ivy/functional/frontends/sklearn/_criterion.pyx new file mode 100644 index 0000000000000..df2da34a5303d --- /dev/null +++ b/ivy/functional/frontends/sklearn/_criterion.pyx @@ -0,0 +1,1548 @@ +# Authors: Gilles Louppe +# Peter Prettenhofer +# Brian Holt +# Noel Dawe +# Satrajit Gosh +# Lars Buitinck +# Arnaud Joly +# Joel Nothman +# Fares Hedayati +# Jacob Schreiber +# Nelson Liu +# +# License: BSD 3 clause + +from libc.string cimport memcpy +from libc.string cimport memset +from libc.math cimport fabs, INFINITY + +import numpy as np +cimport numpy as cnp +cnp.import_array() + +from scipy.special.cython_special cimport xlogy + +from ._utils cimport log +from ._utils cimport WeightedMedianCalculator + +# EPSILON is used in the Poisson criterion +cdef double EPSILON = 10 * np.finfo('double').eps + +cdef class Criterion: + """Interface for impurity criteria. + + This object stores methods on how to calculate how good a split is using + different metrics. + """ + def __getstate__(self): + return {} + + def __setstate__(self, d): + pass + + cdef int init( + self, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight, + double weighted_n_samples, + const SIZE_t[:] sample_indices, + SIZE_t start, + SIZE_t end, + ) except -1 nogil: + """Placeholder for a method which will initialize the criterion. + + Returns -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + + Parameters + ---------- + y : ndarray, dtype=DOUBLE_t + y is a buffer that can store values for n_outputs target variables + stored as a Cython memoryview. + sample_weight : ndarray, dtype=DOUBLE_t + The weight of each sample stored as a Cython memoryview. + weighted_n_samples : double + The total weight of the samples being considered + sample_indices : ndarray, dtype=SIZE_t + A mask on the samples. Indices of the samples in X and y we want to use, + where sample_indices[start:end] correspond to the samples in this node. + start : SIZE_t + The first sample to be used on this node + end : SIZE_t + The last sample used on this node + + """ + pass + + cdef void init_missing(self, SIZE_t n_missing) noexcept nogil: + """Initialize sum_missing if there are missing values. + + This method assumes that caller placed the missing samples in + self.sample_indices[-n_missing:] + + Parameters + ---------- + n_missing: SIZE_t + Number of missing values for specific feature. + """ + pass + + cdef int reset(self) except -1 nogil: + """Reset the criterion at pos=start. + + This method must be implemented by the subclass. + """ + pass + + cdef int reverse_reset(self) except -1 nogil: + """Reset the criterion at pos=end. + + This method must be implemented by the subclass. + """ + pass + + cdef int update(self, SIZE_t new_pos) except -1 nogil: + """Updated statistics by moving sample_indices[pos:new_pos] to the left child. + + This updates the collected statistics by moving sample_indices[pos:new_pos] + from the right child to the left child. It must be implemented by + the subclass. + + Parameters + ---------- + new_pos : SIZE_t + New starting index position of the sample_indices in the right child + """ + pass + + cdef double node_impurity(self) noexcept nogil: + """Placeholder for calculating the impurity of the node. + + Placeholder for a method which will evaluate the impurity of + the current node, i.e. the impurity of sample_indices[start:end]. This is the + primary function of the criterion class. The smaller the impurity the + better. + """ + pass + + cdef void children_impurity(self, double* impurity_left, + double* impurity_right) noexcept nogil: + """Placeholder for calculating the impurity of children. + + Placeholder for a method which evaluates the impurity in + children nodes, i.e. the impurity of sample_indices[start:pos] + the impurity + of sample_indices[pos:end]. + + Parameters + ---------- + impurity_left : double pointer + The memory address where the impurity of the left child should be + stored. + impurity_right : double pointer + The memory address where the impurity of the right child should be + stored + """ + pass + + cdef void node_value(self, double* dest) noexcept nogil: + """Placeholder for storing the node value. + + Placeholder for a method which will compute the node value + of sample_indices[start:end] and save the value into dest. + + Parameters + ---------- + dest : double pointer + The memory address where the node value should be stored. + """ + pass + + cdef double proxy_impurity_improvement(self) noexcept nogil: + """Compute a proxy of the impurity reduction. + + This method is used to speed up the search for the best split. + It is a proxy quantity such that the split that maximizes this value + also maximizes the impurity improvement. It neglects all constant terms + of the impurity decrease for a given split. + + The absolute impurity improvement is only computed by the + impurity_improvement method once the best split has been found. + """ + cdef double impurity_left + cdef double impurity_right + self.children_impurity(&impurity_left, &impurity_right) + + return (- self.weighted_n_right * impurity_right + - self.weighted_n_left * impurity_left) + + cdef double impurity_improvement(self, double impurity_parent, + double impurity_left, + double impurity_right) noexcept nogil: + """Compute the improvement in impurity. + + This method computes the improvement in impurity when a split occurs. + The weighted impurity improvement equation is the following: + + N_t / N * (impurity - N_t_R / N_t * right_impurity + - N_t_L / N_t * left_impurity) + + where N is the total number of samples, N_t is the number of samples + at the current node, N_t_L is the number of samples in the left child, + and N_t_R is the number of samples in the right child, + + Parameters + ---------- + impurity_parent : double + The initial impurity of the parent node before the split + + impurity_left : double + The impurity of the left child + + impurity_right : double + The impurity of the right child + + Return + ------ + double : improvement in impurity after the split occurs + """ + return ((self.weighted_n_node_samples / self.weighted_n_samples) * + (impurity_parent - (self.weighted_n_right / + self.weighted_n_node_samples * impurity_right) + - (self.weighted_n_left / + self.weighted_n_node_samples * impurity_left))) + + cdef void init_sum_missing(self): + """Init sum_missing to hold sums for missing values.""" + +cdef inline void _move_sums_classification( + ClassificationCriterion criterion, + double[:, ::1] sum_1, + double[:, ::1] sum_2, + double* weighted_n_1, + double* weighted_n_2, + bint put_missing_in_1, +) noexcept nogil: + """Distribute sum_total and sum_missing into sum_1 and sum_2. + + If there are missing values and: + - put_missing_in_1 is True, then missing values to go sum_1. Specifically: + sum_1 = sum_missing + sum_2 = sum_total - sum_missing + + - put_missing_in_1 is False, then missing values go to sum_2. Specifically: + sum_1 = 0 + sum_2 = sum_total + """ + cdef SIZE_t k, c, n_bytes + if criterion.n_missing != 0 and put_missing_in_1: + for k in range(criterion.n_outputs): + n_bytes = criterion.n_classes[k] * sizeof(double) + memcpy(&sum_1[k, 0], &criterion.sum_missing[k, 0], n_bytes) + + for k in range(criterion.n_outputs): + for c in range(criterion.n_classes[k]): + sum_2[k, c] = criterion.sum_total[k, c] - criterion.sum_missing[k, c] + + weighted_n_1[0] = criterion.weighted_n_missing + weighted_n_2[0] = criterion.weighted_n_node_samples - criterion.weighted_n_missing + else: + # Assigning sum_2 = sum_total for all outputs. + for k in range(criterion.n_outputs): + n_bytes = criterion.n_classes[k] * sizeof(double) + memset(&sum_1[k, 0], 0, n_bytes) + memcpy(&sum_2[k, 0], &criterion.sum_total[k, 0], n_bytes) + + weighted_n_1[0] = 0.0 + weighted_n_2[0] = criterion.weighted_n_node_samples + + +cdef class ClassificationCriterion(Criterion): + """Abstract criterion for classification.""" + + def __cinit__(self, SIZE_t n_outputs, + cnp.ndarray[SIZE_t, ndim=1] n_classes): + """Initialize attributes for this criterion. + + Parameters + ---------- + n_outputs : SIZE_t + The number of targets, the dimensionality of the prediction + n_classes : numpy.ndarray, dtype=SIZE_t + The number of unique classes in each target + """ + self.start = 0 + self.pos = 0 + self.end = 0 + self.missing_go_to_left = 0 + + self.n_outputs = n_outputs + self.n_samples = 0 + self.n_node_samples = 0 + self.weighted_n_node_samples = 0.0 + self.weighted_n_left = 0.0 + self.weighted_n_right = 0.0 + self.weighted_n_missing = 0.0 + + self.n_classes = np.empty(n_outputs, dtype=np.intp) + + cdef SIZE_t k = 0 + cdef SIZE_t max_n_classes = 0 + + # For each target, set the number of unique classes in that target, + # and also compute the maximal stride of all targets + for k in range(n_outputs): + self.n_classes[k] = n_classes[k] + + if n_classes[k] > max_n_classes: + max_n_classes = n_classes[k] + + self.max_n_classes = max_n_classes + + # Count labels for each output + self.sum_total = np.zeros((n_outputs, max_n_classes), dtype=np.float64) + self.sum_left = np.zeros((n_outputs, max_n_classes), dtype=np.float64) + self.sum_right = np.zeros((n_outputs, max_n_classes), dtype=np.float64) + + def __reduce__(self): + return (type(self), + (self.n_outputs, np.asarray(self.n_classes)), self.__getstate__()) + + cdef int init( + self, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight, + double weighted_n_samples, + const SIZE_t[:] sample_indices, + SIZE_t start, + SIZE_t end + ) except -1 nogil: + """Initialize the criterion. + + This initializes the criterion at node sample_indices[start:end] and children + sample_indices[start:start] and sample_indices[start:end]. + + Returns -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + + Parameters + ---------- + y : ndarray, dtype=DOUBLE_t + The target stored as a buffer for memory efficiency. + sample_weight : ndarray, dtype=DOUBLE_t + The weight of each sample stored as a Cython memoryview. + weighted_n_samples : double + The total weight of all samples + sample_indices : ndarray, dtype=SIZE_t + A mask on the samples. Indices of the samples in X and y we want to use, + where sample_indices[start:end] correspond to the samples in this node. + start : SIZE_t + The first sample to use in the mask + end : SIZE_t + The last sample to use in the mask + """ + self.y = y + self.sample_weight = sample_weight + self.sample_indices = sample_indices + self.start = start + self.end = end + self.n_node_samples = end - start + self.weighted_n_samples = weighted_n_samples + self.weighted_n_node_samples = 0.0 + + cdef SIZE_t i + cdef SIZE_t p + cdef SIZE_t k + cdef SIZE_t c + cdef DOUBLE_t w = 1.0 + + for k in range(self.n_outputs): + memset(&self.sum_total[k, 0], 0, self.n_classes[k] * sizeof(double)) + + for p in range(start, end): + i = sample_indices[p] + + # w is originally set to be 1.0, meaning that if no sample weights + # are given, the default weight of each sample is 1.0. + if sample_weight is not None: + w = sample_weight[i] + + # Count weighted class frequency for each target + for k in range(self.n_outputs): + c = self.y[i, k] + self.sum_total[k, c] += w + + self.weighted_n_node_samples += w + + # Reset to pos=start + self.reset() + return 0 + + cdef void init_sum_missing(self): + """Init sum_missing to hold sums for missing values.""" + self.sum_missing = np.zeros((self.n_outputs, self.max_n_classes), dtype=np.float64) + + cdef void init_missing(self, SIZE_t n_missing) noexcept nogil: + """Initialize sum_missing if there are missing values. + + This method assumes that caller placed the missing samples in + self.sample_indices[-n_missing:] + """ + cdef SIZE_t i, p, k, c + cdef DOUBLE_t w = 1.0 + + self.n_missing = n_missing + if n_missing == 0: + return + + memset(&self.sum_missing[0, 0], 0, self.max_n_classes * self.n_outputs * sizeof(double)) + + self.weighted_n_missing = 0.0 + + # The missing samples are assumed to be in self.sample_indices[-n_missing:] + for p in range(self.end - n_missing, self.end): + i = self.sample_indices[p] + if self.sample_weight is not None: + w = self.sample_weight[i] + + for k in range(self.n_outputs): + c = self.y[i, k] + self.sum_missing[k, c] += w + + self.weighted_n_missing += w + + cdef int reset(self) except -1 nogil: + """Reset the criterion at pos=start. + + Returns -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + """ + self.pos = self.start + _move_sums_classification( + self, + self.sum_left, + self.sum_right, + &self.weighted_n_left, + &self.weighted_n_right, + self.missing_go_to_left, + ) + return 0 + + cdef int reverse_reset(self) except -1 nogil: + """Reset the criterion at pos=end. + + Returns -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + """ + self.pos = self.end + _move_sums_classification( + self, + self.sum_right, + self.sum_left, + &self.weighted_n_right, + &self.weighted_n_left, + not self.missing_go_to_left + ) + return 0 + + cdef int update(self, SIZE_t new_pos) except -1 nogil: + """Updated statistics by moving sample_indices[pos:new_pos] to the left child. + + Returns -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + + Parameters + ---------- + new_pos : SIZE_t + The new ending position for which to move sample_indices from the right + child to the left child. + """ + cdef SIZE_t pos = self.pos + # The missing samples are assumed to be in + # self.sample_indices[-self.n_missing:] that is + # self.sample_indices[end_non_missing:self.end]. + cdef SIZE_t end_non_missing = self.end - self.n_missing + + cdef const SIZE_t[:] sample_indices = self.sample_indices + cdef const DOUBLE_t[:] sample_weight = self.sample_weight + + cdef SIZE_t i + cdef SIZE_t p + cdef SIZE_t k + cdef SIZE_t c + cdef DOUBLE_t w = 1.0 + + # Update statistics up to new_pos + # + # Given that + # sum_left[x] + sum_right[x] = sum_total[x] + # and that sum_total is known, we are going to update + # sum_left from the direction that require the least amount + # of computations, i.e. from pos to new_pos or from end to new_po. + if (new_pos - pos) <= (end_non_missing - new_pos): + for p in range(pos, new_pos): + i = sample_indices[p] + + if sample_weight is not None: + w = sample_weight[i] + + for k in range(self.n_outputs): + self.sum_left[k, self.y[i, k]] += w + + self.weighted_n_left += w + + else: + self.reverse_reset() + + for p in range(end_non_missing - 1, new_pos - 1, -1): + i = sample_indices[p] + + if sample_weight is not None: + w = sample_weight[i] + + for k in range(self.n_outputs): + self.sum_left[k, self.y[i, k]] -= w + + self.weighted_n_left -= w + + # Update right part statistics + self.weighted_n_right = self.weighted_n_node_samples - self.weighted_n_left + for k in range(self.n_outputs): + for c in range(self.n_classes[k]): + self.sum_right[k, c] = self.sum_total[k, c] - self.sum_left[k, c] + + self.pos = new_pos + return 0 + + cdef double node_impurity(self) noexcept nogil: + pass + + cdef void children_impurity(self, double* impurity_left, + double* impurity_right) noexcept nogil: + pass + + cdef void node_value(self, double* dest) noexcept nogil: + """Compute the node value of sample_indices[start:end] and save it into dest. + + Parameters + ---------- + dest : double pointer + The memory address which we will save the node value into. + """ + cdef SIZE_t k + + for k in range(self.n_outputs): + memcpy(dest, &self.sum_total[k, 0], self.n_classes[k] * sizeof(double)) + dest += self.max_n_classes + + +cdef class Entropy(ClassificationCriterion): + r"""Cross Entropy impurity criterion. + + This handles cases where the target is a classification taking values + 0, 1, ... K-2, K-1. If node m represents a region Rm with Nm observations, + then let + + count_k = 1 / Nm \sum_{x_i in Rm} I(yi = k) + + be the proportion of class k observations in node m. + + The cross-entropy is then defined as + + cross-entropy = -\sum_{k=0}^{K-1} count_k log(count_k) + """ + + cdef double node_impurity(self) noexcept nogil: + """Evaluate the impurity of the current node. + + Evaluate the cross-entropy criterion as impurity of the current node, + i.e. the impurity of sample_indices[start:end]. The smaller the impurity the + better. + """ + cdef double entropy = 0.0 + cdef double count_k + cdef SIZE_t k + cdef SIZE_t c + + for k in range(self.n_outputs): + for c in range(self.n_classes[k]): + count_k = self.sum_total[k, c] + if count_k > 0.0: + count_k /= self.weighted_n_node_samples + entropy -= count_k * log(count_k) + + return entropy / self.n_outputs + + cdef void children_impurity(self, double* impurity_left, + double* impurity_right) noexcept nogil: + """Evaluate the impurity in children nodes. + + i.e. the impurity of the left child (sample_indices[start:pos]) and the + impurity the right child (sample_indices[pos:end]). + + Parameters + ---------- + impurity_left : double pointer + The memory address to save the impurity of the left node + impurity_right : double pointer + The memory address to save the impurity of the right node + """ + cdef double entropy_left = 0.0 + cdef double entropy_right = 0.0 + cdef double count_k + cdef SIZE_t k + cdef SIZE_t c + + for k in range(self.n_outputs): + for c in range(self.n_classes[k]): + count_k = self.sum_left[k, c] + if count_k > 0.0: + count_k /= self.weighted_n_left + entropy_left -= count_k * log(count_k) + + count_k = self.sum_right[k, c] + if count_k > 0.0: + count_k /= self.weighted_n_right + entropy_right -= count_k * log(count_k) + + impurity_left[0] = entropy_left / self.n_outputs + impurity_right[0] = entropy_right / self.n_outputs + + +cdef class Gini(ClassificationCriterion): + r"""Gini Index impurity criterion. + + This handles cases where the target is a classification taking values + 0, 1, ... K-2, K-1. If node m represents a region Rm with Nm observations, + then let + + count_k = 1/ Nm \sum_{x_i in Rm} I(yi = k) + + be the proportion of class k observations in node m. + + The Gini Index is then defined as: + + index = \sum_{k=0}^{K-1} count_k (1 - count_k) + = 1 - \sum_{k=0}^{K-1} count_k ** 2 + """ + + cdef double node_impurity(self) noexcept nogil: + """Evaluate the impurity of the current node. + + Evaluate the Gini criterion as impurity of the current node, + i.e. the impurity of sample_indices[start:end]. The smaller the impurity the + better. + """ + cdef double gini = 0.0 + cdef double sq_count + cdef double count_k + cdef SIZE_t k + cdef SIZE_t c + + for k in range(self.n_outputs): + sq_count = 0.0 + + for c in range(self.n_classes[k]): + count_k = self.sum_total[k, c] + sq_count += count_k * count_k + + gini += 1.0 - sq_count / (self.weighted_n_node_samples * + self.weighted_n_node_samples) + + return gini / self.n_outputs + + cdef void children_impurity(self, double* impurity_left, + double* impurity_right) noexcept nogil: + """Evaluate the impurity in children nodes. + + i.e. the impurity of the left child (sample_indices[start:pos]) and the + impurity the right child (sample_indices[pos:end]) using the Gini index. + + Parameters + ---------- + impurity_left : double pointer + The memory address to save the impurity of the left node to + impurity_right : double pointer + The memory address to save the impurity of the right node to + """ + cdef double gini_left = 0.0 + cdef double gini_right = 0.0 + cdef double sq_count_left + cdef double sq_count_right + cdef double count_k + cdef SIZE_t k + cdef SIZE_t c + + for k in range(self.n_outputs): + sq_count_left = 0.0 + sq_count_right = 0.0 + + for c in range(self.n_classes[k]): + count_k = self.sum_left[k, c] + sq_count_left += count_k * count_k + + count_k = self.sum_right[k, c] + sq_count_right += count_k * count_k + + gini_left += 1.0 - sq_count_left / (self.weighted_n_left * + self.weighted_n_left) + + gini_right += 1.0 - sq_count_right / (self.weighted_n_right * + self.weighted_n_right) + + impurity_left[0] = gini_left / self.n_outputs + impurity_right[0] = gini_right / self.n_outputs + + +cdef inline void _move_sums_regression( + RegressionCriterion criterion, + double[::1] sum_1, + double[::1] sum_2, + double* weighted_n_1, + double* weighted_n_2, + bint put_missing_in_1, +) noexcept nogil: + """Distribute sum_total and sum_missing into sum_1 and sum_2. + + If there are missing values and: + - put_missing_in_1 is True, then missing values to go sum_1. Specifically: + sum_1 = sum_missing + sum_2 = sum_total - sum_missing + + - put_missing_in_1 is False, then missing values go to sum_2. Specifically: + sum_1 = 0 + sum_2 = sum_total + """ + cdef: + SIZE_t i + SIZE_t n_bytes = criterion.n_outputs * sizeof(double) + bint has_missing = criterion.n_missing != 0 + + if has_missing and put_missing_in_1: + memcpy(&sum_1[0], &criterion.sum_missing[0], n_bytes) + for i in range(criterion.n_outputs): + sum_2[i] = criterion.sum_total[i] - criterion.sum_missing[i] + weighted_n_1[0] = criterion.weighted_n_missing + weighted_n_2[0] = criterion.weighted_n_node_samples - criterion.weighted_n_missing + else: + memset(&sum_1[0], 0, n_bytes) + # Assigning sum_2 = sum_total for all outputs. + memcpy(&sum_2[0], &criterion.sum_total[0], n_bytes) + weighted_n_1[0] = 0.0 + weighted_n_2[0] = criterion.weighted_n_node_samples + + +cdef class RegressionCriterion(Criterion): + r"""Abstract regression criterion. + + This handles cases where the target is a continuous value, and is + evaluated by computing the variance of the target values left and right + of the split point. The computation takes linear time with `n_samples` + by using :: + + var = \sum_i^n (y_i - y_bar) ** 2 + = (\sum_i^n y_i ** 2) - n_samples * y_bar ** 2 + """ + + def __cinit__(self, SIZE_t n_outputs, SIZE_t n_samples): + """Initialize parameters for this criterion. + + Parameters + ---------- + n_outputs : SIZE_t + The number of targets to be predicted + + n_samples : SIZE_t + The total number of samples to fit on + """ + # Default values + self.start = 0 + self.pos = 0 + self.end = 0 + + self.n_outputs = n_outputs + self.n_samples = n_samples + self.n_node_samples = 0 + self.weighted_n_node_samples = 0.0 + self.weighted_n_left = 0.0 + self.weighted_n_right = 0.0 + self.weighted_n_missing = 0.0 + + self.sq_sum_total = 0.0 + + self.sum_total = np.zeros(n_outputs, dtype=np.float64) + self.sum_left = np.zeros(n_outputs, dtype=np.float64) + self.sum_right = np.zeros(n_outputs, dtype=np.float64) + + def __reduce__(self): + return (type(self), (self.n_outputs, self.n_samples), self.__getstate__()) + + cdef int init( + self, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight, + double weighted_n_samples, + const SIZE_t[:] sample_indices, + SIZE_t start, + SIZE_t end, + ) except -1 nogil: + """Initialize the criterion. + + This initializes the criterion at node sample_indices[start:end] and children + sample_indices[start:start] and sample_indices[start:end]. + """ + # Initialize fields + self.y = y + self.sample_weight = sample_weight + self.sample_indices = sample_indices + self.start = start + self.end = end + self.n_node_samples = end - start + self.weighted_n_samples = weighted_n_samples + self.weighted_n_node_samples = 0. + + cdef SIZE_t i + cdef SIZE_t p + cdef SIZE_t k + cdef DOUBLE_t y_ik + cdef DOUBLE_t w_y_ik + cdef DOUBLE_t w = 1.0 + self.sq_sum_total = 0.0 + memset(&self.sum_total[0], 0, self.n_outputs * sizeof(double)) + + for p in range(start, end): + i = sample_indices[p] + + if sample_weight is not None: + w = sample_weight[i] + + for k in range(self.n_outputs): + y_ik = self.y[i, k] + w_y_ik = w * y_ik + self.sum_total[k] += w_y_ik + self.sq_sum_total += w_y_ik * y_ik + + self.weighted_n_node_samples += w + + # Reset to pos=start + self.reset() + return 0 + + cdef void init_sum_missing(self): + """Init sum_missing to hold sums for missing values.""" + self.sum_missing = np.zeros(self.n_outputs, dtype=np.float64) + + cdef void init_missing(self, SIZE_t n_missing) noexcept nogil: + """Initialize sum_missing if there are missing values. + + This method assumes that caller placed the missing samples in + self.sample_indices[-n_missing:] + """ + cdef SIZE_t i, p, k + cdef DOUBLE_t y_ik + cdef DOUBLE_t w_y_ik + cdef DOUBLE_t w = 1.0 + + self.n_missing = n_missing + if n_missing == 0: + return + + memset(&self.sum_missing[0], 0, self.n_outputs * sizeof(double)) + + self.weighted_n_missing = 0.0 + + # The missing samples are assumed to be in self.sample_indices[-n_missing:] + for p in range(self.end - n_missing, self.end): + i = self.sample_indices[p] + if self.sample_weight is not None: + w = self.sample_weight[i] + + for k in range(self.n_outputs): + y_ik = self.y[i, k] + w_y_ik = w * y_ik + self.sum_missing[k] += w_y_ik + + self.weighted_n_missing += w + + cdef int reset(self) except -1 nogil: + """Reset the criterion at pos=start.""" + self.pos = self.start + _move_sums_regression( + self, + self.sum_left, + self.sum_right, + &self.weighted_n_left, + &self.weighted_n_right, + self.missing_go_to_left + ) + return 0 + + cdef int reverse_reset(self) except -1 nogil: + """Reset the criterion at pos=end.""" + self.pos = self.end + _move_sums_regression( + self, + self.sum_right, + self.sum_left, + &self.weighted_n_right, + &self.weighted_n_left, + not self.missing_go_to_left + ) + return 0 + + cdef int update(self, SIZE_t new_pos) except -1 nogil: + """Updated statistics by moving sample_indices[pos:new_pos] to the left.""" + cdef const DOUBLE_t[:] sample_weight = self.sample_weight + cdef const SIZE_t[:] sample_indices = self.sample_indices + + cdef SIZE_t pos = self.pos + + # The missing samples are assumed to be in + # self.sample_indices[-self.n_missing:] that is + # self.sample_indices[end_non_missing:self.end]. + cdef SIZE_t end_non_missing = self.end - self.n_missing + cdef SIZE_t i + cdef SIZE_t p + cdef SIZE_t k + cdef DOUBLE_t w = 1.0 + + # Update statistics up to new_pos + # + # Given that + # sum_left[x] + sum_right[x] = sum_total[x] + # and that sum_total is known, we are going to update + # sum_left from the direction that require the least amount + # of computations, i.e. from pos to new_pos or from end to new_pos. + if (new_pos - pos) <= (end_non_missing - new_pos): + for p in range(pos, new_pos): + i = sample_indices[p] + + if sample_weight is not None: + w = sample_weight[i] + + for k in range(self.n_outputs): + self.sum_left[k] += w * self.y[i, k] + + self.weighted_n_left += w + else: + self.reverse_reset() + + for p in range(end_non_missing - 1, new_pos - 1, -1): + i = sample_indices[p] + + if sample_weight is not None: + w = sample_weight[i] + + for k in range(self.n_outputs): + self.sum_left[k] -= w * self.y[i, k] + + self.weighted_n_left -= w + + self.weighted_n_right = (self.weighted_n_node_samples - + self.weighted_n_left) + for k in range(self.n_outputs): + self.sum_right[k] = self.sum_total[k] - self.sum_left[k] + + self.pos = new_pos + return 0 + + cdef double node_impurity(self) noexcept nogil: + pass + + cdef void children_impurity(self, double* impurity_left, + double* impurity_right) noexcept nogil: + pass + + cdef void node_value(self, double* dest) noexcept nogil: + """Compute the node value of sample_indices[start:end] into dest.""" + cdef SIZE_t k + + for k in range(self.n_outputs): + dest[k] = self.sum_total[k] / self.weighted_n_node_samples + + +cdef class MSE(RegressionCriterion): + """Mean squared error impurity criterion. + + MSE = var_left + var_right + """ + + cdef double node_impurity(self) noexcept nogil: + """Evaluate the impurity of the current node. + + Evaluate the MSE criterion as impurity of the current node, + i.e. the impurity of sample_indices[start:end]. The smaller the impurity the + better. + """ + cdef double impurity + cdef SIZE_t k + + impurity = self.sq_sum_total / self.weighted_n_node_samples + for k in range(self.n_outputs): + impurity -= (self.sum_total[k] / self.weighted_n_node_samples)**2.0 + + return impurity / self.n_outputs + + cdef double proxy_impurity_improvement(self) noexcept nogil: + """Compute a proxy of the impurity reduction. + + This method is used to speed up the search for the best split. + It is a proxy quantity such that the split that maximizes this value + also maximizes the impurity improvement. It neglects all constant terms + of the impurity decrease for a given split. + + The absolute impurity improvement is only computed by the + impurity_improvement method once the best split has been found. + + The MSE proxy is derived from + + sum_{i left}(y_i - y_pred_L)^2 + sum_{i right}(y_i - y_pred_R)^2 + = sum(y_i^2) - n_L * mean_{i left}(y_i)^2 - n_R * mean_{i right}(y_i)^2 + + Neglecting constant terms, this gives: + + - 1/n_L * sum_{i left}(y_i)^2 - 1/n_R * sum_{i right}(y_i)^2 + """ + cdef SIZE_t k + cdef double proxy_impurity_left = 0.0 + cdef double proxy_impurity_right = 0.0 + + for k in range(self.n_outputs): + proxy_impurity_left += self.sum_left[k] * self.sum_left[k] + proxy_impurity_right += self.sum_right[k] * self.sum_right[k] + + return (proxy_impurity_left / self.weighted_n_left + + proxy_impurity_right / self.weighted_n_right) + + cdef void children_impurity(self, double* impurity_left, + double* impurity_right) noexcept nogil: + """Evaluate the impurity in children nodes. + + i.e. the impurity of the left child (sample_indices[start:pos]) and the + impurity the right child (sample_indices[pos:end]). + """ + cdef const DOUBLE_t[:] sample_weight = self.sample_weight + cdef const SIZE_t[:] sample_indices = self.sample_indices + cdef SIZE_t pos = self.pos + cdef SIZE_t start = self.start + + cdef DOUBLE_t y_ik + + cdef double sq_sum_left = 0.0 + cdef double sq_sum_right + + cdef SIZE_t i + cdef SIZE_t p + cdef SIZE_t k + cdef DOUBLE_t w = 1.0 + + for p in range(start, pos): + i = sample_indices[p] + + if sample_weight is not None: + w = sample_weight[i] + + for k in range(self.n_outputs): + y_ik = self.y[i, k] + sq_sum_left += w * y_ik * y_ik + + sq_sum_right = self.sq_sum_total - sq_sum_left + + impurity_left[0] = sq_sum_left / self.weighted_n_left + impurity_right[0] = sq_sum_right / self.weighted_n_right + + for k in range(self.n_outputs): + impurity_left[0] -= (self.sum_left[k] / self.weighted_n_left) ** 2.0 + impurity_right[0] -= (self.sum_right[k] / self.weighted_n_right) ** 2.0 + + impurity_left[0] /= self.n_outputs + impurity_right[0] /= self.n_outputs + + +cdef class MAE(RegressionCriterion): + r"""Mean absolute error impurity criterion. + + MAE = (1 / n)*(\sum_i |y_i - f_i|), where y_i is the true + value and f_i is the predicted value.""" + + cdef cnp.ndarray left_child + cdef cnp.ndarray right_child + cdef void** left_child_ptr + cdef void** right_child_ptr + cdef DOUBLE_t[::1] node_medians + + def __cinit__(self, SIZE_t n_outputs, SIZE_t n_samples): + """Initialize parameters for this criterion. + + Parameters + ---------- + n_outputs : SIZE_t + The number of targets to be predicted + + n_samples : SIZE_t + The total number of samples to fit on + """ + # Default values + self.start = 0 + self.pos = 0 + self.end = 0 + + self.n_outputs = n_outputs + self.n_samples = n_samples + self.n_node_samples = 0 + self.weighted_n_node_samples = 0.0 + self.weighted_n_left = 0.0 + self.weighted_n_right = 0.0 + + self.node_medians = np.zeros(n_outputs, dtype=np.float64) + + self.left_child = np.empty(n_outputs, dtype='object') + self.right_child = np.empty(n_outputs, dtype='object') + # initialize WeightedMedianCalculators + for k in range(n_outputs): + self.left_child[k] = WeightedMedianCalculator(n_samples) + self.right_child[k] = WeightedMedianCalculator(n_samples) + + self.left_child_ptr = cnp.PyArray_DATA(self.left_child) + self.right_child_ptr = cnp.PyArray_DATA(self.right_child) + + cdef int init( + self, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight, + double weighted_n_samples, + const SIZE_t[:] sample_indices, + SIZE_t start, + SIZE_t end, + ) except -1 nogil: + """Initialize the criterion. + + This initializes the criterion at node sample_indices[start:end] and children + sample_indices[start:start] and sample_indices[start:end]. + """ + cdef SIZE_t i, p, k + cdef DOUBLE_t w = 1.0 + + # Initialize fields + self.y = y + self.sample_weight = sample_weight + self.sample_indices = sample_indices + self.start = start + self.end = end + self.n_node_samples = end - start + self.weighted_n_samples = weighted_n_samples + self.weighted_n_node_samples = 0. + + cdef void** left_child = self.left_child_ptr + cdef void** right_child = self.right_child_ptr + + for k in range(self.n_outputs): + ( left_child[k]).reset() + ( right_child[k]).reset() + + for p in range(start, end): + i = sample_indices[p] + + if sample_weight is not None: + w = sample_weight[i] + + for k in range(self.n_outputs): + # push method ends up calling safe_realloc, hence `except -1` + # push all values to the right side, + # since pos = start initially anyway + ( right_child[k]).push(self.y[i, k], w) + + self.weighted_n_node_samples += w + # calculate the node medians + for k in range(self.n_outputs): + self.node_medians[k] = ( right_child[k]).get_median() + + # Reset to pos=start + self.reset() + return 0 + + cdef void init_missing(self, SIZE_t n_missing) noexcept nogil: + """Raise error if n_missing != 0.""" + if n_missing == 0: + return + with gil: + raise ValueError("missing values is not supported for MAE.") + + cdef int reset(self) except -1 nogil: + """Reset the criterion at pos=start. + + Returns -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + """ + cdef SIZE_t i, k + cdef DOUBLE_t value + cdef DOUBLE_t weight + + cdef void** left_child = self.left_child_ptr + cdef void** right_child = self.right_child_ptr + + self.weighted_n_left = 0.0 + self.weighted_n_right = self.weighted_n_node_samples + self.pos = self.start + + # reset the WeightedMedianCalculators, left should have no + # elements and right should have all elements. + + for k in range(self.n_outputs): + # if left has no elements, it's already reset + for i in range(( left_child[k]).size()): + # remove everything from left and put it into right + ( left_child[k]).pop(&value, + &weight) + # push method ends up calling safe_realloc, hence `except -1` + ( right_child[k]).push(value, + weight) + return 0 + + cdef int reverse_reset(self) except -1 nogil: + """Reset the criterion at pos=end. + + Returns -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + """ + self.weighted_n_right = 0.0 + self.weighted_n_left = self.weighted_n_node_samples + self.pos = self.end + + cdef DOUBLE_t value + cdef DOUBLE_t weight + cdef void** left_child = self.left_child_ptr + cdef void** right_child = self.right_child_ptr + + # reverse reset the WeightedMedianCalculators, right should have no + # elements and left should have all elements. + for k in range(self.n_outputs): + # if right has no elements, it's already reset + for i in range(( right_child[k]).size()): + # remove everything from right and put it into left + ( right_child[k]).pop(&value, + &weight) + # push method ends up calling safe_realloc, hence `except -1` + ( left_child[k]).push(value, + weight) + return 0 + + cdef int update(self, SIZE_t new_pos) except -1 nogil: + """Updated statistics by moving sample_indices[pos:new_pos] to the left. + + Returns -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + """ + cdef const DOUBLE_t[:] sample_weight = self.sample_weight + cdef const SIZE_t[:] sample_indices = self.sample_indices + + cdef void** left_child = self.left_child_ptr + cdef void** right_child = self.right_child_ptr + + cdef SIZE_t pos = self.pos + cdef SIZE_t end = self.end + cdef SIZE_t i, p, k + cdef DOUBLE_t w = 1.0 + + # Update statistics up to new_pos + # + # We are going to update right_child and left_child + # from the direction that require the least amount of + # computations, i.e. from pos to new_pos or from end to new_pos. + if (new_pos - pos) <= (end - new_pos): + for p in range(pos, new_pos): + i = sample_indices[p] + + if sample_weight is not None: + w = sample_weight[i] + + for k in range(self.n_outputs): + # remove y_ik and its weight w from right and add to left + ( right_child[k]).remove(self.y[i, k], w) + # push method ends up calling safe_realloc, hence except -1 + ( left_child[k]).push(self.y[i, k], w) + + self.weighted_n_left += w + else: + self.reverse_reset() + + for p in range(end - 1, new_pos - 1, -1): + i = sample_indices[p] + + if sample_weight is not None: + w = sample_weight[i] + + for k in range(self.n_outputs): + # remove y_ik and its weight w from left and add to right + ( left_child[k]).remove(self.y[i, k], w) + ( right_child[k]).push(self.y[i, k], w) + + self.weighted_n_left -= w + + self.weighted_n_right = (self.weighted_n_node_samples - + self.weighted_n_left) + self.pos = new_pos + return 0 + + cdef void node_value(self, double* dest) noexcept nogil: + """Computes the node value of sample_indices[start:end] into dest.""" + cdef SIZE_t k + for k in range(self.n_outputs): + dest[k] = self.node_medians[k] + + cdef double node_impurity(self) noexcept nogil: + """Evaluate the impurity of the current node. + + Evaluate the MAE criterion as impurity of the current node, + i.e. the impurity of sample_indices[start:end]. The smaller the impurity the + better. + """ + cdef const DOUBLE_t[:] sample_weight = self.sample_weight + cdef const SIZE_t[:] sample_indices = self.sample_indices + cdef SIZE_t i, p, k + cdef DOUBLE_t w = 1.0 + cdef DOUBLE_t impurity = 0.0 + + for k in range(self.n_outputs): + for p in range(self.start, self.end): + i = sample_indices[p] + + if sample_weight is not None: + w = sample_weight[i] + + impurity += fabs(self.y[i, k] - self.node_medians[k]) * w + + return impurity / (self.weighted_n_node_samples * self.n_outputs) + + cdef void children_impurity(self, double* p_impurity_left, + double* p_impurity_right) noexcept nogil: + """Evaluate the impurity in children nodes. + + i.e. the impurity of the left child (sample_indices[start:pos]) and the + impurity the right child (sample_indices[pos:end]). + """ + cdef const DOUBLE_t[:] sample_weight = self.sample_weight + cdef const SIZE_t[:] sample_indices = self.sample_indices + + cdef SIZE_t start = self.start + cdef SIZE_t pos = self.pos + cdef SIZE_t end = self.end + + cdef SIZE_t i, p, k + cdef DOUBLE_t median + cdef DOUBLE_t w = 1.0 + cdef DOUBLE_t impurity_left = 0.0 + cdef DOUBLE_t impurity_right = 0.0 + + cdef void** left_child = self.left_child_ptr + cdef void** right_child = self.right_child_ptr + + for k in range(self.n_outputs): + median = ( left_child[k]).get_median() + for p in range(start, pos): + i = sample_indices[p] + + if sample_weight is not None: + w = sample_weight[i] + + impurity_left += fabs(self.y[i, k] - median) * w + p_impurity_left[0] = impurity_left / (self.weighted_n_left * + self.n_outputs) + + for k in range(self.n_outputs): + median = ( right_child[k]).get_median() + for p in range(pos, end): + i = sample_indices[p] + + if sample_weight is not None: + w = sample_weight[i] + + impurity_right += fabs(self.y[i, k] - median) * w + p_impurity_right[0] = impurity_right / (self.weighted_n_right * + self.n_outputs) + + +cdef class FriedmanMSE(MSE): + """Mean squared error impurity criterion with improvement score by Friedman. + + Uses the formula (35) in Friedman's original Gradient Boosting paper: + + diff = mean_left - mean_right + improvement = n_left * n_right * diff^2 / (n_left + n_right) + """ + + cdef double proxy_impurity_improvement(self) noexcept nogil: + """Compute a proxy of the impurity reduction. + + This method is used to speed up the search for the best split. + It is a proxy quantity such that the split that maximizes this value + also maximizes the impurity improvement. It neglects all constant terms + of the impurity decrease for a given split. + + The absolute impurity improvement is only computed by the + impurity_improvement method once the best split has been found. + """ + cdef double total_sum_left = 0.0 + cdef double total_sum_right = 0.0 + + cdef SIZE_t k + cdef double diff = 0.0 + + for k in range(self.n_outputs): + total_sum_left += self.sum_left[k] + total_sum_right += self.sum_right[k] + + diff = (self.weighted_n_right * total_sum_left - + self.weighted_n_left * total_sum_right) + + return diff * diff / (self.weighted_n_left * self.weighted_n_right) + + cdef double impurity_improvement(self, double impurity_parent, double + impurity_left, double impurity_right) noexcept nogil: + # Note: none of the arguments are used here + cdef double total_sum_left = 0.0 + cdef double total_sum_right = 0.0 + + cdef SIZE_t k + cdef double diff = 0.0 + + for k in range(self.n_outputs): + total_sum_left += self.sum_left[k] + total_sum_right += self.sum_right[k] + + diff = (self.weighted_n_right * total_sum_left - + self.weighted_n_left * total_sum_right) / self.n_outputs + + return (diff * diff / (self.weighted_n_left * self.weighted_n_right * + self.weighted_n_node_samples)) + + +cdef class Poisson(RegressionCriterion): + """Half Poisson deviance as impurity criterion. + + Poisson deviance = 2/n * sum(y_true * log(y_true/y_pred) + y_pred - y_true) + + Note that the deviance is >= 0, and since we have `y_pred = mean(y_true)` + at the leaves, one always has `sum(y_pred - y_true) = 0`. It remains the + implemented impurity (factor 2 is skipped): + 1/n * sum(y_true * log(y_true/y_pred) + """ + # FIXME in 1.0: + # min_impurity_split with default = 0 forces us to use a non-negative + # impurity like the Poisson deviance. Without this restriction, one could + # throw away the 'constant' term sum(y_true * log(y_true)) and just use + # Poisson loss = - 1/n * sum(y_true * log(y_pred)) + # = - 1/n * sum(y_true * log(mean(y_true)) + # = - mean(y_true) * log(mean(y_true)) + # With this trick (used in proxy_impurity_improvement()), as for MSE, + # children_impurity would only need to go over left xor right split, not + # both. This could be faster. + + cdef double node_impurity(self) noexcept nogil: + """Evaluate the impurity of the current node. + + Evaluate the Poisson criterion as impurity of the current node, + i.e. the impurity of sample_indices[start:end]. The smaller the impurity the + better. + """ + return self.poisson_loss(self.start, self.end, self.sum_total, + self.weighted_n_node_samples) + + cdef double proxy_impurity_improvement(self) noexcept nogil: + """Compute a proxy of the impurity reduction. + + This method is used to speed up the search for the best split. + It is a proxy quantity such that the split that maximizes this value + also maximizes the impurity improvement. It neglects all constant terms + of the impurity decrease for a given split. + + The absolute impurity improvement is only computed by the + impurity_improvement method once the best split has been found. + + The Poisson proxy is derived from: + + sum_{i left }(y_i * log(y_i / y_pred_L)) + + sum_{i right}(y_i * log(y_i / y_pred_R)) + = sum(y_i * log(y_i) - n_L * mean_{i left}(y_i) * log(mean_{i left}(y_i)) + - n_R * mean_{i right}(y_i) * log(mean_{i right}(y_i)) + + Neglecting constant terms, this gives + + - sum{i left }(y_i) * log(mean{i left}(y_i)) + - sum{i right}(y_i) * log(mean{i right}(y_i)) + """ + cdef SIZE_t k + cdef double proxy_impurity_left = 0.0 + cdef double proxy_impurity_right = 0.0 + cdef double y_mean_left = 0. + cdef double y_mean_right = 0. + + for k in range(self.n_outputs): + if (self.sum_left[k] <= EPSILON) or (self.sum_right[k] <= EPSILON): + # Poisson loss does not allow non-positive predictions. We + # therefore forbid splits that have child nodes with + # sum(y_i) <= 0. + # Since sum_right = sum_total - sum_left, it can lead to + # floating point rounding error and will not give zero. Thus, + # we relax the above comparison to sum(y_i) <= EPSILON. + return -INFINITY + else: + y_mean_left = self.sum_left[k] / self.weighted_n_left + y_mean_right = self.sum_right[k] / self.weighted_n_right + proxy_impurity_left -= self.sum_left[k] * log(y_mean_left) + proxy_impurity_right -= self.sum_right[k] * log(y_mean_right) + + return - proxy_impurity_left - proxy_impurity_right + + cdef void children_impurity(self, double* impurity_left, + double* impurity_right) noexcept nogil: + """Evaluate the impurity in children nodes. + + i.e. the impurity of the left child (sample_indices[start:pos]) and the + impurity of the right child (sample_indices[pos:end]) for Poisson. + """ + cdef SIZE_t start = self.start + cdef SIZE_t pos = self.pos + cdef SIZE_t end = self.end + + impurity_left[0] = self.poisson_loss(start, pos, self.sum_left, + self.weighted_n_left) + + impurity_right[0] = self.poisson_loss(pos, end, self.sum_right, + self.weighted_n_right) + + cdef inline DOUBLE_t poisson_loss(self, + SIZE_t start, + SIZE_t end, + const double[::1] y_sum, + DOUBLE_t weight_sum) noexcept nogil: + """Helper function to compute Poisson loss (~deviance) of a given node. + """ + cdef const DOUBLE_t[:, ::1] y = self.y + cdef const DOUBLE_t[:] sample_weight = self.sample_weight + cdef const SIZE_t[:] sample_indices = self.sample_indices + + cdef DOUBLE_t y_mean = 0. + cdef DOUBLE_t poisson_loss = 0. + cdef DOUBLE_t w = 1.0 + cdef SIZE_t i, k, p + cdef SIZE_t n_outputs = self.n_outputs + + for k in range(n_outputs): + if y_sum[k] <= EPSILON: + # y_sum could be computed from the subtraction + # sum_right = sum_total - sum_left leading to a potential + # floating point rounding error. + # Thus, we relax the comparison y_sum <= 0 to + # y_sum <= EPSILON. + return INFINITY + + y_mean = y_sum[k] / weight_sum + + for p in range(start, end): + i = sample_indices[p] + + if sample_weight is not None: + w = sample_weight[i] + + poisson_loss += w * xlogy(y[i, k], y[i, k] / y_mean) + return poisson_loss / (weight_sum * n_outputs) \ No newline at end of file diff --git a/ivy/functional/frontends/sklearn/_splitter copy.pyx b/ivy/functional/frontends/sklearn/_splitter copy.pyx new file mode 100644 index 0000000000000..bbe67f15c9bb5 --- /dev/null +++ b/ivy/functional/frontends/sklearn/_splitter copy.pyx @@ -0,0 +1,1531 @@ +# Authors: Gilles Louppe +# Peter Prettenhofer +# Brian Holt +# Noel Dawe +# Satrajit Gosh +# Lars Buitinck +# Arnaud Joly +# Joel Nothman +# Fares Hedayati +# Jacob Schreiber +# +# License: BSD 3 clause + +from ._criterion cimport Criterion + +from libc.stdlib cimport qsort +from libc.string cimport memcpy +from libc.math cimport isnan +from cython cimport final + +import numpy as np + +from scipy.sparse import isspmatrix_csc + +from ._utils cimport log +from ._utils cimport rand_int +from ._utils cimport rand_uniform +from ._utils cimport RAND_R_MAX + +cdef double INFINITY = np.inf + +# Mitigate precision differences between 32 bit and 64 bit +cdef DTYPE_t FEATURE_THRESHOLD = 1e-7 + +# Constant to switch between algorithm non zero value extract algorithm +# in SparsePartitioner +cdef DTYPE_t EXTRACT_NNZ_SWITCH = 0.1 + +cdef inline void _init_split(SplitRecord* self, SIZE_t start_pos) noexcept nogil: + self.impurity_left = INFINITY + self.impurity_right = INFINITY + self.pos = start_pos + self.feature = 0 + self.threshold = 0. + self.improvement = -INFINITY + self.missing_go_to_left = False + self.n_missing = 0 + +cdef class Splitter: + """Abstract splitter class. + + Splitters are called by tree builders to find the best splits on both + sparse and dense data, one split at a time. + """ + + def __cinit__(self, Criterion criterion, SIZE_t max_features, + SIZE_t min_samples_leaf, double min_weight_leaf, + object random_state): + """ + Parameters + ---------- + criterion : Criterion + The criterion to measure the quality of a split. + + max_features : SIZE_t + The maximal number of randomly selected features which can be + considered for a split. + + min_samples_leaf : SIZE_t + The minimal number of samples each leaf can have, where splits + which would result in having less samples in a leaf are not + considered. + + min_weight_leaf : double + The minimal weight each leaf can have, where the weight is the sum + of the weights of each sample in it. + + random_state : object + The user inputted random state to be used for pseudo-randomness + """ + + self.criterion = criterion + + self.n_samples = 0 + self.n_features = 0 + + self.max_features = max_features + self.min_samples_leaf = min_samples_leaf + self.min_weight_leaf = min_weight_leaf + self.random_state = random_state + + def __getstate__(self): + return {} + + def __setstate__(self, d): + pass + + def __reduce__(self): + return (type(self), (self.criterion, + self.max_features, + self.min_samples_leaf, + self.min_weight_leaf, + self.random_state), self.__getstate__()) + + cdef int init( + self, + object X, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight, + const unsigned char[::1] missing_values_in_feature_mask, + ) except -1: + """Initialize the splitter. + + Take in the input data X, the target Y, and optional sample weights. + + Returns -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + + Parameters + ---------- + X : object + This contains the inputs. Usually it is a 2d numpy array. + + y : ndarray, dtype=DOUBLE_t + This is the vector of targets, or true labels, for the samples represented + as a Cython memoryview. + + sample_weight : ndarray, dtype=DOUBLE_t + The weights of the samples, where higher weighted samples are fit + closer than lower weight samples. If not provided, all samples + are assumed to have uniform weight. This is represented + as a Cython memoryview. + + has_missing : bool + At least one missing values is in X. + """ + + self.rand_r_state = self.random_state.randint(0, RAND_R_MAX) + cdef SIZE_t n_samples = X.shape[0] + + # Create a new array which will be used to store nonzero + # samples from the feature of interest + self.samples = np.empty(n_samples, dtype=np.intp) + cdef SIZE_t[::1] samples = self.samples + + cdef SIZE_t i, j + cdef double weighted_n_samples = 0.0 + j = 0 + + for i in range(n_samples): + # Only work with positively weighted samples + if sample_weight is None or sample_weight[i] != 0.0: + samples[j] = i + j += 1 + + if sample_weight is not None: + weighted_n_samples += sample_weight[i] + else: + weighted_n_samples += 1.0 + + # Number of samples is number of positively weighted samples + self.n_samples = j + self.weighted_n_samples = weighted_n_samples + + cdef SIZE_t n_features = X.shape[1] + self.features = np.arange(n_features, dtype=np.intp) + self.n_features = n_features + + self.feature_values = np.empty(n_samples, dtype=np.float32) + self.constant_features = np.empty(n_features, dtype=np.intp) + + self.y = y + + self.sample_weight = sample_weight + if missing_values_in_feature_mask is not None: + self.criterion.init_sum_missing() + return 0 + + cdef int node_reset(self, SIZE_t start, SIZE_t end, + double* weighted_n_node_samples) except -1 nogil: + """Reset splitter on node samples[start:end]. + + Returns -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + + Parameters + ---------- + start : SIZE_t + The index of the first sample to consider + end : SIZE_t + The index of the last sample to consider + weighted_n_node_samples : ndarray, dtype=double pointer + The total weight of those samples + """ + + self.start = start + self.end = end + + self.criterion.init( + self.y, + self.sample_weight, + self.weighted_n_samples, + self.samples, + start, + end + ) + + weighted_n_node_samples[0] = self.criterion.weighted_n_node_samples + return 0 + + cdef int node_split(self, double impurity, SplitRecord* split, + SIZE_t* n_constant_features) except -1 nogil: + """Find the best split on node samples[start:end]. + + This is a placeholder method. The majority of computation will be done + here. + + It should return -1 upon errors. + """ + + pass + + cdef void node_value(self, double* dest) noexcept nogil: + """Copy the value of node samples[start:end] into dest.""" + + self.criterion.node_value(dest) + + cdef double node_impurity(self) noexcept nogil: + """Return the impurity of the current node.""" + + return self.criterion.node_impurity() + +cdef inline void shift_missing_values_to_left_if_required( + SplitRecord* best, + SIZE_t[::1] samples, + SIZE_t end, +) nogil: + cdef SIZE_t i, p, current_end + # The partitioner partitions the data such that the missing values are in + # samples[-n_missing:] for the criterion to consume. If the missing values + # are going to the right node, then the missing values are already in the + # correct position. If the missing values go left, then we move the missing + # values to samples[best.pos:best.pos+n_missing] and update `best.pos`. + if best.n_missing > 0 and best.missing_go_to_left: + for p in range(best.n_missing): + i = best.pos + p + current_end = end - 1 - p + samples[i], samples[current_end] = samples[current_end], samples[i] + best.pos += best.n_missing + +# Introduce a fused-class to make it possible to share the split implementation +# between the dense and sparse cases in the node_split_best and node_split_random +# functions. The alternative would have been to use inheritance-based polymorphism +# but it would have resulted in a ~10% overall tree fitting performance +# degradation caused by the overhead frequent virtual method lookups. +ctypedef fused Partitioner: + DensePartitioner + SparsePartitioner + +cdef inline int node_split_best( + Splitter splitter, + Partitioner partitioner, + Criterion criterion, + double impurity, + SplitRecord* split, + SIZE_t* n_constant_features, +) except -1 nogil: + """Find the best split on node samples[start:end] + + Returns -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + """ + # Find the best split + cdef SIZE_t start = splitter.start + cdef SIZE_t end = splitter.end + cdef SIZE_t end_non_missing + cdef SIZE_t n_missing = 0 + cdef bint has_missing = 0 + cdef SIZE_t n_searches + cdef SIZE_t n_left, n_right + cdef bint missing_go_to_left + + cdef SIZE_t[::1] samples = splitter.samples + cdef SIZE_t[::1] features = splitter.features + cdef SIZE_t[::1] constant_features = splitter.constant_features + cdef SIZE_t n_features = splitter.n_features + + cdef DTYPE_t[::1] feature_values = splitter.feature_values + cdef SIZE_t max_features = splitter.max_features + cdef SIZE_t min_samples_leaf = splitter.min_samples_leaf + cdef double min_weight_leaf = splitter.min_weight_leaf + cdef UINT32_t* random_state = &splitter.rand_r_state + + cdef SplitRecord best_split, current_split + cdef double current_proxy_improvement = -INFINITY + cdef double best_proxy_improvement = -INFINITY + + cdef SIZE_t f_i = n_features + cdef SIZE_t f_j + cdef SIZE_t p + cdef SIZE_t p_prev + + cdef SIZE_t n_visited_features = 0 + # Number of features discovered to be constant during the split search + cdef SIZE_t n_found_constants = 0 + # Number of features known to be constant and drawn without replacement + cdef SIZE_t n_drawn_constants = 0 + cdef SIZE_t n_known_constants = n_constant_features[0] + # n_total_constants = n_known_constants + n_found_constants + cdef SIZE_t n_total_constants = n_known_constants + + _init_split(&best_split, end) + + partitioner.init_node_split(start, end) + + # Sample up to max_features without replacement using a + # Fisher-Yates-based algorithm (using the local variables `f_i` and + # `f_j` to compute a permutation of the `features` array). + # + # Skip the CPU intensive evaluation of the impurity criterion for + # features that were already detected as constant (hence not suitable + # for good splitting) by ancestor nodes and save the information on + # newly discovered constant features to spare computation on descendant + # nodes. + while (f_i > n_total_constants and # Stop early if remaining features + # are constant + (n_visited_features < max_features or + # At least one drawn features must be non constant + n_visited_features <= n_found_constants + n_drawn_constants)): + + n_visited_features += 1 + + # Loop invariant: elements of features in + # - [:n_drawn_constant[ holds drawn and known constant features; + # - [n_drawn_constant:n_known_constant[ holds known constant + # features that haven't been drawn yet; + # - [n_known_constant:n_total_constant[ holds newly found constant + # features; + # - [n_total_constant:f_i[ holds features that haven't been drawn + # yet and aren't constant apriori. + # - [f_i:n_features[ holds features that have been drawn + # and aren't constant. + + # Draw a feature at random + f_j = rand_int(n_drawn_constants, f_i - n_found_constants, + random_state) + + if f_j < n_known_constants: + # f_j in the interval [n_drawn_constants, n_known_constants[ + features[n_drawn_constants], features[f_j] = features[f_j], features[n_drawn_constants] + + n_drawn_constants += 1 + continue + + # f_j in the interval [n_known_constants, f_i - n_found_constants[ + f_j += n_found_constants + # f_j in the interval [n_total_constants, f_i[ + current_split.feature = features[f_j] + partitioner.sort_samples_and_feature_values(current_split.feature) + n_missing = partitioner.n_missing + end_non_missing = end - n_missing + + if ( + # All values for this feature are missing, or + end_non_missing == start or + # This feature is considered constant (max - min <= FEATURE_THRESHOLD) + feature_values[end_non_missing - 1] <= feature_values[start] + FEATURE_THRESHOLD + ): + # We consider this feature constant in this case. + # Since finding a split among constant feature is not valuable, + # we do not consider this feature for splitting. + features[f_j], features[n_total_constants] = features[n_total_constants], features[f_j] + + n_found_constants += 1 + n_total_constants += 1 + continue + + f_i -= 1 + features[f_i], features[f_j] = features[f_j], features[f_i] + has_missing = n_missing != 0 + if has_missing: + criterion.init_missing(n_missing) + # Evaluate all splits + + # If there are missing values, then we search twice for the most optimal split. + # The first search will have all the missing values going to the right node. + # The second search will have all the missing values going to the left node. + # If there are no missing values, then we search only once for the most + # optimal split. + n_searches = 2 if has_missing else 1 + + for i in range(n_searches): + missing_go_to_left = i == 1 + criterion.missing_go_to_left = missing_go_to_left + criterion.reset() + + p = start + + while p < end_non_missing: + partitioner.next_p(&p_prev, &p) + + if p >= end_non_missing: + continue + + if missing_go_to_left: + n_left = p - start + n_missing + n_right = end_non_missing - p + else: + n_left = p - start + n_right = end_non_missing - p + n_missing + + # Reject if min_samples_leaf is not guaranteed + if n_left < min_samples_leaf or n_right < min_samples_leaf: + continue + + current_split.pos = p + criterion.update(current_split.pos) + + # Reject if min_weight_leaf is not satisfied + if ((criterion.weighted_n_left < min_weight_leaf) or + (criterion.weighted_n_right < min_weight_leaf)): + continue + + current_proxy_improvement = criterion.proxy_impurity_improvement() + + if current_proxy_improvement > best_proxy_improvement: + best_proxy_improvement = current_proxy_improvement + # sum of halves is used to avoid infinite value + current_split.threshold = ( + feature_values[p_prev] / 2.0 + feature_values[p] / 2.0 + ) + + if ( + current_split.threshold == feature_values[p] or + current_split.threshold == INFINITY or + current_split.threshold == -INFINITY + ): + current_split.threshold = feature_values[p_prev] + + current_split.n_missing = n_missing + if n_missing == 0: + current_split.missing_go_to_left = n_left > n_right + else: + current_split.missing_go_to_left = missing_go_to_left + + best_split = current_split # copy + + # Evaluate when there are missing values and all missing values goes + # to the right node and non-missing values goes to the left node. + if has_missing: + n_left, n_right = end - start - n_missing, n_missing + p = end - n_missing + missing_go_to_left = 0 + + if not (n_left < min_samples_leaf or n_right < min_samples_leaf): + criterion.missing_go_to_left = missing_go_to_left + criterion.update(p) + + if not ((criterion.weighted_n_left < min_weight_leaf) or + (criterion.weighted_n_right < min_weight_leaf)): + current_proxy_improvement = criterion.proxy_impurity_improvement() + + if current_proxy_improvement > best_proxy_improvement: + best_proxy_improvement = current_proxy_improvement + current_split.threshold = INFINITY + current_split.missing_go_to_left = missing_go_to_left + current_split.n_missing = n_missing + current_split.pos = p + best_split = current_split + + # Reorganize into samples[start:best_split.pos] + samples[best_split.pos:end] + if best_split.pos < end: + partitioner.partition_samples_final( + best_split.pos, + best_split.threshold, + best_split.feature, + best_split.n_missing + ) + if best_split.n_missing != 0: + criterion.init_missing(best_split.n_missing) + criterion.missing_go_to_left = best_split.missing_go_to_left + + criterion.reset() + criterion.update(best_split.pos) + criterion.children_impurity( + &best_split.impurity_left, &best_split.impurity_right + ) + best_split.improvement = criterion.impurity_improvement( + impurity, + best_split.impurity_left, + best_split.impurity_right + ) + + shift_missing_values_to_left_if_required(&best_split, samples, end) + + # Respect invariant for constant features: the original order of + # element in features[:n_known_constants] must be preserved for sibling + # and child nodes + memcpy(&features[0], &constant_features[0], sizeof(SIZE_t) * n_known_constants) + + # Copy newly found constant features + memcpy(&constant_features[n_known_constants], + &features[n_known_constants], + sizeof(SIZE_t) * n_found_constants) + + # Return values + split[0] = best_split + n_constant_features[0] = n_total_constants + return 0 + + +# Sort n-element arrays pointed to by feature_values and samples, simultaneously, +# by the values in feature_values. Algorithm: Introsort (Musser, SP&E, 1997). +cdef inline void sort(DTYPE_t* feature_values, SIZE_t* samples, SIZE_t n) noexcept nogil: + if n == 0: + return + cdef int maxd = 2 * log(n) + introsort(feature_values, samples, n, maxd) + + +cdef inline void swap(DTYPE_t* feature_values, SIZE_t* samples, + SIZE_t i, SIZE_t j) noexcept nogil: + # Helper for sort + feature_values[i], feature_values[j] = feature_values[j], feature_values[i] + samples[i], samples[j] = samples[j], samples[i] + + +cdef inline DTYPE_t median3(DTYPE_t* feature_values, SIZE_t n) noexcept nogil: + # Median of three pivot selection, after Bentley and McIlroy (1993). + # Engineering a sort function. SP&E. Requires 8/3 comparisons on average. + cdef DTYPE_t a = feature_values[0], b = feature_values[n / 2], c = feature_values[n - 1] + if a < b: + if b < c: + return b + elif a < c: + return c + else: + return a + elif b < c: + if a < c: + return a + else: + return c + else: + return b + + +# Introsort with median of 3 pivot selection and 3-way partition function +# (robust to repeated elements, e.g. lots of zero features). +cdef void introsort(DTYPE_t* feature_values, SIZE_t *samples, + SIZE_t n, int maxd) noexcept nogil: + cdef DTYPE_t pivot + cdef SIZE_t i, l, r + + while n > 1: + if maxd <= 0: # max depth limit exceeded ("gone quadratic") + heapsort(feature_values, samples, n) + return + maxd -= 1 + + pivot = median3(feature_values, n) + + # Three-way partition. + i = l = 0 + r = n + while i < r: + if feature_values[i] < pivot: + swap(feature_values, samples, i, l) + i += 1 + l += 1 + elif feature_values[i] > pivot: + r -= 1 + swap(feature_values, samples, i, r) + else: + i += 1 + + introsort(feature_values, samples, l, maxd) + feature_values += r + samples += r + n -= r + + +cdef inline void sift_down(DTYPE_t* feature_values, SIZE_t* samples, + SIZE_t start, SIZE_t end) noexcept nogil: + # Restore heap order in feature_values[start:end] by moving the max element to start. + cdef SIZE_t child, maxind, root + + root = start + while True: + child = root * 2 + 1 + + # find max of root, left child, right child + maxind = root + if child < end and feature_values[maxind] < feature_values[child]: + maxind = child + if child + 1 < end and feature_values[maxind] < feature_values[child + 1]: + maxind = child + 1 + + if maxind == root: + break + else: + swap(feature_values, samples, root, maxind) + root = maxind + + +cdef void heapsort(DTYPE_t* feature_values, SIZE_t* samples, SIZE_t n) noexcept nogil: + cdef SIZE_t start, end + + # heapify + start = (n - 2) / 2 + end = n + while True: + sift_down(feature_values, samples, start, end) + if start == 0: + break + start -= 1 + + # sort by shrinking the heap, putting the max element immediately after it + end = n - 1 + while end > 0: + swap(feature_values, samples, 0, end) + sift_down(feature_values, samples, 0, end) + end = end - 1 + +cdef inline int node_split_random( + Splitter splitter, + Partitioner partitioner, + Criterion criterion, + double impurity, + SplitRecord* split, + SIZE_t* n_constant_features +) except -1 nogil: + """Find the best random split on node samples[start:end] + + Returns -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + """ + # Draw random splits and pick the best + cdef SIZE_t start = splitter.start + cdef SIZE_t end = splitter.end + + cdef SIZE_t[::1] features = splitter.features + cdef SIZE_t[::1] constant_features = splitter.constant_features + cdef SIZE_t n_features = splitter.n_features + + cdef SIZE_t max_features = splitter.max_features + cdef SIZE_t min_samples_leaf = splitter.min_samples_leaf + cdef double min_weight_leaf = splitter.min_weight_leaf + cdef UINT32_t* random_state = &splitter.rand_r_state + + cdef SplitRecord best_split, current_split + cdef double current_proxy_improvement = - INFINITY + cdef double best_proxy_improvement = - INFINITY + + cdef SIZE_t f_i = n_features + cdef SIZE_t f_j + # Number of features discovered to be constant during the split search + cdef SIZE_t n_found_constants = 0 + # Number of features known to be constant and drawn without replacement + cdef SIZE_t n_drawn_constants = 0 + cdef SIZE_t n_known_constants = n_constant_features[0] + # n_total_constants = n_known_constants + n_found_constants + cdef SIZE_t n_total_constants = n_known_constants + cdef SIZE_t n_visited_features = 0 + cdef DTYPE_t min_feature_value + cdef DTYPE_t max_feature_value + + _init_split(&best_split, end) + + partitioner.init_node_split(start, end) + + # Sample up to max_features without replacement using a + # Fisher-Yates-based algorithm (using the local variables `f_i` and + # `f_j` to compute a permutation of the `features` array). + # + # Skip the CPU intensive evaluation of the impurity criterion for + # features that were already detected as constant (hence not suitable + # for good splitting) by ancestor nodes and save the information on + # newly discovered constant features to spare computation on descendant + # nodes. + while (f_i > n_total_constants and # Stop early if remaining features + # are constant + (n_visited_features < max_features or + # At least one drawn features must be non constant + n_visited_features <= n_found_constants + n_drawn_constants)): + n_visited_features += 1 + + # Loop invariant: elements of features in + # - [:n_drawn_constant[ holds drawn and known constant features; + # - [n_drawn_constant:n_known_constant[ holds known constant + # features that haven't been drawn yet; + # - [n_known_constant:n_total_constant[ holds newly found constant + # features; + # - [n_total_constant:f_i[ holds features that haven't been drawn + # yet and aren't constant apriori. + # - [f_i:n_features[ holds features that have been drawn + # and aren't constant. + + # Draw a feature at random + f_j = rand_int(n_drawn_constants, f_i - n_found_constants, + random_state) + + if f_j < n_known_constants: + # f_j in the interval [n_drawn_constants, n_known_constants[ + features[n_drawn_constants], features[f_j] = features[f_j], features[n_drawn_constants] + n_drawn_constants += 1 + continue + + # f_j in the interval [n_known_constants, f_i - n_found_constants[ + f_j += n_found_constants + # f_j in the interval [n_total_constants, f_i[ + + current_split.feature = features[f_j] + + # Find min, max + partitioner.find_min_max( + current_split.feature, &min_feature_value, &max_feature_value + ) + + if max_feature_value <= min_feature_value + FEATURE_THRESHOLD: + features[f_j], features[n_total_constants] = features[n_total_constants], current_split.feature + + n_found_constants += 1 + n_total_constants += 1 + continue + + f_i -= 1 + features[f_i], features[f_j] = features[f_j], features[f_i] + + # Draw a random threshold + current_split.threshold = rand_uniform( + min_feature_value, + max_feature_value, + random_state, + ) + + if current_split.threshold == max_feature_value: + current_split.threshold = min_feature_value + + # Partition + current_split.pos = partitioner.partition_samples(current_split.threshold) + + # Reject if min_samples_leaf is not guaranteed + if (((current_split.pos - start) < min_samples_leaf) or + ((end - current_split.pos) < min_samples_leaf)): + continue + + # Evaluate split + # At this point, the criterion has a view into the samples that was partitioned + # by the partitioner. The criterion will use the partition to evaluating the split. + criterion.reset() + criterion.update(current_split.pos) + + # Reject if min_weight_leaf is not satisfied + if ((criterion.weighted_n_left < min_weight_leaf) or + (criterion.weighted_n_right < min_weight_leaf)): + continue + + current_proxy_improvement = criterion.proxy_impurity_improvement() + + if current_proxy_improvement > best_proxy_improvement: + best_proxy_improvement = current_proxy_improvement + best_split = current_split # copy + + # Reorganize into samples[start:best.pos] + samples[best.pos:end] + if best_split.pos < end: + if current_split.feature != best_split.feature: + # TODO: Pass in best.n_missing when random splitter supports missing values. + partitioner.partition_samples_final( + best_split.pos, best_split.threshold, best_split.feature, 0 + ) + + criterion.reset() + criterion.update(best_split.pos) + criterion.children_impurity( + &best_split.impurity_left, &best_split.impurity_right + ) + best_split.improvement = criterion.impurity_improvement( + impurity, best_split.impurity_left, best_split.impurity_right + ) + + # Respect invariant for constant features: the original order of + # element in features[:n_known_constants] must be preserved for sibling + # and child nodes + memcpy(&features[0], &constant_features[0], sizeof(SIZE_t) * n_known_constants) + + # Copy newly found constant features + memcpy(&constant_features[n_known_constants], + &features[n_known_constants], + sizeof(SIZE_t) * n_found_constants) + + # Return values + split[0] = best_split + n_constant_features[0] = n_total_constants + return 0 + + +@final +cdef class DensePartitioner: + """Partitioner specialized for dense data. + + Note that this partitioner is agnostic to the splitting strategy (best vs. random). + """ + cdef: + const DTYPE_t[:, :] X + cdef SIZE_t[::1] samples + cdef DTYPE_t[::1] feature_values + cdef SIZE_t start + cdef SIZE_t end + cdef SIZE_t n_missing + cdef const unsigned char[::1] missing_values_in_feature_mask + + def __init__( + self, + const DTYPE_t[:, :] X, + SIZE_t[::1] samples, + DTYPE_t[::1] feature_values, + const unsigned char[::1] missing_values_in_feature_mask, + ): + self.X = X + self.samples = samples + self.feature_values = feature_values + self.missing_values_in_feature_mask = missing_values_in_feature_mask + + cdef inline void init_node_split(self, SIZE_t start, SIZE_t end) noexcept nogil: + """Initialize splitter at the beginning of node_split.""" + self.start = start + self.end = end + self.n_missing = 0 + + cdef inline void sort_samples_and_feature_values( + self, SIZE_t current_feature + ) noexcept nogil: + """Simultaneously sort based on the feature_values. + + Missing values are stored at the end of feature_values. + The number of missing values observed in feature_values is stored + in self.n_missing. + """ + cdef: + SIZE_t i, current_end + DTYPE_t[::1] feature_values = self.feature_values + const DTYPE_t[:, :] X = self.X + SIZE_t[::1] samples = self.samples + SIZE_t n_missing = 0 + const unsigned char[::1] missing_values_in_feature_mask = self.missing_values_in_feature_mask + + # Sort samples along that feature; by + # copying the values into an array and + # sorting the array in a manner which utilizes the cache more + # effectively. + if missing_values_in_feature_mask is not None and missing_values_in_feature_mask[current_feature]: + i, current_end = self.start, self.end - 1 + # Missing values are placed at the end and do not participate in the sorting. + while i <= current_end: + # Finds the right-most value that is not missing so that + # it can be swapped with missing values at its left. + if isnan(X[samples[current_end], current_feature]): + n_missing += 1 + current_end -= 1 + continue + + # X[samples[current_end], current_feature] is a non-missing value + if isnan(X[samples[i], current_feature]): + samples[i], samples[current_end] = samples[current_end], samples[i] + n_missing += 1 + current_end -= 1 + + feature_values[i] = X[samples[i], current_feature] + i += 1 + else: + # When there are no missing values, we only need to copy the data into + # feature_values + for i in range(self.start, self.end): + feature_values[i] = X[samples[i], current_feature] + + sort(&feature_values[self.start], &samples[self.start], self.end - self.start - n_missing) + self.n_missing = n_missing + + cdef inline void find_min_max( + self, + SIZE_t current_feature, + DTYPE_t* min_feature_value_out, + DTYPE_t* max_feature_value_out, + ) noexcept nogil: + """Find the minimum and maximum value for current_feature.""" + cdef: + SIZE_t p + DTYPE_t current_feature_value + const DTYPE_t[:, :] X = self.X + SIZE_t[::1] samples = self.samples + DTYPE_t min_feature_value = X[samples[self.start], current_feature] + DTYPE_t max_feature_value = min_feature_value + DTYPE_t[::1] feature_values = self.feature_values + + feature_values[self.start] = min_feature_value + + for p in range(self.start + 1, self.end): + current_feature_value = X[samples[p], current_feature] + feature_values[p] = current_feature_value + + if current_feature_value < min_feature_value: + min_feature_value = current_feature_value + elif current_feature_value > max_feature_value: + max_feature_value = current_feature_value + + min_feature_value_out[0] = min_feature_value + max_feature_value_out[0] = max_feature_value + + cdef inline void next_p(self, SIZE_t* p_prev, SIZE_t* p) noexcept nogil: + """Compute the next p_prev and p for iteratiing over feature values. + + The missing values are not included when iterating through the feature values. + """ + cdef: + DTYPE_t[::1] feature_values = self.feature_values + SIZE_t end_non_missing = self.end - self.n_missing + + while ( + p[0] + 1 < end_non_missing and + feature_values[p[0] + 1] <= feature_values[p[0]] + FEATURE_THRESHOLD + ): + p[0] += 1 + + p_prev[0] = p[0] + + # By adding 1, we have + # (feature_values[p] >= end) or (feature_values[p] > feature_values[p - 1]) + p[0] += 1 + + cdef inline SIZE_t partition_samples(self, double current_threshold) noexcept nogil: + """Partition samples for feature_values at the current_threshold.""" + cdef: + SIZE_t p = self.start + SIZE_t partition_end = self.end + SIZE_t[::1] samples = self.samples + DTYPE_t[::1] feature_values = self.feature_values + + while p < partition_end: + if feature_values[p] <= current_threshold: + p += 1 + else: + partition_end -= 1 + + feature_values[p], feature_values[partition_end] = ( + feature_values[partition_end], feature_values[p] + ) + samples[p], samples[partition_end] = samples[partition_end], samples[p] + + return partition_end + + cdef inline void partition_samples_final( + self, + SIZE_t best_pos, + double best_threshold, + SIZE_t best_feature, + SIZE_t best_n_missing, + ) noexcept nogil: + """Partition samples for X at the best_threshold and best_feature. + + If missing values are present, this method partitions `samples` + so that the `best_n_missing` missing values' indices are in the + right-most end of `samples`, that is `samples[end_non_missing:end]`. + """ + cdef: + # Local invariance: start <= p <= partition_end <= end + SIZE_t start = self.start + SIZE_t p = start + SIZE_t end = self.end - 1 + SIZE_t partition_end = end - best_n_missing + SIZE_t[::1] samples = self.samples + const DTYPE_t[:, :] X = self.X + DTYPE_t current_value + + if best_n_missing != 0: + # Move samples with missing values to the end while partitioning the + # non-missing samples + while p < partition_end: + # Keep samples with missing values at the end + if isnan(X[samples[end], best_feature]): + end -= 1 + continue + + # Swap sample with missing values with the sample at the end + current_value = X[samples[p], best_feature] + if isnan(current_value): + samples[p], samples[end] = samples[end], samples[p] + end -= 1 + + # The swapped sample at the end is always a non-missing value, so + # we can continue the algorithm without checking for missingness. + current_value = X[samples[p], best_feature] + + # Partition the non-missing samples + if current_value <= best_threshold: + p += 1 + else: + samples[p], samples[partition_end] = samples[partition_end], samples[p] + partition_end -= 1 + else: + # Partitioning routine when there are no missing values + while p < partition_end: + if X[samples[p], best_feature] <= best_threshold: + p += 1 + else: + samples[p], samples[partition_end] = samples[partition_end], samples[p] + partition_end -= 1 + + +@final +cdef class SparsePartitioner: + """Partitioner specialized for sparse CSC data. + + Note that this partitioner is agnostic to the splitting strategy (best vs. random). + """ + cdef SIZE_t[::1] samples + cdef DTYPE_t[::1] feature_values + cdef SIZE_t start + cdef SIZE_t end + cdef SIZE_t n_missing + cdef const unsigned char[::1] missing_values_in_feature_mask + + cdef const DTYPE_t[::1] X_data + cdef const INT32_t[::1] X_indices + cdef const INT32_t[::1] X_indptr + + cdef SIZE_t n_total_samples + + cdef SIZE_t[::1] index_to_samples + cdef SIZE_t[::1] sorted_samples + + cdef SIZE_t start_positive + cdef SIZE_t end_negative + cdef bint is_samples_sorted + + def __init__( + self, + object X, + SIZE_t[::1] samples, + SIZE_t n_samples, + DTYPE_t[::1] feature_values, + const unsigned char[::1] missing_values_in_feature_mask, + ): + if not isspmatrix_csc(X): + raise ValueError("X should be in csc format") + + self.samples = samples + self.feature_values = feature_values + + # Initialize X + cdef SIZE_t n_total_samples = X.shape[0] + + self.X_data = X.data + self.X_indices = X.indices + self.X_indptr = X.indptr + self.n_total_samples = n_total_samples + + # Initialize auxiliary array used to perform split + self.index_to_samples = np.full(n_total_samples, fill_value=-1, dtype=np.intp) + self.sorted_samples = np.empty(n_samples, dtype=np.intp) + + cdef SIZE_t p + for p in range(n_samples): + self.index_to_samples[samples[p]] = p + + self.missing_values_in_feature_mask = missing_values_in_feature_mask + + cdef inline void init_node_split(self, SIZE_t start, SIZE_t end) noexcept nogil: + """Initialize splitter at the beginning of node_split.""" + self.start = start + self.end = end + self.is_samples_sorted = 0 + self.n_missing = 0 + + cdef inline void sort_samples_and_feature_values( + self, SIZE_t current_feature + ) noexcept nogil: + """Simultaneously sort based on the feature_values.""" + cdef: + DTYPE_t[::1] feature_values = self.feature_values + SIZE_t[::1] index_to_samples = self.index_to_samples + SIZE_t[::1] samples = self.samples + + self.extract_nnz(current_feature) + # Sort the positive and negative parts of `feature_values` + sort(&feature_values[self.start], &samples[self.start], self.end_negative - self.start) + if self.start_positive < self.end: + sort( + &feature_values[self.start_positive], + &samples[self.start_positive], + self.end - self.start_positive + ) + + # Update index_to_samples to take into account the sort + for p in range(self.start, self.end_negative): + index_to_samples[samples[p]] = p + for p in range(self.start_positive, self.end): + index_to_samples[samples[p]] = p + + # Add one or two zeros in feature_values, if there is any + if self.end_negative < self.start_positive: + self.start_positive -= 1 + feature_values[self.start_positive] = 0. + + if self.end_negative != self.start_positive: + feature_values[self.end_negative] = 0. + self.end_negative += 1 + + # XXX: When sparse supports missing values, this should be set to the + # number of missing values for current_feature + self.n_missing = 0 + + cdef inline void find_min_max( + self, + SIZE_t current_feature, + DTYPE_t* min_feature_value_out, + DTYPE_t* max_feature_value_out, + ) noexcept nogil: + """Find the minimum and maximum value for current_feature.""" + cdef: + SIZE_t p + DTYPE_t current_feature_value, min_feature_value, max_feature_value + DTYPE_t[::1] feature_values = self.feature_values + + self.extract_nnz(current_feature) + + if self.end_negative != self.start_positive: + # There is a zero + min_feature_value = 0 + max_feature_value = 0 + else: + min_feature_value = feature_values[self.start] + max_feature_value = min_feature_value + + # Find min, max in feature_values[start:end_negative] + for p in range(self.start, self.end_negative): + current_feature_value = feature_values[p] + + if current_feature_value < min_feature_value: + min_feature_value = current_feature_value + elif current_feature_value > max_feature_value: + max_feature_value = current_feature_value + + # Update min, max given feature_values[start_positive:end] + for p in range(self.start_positive, self.end): + current_feature_value = feature_values[p] + + if current_feature_value < min_feature_value: + min_feature_value = current_feature_value + elif current_feature_value > max_feature_value: + max_feature_value = current_feature_value + + min_feature_value_out[0] = min_feature_value + max_feature_value_out[0] = max_feature_value + + cdef inline void next_p(self, SIZE_t* p_prev, SIZE_t* p) noexcept nogil: + """Compute the next p_prev and p for iteratiing over feature values.""" + cdef: + SIZE_t p_next + DTYPE_t[::1] feature_values = self.feature_values + + if p[0] + 1 != self.end_negative: + p_next = p[0] + 1 + else: + p_next = self.start_positive + + while (p_next < self.end and + feature_values[p_next] <= feature_values[p[0]] + FEATURE_THRESHOLD): + p[0] = p_next + if p[0] + 1 != self.end_negative: + p_next = p[0] + 1 + else: + p_next = self.start_positive + + p_prev[0] = p[0] + p[0] = p_next + + cdef inline SIZE_t partition_samples(self, double current_threshold) noexcept nogil: + """Partition samples for feature_values at the current_threshold.""" + return self._partition(current_threshold, self.start_positive) + + cdef inline void partition_samples_final( + self, + SIZE_t best_pos, + double best_threshold, + SIZE_t best_feature, + SIZE_t n_missing, + ) noexcept nogil: + """Partition samples for X at the best_threshold and best_feature.""" + self.extract_nnz(best_feature) + self._partition(best_threshold, best_pos) + + cdef inline SIZE_t _partition(self, double threshold, SIZE_t zero_pos) noexcept nogil: + """Partition samples[start:end] based on threshold.""" + cdef: + SIZE_t p, partition_end + SIZE_t[::1] index_to_samples = self.index_to_samples + DTYPE_t[::1] feature_values = self.feature_values + SIZE_t[::1] samples = self.samples + + if threshold < 0.: + p = self.start + partition_end = self.end_negative + elif threshold > 0.: + p = self.start_positive + partition_end = self.end + else: + # Data are already split + return zero_pos + + while p < partition_end: + if feature_values[p] <= threshold: + p += 1 + + else: + partition_end -= 1 + + feature_values[p], feature_values[partition_end] = ( + feature_values[partition_end], feature_values[p] + ) + sparse_swap(index_to_samples, samples, p, partition_end) + + return partition_end + + cdef inline void extract_nnz(self, SIZE_t feature) noexcept nogil: + """Extract and partition values for a given feature. + + The extracted values are partitioned between negative values + feature_values[start:end_negative[0]] and positive values + feature_values[start_positive[0]:end]. + The samples and index_to_samples are modified according to this + partition. + + The extraction corresponds to the intersection between the arrays + X_indices[indptr_start:indptr_end] and samples[start:end]. + This is done efficiently using either an index_to_samples based approach + or binary search based approach. + + Parameters + ---------- + feature : SIZE_t, + Index of the feature we want to extract non zero value. + """ + cdef SIZE_t[::1] samples = self.samples + cdef DTYPE_t[::1] feature_values = self.feature_values + cdef SIZE_t indptr_start = self.X_indptr[feature], + cdef SIZE_t indptr_end = self.X_indptr[feature + 1] + cdef SIZE_t n_indices = (indptr_end - indptr_start) + cdef SIZE_t n_samples = self.end - self.start + cdef SIZE_t[::1] index_to_samples = self.index_to_samples + cdef SIZE_t[::1] sorted_samples = self.sorted_samples + cdef const INT32_t[::1] X_indices = self.X_indices + cdef const DTYPE_t[::1] X_data = self.X_data + + # Use binary search if n_samples * log(n_indices) < + # n_indices and index_to_samples approach otherwise. + # O(n_samples * log(n_indices)) is the running time of binary + # search and O(n_indices) is the running time of index_to_samples + # approach. + if ((1 - self.is_samples_sorted) * n_samples * log(n_samples) + + n_samples * log(n_indices) < EXTRACT_NNZ_SWITCH * n_indices): + extract_nnz_binary_search(X_indices, X_data, + indptr_start, indptr_end, + samples, self.start, self.end, + index_to_samples, + feature_values, + &self.end_negative, &self.start_positive, + sorted_samples, &self.is_samples_sorted) + + # Using an index to samples technique to extract non zero values + # index_to_samples is a mapping from X_indices to samples + else: + extract_nnz_index_to_samples(X_indices, X_data, + indptr_start, indptr_end, + samples, self.start, self.end, + index_to_samples, + feature_values, + &self.end_negative, &self.start_positive) + + +cdef int compare_SIZE_t(const void* a, const void* b) noexcept nogil: + """Comparison function for sort.""" + return ((a)[0] - (b)[0]) + + +cdef inline void binary_search(const INT32_t[::1] sorted_array, + INT32_t start, INT32_t end, + SIZE_t value, SIZE_t* index, + INT32_t* new_start) noexcept nogil: + """Return the index of value in the sorted array. + + If not found, return -1. new_start is the last pivot + 1 + """ + cdef INT32_t pivot + index[0] = -1 + while start < end: + pivot = start + (end - start) / 2 + + if sorted_array[pivot] == value: + index[0] = pivot + start = pivot + 1 + break + + if sorted_array[pivot] < value: + start = pivot + 1 + else: + end = pivot + new_start[0] = start + + +cdef inline void extract_nnz_index_to_samples(const INT32_t[::1] X_indices, + const DTYPE_t[::1] X_data, + INT32_t indptr_start, + INT32_t indptr_end, + SIZE_t[::1] samples, + SIZE_t start, + SIZE_t end, + SIZE_t[::1] index_to_samples, + DTYPE_t[::1] feature_values, + SIZE_t* end_negative, + SIZE_t* start_positive) noexcept nogil: + """Extract and partition values for a feature using index_to_samples. + + Complexity is O(indptr_end - indptr_start). + """ + cdef INT32_t k + cdef SIZE_t index + cdef SIZE_t end_negative_ = start + cdef SIZE_t start_positive_ = end + + for k in range(indptr_start, indptr_end): + if start <= index_to_samples[X_indices[k]] < end: + if X_data[k] > 0: + start_positive_ -= 1 + feature_values[start_positive_] = X_data[k] + index = index_to_samples[X_indices[k]] + sparse_swap(index_to_samples, samples, index, start_positive_) + + elif X_data[k] < 0: + feature_values[end_negative_] = X_data[k] + index = index_to_samples[X_indices[k]] + sparse_swap(index_to_samples, samples, index, end_negative_) + end_negative_ += 1 + + # Returned values + end_negative[0] = end_negative_ + start_positive[0] = start_positive_ + + +cdef inline void extract_nnz_binary_search(const INT32_t[::1] X_indices, + const DTYPE_t[::1] X_data, + INT32_t indptr_start, + INT32_t indptr_end, + SIZE_t[::1] samples, + SIZE_t start, + SIZE_t end, + SIZE_t[::1] index_to_samples, + DTYPE_t[::1] feature_values, + SIZE_t* end_negative, + SIZE_t* start_positive, + SIZE_t[::1] sorted_samples, + bint* is_samples_sorted) noexcept nogil: + """Extract and partition values for a given feature using binary search. + + If n_samples = end - start and n_indices = indptr_end - indptr_start, + the complexity is + + O((1 - is_samples_sorted[0]) * n_samples * log(n_samples) + + n_samples * log(n_indices)). + """ + cdef SIZE_t n_samples + + if not is_samples_sorted[0]: + n_samples = end - start + memcpy(&sorted_samples[start], &samples[start], + n_samples * sizeof(SIZE_t)) + qsort(&sorted_samples[start], n_samples, sizeof(SIZE_t), + compare_SIZE_t) + is_samples_sorted[0] = 1 + + while (indptr_start < indptr_end and + sorted_samples[start] > X_indices[indptr_start]): + indptr_start += 1 + + while (indptr_start < indptr_end and + sorted_samples[end - 1] < X_indices[indptr_end - 1]): + indptr_end -= 1 + + cdef SIZE_t p = start + cdef SIZE_t index + cdef SIZE_t k + cdef SIZE_t end_negative_ = start + cdef SIZE_t start_positive_ = end + + while (p < end and indptr_start < indptr_end): + # Find index of sorted_samples[p] in X_indices + binary_search(X_indices, indptr_start, indptr_end, + sorted_samples[p], &k, &indptr_start) + + if k != -1: + # If k != -1, we have found a non zero value + + if X_data[k] > 0: + start_positive_ -= 1 + feature_values[start_positive_] = X_data[k] + index = index_to_samples[X_indices[k]] + sparse_swap(index_to_samples, samples, index, start_positive_) + + elif X_data[k] < 0: + feature_values[end_negative_] = X_data[k] + index = index_to_samples[X_indices[k]] + sparse_swap(index_to_samples, samples, index, end_negative_) + end_negative_ += 1 + p += 1 + + # Returned values + end_negative[0] = end_negative_ + start_positive[0] = start_positive_ + + +cdef inline void sparse_swap(SIZE_t[::1] index_to_samples, SIZE_t[::1] samples, + SIZE_t pos_1, SIZE_t pos_2) noexcept nogil: + """Swap sample pos_1 and pos_2 preserving sparse invariant.""" + samples[pos_1], samples[pos_2] = samples[pos_2], samples[pos_1] + index_to_samples[samples[pos_1]] = pos_1 + index_to_samples[samples[pos_2]] = pos_2 + + +cdef class BestSplitter(Splitter): + """Splitter for finding the best split on dense data.""" + cdef DensePartitioner partitioner + cdef int init( + self, + object X, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight, + const unsigned char[::1] missing_values_in_feature_mask, + ) except -1: + Splitter.init(self, X, y, sample_weight, missing_values_in_feature_mask) + self.partitioner = DensePartitioner( + X, self.samples, self.feature_values, missing_values_in_feature_mask + ) + + cdef int node_split(self, double impurity, SplitRecord* split, + SIZE_t* n_constant_features) except -1 nogil: + return node_split_best( + self, + self.partitioner, + self.criterion, + impurity, + split, + n_constant_features, + ) + +cdef class BestSparseSplitter(Splitter): + """Splitter for finding the best split, using the sparse data.""" + cdef SparsePartitioner partitioner + cdef int init( + self, + object X, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight, + const unsigned char[::1] missing_values_in_feature_mask, + ) except -1: + Splitter.init(self, X, y, sample_weight, missing_values_in_feature_mask) + self.partitioner = SparsePartitioner( + X, self.samples, self.n_samples, self.feature_values, missing_values_in_feature_mask + ) + + cdef int node_split(self, double impurity, SplitRecord* split, + SIZE_t* n_constant_features) except -1 nogil: + return node_split_best( + self, + self.partitioner, + self.criterion, + impurity, + split, + n_constant_features, + ) + +cdef class RandomSplitter(Splitter): + """Splitter for finding the best random split on dense data.""" + cdef DensePartitioner partitioner + cdef int init( + self, + object X, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight, + const unsigned char[::1] missing_values_in_feature_mask, + ) except -1: + Splitter.init(self, X, y, sample_weight, missing_values_in_feature_mask) + self.partitioner = DensePartitioner( + X, self.samples, self.feature_values, missing_values_in_feature_mask + ) + + cdef int node_split(self, double impurity, SplitRecord* split, + SIZE_t* n_constant_features) except -1 nogil: + return node_split_random( + self, + self.partitioner, + self.criterion, + impurity, + split, + n_constant_features, + ) + +cdef class RandomSparseSplitter(Splitter): + """Splitter for finding the best random split, using the sparse data.""" + cdef SparsePartitioner partitioner + cdef int init( + self, + object X, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight, + const unsigned char[::1] missing_values_in_feature_mask, + ) except -1: + Splitter.init(self, X, y, sample_weight, missing_values_in_feature_mask) + self.partitioner = SparsePartitioner( + X, self.samples, self.n_samples, self.feature_values, missing_values_in_feature_mask + ) + + cdef int node_split(self, double impurity, SplitRecord* split, + SIZE_t* n_constant_features) except -1 nogil: + return node_split_random( + self, + self.partitioner, + self.criterion, + impurity, + split, + n_constant_features, + ) \ No newline at end of file diff --git a/ivy/functional/frontends/sklearn/_splitter.pyx b/ivy/functional/frontends/sklearn/_splitter.pyx new file mode 100644 index 0000000000000..bbe67f15c9bb5 --- /dev/null +++ b/ivy/functional/frontends/sklearn/_splitter.pyx @@ -0,0 +1,1531 @@ +# Authors: Gilles Louppe +# Peter Prettenhofer +# Brian Holt +# Noel Dawe +# Satrajit Gosh +# Lars Buitinck +# Arnaud Joly +# Joel Nothman +# Fares Hedayati +# Jacob Schreiber +# +# License: BSD 3 clause + +from ._criterion cimport Criterion + +from libc.stdlib cimport qsort +from libc.string cimport memcpy +from libc.math cimport isnan +from cython cimport final + +import numpy as np + +from scipy.sparse import isspmatrix_csc + +from ._utils cimport log +from ._utils cimport rand_int +from ._utils cimport rand_uniform +from ._utils cimport RAND_R_MAX + +cdef double INFINITY = np.inf + +# Mitigate precision differences between 32 bit and 64 bit +cdef DTYPE_t FEATURE_THRESHOLD = 1e-7 + +# Constant to switch between algorithm non zero value extract algorithm +# in SparsePartitioner +cdef DTYPE_t EXTRACT_NNZ_SWITCH = 0.1 + +cdef inline void _init_split(SplitRecord* self, SIZE_t start_pos) noexcept nogil: + self.impurity_left = INFINITY + self.impurity_right = INFINITY + self.pos = start_pos + self.feature = 0 + self.threshold = 0. + self.improvement = -INFINITY + self.missing_go_to_left = False + self.n_missing = 0 + +cdef class Splitter: + """Abstract splitter class. + + Splitters are called by tree builders to find the best splits on both + sparse and dense data, one split at a time. + """ + + def __cinit__(self, Criterion criterion, SIZE_t max_features, + SIZE_t min_samples_leaf, double min_weight_leaf, + object random_state): + """ + Parameters + ---------- + criterion : Criterion + The criterion to measure the quality of a split. + + max_features : SIZE_t + The maximal number of randomly selected features which can be + considered for a split. + + min_samples_leaf : SIZE_t + The minimal number of samples each leaf can have, where splits + which would result in having less samples in a leaf are not + considered. + + min_weight_leaf : double + The minimal weight each leaf can have, where the weight is the sum + of the weights of each sample in it. + + random_state : object + The user inputted random state to be used for pseudo-randomness + """ + + self.criterion = criterion + + self.n_samples = 0 + self.n_features = 0 + + self.max_features = max_features + self.min_samples_leaf = min_samples_leaf + self.min_weight_leaf = min_weight_leaf + self.random_state = random_state + + def __getstate__(self): + return {} + + def __setstate__(self, d): + pass + + def __reduce__(self): + return (type(self), (self.criterion, + self.max_features, + self.min_samples_leaf, + self.min_weight_leaf, + self.random_state), self.__getstate__()) + + cdef int init( + self, + object X, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight, + const unsigned char[::1] missing_values_in_feature_mask, + ) except -1: + """Initialize the splitter. + + Take in the input data X, the target Y, and optional sample weights. + + Returns -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + + Parameters + ---------- + X : object + This contains the inputs. Usually it is a 2d numpy array. + + y : ndarray, dtype=DOUBLE_t + This is the vector of targets, or true labels, for the samples represented + as a Cython memoryview. + + sample_weight : ndarray, dtype=DOUBLE_t + The weights of the samples, where higher weighted samples are fit + closer than lower weight samples. If not provided, all samples + are assumed to have uniform weight. This is represented + as a Cython memoryview. + + has_missing : bool + At least one missing values is in X. + """ + + self.rand_r_state = self.random_state.randint(0, RAND_R_MAX) + cdef SIZE_t n_samples = X.shape[0] + + # Create a new array which will be used to store nonzero + # samples from the feature of interest + self.samples = np.empty(n_samples, dtype=np.intp) + cdef SIZE_t[::1] samples = self.samples + + cdef SIZE_t i, j + cdef double weighted_n_samples = 0.0 + j = 0 + + for i in range(n_samples): + # Only work with positively weighted samples + if sample_weight is None or sample_weight[i] != 0.0: + samples[j] = i + j += 1 + + if sample_weight is not None: + weighted_n_samples += sample_weight[i] + else: + weighted_n_samples += 1.0 + + # Number of samples is number of positively weighted samples + self.n_samples = j + self.weighted_n_samples = weighted_n_samples + + cdef SIZE_t n_features = X.shape[1] + self.features = np.arange(n_features, dtype=np.intp) + self.n_features = n_features + + self.feature_values = np.empty(n_samples, dtype=np.float32) + self.constant_features = np.empty(n_features, dtype=np.intp) + + self.y = y + + self.sample_weight = sample_weight + if missing_values_in_feature_mask is not None: + self.criterion.init_sum_missing() + return 0 + + cdef int node_reset(self, SIZE_t start, SIZE_t end, + double* weighted_n_node_samples) except -1 nogil: + """Reset splitter on node samples[start:end]. + + Returns -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + + Parameters + ---------- + start : SIZE_t + The index of the first sample to consider + end : SIZE_t + The index of the last sample to consider + weighted_n_node_samples : ndarray, dtype=double pointer + The total weight of those samples + """ + + self.start = start + self.end = end + + self.criterion.init( + self.y, + self.sample_weight, + self.weighted_n_samples, + self.samples, + start, + end + ) + + weighted_n_node_samples[0] = self.criterion.weighted_n_node_samples + return 0 + + cdef int node_split(self, double impurity, SplitRecord* split, + SIZE_t* n_constant_features) except -1 nogil: + """Find the best split on node samples[start:end]. + + This is a placeholder method. The majority of computation will be done + here. + + It should return -1 upon errors. + """ + + pass + + cdef void node_value(self, double* dest) noexcept nogil: + """Copy the value of node samples[start:end] into dest.""" + + self.criterion.node_value(dest) + + cdef double node_impurity(self) noexcept nogil: + """Return the impurity of the current node.""" + + return self.criterion.node_impurity() + +cdef inline void shift_missing_values_to_left_if_required( + SplitRecord* best, + SIZE_t[::1] samples, + SIZE_t end, +) nogil: + cdef SIZE_t i, p, current_end + # The partitioner partitions the data such that the missing values are in + # samples[-n_missing:] for the criterion to consume. If the missing values + # are going to the right node, then the missing values are already in the + # correct position. If the missing values go left, then we move the missing + # values to samples[best.pos:best.pos+n_missing] and update `best.pos`. + if best.n_missing > 0 and best.missing_go_to_left: + for p in range(best.n_missing): + i = best.pos + p + current_end = end - 1 - p + samples[i], samples[current_end] = samples[current_end], samples[i] + best.pos += best.n_missing + +# Introduce a fused-class to make it possible to share the split implementation +# between the dense and sparse cases in the node_split_best and node_split_random +# functions. The alternative would have been to use inheritance-based polymorphism +# but it would have resulted in a ~10% overall tree fitting performance +# degradation caused by the overhead frequent virtual method lookups. +ctypedef fused Partitioner: + DensePartitioner + SparsePartitioner + +cdef inline int node_split_best( + Splitter splitter, + Partitioner partitioner, + Criterion criterion, + double impurity, + SplitRecord* split, + SIZE_t* n_constant_features, +) except -1 nogil: + """Find the best split on node samples[start:end] + + Returns -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + """ + # Find the best split + cdef SIZE_t start = splitter.start + cdef SIZE_t end = splitter.end + cdef SIZE_t end_non_missing + cdef SIZE_t n_missing = 0 + cdef bint has_missing = 0 + cdef SIZE_t n_searches + cdef SIZE_t n_left, n_right + cdef bint missing_go_to_left + + cdef SIZE_t[::1] samples = splitter.samples + cdef SIZE_t[::1] features = splitter.features + cdef SIZE_t[::1] constant_features = splitter.constant_features + cdef SIZE_t n_features = splitter.n_features + + cdef DTYPE_t[::1] feature_values = splitter.feature_values + cdef SIZE_t max_features = splitter.max_features + cdef SIZE_t min_samples_leaf = splitter.min_samples_leaf + cdef double min_weight_leaf = splitter.min_weight_leaf + cdef UINT32_t* random_state = &splitter.rand_r_state + + cdef SplitRecord best_split, current_split + cdef double current_proxy_improvement = -INFINITY + cdef double best_proxy_improvement = -INFINITY + + cdef SIZE_t f_i = n_features + cdef SIZE_t f_j + cdef SIZE_t p + cdef SIZE_t p_prev + + cdef SIZE_t n_visited_features = 0 + # Number of features discovered to be constant during the split search + cdef SIZE_t n_found_constants = 0 + # Number of features known to be constant and drawn without replacement + cdef SIZE_t n_drawn_constants = 0 + cdef SIZE_t n_known_constants = n_constant_features[0] + # n_total_constants = n_known_constants + n_found_constants + cdef SIZE_t n_total_constants = n_known_constants + + _init_split(&best_split, end) + + partitioner.init_node_split(start, end) + + # Sample up to max_features without replacement using a + # Fisher-Yates-based algorithm (using the local variables `f_i` and + # `f_j` to compute a permutation of the `features` array). + # + # Skip the CPU intensive evaluation of the impurity criterion for + # features that were already detected as constant (hence not suitable + # for good splitting) by ancestor nodes and save the information on + # newly discovered constant features to spare computation on descendant + # nodes. + while (f_i > n_total_constants and # Stop early if remaining features + # are constant + (n_visited_features < max_features or + # At least one drawn features must be non constant + n_visited_features <= n_found_constants + n_drawn_constants)): + + n_visited_features += 1 + + # Loop invariant: elements of features in + # - [:n_drawn_constant[ holds drawn and known constant features; + # - [n_drawn_constant:n_known_constant[ holds known constant + # features that haven't been drawn yet; + # - [n_known_constant:n_total_constant[ holds newly found constant + # features; + # - [n_total_constant:f_i[ holds features that haven't been drawn + # yet and aren't constant apriori. + # - [f_i:n_features[ holds features that have been drawn + # and aren't constant. + + # Draw a feature at random + f_j = rand_int(n_drawn_constants, f_i - n_found_constants, + random_state) + + if f_j < n_known_constants: + # f_j in the interval [n_drawn_constants, n_known_constants[ + features[n_drawn_constants], features[f_j] = features[f_j], features[n_drawn_constants] + + n_drawn_constants += 1 + continue + + # f_j in the interval [n_known_constants, f_i - n_found_constants[ + f_j += n_found_constants + # f_j in the interval [n_total_constants, f_i[ + current_split.feature = features[f_j] + partitioner.sort_samples_and_feature_values(current_split.feature) + n_missing = partitioner.n_missing + end_non_missing = end - n_missing + + if ( + # All values for this feature are missing, or + end_non_missing == start or + # This feature is considered constant (max - min <= FEATURE_THRESHOLD) + feature_values[end_non_missing - 1] <= feature_values[start] + FEATURE_THRESHOLD + ): + # We consider this feature constant in this case. + # Since finding a split among constant feature is not valuable, + # we do not consider this feature for splitting. + features[f_j], features[n_total_constants] = features[n_total_constants], features[f_j] + + n_found_constants += 1 + n_total_constants += 1 + continue + + f_i -= 1 + features[f_i], features[f_j] = features[f_j], features[f_i] + has_missing = n_missing != 0 + if has_missing: + criterion.init_missing(n_missing) + # Evaluate all splits + + # If there are missing values, then we search twice for the most optimal split. + # The first search will have all the missing values going to the right node. + # The second search will have all the missing values going to the left node. + # If there are no missing values, then we search only once for the most + # optimal split. + n_searches = 2 if has_missing else 1 + + for i in range(n_searches): + missing_go_to_left = i == 1 + criterion.missing_go_to_left = missing_go_to_left + criterion.reset() + + p = start + + while p < end_non_missing: + partitioner.next_p(&p_prev, &p) + + if p >= end_non_missing: + continue + + if missing_go_to_left: + n_left = p - start + n_missing + n_right = end_non_missing - p + else: + n_left = p - start + n_right = end_non_missing - p + n_missing + + # Reject if min_samples_leaf is not guaranteed + if n_left < min_samples_leaf or n_right < min_samples_leaf: + continue + + current_split.pos = p + criterion.update(current_split.pos) + + # Reject if min_weight_leaf is not satisfied + if ((criterion.weighted_n_left < min_weight_leaf) or + (criterion.weighted_n_right < min_weight_leaf)): + continue + + current_proxy_improvement = criterion.proxy_impurity_improvement() + + if current_proxy_improvement > best_proxy_improvement: + best_proxy_improvement = current_proxy_improvement + # sum of halves is used to avoid infinite value + current_split.threshold = ( + feature_values[p_prev] / 2.0 + feature_values[p] / 2.0 + ) + + if ( + current_split.threshold == feature_values[p] or + current_split.threshold == INFINITY or + current_split.threshold == -INFINITY + ): + current_split.threshold = feature_values[p_prev] + + current_split.n_missing = n_missing + if n_missing == 0: + current_split.missing_go_to_left = n_left > n_right + else: + current_split.missing_go_to_left = missing_go_to_left + + best_split = current_split # copy + + # Evaluate when there are missing values and all missing values goes + # to the right node and non-missing values goes to the left node. + if has_missing: + n_left, n_right = end - start - n_missing, n_missing + p = end - n_missing + missing_go_to_left = 0 + + if not (n_left < min_samples_leaf or n_right < min_samples_leaf): + criterion.missing_go_to_left = missing_go_to_left + criterion.update(p) + + if not ((criterion.weighted_n_left < min_weight_leaf) or + (criterion.weighted_n_right < min_weight_leaf)): + current_proxy_improvement = criterion.proxy_impurity_improvement() + + if current_proxy_improvement > best_proxy_improvement: + best_proxy_improvement = current_proxy_improvement + current_split.threshold = INFINITY + current_split.missing_go_to_left = missing_go_to_left + current_split.n_missing = n_missing + current_split.pos = p + best_split = current_split + + # Reorganize into samples[start:best_split.pos] + samples[best_split.pos:end] + if best_split.pos < end: + partitioner.partition_samples_final( + best_split.pos, + best_split.threshold, + best_split.feature, + best_split.n_missing + ) + if best_split.n_missing != 0: + criterion.init_missing(best_split.n_missing) + criterion.missing_go_to_left = best_split.missing_go_to_left + + criterion.reset() + criterion.update(best_split.pos) + criterion.children_impurity( + &best_split.impurity_left, &best_split.impurity_right + ) + best_split.improvement = criterion.impurity_improvement( + impurity, + best_split.impurity_left, + best_split.impurity_right + ) + + shift_missing_values_to_left_if_required(&best_split, samples, end) + + # Respect invariant for constant features: the original order of + # element in features[:n_known_constants] must be preserved for sibling + # and child nodes + memcpy(&features[0], &constant_features[0], sizeof(SIZE_t) * n_known_constants) + + # Copy newly found constant features + memcpy(&constant_features[n_known_constants], + &features[n_known_constants], + sizeof(SIZE_t) * n_found_constants) + + # Return values + split[0] = best_split + n_constant_features[0] = n_total_constants + return 0 + + +# Sort n-element arrays pointed to by feature_values and samples, simultaneously, +# by the values in feature_values. Algorithm: Introsort (Musser, SP&E, 1997). +cdef inline void sort(DTYPE_t* feature_values, SIZE_t* samples, SIZE_t n) noexcept nogil: + if n == 0: + return + cdef int maxd = 2 * log(n) + introsort(feature_values, samples, n, maxd) + + +cdef inline void swap(DTYPE_t* feature_values, SIZE_t* samples, + SIZE_t i, SIZE_t j) noexcept nogil: + # Helper for sort + feature_values[i], feature_values[j] = feature_values[j], feature_values[i] + samples[i], samples[j] = samples[j], samples[i] + + +cdef inline DTYPE_t median3(DTYPE_t* feature_values, SIZE_t n) noexcept nogil: + # Median of three pivot selection, after Bentley and McIlroy (1993). + # Engineering a sort function. SP&E. Requires 8/3 comparisons on average. + cdef DTYPE_t a = feature_values[0], b = feature_values[n / 2], c = feature_values[n - 1] + if a < b: + if b < c: + return b + elif a < c: + return c + else: + return a + elif b < c: + if a < c: + return a + else: + return c + else: + return b + + +# Introsort with median of 3 pivot selection and 3-way partition function +# (robust to repeated elements, e.g. lots of zero features). +cdef void introsort(DTYPE_t* feature_values, SIZE_t *samples, + SIZE_t n, int maxd) noexcept nogil: + cdef DTYPE_t pivot + cdef SIZE_t i, l, r + + while n > 1: + if maxd <= 0: # max depth limit exceeded ("gone quadratic") + heapsort(feature_values, samples, n) + return + maxd -= 1 + + pivot = median3(feature_values, n) + + # Three-way partition. + i = l = 0 + r = n + while i < r: + if feature_values[i] < pivot: + swap(feature_values, samples, i, l) + i += 1 + l += 1 + elif feature_values[i] > pivot: + r -= 1 + swap(feature_values, samples, i, r) + else: + i += 1 + + introsort(feature_values, samples, l, maxd) + feature_values += r + samples += r + n -= r + + +cdef inline void sift_down(DTYPE_t* feature_values, SIZE_t* samples, + SIZE_t start, SIZE_t end) noexcept nogil: + # Restore heap order in feature_values[start:end] by moving the max element to start. + cdef SIZE_t child, maxind, root + + root = start + while True: + child = root * 2 + 1 + + # find max of root, left child, right child + maxind = root + if child < end and feature_values[maxind] < feature_values[child]: + maxind = child + if child + 1 < end and feature_values[maxind] < feature_values[child + 1]: + maxind = child + 1 + + if maxind == root: + break + else: + swap(feature_values, samples, root, maxind) + root = maxind + + +cdef void heapsort(DTYPE_t* feature_values, SIZE_t* samples, SIZE_t n) noexcept nogil: + cdef SIZE_t start, end + + # heapify + start = (n - 2) / 2 + end = n + while True: + sift_down(feature_values, samples, start, end) + if start == 0: + break + start -= 1 + + # sort by shrinking the heap, putting the max element immediately after it + end = n - 1 + while end > 0: + swap(feature_values, samples, 0, end) + sift_down(feature_values, samples, 0, end) + end = end - 1 + +cdef inline int node_split_random( + Splitter splitter, + Partitioner partitioner, + Criterion criterion, + double impurity, + SplitRecord* split, + SIZE_t* n_constant_features +) except -1 nogil: + """Find the best random split on node samples[start:end] + + Returns -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + """ + # Draw random splits and pick the best + cdef SIZE_t start = splitter.start + cdef SIZE_t end = splitter.end + + cdef SIZE_t[::1] features = splitter.features + cdef SIZE_t[::1] constant_features = splitter.constant_features + cdef SIZE_t n_features = splitter.n_features + + cdef SIZE_t max_features = splitter.max_features + cdef SIZE_t min_samples_leaf = splitter.min_samples_leaf + cdef double min_weight_leaf = splitter.min_weight_leaf + cdef UINT32_t* random_state = &splitter.rand_r_state + + cdef SplitRecord best_split, current_split + cdef double current_proxy_improvement = - INFINITY + cdef double best_proxy_improvement = - INFINITY + + cdef SIZE_t f_i = n_features + cdef SIZE_t f_j + # Number of features discovered to be constant during the split search + cdef SIZE_t n_found_constants = 0 + # Number of features known to be constant and drawn without replacement + cdef SIZE_t n_drawn_constants = 0 + cdef SIZE_t n_known_constants = n_constant_features[0] + # n_total_constants = n_known_constants + n_found_constants + cdef SIZE_t n_total_constants = n_known_constants + cdef SIZE_t n_visited_features = 0 + cdef DTYPE_t min_feature_value + cdef DTYPE_t max_feature_value + + _init_split(&best_split, end) + + partitioner.init_node_split(start, end) + + # Sample up to max_features without replacement using a + # Fisher-Yates-based algorithm (using the local variables `f_i` and + # `f_j` to compute a permutation of the `features` array). + # + # Skip the CPU intensive evaluation of the impurity criterion for + # features that were already detected as constant (hence not suitable + # for good splitting) by ancestor nodes and save the information on + # newly discovered constant features to spare computation on descendant + # nodes. + while (f_i > n_total_constants and # Stop early if remaining features + # are constant + (n_visited_features < max_features or + # At least one drawn features must be non constant + n_visited_features <= n_found_constants + n_drawn_constants)): + n_visited_features += 1 + + # Loop invariant: elements of features in + # - [:n_drawn_constant[ holds drawn and known constant features; + # - [n_drawn_constant:n_known_constant[ holds known constant + # features that haven't been drawn yet; + # - [n_known_constant:n_total_constant[ holds newly found constant + # features; + # - [n_total_constant:f_i[ holds features that haven't been drawn + # yet and aren't constant apriori. + # - [f_i:n_features[ holds features that have been drawn + # and aren't constant. + + # Draw a feature at random + f_j = rand_int(n_drawn_constants, f_i - n_found_constants, + random_state) + + if f_j < n_known_constants: + # f_j in the interval [n_drawn_constants, n_known_constants[ + features[n_drawn_constants], features[f_j] = features[f_j], features[n_drawn_constants] + n_drawn_constants += 1 + continue + + # f_j in the interval [n_known_constants, f_i - n_found_constants[ + f_j += n_found_constants + # f_j in the interval [n_total_constants, f_i[ + + current_split.feature = features[f_j] + + # Find min, max + partitioner.find_min_max( + current_split.feature, &min_feature_value, &max_feature_value + ) + + if max_feature_value <= min_feature_value + FEATURE_THRESHOLD: + features[f_j], features[n_total_constants] = features[n_total_constants], current_split.feature + + n_found_constants += 1 + n_total_constants += 1 + continue + + f_i -= 1 + features[f_i], features[f_j] = features[f_j], features[f_i] + + # Draw a random threshold + current_split.threshold = rand_uniform( + min_feature_value, + max_feature_value, + random_state, + ) + + if current_split.threshold == max_feature_value: + current_split.threshold = min_feature_value + + # Partition + current_split.pos = partitioner.partition_samples(current_split.threshold) + + # Reject if min_samples_leaf is not guaranteed + if (((current_split.pos - start) < min_samples_leaf) or + ((end - current_split.pos) < min_samples_leaf)): + continue + + # Evaluate split + # At this point, the criterion has a view into the samples that was partitioned + # by the partitioner. The criterion will use the partition to evaluating the split. + criterion.reset() + criterion.update(current_split.pos) + + # Reject if min_weight_leaf is not satisfied + if ((criterion.weighted_n_left < min_weight_leaf) or + (criterion.weighted_n_right < min_weight_leaf)): + continue + + current_proxy_improvement = criterion.proxy_impurity_improvement() + + if current_proxy_improvement > best_proxy_improvement: + best_proxy_improvement = current_proxy_improvement + best_split = current_split # copy + + # Reorganize into samples[start:best.pos] + samples[best.pos:end] + if best_split.pos < end: + if current_split.feature != best_split.feature: + # TODO: Pass in best.n_missing when random splitter supports missing values. + partitioner.partition_samples_final( + best_split.pos, best_split.threshold, best_split.feature, 0 + ) + + criterion.reset() + criterion.update(best_split.pos) + criterion.children_impurity( + &best_split.impurity_left, &best_split.impurity_right + ) + best_split.improvement = criterion.impurity_improvement( + impurity, best_split.impurity_left, best_split.impurity_right + ) + + # Respect invariant for constant features: the original order of + # element in features[:n_known_constants] must be preserved for sibling + # and child nodes + memcpy(&features[0], &constant_features[0], sizeof(SIZE_t) * n_known_constants) + + # Copy newly found constant features + memcpy(&constant_features[n_known_constants], + &features[n_known_constants], + sizeof(SIZE_t) * n_found_constants) + + # Return values + split[0] = best_split + n_constant_features[0] = n_total_constants + return 0 + + +@final +cdef class DensePartitioner: + """Partitioner specialized for dense data. + + Note that this partitioner is agnostic to the splitting strategy (best vs. random). + """ + cdef: + const DTYPE_t[:, :] X + cdef SIZE_t[::1] samples + cdef DTYPE_t[::1] feature_values + cdef SIZE_t start + cdef SIZE_t end + cdef SIZE_t n_missing + cdef const unsigned char[::1] missing_values_in_feature_mask + + def __init__( + self, + const DTYPE_t[:, :] X, + SIZE_t[::1] samples, + DTYPE_t[::1] feature_values, + const unsigned char[::1] missing_values_in_feature_mask, + ): + self.X = X + self.samples = samples + self.feature_values = feature_values + self.missing_values_in_feature_mask = missing_values_in_feature_mask + + cdef inline void init_node_split(self, SIZE_t start, SIZE_t end) noexcept nogil: + """Initialize splitter at the beginning of node_split.""" + self.start = start + self.end = end + self.n_missing = 0 + + cdef inline void sort_samples_and_feature_values( + self, SIZE_t current_feature + ) noexcept nogil: + """Simultaneously sort based on the feature_values. + + Missing values are stored at the end of feature_values. + The number of missing values observed in feature_values is stored + in self.n_missing. + """ + cdef: + SIZE_t i, current_end + DTYPE_t[::1] feature_values = self.feature_values + const DTYPE_t[:, :] X = self.X + SIZE_t[::1] samples = self.samples + SIZE_t n_missing = 0 + const unsigned char[::1] missing_values_in_feature_mask = self.missing_values_in_feature_mask + + # Sort samples along that feature; by + # copying the values into an array and + # sorting the array in a manner which utilizes the cache more + # effectively. + if missing_values_in_feature_mask is not None and missing_values_in_feature_mask[current_feature]: + i, current_end = self.start, self.end - 1 + # Missing values are placed at the end and do not participate in the sorting. + while i <= current_end: + # Finds the right-most value that is not missing so that + # it can be swapped with missing values at its left. + if isnan(X[samples[current_end], current_feature]): + n_missing += 1 + current_end -= 1 + continue + + # X[samples[current_end], current_feature] is a non-missing value + if isnan(X[samples[i], current_feature]): + samples[i], samples[current_end] = samples[current_end], samples[i] + n_missing += 1 + current_end -= 1 + + feature_values[i] = X[samples[i], current_feature] + i += 1 + else: + # When there are no missing values, we only need to copy the data into + # feature_values + for i in range(self.start, self.end): + feature_values[i] = X[samples[i], current_feature] + + sort(&feature_values[self.start], &samples[self.start], self.end - self.start - n_missing) + self.n_missing = n_missing + + cdef inline void find_min_max( + self, + SIZE_t current_feature, + DTYPE_t* min_feature_value_out, + DTYPE_t* max_feature_value_out, + ) noexcept nogil: + """Find the minimum and maximum value for current_feature.""" + cdef: + SIZE_t p + DTYPE_t current_feature_value + const DTYPE_t[:, :] X = self.X + SIZE_t[::1] samples = self.samples + DTYPE_t min_feature_value = X[samples[self.start], current_feature] + DTYPE_t max_feature_value = min_feature_value + DTYPE_t[::1] feature_values = self.feature_values + + feature_values[self.start] = min_feature_value + + for p in range(self.start + 1, self.end): + current_feature_value = X[samples[p], current_feature] + feature_values[p] = current_feature_value + + if current_feature_value < min_feature_value: + min_feature_value = current_feature_value + elif current_feature_value > max_feature_value: + max_feature_value = current_feature_value + + min_feature_value_out[0] = min_feature_value + max_feature_value_out[0] = max_feature_value + + cdef inline void next_p(self, SIZE_t* p_prev, SIZE_t* p) noexcept nogil: + """Compute the next p_prev and p for iteratiing over feature values. + + The missing values are not included when iterating through the feature values. + """ + cdef: + DTYPE_t[::1] feature_values = self.feature_values + SIZE_t end_non_missing = self.end - self.n_missing + + while ( + p[0] + 1 < end_non_missing and + feature_values[p[0] + 1] <= feature_values[p[0]] + FEATURE_THRESHOLD + ): + p[0] += 1 + + p_prev[0] = p[0] + + # By adding 1, we have + # (feature_values[p] >= end) or (feature_values[p] > feature_values[p - 1]) + p[0] += 1 + + cdef inline SIZE_t partition_samples(self, double current_threshold) noexcept nogil: + """Partition samples for feature_values at the current_threshold.""" + cdef: + SIZE_t p = self.start + SIZE_t partition_end = self.end + SIZE_t[::1] samples = self.samples + DTYPE_t[::1] feature_values = self.feature_values + + while p < partition_end: + if feature_values[p] <= current_threshold: + p += 1 + else: + partition_end -= 1 + + feature_values[p], feature_values[partition_end] = ( + feature_values[partition_end], feature_values[p] + ) + samples[p], samples[partition_end] = samples[partition_end], samples[p] + + return partition_end + + cdef inline void partition_samples_final( + self, + SIZE_t best_pos, + double best_threshold, + SIZE_t best_feature, + SIZE_t best_n_missing, + ) noexcept nogil: + """Partition samples for X at the best_threshold and best_feature. + + If missing values are present, this method partitions `samples` + so that the `best_n_missing` missing values' indices are in the + right-most end of `samples`, that is `samples[end_non_missing:end]`. + """ + cdef: + # Local invariance: start <= p <= partition_end <= end + SIZE_t start = self.start + SIZE_t p = start + SIZE_t end = self.end - 1 + SIZE_t partition_end = end - best_n_missing + SIZE_t[::1] samples = self.samples + const DTYPE_t[:, :] X = self.X + DTYPE_t current_value + + if best_n_missing != 0: + # Move samples with missing values to the end while partitioning the + # non-missing samples + while p < partition_end: + # Keep samples with missing values at the end + if isnan(X[samples[end], best_feature]): + end -= 1 + continue + + # Swap sample with missing values with the sample at the end + current_value = X[samples[p], best_feature] + if isnan(current_value): + samples[p], samples[end] = samples[end], samples[p] + end -= 1 + + # The swapped sample at the end is always a non-missing value, so + # we can continue the algorithm without checking for missingness. + current_value = X[samples[p], best_feature] + + # Partition the non-missing samples + if current_value <= best_threshold: + p += 1 + else: + samples[p], samples[partition_end] = samples[partition_end], samples[p] + partition_end -= 1 + else: + # Partitioning routine when there are no missing values + while p < partition_end: + if X[samples[p], best_feature] <= best_threshold: + p += 1 + else: + samples[p], samples[partition_end] = samples[partition_end], samples[p] + partition_end -= 1 + + +@final +cdef class SparsePartitioner: + """Partitioner specialized for sparse CSC data. + + Note that this partitioner is agnostic to the splitting strategy (best vs. random). + """ + cdef SIZE_t[::1] samples + cdef DTYPE_t[::1] feature_values + cdef SIZE_t start + cdef SIZE_t end + cdef SIZE_t n_missing + cdef const unsigned char[::1] missing_values_in_feature_mask + + cdef const DTYPE_t[::1] X_data + cdef const INT32_t[::1] X_indices + cdef const INT32_t[::1] X_indptr + + cdef SIZE_t n_total_samples + + cdef SIZE_t[::1] index_to_samples + cdef SIZE_t[::1] sorted_samples + + cdef SIZE_t start_positive + cdef SIZE_t end_negative + cdef bint is_samples_sorted + + def __init__( + self, + object X, + SIZE_t[::1] samples, + SIZE_t n_samples, + DTYPE_t[::1] feature_values, + const unsigned char[::1] missing_values_in_feature_mask, + ): + if not isspmatrix_csc(X): + raise ValueError("X should be in csc format") + + self.samples = samples + self.feature_values = feature_values + + # Initialize X + cdef SIZE_t n_total_samples = X.shape[0] + + self.X_data = X.data + self.X_indices = X.indices + self.X_indptr = X.indptr + self.n_total_samples = n_total_samples + + # Initialize auxiliary array used to perform split + self.index_to_samples = np.full(n_total_samples, fill_value=-1, dtype=np.intp) + self.sorted_samples = np.empty(n_samples, dtype=np.intp) + + cdef SIZE_t p + for p in range(n_samples): + self.index_to_samples[samples[p]] = p + + self.missing_values_in_feature_mask = missing_values_in_feature_mask + + cdef inline void init_node_split(self, SIZE_t start, SIZE_t end) noexcept nogil: + """Initialize splitter at the beginning of node_split.""" + self.start = start + self.end = end + self.is_samples_sorted = 0 + self.n_missing = 0 + + cdef inline void sort_samples_and_feature_values( + self, SIZE_t current_feature + ) noexcept nogil: + """Simultaneously sort based on the feature_values.""" + cdef: + DTYPE_t[::1] feature_values = self.feature_values + SIZE_t[::1] index_to_samples = self.index_to_samples + SIZE_t[::1] samples = self.samples + + self.extract_nnz(current_feature) + # Sort the positive and negative parts of `feature_values` + sort(&feature_values[self.start], &samples[self.start], self.end_negative - self.start) + if self.start_positive < self.end: + sort( + &feature_values[self.start_positive], + &samples[self.start_positive], + self.end - self.start_positive + ) + + # Update index_to_samples to take into account the sort + for p in range(self.start, self.end_negative): + index_to_samples[samples[p]] = p + for p in range(self.start_positive, self.end): + index_to_samples[samples[p]] = p + + # Add one or two zeros in feature_values, if there is any + if self.end_negative < self.start_positive: + self.start_positive -= 1 + feature_values[self.start_positive] = 0. + + if self.end_negative != self.start_positive: + feature_values[self.end_negative] = 0. + self.end_negative += 1 + + # XXX: When sparse supports missing values, this should be set to the + # number of missing values for current_feature + self.n_missing = 0 + + cdef inline void find_min_max( + self, + SIZE_t current_feature, + DTYPE_t* min_feature_value_out, + DTYPE_t* max_feature_value_out, + ) noexcept nogil: + """Find the minimum and maximum value for current_feature.""" + cdef: + SIZE_t p + DTYPE_t current_feature_value, min_feature_value, max_feature_value + DTYPE_t[::1] feature_values = self.feature_values + + self.extract_nnz(current_feature) + + if self.end_negative != self.start_positive: + # There is a zero + min_feature_value = 0 + max_feature_value = 0 + else: + min_feature_value = feature_values[self.start] + max_feature_value = min_feature_value + + # Find min, max in feature_values[start:end_negative] + for p in range(self.start, self.end_negative): + current_feature_value = feature_values[p] + + if current_feature_value < min_feature_value: + min_feature_value = current_feature_value + elif current_feature_value > max_feature_value: + max_feature_value = current_feature_value + + # Update min, max given feature_values[start_positive:end] + for p in range(self.start_positive, self.end): + current_feature_value = feature_values[p] + + if current_feature_value < min_feature_value: + min_feature_value = current_feature_value + elif current_feature_value > max_feature_value: + max_feature_value = current_feature_value + + min_feature_value_out[0] = min_feature_value + max_feature_value_out[0] = max_feature_value + + cdef inline void next_p(self, SIZE_t* p_prev, SIZE_t* p) noexcept nogil: + """Compute the next p_prev and p for iteratiing over feature values.""" + cdef: + SIZE_t p_next + DTYPE_t[::1] feature_values = self.feature_values + + if p[0] + 1 != self.end_negative: + p_next = p[0] + 1 + else: + p_next = self.start_positive + + while (p_next < self.end and + feature_values[p_next] <= feature_values[p[0]] + FEATURE_THRESHOLD): + p[0] = p_next + if p[0] + 1 != self.end_negative: + p_next = p[0] + 1 + else: + p_next = self.start_positive + + p_prev[0] = p[0] + p[0] = p_next + + cdef inline SIZE_t partition_samples(self, double current_threshold) noexcept nogil: + """Partition samples for feature_values at the current_threshold.""" + return self._partition(current_threshold, self.start_positive) + + cdef inline void partition_samples_final( + self, + SIZE_t best_pos, + double best_threshold, + SIZE_t best_feature, + SIZE_t n_missing, + ) noexcept nogil: + """Partition samples for X at the best_threshold and best_feature.""" + self.extract_nnz(best_feature) + self._partition(best_threshold, best_pos) + + cdef inline SIZE_t _partition(self, double threshold, SIZE_t zero_pos) noexcept nogil: + """Partition samples[start:end] based on threshold.""" + cdef: + SIZE_t p, partition_end + SIZE_t[::1] index_to_samples = self.index_to_samples + DTYPE_t[::1] feature_values = self.feature_values + SIZE_t[::1] samples = self.samples + + if threshold < 0.: + p = self.start + partition_end = self.end_negative + elif threshold > 0.: + p = self.start_positive + partition_end = self.end + else: + # Data are already split + return zero_pos + + while p < partition_end: + if feature_values[p] <= threshold: + p += 1 + + else: + partition_end -= 1 + + feature_values[p], feature_values[partition_end] = ( + feature_values[partition_end], feature_values[p] + ) + sparse_swap(index_to_samples, samples, p, partition_end) + + return partition_end + + cdef inline void extract_nnz(self, SIZE_t feature) noexcept nogil: + """Extract and partition values for a given feature. + + The extracted values are partitioned between negative values + feature_values[start:end_negative[0]] and positive values + feature_values[start_positive[0]:end]. + The samples and index_to_samples are modified according to this + partition. + + The extraction corresponds to the intersection between the arrays + X_indices[indptr_start:indptr_end] and samples[start:end]. + This is done efficiently using either an index_to_samples based approach + or binary search based approach. + + Parameters + ---------- + feature : SIZE_t, + Index of the feature we want to extract non zero value. + """ + cdef SIZE_t[::1] samples = self.samples + cdef DTYPE_t[::1] feature_values = self.feature_values + cdef SIZE_t indptr_start = self.X_indptr[feature], + cdef SIZE_t indptr_end = self.X_indptr[feature + 1] + cdef SIZE_t n_indices = (indptr_end - indptr_start) + cdef SIZE_t n_samples = self.end - self.start + cdef SIZE_t[::1] index_to_samples = self.index_to_samples + cdef SIZE_t[::1] sorted_samples = self.sorted_samples + cdef const INT32_t[::1] X_indices = self.X_indices + cdef const DTYPE_t[::1] X_data = self.X_data + + # Use binary search if n_samples * log(n_indices) < + # n_indices and index_to_samples approach otherwise. + # O(n_samples * log(n_indices)) is the running time of binary + # search and O(n_indices) is the running time of index_to_samples + # approach. + if ((1 - self.is_samples_sorted) * n_samples * log(n_samples) + + n_samples * log(n_indices) < EXTRACT_NNZ_SWITCH * n_indices): + extract_nnz_binary_search(X_indices, X_data, + indptr_start, indptr_end, + samples, self.start, self.end, + index_to_samples, + feature_values, + &self.end_negative, &self.start_positive, + sorted_samples, &self.is_samples_sorted) + + # Using an index to samples technique to extract non zero values + # index_to_samples is a mapping from X_indices to samples + else: + extract_nnz_index_to_samples(X_indices, X_data, + indptr_start, indptr_end, + samples, self.start, self.end, + index_to_samples, + feature_values, + &self.end_negative, &self.start_positive) + + +cdef int compare_SIZE_t(const void* a, const void* b) noexcept nogil: + """Comparison function for sort.""" + return ((a)[0] - (b)[0]) + + +cdef inline void binary_search(const INT32_t[::1] sorted_array, + INT32_t start, INT32_t end, + SIZE_t value, SIZE_t* index, + INT32_t* new_start) noexcept nogil: + """Return the index of value in the sorted array. + + If not found, return -1. new_start is the last pivot + 1 + """ + cdef INT32_t pivot + index[0] = -1 + while start < end: + pivot = start + (end - start) / 2 + + if sorted_array[pivot] == value: + index[0] = pivot + start = pivot + 1 + break + + if sorted_array[pivot] < value: + start = pivot + 1 + else: + end = pivot + new_start[0] = start + + +cdef inline void extract_nnz_index_to_samples(const INT32_t[::1] X_indices, + const DTYPE_t[::1] X_data, + INT32_t indptr_start, + INT32_t indptr_end, + SIZE_t[::1] samples, + SIZE_t start, + SIZE_t end, + SIZE_t[::1] index_to_samples, + DTYPE_t[::1] feature_values, + SIZE_t* end_negative, + SIZE_t* start_positive) noexcept nogil: + """Extract and partition values for a feature using index_to_samples. + + Complexity is O(indptr_end - indptr_start). + """ + cdef INT32_t k + cdef SIZE_t index + cdef SIZE_t end_negative_ = start + cdef SIZE_t start_positive_ = end + + for k in range(indptr_start, indptr_end): + if start <= index_to_samples[X_indices[k]] < end: + if X_data[k] > 0: + start_positive_ -= 1 + feature_values[start_positive_] = X_data[k] + index = index_to_samples[X_indices[k]] + sparse_swap(index_to_samples, samples, index, start_positive_) + + elif X_data[k] < 0: + feature_values[end_negative_] = X_data[k] + index = index_to_samples[X_indices[k]] + sparse_swap(index_to_samples, samples, index, end_negative_) + end_negative_ += 1 + + # Returned values + end_negative[0] = end_negative_ + start_positive[0] = start_positive_ + + +cdef inline void extract_nnz_binary_search(const INT32_t[::1] X_indices, + const DTYPE_t[::1] X_data, + INT32_t indptr_start, + INT32_t indptr_end, + SIZE_t[::1] samples, + SIZE_t start, + SIZE_t end, + SIZE_t[::1] index_to_samples, + DTYPE_t[::1] feature_values, + SIZE_t* end_negative, + SIZE_t* start_positive, + SIZE_t[::1] sorted_samples, + bint* is_samples_sorted) noexcept nogil: + """Extract and partition values for a given feature using binary search. + + If n_samples = end - start and n_indices = indptr_end - indptr_start, + the complexity is + + O((1 - is_samples_sorted[0]) * n_samples * log(n_samples) + + n_samples * log(n_indices)). + """ + cdef SIZE_t n_samples + + if not is_samples_sorted[0]: + n_samples = end - start + memcpy(&sorted_samples[start], &samples[start], + n_samples * sizeof(SIZE_t)) + qsort(&sorted_samples[start], n_samples, sizeof(SIZE_t), + compare_SIZE_t) + is_samples_sorted[0] = 1 + + while (indptr_start < indptr_end and + sorted_samples[start] > X_indices[indptr_start]): + indptr_start += 1 + + while (indptr_start < indptr_end and + sorted_samples[end - 1] < X_indices[indptr_end - 1]): + indptr_end -= 1 + + cdef SIZE_t p = start + cdef SIZE_t index + cdef SIZE_t k + cdef SIZE_t end_negative_ = start + cdef SIZE_t start_positive_ = end + + while (p < end and indptr_start < indptr_end): + # Find index of sorted_samples[p] in X_indices + binary_search(X_indices, indptr_start, indptr_end, + sorted_samples[p], &k, &indptr_start) + + if k != -1: + # If k != -1, we have found a non zero value + + if X_data[k] > 0: + start_positive_ -= 1 + feature_values[start_positive_] = X_data[k] + index = index_to_samples[X_indices[k]] + sparse_swap(index_to_samples, samples, index, start_positive_) + + elif X_data[k] < 0: + feature_values[end_negative_] = X_data[k] + index = index_to_samples[X_indices[k]] + sparse_swap(index_to_samples, samples, index, end_negative_) + end_negative_ += 1 + p += 1 + + # Returned values + end_negative[0] = end_negative_ + start_positive[0] = start_positive_ + + +cdef inline void sparse_swap(SIZE_t[::1] index_to_samples, SIZE_t[::1] samples, + SIZE_t pos_1, SIZE_t pos_2) noexcept nogil: + """Swap sample pos_1 and pos_2 preserving sparse invariant.""" + samples[pos_1], samples[pos_2] = samples[pos_2], samples[pos_1] + index_to_samples[samples[pos_1]] = pos_1 + index_to_samples[samples[pos_2]] = pos_2 + + +cdef class BestSplitter(Splitter): + """Splitter for finding the best split on dense data.""" + cdef DensePartitioner partitioner + cdef int init( + self, + object X, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight, + const unsigned char[::1] missing_values_in_feature_mask, + ) except -1: + Splitter.init(self, X, y, sample_weight, missing_values_in_feature_mask) + self.partitioner = DensePartitioner( + X, self.samples, self.feature_values, missing_values_in_feature_mask + ) + + cdef int node_split(self, double impurity, SplitRecord* split, + SIZE_t* n_constant_features) except -1 nogil: + return node_split_best( + self, + self.partitioner, + self.criterion, + impurity, + split, + n_constant_features, + ) + +cdef class BestSparseSplitter(Splitter): + """Splitter for finding the best split, using the sparse data.""" + cdef SparsePartitioner partitioner + cdef int init( + self, + object X, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight, + const unsigned char[::1] missing_values_in_feature_mask, + ) except -1: + Splitter.init(self, X, y, sample_weight, missing_values_in_feature_mask) + self.partitioner = SparsePartitioner( + X, self.samples, self.n_samples, self.feature_values, missing_values_in_feature_mask + ) + + cdef int node_split(self, double impurity, SplitRecord* split, + SIZE_t* n_constant_features) except -1 nogil: + return node_split_best( + self, + self.partitioner, + self.criterion, + impurity, + split, + n_constant_features, + ) + +cdef class RandomSplitter(Splitter): + """Splitter for finding the best random split on dense data.""" + cdef DensePartitioner partitioner + cdef int init( + self, + object X, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight, + const unsigned char[::1] missing_values_in_feature_mask, + ) except -1: + Splitter.init(self, X, y, sample_weight, missing_values_in_feature_mask) + self.partitioner = DensePartitioner( + X, self.samples, self.feature_values, missing_values_in_feature_mask + ) + + cdef int node_split(self, double impurity, SplitRecord* split, + SIZE_t* n_constant_features) except -1 nogil: + return node_split_random( + self, + self.partitioner, + self.criterion, + impurity, + split, + n_constant_features, + ) + +cdef class RandomSparseSplitter(Splitter): + """Splitter for finding the best random split, using the sparse data.""" + cdef SparsePartitioner partitioner + cdef int init( + self, + object X, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight, + const unsigned char[::1] missing_values_in_feature_mask, + ) except -1: + Splitter.init(self, X, y, sample_weight, missing_values_in_feature_mask) + self.partitioner = SparsePartitioner( + X, self.samples, self.n_samples, self.feature_values, missing_values_in_feature_mask + ) + + cdef int node_split(self, double impurity, SplitRecord* split, + SIZE_t* n_constant_features) except -1 nogil: + return node_split_random( + self, + self.partitioner, + self.criterion, + impurity, + split, + n_constant_features, + ) \ No newline at end of file diff --git a/ivy/functional/frontends/sklearn/_tree copy.pyx b/ivy/functional/frontends/sklearn/_tree copy.pyx new file mode 100644 index 0000000000000..f436c919f8bc2 --- /dev/null +++ b/ivy/functional/frontends/sklearn/_tree copy.pyx @@ -0,0 +1,1833 @@ +# Authors: Gilles Louppe +# Peter Prettenhofer +# Brian Holt +# Noel Dawe +# Satrajit Gosh +# Lars Buitinck +# Arnaud Joly +# Joel Nothman +# Fares Hedayati +# Jacob Schreiber +# Nelson Liu +# +# License: BSD 3 clause + +from cpython cimport Py_INCREF, PyObject, PyTypeObject + +from libc.stdlib cimport free +from libc.string cimport memcpy +from libc.string cimport memset +from libc.stdint cimport INTPTR_MAX +from libc.math cimport isnan +from libcpp.vector cimport vector +from libcpp.algorithm cimport pop_heap +from libcpp.algorithm cimport push_heap +from libcpp cimport bool + +import struct + +import numpy as np +cimport numpy as cnp +cnp.import_array() + +from scipy.sparse import issparse +from scipy.sparse import csr_matrix +from scipy.sparse import isspmatrix_csr + +from ._utils cimport safe_realloc +from ._utils cimport sizet_ptr_to_ndarray + +cdef extern from "numpy/arrayobject.h": + object PyArray_NewFromDescr(PyTypeObject* subtype, cnp.dtype descr, + int nd, cnp.npy_intp* dims, + cnp.npy_intp* strides, + void* data, int flags, object obj) + int PyArray_SetBaseObject(cnp.ndarray arr, PyObject* obj) + +cdef extern from "" namespace "std" nogil: + cdef cppclass stack[T]: + ctypedef T value_type + stack() except + + bint empty() + void pop() + void push(T&) except + # Raise c++ exception for bad_alloc -> MemoryError + T& top() + +# ============================================================================= +# Types and constants +# ============================================================================= + +from numpy import float32 as DTYPE +from numpy import float64 as DOUBLE + +cdef double INFINITY = np.inf +cdef double EPSILON = np.finfo('double').eps + +# Some handy constants (BestFirstTreeBuilder) +cdef int IS_FIRST = 1 +cdef int IS_NOT_FIRST = 0 +cdef int IS_LEFT = 1 +cdef int IS_NOT_LEFT = 0 + +TREE_LEAF = -1 +TREE_UNDEFINED = -2 +cdef SIZE_t _TREE_LEAF = TREE_LEAF +cdef SIZE_t _TREE_UNDEFINED = TREE_UNDEFINED + +# Build the corresponding numpy dtype for Node. +# This works by casting `dummy` to an array of Node of length 1, which numpy +# can construct a `dtype`-object for. See https://stackoverflow.com/q/62448946 +# for a more detailed explanation. +cdef Node dummy +NODE_DTYPE = np.asarray((&dummy)).dtype + +# ============================================================================= +# TreeBuilder +# ============================================================================= + +cdef class TreeBuilder: + """Interface for different tree building strategies.""" + + cpdef build( + self, + Tree tree, + object X, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight=None, + const unsigned char[::1] missing_values_in_feature_mask=None, + ): + """Build a decision tree from the training set (X, y).""" + pass + + cdef inline _check_input( + self, + object X, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight, + ): + """Check input dtype, layout and format""" + if issparse(X): + X = X.tocsc() + X.sort_indices() + + if X.data.dtype != DTYPE: + X.data = np.ascontiguousarray(X.data, dtype=DTYPE) + + if X.indices.dtype != np.int32 or X.indptr.dtype != np.int32: + raise ValueError("No support for np.int64 index based " + "sparse matrices") + + elif X.dtype != DTYPE: + # since we have to copy we will make it fortran for efficiency + X = np.asfortranarray(X, dtype=DTYPE) + + # TODO: This check for y seems to be redundant, as it is also + # present in the BaseDecisionTree's fit method, and therefore + # can be removed. + if y.base.dtype != DOUBLE or not y.base.flags.contiguous: + y = np.ascontiguousarray(y, dtype=DOUBLE) + + if ( + sample_weight is not None and + ( + sample_weight.base.dtype != DOUBLE or + not sample_weight.base.flags.contiguous + ) + ): + sample_weight = np.asarray(sample_weight, dtype=DOUBLE, order="C") + + return X, y, sample_weight + +# Depth first builder --------------------------------------------------------- +# A record on the stack for depth-first tree growing +cdef struct StackRecord: + SIZE_t start + SIZE_t end + SIZE_t depth + SIZE_t parent + bint is_left + double impurity + SIZE_t n_constant_features + + + + + + + +cdef class DepthFirstTreeBuilder(TreeBuilder): + """Build a decision tree in depth-first fashion.""" + + def __cinit__(self, Splitter splitter, SIZE_t min_samples_split, + SIZE_t min_samples_leaf, double min_weight_leaf, + SIZE_t max_depth, double min_impurity_decrease): + self.splitter = splitter + self.min_samples_split = min_samples_split + self.min_samples_leaf = min_samples_leaf + self.min_weight_leaf = min_weight_leaf + self.max_depth = max_depth + self.min_impurity_decrease = min_impurity_decrease + + cpdef build( + self, + Tree tree, + object X, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight=None, + const unsigned char[::1] missing_values_in_feature_mask=None, + ): + """Build a decision tree from the training set (X, y).""" + + # check input + X, y, sample_weight = self._check_input(X, y, sample_weight) + + # Initial capacity + cdef int init_capacity + + if tree.max_depth <= 10: + init_capacity = (2 ** (tree.max_depth + 1)) - 1 + else: + init_capacity = 2047 + + tree._resize(init_capacity) + + # Parameters + cdef Splitter splitter = self.splitter + cdef SIZE_t max_depth = self.max_depth + cdef SIZE_t min_samples_leaf = self.min_samples_leaf + cdef double min_weight_leaf = self.min_weight_leaf + cdef SIZE_t min_samples_split = self.min_samples_split + cdef double min_impurity_decrease = self.min_impurity_decrease + + # Recursive partition (without actual recursion) + splitter.init(X, y, sample_weight, missing_values_in_feature_mask) + + cdef SIZE_t start + cdef SIZE_t end + cdef SIZE_t depth + cdef SIZE_t parent + cdef bint is_left + cdef SIZE_t n_node_samples = splitter.n_samples + cdef double weighted_n_node_samples + cdef SplitRecord split + cdef SIZE_t node_id + + cdef double impurity = INFINITY + cdef SIZE_t n_constant_features + cdef bint is_leaf + cdef bint first = 1 + cdef SIZE_t max_depth_seen = -1 + cdef int rc = 0 + + cdef stack[StackRecord] builder_stack + cdef StackRecord stack_record + + with nogil: + # push root node onto stack + builder_stack.push({ + "start": 0, + "end": n_node_samples, + "depth": 0, + "parent": _TREE_UNDEFINED, + "is_left": 0, + "impurity": INFINITY, + "n_constant_features": 0}) + + while not builder_stack.empty(): + stack_record = builder_stack.top() + builder_stack.pop() + + start = stack_record.start + end = stack_record.end + depth = stack_record.depth + parent = stack_record.parent + is_left = stack_record.is_left + impurity = stack_record.impurity + n_constant_features = stack_record.n_constant_features + + n_node_samples = end - start + splitter.node_reset(start, end, &weighted_n_node_samples) + + is_leaf = (depth >= max_depth or + n_node_samples < min_samples_split or + n_node_samples < 2 * min_samples_leaf or + weighted_n_node_samples < 2 * min_weight_leaf) + + if first: + impurity = splitter.node_impurity() + first = 0 + + # impurity == 0 with tolerance due to rounding errors + is_leaf = is_leaf or impurity <= EPSILON + + if not is_leaf: + splitter.node_split(impurity, &split, &n_constant_features) + # If EPSILON=0 in the below comparison, float precision + # issues stop splitting, producing trees that are + # dissimilar to v0.18 + is_leaf = (is_leaf or split.pos >= end or + (split.improvement + EPSILON < + min_impurity_decrease)) + + node_id = tree._add_node(parent, is_left, is_leaf, split.feature, + split.threshold, impurity, n_node_samples, + weighted_n_node_samples, + split.missing_go_to_left) + + if node_id == INTPTR_MAX: + rc = -1 + break + + # Store value for all nodes, to facilitate tree/model + # inspection and interpretation + splitter.node_value(tree.value + node_id * tree.value_stride) + + if not is_leaf: + # Push right child on stack + builder_stack.push({ + "start": split.pos, + "end": end, + "depth": depth + 1, + "parent": node_id, + "is_left": 0, + "impurity": split.impurity_right, + "n_constant_features": n_constant_features}) + + # Push left child on stack + builder_stack.push({ + "start": start, + "end": split.pos, + "depth": depth + 1, + "parent": node_id, + "is_left": 1, + "impurity": split.impurity_left, + "n_constant_features": n_constant_features}) + + if depth > max_depth_seen: + max_depth_seen = depth + + if rc >= 0: + rc = tree._resize_c(tree.node_count) + + if rc >= 0: + tree.max_depth = max_depth_seen + if rc == -1: + raise MemoryError() + + +# Best first builder ---------------------------------------------------------- +cdef struct FrontierRecord: + # Record of information of a Node, the frontier for a split. Those records are + # maintained in a heap to access the Node with the best improvement in impurity, + # allowing growing trees greedily on this improvement. + SIZE_t node_id + SIZE_t start + SIZE_t end + SIZE_t pos + SIZE_t depth + bint is_leaf + double impurity + double impurity_left + double impurity_right + double improvement + +cdef inline bool _compare_records( + const FrontierRecord& left, + const FrontierRecord& right, +): + return left.improvement < right.improvement + +cdef inline void _add_to_frontier( + FrontierRecord rec, + vector[FrontierRecord]& frontier, +) noexcept nogil: + """Adds record `rec` to the priority queue `frontier`.""" + frontier.push_back(rec) + push_heap(frontier.begin(), frontier.end(), &_compare_records) + + +cdef class BestFirstTreeBuilder(TreeBuilder): + """Build a decision tree in best-first fashion. + + The best node to expand is given by the node at the frontier that has the + highest impurity improvement. + """ + cdef SIZE_t max_leaf_nodes + + def __cinit__(self, Splitter splitter, SIZE_t min_samples_split, + SIZE_t min_samples_leaf, min_weight_leaf, + SIZE_t max_depth, SIZE_t max_leaf_nodes, + double min_impurity_decrease): + self.splitter = splitter + self.min_samples_split = min_samples_split + self.min_samples_leaf = min_samples_leaf + self.min_weight_leaf = min_weight_leaf + self.max_depth = max_depth + self.max_leaf_nodes = max_leaf_nodes + self.min_impurity_decrease = min_impurity_decrease + + cpdef build( + self, + Tree tree, + object X, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight=None, + const unsigned char[::1] missing_values_in_feature_mask=None, + ): + """Build a decision tree from the training set (X, y).""" + + # check input + X, y, sample_weight = self._check_input(X, y, sample_weight) + + # Parameters + cdef Splitter splitter = self.splitter + cdef SIZE_t max_leaf_nodes = self.max_leaf_nodes + + # Recursive partition (without actual recursion) + splitter.init(X, y, sample_weight, missing_values_in_feature_mask) + + cdef vector[FrontierRecord] frontier + cdef FrontierRecord record + cdef FrontierRecord split_node_left + cdef FrontierRecord split_node_right + + cdef SIZE_t n_node_samples = splitter.n_samples + cdef SIZE_t max_split_nodes = max_leaf_nodes - 1 + cdef bint is_leaf + cdef SIZE_t max_depth_seen = -1 + cdef int rc = 0 + cdef Node* node + + # Initial capacity + cdef SIZE_t init_capacity = max_split_nodes + max_leaf_nodes + tree._resize(init_capacity) + + with nogil: + # add root to frontier + rc = self._add_split_node(splitter, tree, 0, n_node_samples, + INFINITY, IS_FIRST, IS_LEFT, NULL, 0, + &split_node_left) + if rc >= 0: + _add_to_frontier(split_node_left, frontier) + + while not frontier.empty(): + pop_heap(frontier.begin(), frontier.end(), &_compare_records) + record = frontier.back() + frontier.pop_back() + + node = &tree.nodes[record.node_id] + is_leaf = (record.is_leaf or max_split_nodes <= 0) + + if is_leaf: + # Node is not expandable; set node as leaf + node.left_child = _TREE_LEAF + node.right_child = _TREE_LEAF + node.feature = _TREE_UNDEFINED + node.threshold = _TREE_UNDEFINED + + else: + # Node is expandable + + # Decrement number of split nodes available + max_split_nodes -= 1 + + # Compute left split node + rc = self._add_split_node(splitter, tree, + record.start, record.pos, + record.impurity_left, + IS_NOT_FIRST, IS_LEFT, node, + record.depth + 1, + &split_node_left) + if rc == -1: + break + + # tree.nodes may have changed + node = &tree.nodes[record.node_id] + + # Compute right split node + rc = self._add_split_node(splitter, tree, record.pos, + record.end, + record.impurity_right, + IS_NOT_FIRST, IS_NOT_LEFT, node, + record.depth + 1, + &split_node_right) + if rc == -1: + break + + # Add nodes to queue + _add_to_frontier(split_node_left, frontier) + _add_to_frontier(split_node_right, frontier) + + if record.depth > max_depth_seen: + max_depth_seen = record.depth + + if rc >= 0: + rc = tree._resize_c(tree.node_count) + + if rc >= 0: + tree.max_depth = max_depth_seen + + if rc == -1: + raise MemoryError() + + cdef inline int _add_split_node(self, Splitter splitter, Tree tree, + SIZE_t start, SIZE_t end, double impurity, + bint is_first, bint is_left, Node* parent, + SIZE_t depth, + FrontierRecord* res) except -1 nogil: + """Adds node w/ partition ``[start, end)`` to the frontier. """ + cdef SplitRecord split + cdef SIZE_t node_id + cdef SIZE_t n_node_samples + cdef SIZE_t n_constant_features = 0 + cdef double min_impurity_decrease = self.min_impurity_decrease + cdef double weighted_n_node_samples + cdef bint is_leaf + + splitter.node_reset(start, end, &weighted_n_node_samples) + + if is_first: + impurity = splitter.node_impurity() + + n_node_samples = end - start + is_leaf = (depth >= self.max_depth or + n_node_samples < self.min_samples_split or + n_node_samples < 2 * self.min_samples_leaf or + weighted_n_node_samples < 2 * self.min_weight_leaf or + impurity <= EPSILON # impurity == 0 with tolerance + ) + + if not is_leaf: + splitter.node_split(impurity, &split, &n_constant_features) + # If EPSILON=0 in the below comparison, float precision issues stop + # splitting early, producing trees that are dissimilar to v0.18 + is_leaf = (is_leaf or split.pos >= end or + split.improvement + EPSILON < min_impurity_decrease) + + node_id = tree._add_node(parent - tree.nodes + if parent != NULL + else _TREE_UNDEFINED, + is_left, is_leaf, + split.feature, split.threshold, impurity, n_node_samples, + weighted_n_node_samples, + split.missing_go_to_left) + if node_id == INTPTR_MAX: + return -1 + + # compute values also for split nodes (might become leafs later). + splitter.node_value(tree.value + node_id * tree.value_stride) + + res.node_id = node_id + res.start = start + res.end = end + res.depth = depth + res.impurity = impurity + + if not is_leaf: + # is split node + res.pos = split.pos + res.is_leaf = 0 + res.improvement = split.improvement + res.impurity_left = split.impurity_left + res.impurity_right = split.impurity_right + + else: + # is leaf => 0 improvement + res.pos = end + res.is_leaf = 1 + res.improvement = 0.0 + res.impurity_left = impurity + res.impurity_right = impurity + + return 0 + + +# ============================================================================= +# Tree +# ============================================================================= + +cdef class Tree: + """Array-based representation of a binary decision tree. + + The binary tree is represented as a number of parallel arrays. The i-th + element of each array holds information about the node `i`. Node 0 is the + tree's root. You can find a detailed description of all arrays in + `_tree.pxd`. NOTE: Some of the arrays only apply to either leaves or split + nodes, resp. In this case the values of nodes of the other type are + arbitrary! + + Attributes + ---------- + node_count : int + The number of nodes (internal nodes + leaves) in the tree. + + capacity : int + The current capacity (i.e., size) of the arrays, which is at least as + great as `node_count`. + + max_depth : int + The depth of the tree, i.e. the maximum depth of its leaves. + + children_left : array of int, shape [node_count] + children_left[i] holds the node id of the left child of node i. + For leaves, children_left[i] == TREE_LEAF. Otherwise, + children_left[i] > i. This child handles the case where + X[:, feature[i]] <= threshold[i]. + + children_right : array of int, shape [node_count] + children_right[i] holds the node id of the right child of node i. + For leaves, children_right[i] == TREE_LEAF. Otherwise, + children_right[i] > i. This child handles the case where + X[:, feature[i]] > threshold[i]. + + feature : array of int, shape [node_count] + feature[i] holds the feature to split on, for the internal node i. + + threshold : array of double, shape [node_count] + threshold[i] holds the threshold for the internal node i. + + value : array of double, shape [node_count, n_outputs, max_n_classes] + Contains the constant prediction value of each node. + + impurity : array of double, shape [node_count] + impurity[i] holds the impurity (i.e., the value of the splitting + criterion) at node i. + + n_node_samples : array of int, shape [node_count] + n_node_samples[i] holds the number of training samples reaching node i. + + weighted_n_node_samples : array of double, shape [node_count] + weighted_n_node_samples[i] holds the weighted number of training samples + reaching node i. + """ + # Wrap for outside world. + # WARNING: these reference the current `nodes` and `value` buffers, which + # must not be freed by a subsequent memory allocation. + # (i.e. through `_resize` or `__setstate__`) + @property + def n_classes(self): + return sizet_ptr_to_ndarray(self.n_classes, self.n_outputs) + + @property + def children_left(self): + return self._get_node_ndarray()['left_child'][:self.node_count] + + @property + def children_right(self): + return self._get_node_ndarray()['right_child'][:self.node_count] + + @property + def n_leaves(self): + return np.sum(np.logical_and( + self.children_left == -1, + self.children_right == -1)) + + @property + def feature(self): + return self._get_node_ndarray()['feature'][:self.node_count] + + @property + def threshold(self): + return self._get_node_ndarray()['threshold'][:self.node_count] + + @property + def impurity(self): + return self._get_node_ndarray()['impurity'][:self.node_count] + + @property + def n_node_samples(self): + return self._get_node_ndarray()['n_node_samples'][:self.node_count] + + @property + def weighted_n_node_samples(self): + return self._get_node_ndarray()['weighted_n_node_samples'][:self.node_count] + + @property + def missing_go_to_left(self): + return self._get_node_ndarray()['missing_go_to_left'][:self.node_count] + + @property + def value(self): + return self._get_value_ndarray()[:self.node_count] + + # TODO: Convert n_classes to cython.integral memory view once + # https://github.com/cython/cython/issues/5243 is fixed + def __cinit__(self, int n_features, cnp.ndarray n_classes, int n_outputs): + """Constructor.""" + cdef SIZE_t dummy = 0 + size_t_dtype = np.array(dummy).dtype + + n_classes = _check_n_classes(n_classes, size_t_dtype) + + # Input/Output layout + self.n_features = n_features + self.n_outputs = n_outputs + self.n_classes = NULL + safe_realloc(&self.n_classes, n_outputs) + + self.max_n_classes = np.max(n_classes) + self.value_stride = n_outputs * self.max_n_classes + + cdef SIZE_t k + for k in range(n_outputs): + self.n_classes[k] = n_classes[k] + + # Inner structures + self.max_depth = 0 + self.node_count = 0 + self.capacity = 0 + self.value = NULL + self.nodes = NULL + + def __dealloc__(self): + """Destructor.""" + # Free all inner structures + free(self.n_classes) + free(self.value) + free(self.nodes) + + def __reduce__(self): + """Reduce re-implementation, for pickling.""" + return (Tree, (self.n_features, + sizet_ptr_to_ndarray(self.n_classes, self.n_outputs), + self.n_outputs), self.__getstate__()) + + def __getstate__(self): + """Getstate re-implementation, for pickling.""" + d = {} + # capacity is inferred during the __setstate__ using nodes + d["max_depth"] = self.max_depth + d["node_count"] = self.node_count + d["nodes"] = self._get_node_ndarray() + d["values"] = self._get_value_ndarray() + return d + + def __setstate__(self, d): + """Setstate re-implementation, for unpickling.""" + self.max_depth = d["max_depth"] + self.node_count = d["node_count"] + + if 'nodes' not in d: + raise ValueError('You have loaded Tree version which ' + 'cannot be imported') + + node_ndarray = d['nodes'] + value_ndarray = d['values'] + + value_shape = (node_ndarray.shape[0], self.n_outputs, + self.max_n_classes) + + node_ndarray = _check_node_ndarray(node_ndarray, expected_dtype=NODE_DTYPE) + value_ndarray = _check_value_ndarray( + value_ndarray, + expected_dtype=np.dtype(np.float64), + expected_shape=value_shape + ) + + self.capacity = node_ndarray.shape[0] + if self._resize_c(self.capacity) != 0: + raise MemoryError("resizing tree to %d" % self.capacity) + + memcpy(self.nodes, cnp.PyArray_DATA(node_ndarray), + self.capacity * sizeof(Node)) + memcpy(self.value, cnp.PyArray_DATA(value_ndarray), + self.capacity * self.value_stride * sizeof(double)) + + cdef int _resize(self, SIZE_t capacity) except -1 nogil: + """Resize all inner arrays to `capacity`, if `capacity` == -1, then + double the size of the inner arrays. + + Returns -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + """ + if self._resize_c(capacity) != 0: + # Acquire gil only if we need to raise + with gil: + raise MemoryError() + + cdef int _resize_c(self, SIZE_t capacity=INTPTR_MAX) except -1 nogil: + """Guts of _resize + + Returns -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + """ + if capacity == self.capacity and self.nodes != NULL: + return 0 + + if capacity == INTPTR_MAX: + if self.capacity == 0: + capacity = 3 # default initial value + else: + capacity = 2 * self.capacity + + safe_realloc(&self.nodes, capacity) + safe_realloc(&self.value, capacity * self.value_stride) + + # value memory is initialised to 0 to enable classifier argmax + if capacity > self.capacity: + memset((self.value + self.capacity * self.value_stride), 0, + (capacity - self.capacity) * self.value_stride * + sizeof(double)) + + # if capacity smaller than node_count, adjust the counter + if capacity < self.node_count: + self.node_count = capacity + + self.capacity = capacity + return 0 + + cdef SIZE_t _add_node(self, SIZE_t parent, bint is_left, bint is_leaf, + SIZE_t feature, double threshold, double impurity, + SIZE_t n_node_samples, + double weighted_n_node_samples, + unsigned char missing_go_to_left) except -1 nogil: + """Add a node to the tree. + + The new node registers itself as the child of its parent. + + Returns (size_t)(-1) on error. + """ + cdef SIZE_t node_id = self.node_count + + if node_id >= self.capacity: + if self._resize_c() != 0: + return INTPTR_MAX + + cdef Node* node = &self.nodes[node_id] + node.impurity = impurity + node.n_node_samples = n_node_samples + node.weighted_n_node_samples = weighted_n_node_samples + + if parent != _TREE_UNDEFINED: + if is_left: + self.nodes[parent].left_child = node_id + else: + self.nodes[parent].right_child = node_id + + if is_leaf: + node.left_child = _TREE_LEAF + node.right_child = _TREE_LEAF + node.feature = _TREE_UNDEFINED + node.threshold = _TREE_UNDEFINED + + else: + # left_child and right_child will be set later + node.feature = feature + node.threshold = threshold + node.missing_go_to_left = missing_go_to_left + + self.node_count += 1 + + return node_id + + cpdef cnp.ndarray predict(self, object X): + """Predict target for X.""" + out = self._get_value_ndarray().take(self.apply(X), axis=0, + mode='clip') + if self.n_outputs == 1: + out = out.reshape(X.shape[0], self.max_n_classes) + return out + + cpdef cnp.ndarray apply(self, object X): + """Finds the terminal region (=leaf node) for each sample in X.""" + if issparse(X): + return self._apply_sparse_csr(X) + else: + return self._apply_dense(X) + + cdef inline cnp.ndarray _apply_dense(self, object X): + """Finds the terminal region (=leaf node) for each sample in X.""" + + # Check input + if not isinstance(X, np.ndarray): + raise ValueError("X should be in np.ndarray format, got %s" + % type(X)) + + if X.dtype != DTYPE: + raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + + # Extract input + cdef const DTYPE_t[:, :] X_ndarray = X + cdef SIZE_t n_samples = X.shape[0] + cdef DTYPE_t X_i_node_feature + + # Initialize output + cdef SIZE_t[:] out = np.zeros(n_samples, dtype=np.intp) + + # Initialize auxiliary data-structure + cdef Node* node = NULL + cdef SIZE_t i = 0 + + with nogil: + for i in range(n_samples): + node = self.nodes + # While node not a leaf + while node.left_child != _TREE_LEAF: + X_i_node_feature = X_ndarray[i, node.feature] + # ... and node.right_child != _TREE_LEAF: + if isnan(X_i_node_feature): + if node.missing_go_to_left: + node = &self.nodes[node.left_child] + else: + node = &self.nodes[node.right_child] + elif X_i_node_feature <= node.threshold: + node = &self.nodes[node.left_child] + else: + node = &self.nodes[node.right_child] + + out[i] = (node - self.nodes) # node offset + + return np.asarray(out) + + cdef inline cnp.ndarray _apply_sparse_csr(self, object X): + """Finds the terminal region (=leaf node) for each sample in sparse X. + """ + # Check input + if not isspmatrix_csr(X): + raise ValueError("X should be in csr_matrix format, got %s" + % type(X)) + + if X.dtype != DTYPE: + raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + + # Extract input + cdef const DTYPE_t[:] X_data = X.data + cdef const INT32_t[:] X_indices = X.indices + cdef const INT32_t[:] X_indptr = X.indptr + + cdef SIZE_t n_samples = X.shape[0] + cdef SIZE_t n_features = X.shape[1] + + # Initialize output + cdef SIZE_t[:] out = np.zeros(n_samples, dtype=np.intp) + + # Initialize auxiliary data-structure + cdef DTYPE_t feature_value = 0. + cdef Node* node = NULL + cdef DTYPE_t* X_sample = NULL + cdef SIZE_t i = 0 + cdef INT32_t k = 0 + + # feature_to_sample as a data structure records the last seen sample + # for each feature; functionally, it is an efficient way to identify + # which features are nonzero in the present sample. + cdef SIZE_t* feature_to_sample = NULL + + safe_realloc(&X_sample, n_features) + safe_realloc(&feature_to_sample, n_features) + + with nogil: + memset(feature_to_sample, -1, n_features * sizeof(SIZE_t)) + + for i in range(n_samples): + node = self.nodes + + for k in range(X_indptr[i], X_indptr[i + 1]): + feature_to_sample[X_indices[k]] = i + X_sample[X_indices[k]] = X_data[k] + + # While node not a leaf + while node.left_child != _TREE_LEAF: + # ... and node.right_child != _TREE_LEAF: + if feature_to_sample[node.feature] == i: + feature_value = X_sample[node.feature] + + else: + feature_value = 0. + + if feature_value <= node.threshold: + node = &self.nodes[node.left_child] + else: + node = &self.nodes[node.right_child] + + out[i] = (node - self.nodes) # node offset + + # Free auxiliary arrays + free(X_sample) + free(feature_to_sample) + + return np.asarray(out) + + cpdef object decision_path(self, object X): + """Finds the decision path (=node) for each sample in X.""" + if issparse(X): + return self._decision_path_sparse_csr(X) + else: + return self._decision_path_dense(X) + + cdef inline object _decision_path_dense(self, object X): + """Finds the decision path (=node) for each sample in X.""" + + # Check input + if not isinstance(X, np.ndarray): + raise ValueError("X should be in np.ndarray format, got %s" + % type(X)) + + if X.dtype != DTYPE: + raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + + # Extract input + cdef const DTYPE_t[:, :] X_ndarray = X + cdef SIZE_t n_samples = X.shape[0] + + # Initialize output + cdef SIZE_t[:] indptr = np.zeros(n_samples + 1, dtype=np.intp) + cdef SIZE_t[:] indices = np.zeros( + n_samples * (1 + self.max_depth), dtype=np.intp + ) + + # Initialize auxiliary data-structure + cdef Node* node = NULL + cdef SIZE_t i = 0 + + with nogil: + for i in range(n_samples): + node = self.nodes + indptr[i + 1] = indptr[i] + + # Add all external nodes + while node.left_child != _TREE_LEAF: + # ... and node.right_child != _TREE_LEAF: + indices[indptr[i + 1]] = (node - self.nodes) + indptr[i + 1] += 1 + + if X_ndarray[i, node.feature] <= node.threshold: + node = &self.nodes[node.left_child] + else: + node = &self.nodes[node.right_child] + + # Add the leave node + indices[indptr[i + 1]] = (node - self.nodes) + indptr[i + 1] += 1 + + indices = indices[:indptr[n_samples]] + cdef SIZE_t[:] data = np.ones(shape=len(indices), dtype=np.intp) + out = csr_matrix((data, indices, indptr), + shape=(n_samples, self.node_count)) + + return out + + cdef inline object _decision_path_sparse_csr(self, object X): + """Finds the decision path (=node) for each sample in X.""" + + # Check input + if not isspmatrix_csr(X): + raise ValueError("X should be in csr_matrix format, got %s" + % type(X)) + + if X.dtype != DTYPE: + raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + + # Extract input + cdef const DTYPE_t[:] X_data = X.data + cdef const INT32_t[:] X_indices = X.indices + cdef const INT32_t[:] X_indptr = X.indptr + + cdef SIZE_t n_samples = X.shape[0] + cdef SIZE_t n_features = X.shape[1] + + # Initialize output + cdef SIZE_t[:] indptr = np.zeros(n_samples + 1, dtype=np.intp) + cdef SIZE_t[:] indices = np.zeros( + n_samples * (1 + self.max_depth), dtype=np.intp + ) + + # Initialize auxiliary data-structure + cdef DTYPE_t feature_value = 0. + cdef Node* node = NULL + cdef DTYPE_t* X_sample = NULL + cdef SIZE_t i = 0 + cdef INT32_t k = 0 + + # feature_to_sample as a data structure records the last seen sample + # for each feature; functionally, it is an efficient way to identify + # which features are nonzero in the present sample. + cdef SIZE_t* feature_to_sample = NULL + + safe_realloc(&X_sample, n_features) + safe_realloc(&feature_to_sample, n_features) + + with nogil: + memset(feature_to_sample, -1, n_features * sizeof(SIZE_t)) + + for i in range(n_samples): + node = self.nodes + indptr[i + 1] = indptr[i] + + for k in range(X_indptr[i], X_indptr[i + 1]): + feature_to_sample[X_indices[k]] = i + X_sample[X_indices[k]] = X_data[k] + + # While node not a leaf + while node.left_child != _TREE_LEAF: + # ... and node.right_child != _TREE_LEAF: + + indices[indptr[i + 1]] = (node - self.nodes) + indptr[i + 1] += 1 + + if feature_to_sample[node.feature] == i: + feature_value = X_sample[node.feature] + + else: + feature_value = 0. + + if feature_value <= node.threshold: + node = &self.nodes[node.left_child] + else: + node = &self.nodes[node.right_child] + + # Add the leave node + indices[indptr[i + 1]] = (node - self.nodes) + indptr[i + 1] += 1 + + # Free auxiliary arrays + free(X_sample) + free(feature_to_sample) + + indices = indices[:indptr[n_samples]] + cdef SIZE_t[:] data = np.ones(shape=len(indices), dtype=np.intp) + out = csr_matrix((data, indices, indptr), + shape=(n_samples, self.node_count)) + + return out + + cpdef compute_node_depths(self): + """Compute the depth of each node in a tree. + + .. versionadded:: 1.3 + + Returns + ------- + depths : ndarray of shape (self.node_count,), dtype=np.int64 + The depth of each node in the tree. + """ + cdef: + cnp.int64_t[::1] depths = np.empty(self.node_count, dtype=np.int64) + cnp.npy_intp[:] children_left = self.children_left + cnp.npy_intp[:] children_right = self.children_right + cnp.npy_intp node_id + cnp.npy_intp node_count = self.node_count + cnp.int64_t depth + + depths[0] = 1 # init root node + for node_id in range(node_count): + if children_left[node_id] != _TREE_LEAF: + depth = depths[node_id] + 1 + depths[children_left[node_id]] = depth + depths[children_right[node_id]] = depth + + return depths.base + + cpdef compute_feature_importances(self, normalize=True): + """Computes the importance of each feature (aka variable).""" + cdef Node* left + cdef Node* right + cdef Node* nodes = self.nodes + cdef Node* node = nodes + cdef Node* end_node = node + self.node_count + + cdef double normalizer = 0. + + cdef cnp.float64_t[:] importances = np.zeros(self.n_features) + + with nogil: + while node != end_node: + if node.left_child != _TREE_LEAF: + # ... and node.right_child != _TREE_LEAF: + left = &nodes[node.left_child] + right = &nodes[node.right_child] + + importances[node.feature] += ( + node.weighted_n_node_samples * node.impurity - + left.weighted_n_node_samples * left.impurity - + right.weighted_n_node_samples * right.impurity) + node += 1 + + for i in range(self.n_features): + importances[i] /= nodes[0].weighted_n_node_samples + + if normalize: + normalizer = np.sum(importances) + + if normalizer > 0.0: + # Avoid dividing by zero (e.g., when root is pure) + for i in range(self.n_features): + importances[i] /= normalizer + + return np.asarray(importances) + + cdef cnp.ndarray _get_value_ndarray(self): + """Wraps value as a 3-d NumPy array. + + The array keeps a reference to this Tree, which manages the underlying + memory. + """ + cdef cnp.npy_intp shape[3] + shape[0] = self.node_count + shape[1] = self.n_outputs + shape[2] = self.max_n_classes + cdef cnp.ndarray arr + arr = cnp.PyArray_SimpleNewFromData(3, shape, cnp.NPY_DOUBLE, self.value) + Py_INCREF(self) + if PyArray_SetBaseObject(arr, self) < 0: + raise ValueError("Can't initialize array.") + return arr + + cdef cnp.ndarray _get_node_ndarray(self): + """Wraps nodes as a NumPy struct array. + + The array keeps a reference to this Tree, which manages the underlying + memory. Individual fields are publicly accessible as properties of the + Tree. + """ + cdef cnp.npy_intp shape[1] + shape[0] = self.node_count + cdef cnp.npy_intp strides[1] + strides[0] = sizeof(Node) + cdef cnp.ndarray arr + Py_INCREF(NODE_DTYPE) + arr = PyArray_NewFromDescr( cnp.ndarray, + NODE_DTYPE, 1, shape, + strides, self.nodes, + cnp.NPY_ARRAY_DEFAULT, None) + Py_INCREF(self) + if PyArray_SetBaseObject(arr, self) < 0: + raise ValueError("Can't initialize array.") + return arr + + def compute_partial_dependence(self, DTYPE_t[:, ::1] X, + int[::1] target_features, + double[::1] out): + """Partial dependence of the response on the ``target_feature`` set. + + For each sample in ``X`` a tree traversal is performed. + Each traversal starts from the root with weight 1.0. + + At each non-leaf node that splits on a target feature, either + the left child or the right child is visited based on the feature + value of the current sample, and the weight is not modified. + At each non-leaf node that splits on a complementary feature, + both children are visited and the weight is multiplied by the fraction + of training samples which went to each child. + + At each leaf, the value of the node is multiplied by the current + weight (weights sum to 1 for all visited terminal nodes). + + Parameters + ---------- + X : view on 2d ndarray, shape (n_samples, n_target_features) + The grid points on which the partial dependence should be + evaluated. + target_features : view on 1d ndarray, shape (n_target_features) + The set of target features for which the partial dependence + should be evaluated. + out : view on 1d ndarray, shape (n_samples) + The value of the partial dependence function on each grid + point. + """ + cdef: + double[::1] weight_stack = np.zeros(self.node_count, + dtype=np.float64) + SIZE_t[::1] node_idx_stack = np.zeros(self.node_count, + dtype=np.intp) + SIZE_t sample_idx + SIZE_t feature_idx + int stack_size + double left_sample_frac + double current_weight + double total_weight # used for sanity check only + Node *current_node # use a pointer to avoid copying attributes + SIZE_t current_node_idx + bint is_target_feature + SIZE_t _TREE_LEAF = TREE_LEAF # to avoid python interactions + + for sample_idx in range(X.shape[0]): + # init stacks for current sample + stack_size = 1 + node_idx_stack[0] = 0 # root node + weight_stack[0] = 1 # all the samples are in the root node + total_weight = 0 + + while stack_size > 0: + # pop the stack + stack_size -= 1 + current_node_idx = node_idx_stack[stack_size] + current_node = &self.nodes[current_node_idx] + + if current_node.left_child == _TREE_LEAF: + # leaf node + out[sample_idx] += (weight_stack[stack_size] * + self.value[current_node_idx]) + total_weight += weight_stack[stack_size] + else: + # non-leaf node + + # determine if the split feature is a target feature + is_target_feature = False + for feature_idx in range(target_features.shape[0]): + if target_features[feature_idx] == current_node.feature: + is_target_feature = True + break + + if is_target_feature: + # In this case, we push left or right child on stack + if X[sample_idx, feature_idx] <= current_node.threshold: + node_idx_stack[stack_size] = current_node.left_child + else: + node_idx_stack[stack_size] = current_node.right_child + stack_size += 1 + else: + # In this case, we push both children onto the stack, + # and give a weight proportional to the number of + # samples going through each branch. + + # push left child + node_idx_stack[stack_size] = current_node.left_child + left_sample_frac = ( + self.nodes[current_node.left_child].weighted_n_node_samples / + current_node.weighted_n_node_samples) + current_weight = weight_stack[stack_size] + weight_stack[stack_size] = current_weight * left_sample_frac + stack_size += 1 + + # push right child + node_idx_stack[stack_size] = current_node.right_child + weight_stack[stack_size] = ( + current_weight * (1 - left_sample_frac)) + stack_size += 1 + + # Sanity check. Should never happen. + if not (0.999 < total_weight < 1.001): + raise ValueError("Total weight should be 1.0 but was %.9f" % + total_weight) + + +def _check_n_classes(n_classes, expected_dtype): + if n_classes.ndim != 1: + raise ValueError( + f"Wrong dimensions for n_classes from the pickle: " + f"expected 1, got {n_classes.ndim}" + ) + + if n_classes.dtype == expected_dtype: + return n_classes + + # Handles both different endianness and different bitness + if n_classes.dtype.kind == "i" and n_classes.dtype.itemsize in [4, 8]: + return n_classes.astype(expected_dtype, casting="same_kind") + + raise ValueError( + "n_classes from the pickle has an incompatible dtype:\n" + f"- expected: {expected_dtype}\n" + f"- got: {n_classes.dtype}" + ) + + +def _check_value_ndarray(value_ndarray, expected_dtype, expected_shape): + if value_ndarray.shape != expected_shape: + raise ValueError( + "Wrong shape for value array from the pickle: " + f"expected {expected_shape}, got {value_ndarray.shape}" + ) + + if not value_ndarray.flags.c_contiguous: + raise ValueError( + "value array from the pickle should be a C-contiguous array" + ) + + if value_ndarray.dtype == expected_dtype: + return value_ndarray + + # Handles different endianness + if value_ndarray.dtype.str.endswith('f8'): + return value_ndarray.astype(expected_dtype, casting='equiv') + + raise ValueError( + "value array from the pickle has an incompatible dtype:\n" + f"- expected: {expected_dtype}\n" + f"- got: {value_ndarray.dtype}" + ) + + +def _dtype_to_dict(dtype): + return {name: dt.str for name, (dt, *rest) in dtype.fields.items()} + + +def _dtype_dict_with_modified_bitness(dtype_dict): + # field names in Node struct with SIZE_t types (see sklearn/tree/_tree.pxd) + indexing_field_names = ["left_child", "right_child", "feature", "n_node_samples"] + + expected_dtype_size = str(struct.calcsize("P")) + allowed_dtype_size = "8" if expected_dtype_size == "4" else "4" + + allowed_dtype_dict = dtype_dict.copy() + for name in indexing_field_names: + allowed_dtype_dict[name] = allowed_dtype_dict[name].replace( + expected_dtype_size, allowed_dtype_size + ) + + return allowed_dtype_dict + + +def _all_compatible_dtype_dicts(dtype): + # The Cython code for decision trees uses platform-specific SIZE_t + # typed indexing fields that correspond to either i4 or i8 dtypes for + # the matching fields in the numpy array depending on the bitness of + # the platform (32 bit or 64 bit respectively). + # + # We need to cast the indexing fields of the NODE_DTYPE-dtyped array at + # pickle load time to enable cross-bitness deployment scenarios. We + # typically want to make it possible to run the expensive fit method of + # a tree estimator on a 64 bit server platform, pickle the estimator + # for deployment and run the predict method of a low power 32 bit edge + # platform. + # + # A similar thing happens for endianness, the machine where the pickle was + # saved can have a different endianness than the machine where the pickle + # is loaded + + dtype_dict = _dtype_to_dict(dtype) + dtype_dict_with_modified_bitness = _dtype_dict_with_modified_bitness(dtype_dict) + dtype_dict_with_modified_endianness = _dtype_to_dict(dtype.newbyteorder()) + dtype_dict_with_modified_bitness_and_endianness = _dtype_dict_with_modified_bitness( + dtype_dict_with_modified_endianness + ) + + return [ + dtype_dict, + dtype_dict_with_modified_bitness, + dtype_dict_with_modified_endianness, + dtype_dict_with_modified_bitness_and_endianness, + ] + + +def _check_node_ndarray(node_ndarray, expected_dtype): + if node_ndarray.ndim != 1: + raise ValueError( + "Wrong dimensions for node array from the pickle: " + f"expected 1, got {node_ndarray.ndim}" + ) + + if not node_ndarray.flags.c_contiguous: + raise ValueError( + "node array from the pickle should be a C-contiguous array" + ) + + node_ndarray_dtype = node_ndarray.dtype + if node_ndarray_dtype == expected_dtype: + return node_ndarray + + node_ndarray_dtype_dict = _dtype_to_dict(node_ndarray_dtype) + all_compatible_dtype_dicts = _all_compatible_dtype_dicts(expected_dtype) + + if node_ndarray_dtype_dict not in all_compatible_dtype_dicts: + raise ValueError( + "node array from the pickle has an incompatible dtype:\n" + f"- expected: {expected_dtype}\n" + f"- got : {node_ndarray_dtype}" + ) + + return node_ndarray.astype(expected_dtype, casting="same_kind") + + +# ============================================================================= +# Build Pruned Tree +# ============================================================================= + + +cdef class _CCPPruneController: + """Base class used by build_pruned_tree_ccp and ccp_pruning_path + to control pruning. + """ + cdef bint stop_pruning(self, DOUBLE_t effective_alpha) noexcept nogil: + """Return 1 to stop pruning and 0 to continue pruning""" + return 0 + + cdef void save_metrics(self, DOUBLE_t effective_alpha, + DOUBLE_t subtree_impurities) noexcept nogil: + """Save metrics when pruning""" + pass + + cdef void after_pruning(self, unsigned char[:] in_subtree) noexcept nogil: + """Called after pruning""" + pass + + +cdef class _AlphaPruner(_CCPPruneController): + """Use alpha to control when to stop pruning.""" + cdef DOUBLE_t ccp_alpha + cdef SIZE_t capacity + + def __cinit__(self, DOUBLE_t ccp_alpha): + self.ccp_alpha = ccp_alpha + self.capacity = 0 + + cdef bint stop_pruning(self, DOUBLE_t effective_alpha) noexcept nogil: + # The subtree on the previous iteration has the greatest ccp_alpha + # less than or equal to self.ccp_alpha + return self.ccp_alpha < effective_alpha + + cdef void after_pruning(self, unsigned char[:] in_subtree) noexcept nogil: + """Updates the number of leaves in subtree""" + for i in range(in_subtree.shape[0]): + if in_subtree[i]: + self.capacity += 1 + + +cdef class _PathFinder(_CCPPruneController): + """Record metrics used to return the cost complexity path.""" + cdef DOUBLE_t[:] ccp_alphas + cdef DOUBLE_t[:] impurities + cdef UINT32_t count + + def __cinit__(self, int node_count): + self.ccp_alphas = np.zeros(shape=(node_count), dtype=np.float64) + self.impurities = np.zeros(shape=(node_count), dtype=np.float64) + self.count = 0 + + cdef void save_metrics(self, + DOUBLE_t effective_alpha, + DOUBLE_t subtree_impurities) noexcept nogil: + self.ccp_alphas[self.count] = effective_alpha + self.impurities[self.count] = subtree_impurities + self.count += 1 + + +cdef struct CostComplexityPruningRecord: + SIZE_t node_idx + SIZE_t parent + +cdef _cost_complexity_prune(unsigned char[:] leaves_in_subtree, # OUT + Tree orig_tree, + _CCPPruneController controller): + """Perform cost complexity pruning. + + This function takes an already grown tree, `orig_tree` and outputs a + boolean mask `leaves_in_subtree` which are the leaves in the pruned tree. + During the pruning process, the controller is passed the effective alpha and + the subtree impurities. Furthermore, the controller signals when to stop + pruning. + + Parameters + ---------- + leaves_in_subtree : unsigned char[:] + Output for leaves of subtree + orig_tree : Tree + Original tree + ccp_controller : _CCPPruneController + Cost complexity controller + """ + + cdef: + SIZE_t i + SIZE_t n_nodes = orig_tree.node_count + # prior probability using weighted samples + DOUBLE_t[:] weighted_n_node_samples = orig_tree.weighted_n_node_samples + DOUBLE_t total_sum_weights = weighted_n_node_samples[0] + DOUBLE_t[:] impurity = orig_tree.impurity + # weighted impurity of each node + DOUBLE_t[:] r_node = np.empty(shape=n_nodes, dtype=np.float64) + + SIZE_t[:] child_l = orig_tree.children_left + SIZE_t[:] child_r = orig_tree.children_right + SIZE_t[:] parent = np.zeros(shape=n_nodes, dtype=np.intp) + + stack[CostComplexityPruningRecord] ccp_stack + CostComplexityPruningRecord stack_record + SIZE_t node_idx + stack[SIZE_t] node_indices_stack + + SIZE_t[:] n_leaves = np.zeros(shape=n_nodes, dtype=np.intp) + DOUBLE_t[:] r_branch = np.zeros(shape=n_nodes, dtype=np.float64) + DOUBLE_t current_r + SIZE_t leaf_idx + SIZE_t parent_idx + + # candidate nodes that can be pruned + unsigned char[:] candidate_nodes = np.zeros(shape=n_nodes, + dtype=np.uint8) + # nodes in subtree + unsigned char[:] in_subtree = np.ones(shape=n_nodes, dtype=np.uint8) + SIZE_t pruned_branch_node_idx + DOUBLE_t subtree_alpha + DOUBLE_t effective_alpha + SIZE_t n_pruned_leaves + DOUBLE_t r_diff + DOUBLE_t max_float64 = np.finfo(np.float64).max + + # find parent node ids and leaves + with nogil: + + for i in range(r_node.shape[0]): + r_node[i] = ( + weighted_n_node_samples[i] * impurity[i] / total_sum_weights) + + # Push the root node + ccp_stack.push({"node_idx": 0, "parent": _TREE_UNDEFINED}) + + while not ccp_stack.empty(): + stack_record = ccp_stack.top() + ccp_stack.pop() + + node_idx = stack_record.node_idx + parent[node_idx] = stack_record.parent + + if child_l[node_idx] == _TREE_LEAF: + # ... and child_r[node_idx] == _TREE_LEAF: + leaves_in_subtree[node_idx] = 1 + else: + ccp_stack.push({"node_idx": child_l[node_idx], "parent": node_idx}) + ccp_stack.push({"node_idx": child_r[node_idx], "parent": node_idx}) + + # computes number of leaves in all branches and the overall impurity of + # the branch. The overall impurity is the sum of r_node in its leaves. + for leaf_idx in range(leaves_in_subtree.shape[0]): + if not leaves_in_subtree[leaf_idx]: + continue + r_branch[leaf_idx] = r_node[leaf_idx] + + # bubble up values to ancestor nodes + current_r = r_node[leaf_idx] + while leaf_idx != 0: + parent_idx = parent[leaf_idx] + r_branch[parent_idx] += current_r + n_leaves[parent_idx] += 1 + leaf_idx = parent_idx + + for i in range(leaves_in_subtree.shape[0]): + candidate_nodes[i] = not leaves_in_subtree[i] + + # save metrics before pruning + controller.save_metrics(0.0, r_branch[0]) + + # while root node is not a leaf + while candidate_nodes[0]: + + # computes ccp_alpha for subtrees and finds the minimal alpha + effective_alpha = max_float64 + for i in range(n_nodes): + if not candidate_nodes[i]: + continue + subtree_alpha = (r_node[i] - r_branch[i]) / (n_leaves[i] - 1) + if subtree_alpha < effective_alpha: + effective_alpha = subtree_alpha + pruned_branch_node_idx = i + + if controller.stop_pruning(effective_alpha): + break + + node_indices_stack.push(pruned_branch_node_idx) + + # descendants of branch are not in subtree + while not node_indices_stack.empty(): + node_idx = node_indices_stack.top() + node_indices_stack.pop() + + if not in_subtree[node_idx]: + continue # branch has already been marked for pruning + candidate_nodes[node_idx] = 0 + leaves_in_subtree[node_idx] = 0 + in_subtree[node_idx] = 0 + + if child_l[node_idx] != _TREE_LEAF: + # ... and child_r[node_idx] != _TREE_LEAF: + node_indices_stack.push(child_l[node_idx]) + node_indices_stack.push(child_r[node_idx]) + leaves_in_subtree[pruned_branch_node_idx] = 1 + in_subtree[pruned_branch_node_idx] = 1 + + # updates number of leaves + n_pruned_leaves = n_leaves[pruned_branch_node_idx] - 1 + n_leaves[pruned_branch_node_idx] = 0 + + # computes the increase in r_branch to bubble up + r_diff = r_node[pruned_branch_node_idx] - r_branch[pruned_branch_node_idx] + r_branch[pruned_branch_node_idx] = r_node[pruned_branch_node_idx] + + # bubble up values to ancestors + node_idx = parent[pruned_branch_node_idx] + while node_idx != _TREE_UNDEFINED: + n_leaves[node_idx] -= n_pruned_leaves + r_branch[node_idx] += r_diff + node_idx = parent[node_idx] + + controller.save_metrics(effective_alpha, r_branch[0]) + + controller.after_pruning(in_subtree) + + +def _build_pruned_tree_ccp( + Tree tree, # OUT + Tree orig_tree, + DOUBLE_t ccp_alpha +): + """Build a pruned tree from the original tree using cost complexity + pruning. + + The values and nodes from the original tree are copied into the pruned + tree. + + Parameters + ---------- + tree : Tree + Location to place the pruned tree + orig_tree : Tree + Original tree + ccp_alpha : positive double + Complexity parameter. The subtree with the largest cost complexity + that is smaller than ``ccp_alpha`` will be chosen. By default, + no pruning is performed. + """ + + cdef: + SIZE_t n_nodes = orig_tree.node_count + unsigned char[:] leaves_in_subtree = np.zeros( + shape=n_nodes, dtype=np.uint8) + + pruning_controller = _AlphaPruner(ccp_alpha=ccp_alpha) + + _cost_complexity_prune(leaves_in_subtree, orig_tree, pruning_controller) + + _build_pruned_tree(tree, orig_tree, leaves_in_subtree, + pruning_controller.capacity) + + +def ccp_pruning_path(Tree orig_tree): + """Computes the cost complexity pruning path. + + Parameters + ---------- + tree : Tree + Original tree. + + Returns + ------- + path_info : dict + Information about pruning path with attributes: + + ccp_alphas : ndarray + Effective alphas of subtree during pruning. + + impurities : ndarray + Sum of the impurities of the subtree leaves for the + corresponding alpha value in ``ccp_alphas``. + """ + cdef: + unsigned char[:] leaves_in_subtree = np.zeros( + shape=orig_tree.node_count, dtype=np.uint8) + + path_finder = _PathFinder(orig_tree.node_count) + + _cost_complexity_prune(leaves_in_subtree, orig_tree, path_finder) + + cdef: + UINT32_t total_items = path_finder.count + DOUBLE_t[:] ccp_alphas = np.empty(shape=total_items, dtype=np.float64) + DOUBLE_t[:] impurities = np.empty(shape=total_items, dtype=np.float64) + UINT32_t count = 0 + + while count < total_items: + ccp_alphas[count] = path_finder.ccp_alphas[count] + impurities[count] = path_finder.impurities[count] + count += 1 + + return { + 'ccp_alphas': np.asarray(ccp_alphas), + 'impurities': np.asarray(impurities), + } + + +cdef struct BuildPrunedRecord: + SIZE_t start + SIZE_t depth + SIZE_t parent + bint is_left + +cdef _build_pruned_tree( + Tree tree, # OUT + Tree orig_tree, + const unsigned char[:] leaves_in_subtree, + SIZE_t capacity +): + """Build a pruned tree. + + Build a pruned tree from the original tree by transforming the nodes in + ``leaves_in_subtree`` into leaves. + + Parameters + ---------- + tree : Tree + Location to place the pruned tree + orig_tree : Tree + Original tree + leaves_in_subtree : unsigned char memoryview, shape=(node_count, ) + Boolean mask for leaves to include in subtree + capacity : SIZE_t + Number of nodes to initially allocate in pruned tree + """ + tree._resize(capacity) + + cdef: + SIZE_t orig_node_id + SIZE_t new_node_id + SIZE_t depth + SIZE_t parent + bint is_left + bint is_leaf + + # value_stride for original tree and new tree are the same + SIZE_t value_stride = orig_tree.value_stride + SIZE_t max_depth_seen = -1 + int rc = 0 + Node* node + double* orig_value_ptr + double* new_value_ptr + + stack[BuildPrunedRecord] prune_stack + BuildPrunedRecord stack_record + + with nogil: + # push root node onto stack + prune_stack.push({"start": 0, "depth": 0, "parent": _TREE_UNDEFINED, "is_left": 0}) + + while not prune_stack.empty(): + stack_record = prune_stack.top() + prune_stack.pop() + + orig_node_id = stack_record.start + depth = stack_record.depth + parent = stack_record.parent + is_left = stack_record.is_left + + is_leaf = leaves_in_subtree[orig_node_id] + node = &orig_tree.nodes[orig_node_id] + + new_node_id = tree._add_node( + parent, is_left, is_leaf, node.feature, node.threshold, + node.impurity, node.n_node_samples, + node.weighted_n_node_samples, node.missing_go_to_left) + + if new_node_id == INTPTR_MAX: + rc = -1 + break + + # copy value from original tree to new tree + orig_value_ptr = orig_tree.value + value_stride * orig_node_id + new_value_ptr = tree.value + value_stride * new_node_id + memcpy(new_value_ptr, orig_value_ptr, sizeof(double) * value_stride) + + if not is_leaf: + # Push right child on stack + prune_stack.push({"start": node.right_child, "depth": depth + 1, + "parent": new_node_id, "is_left": 0}) + # push left child on stack + prune_stack.push({"start": node.left_child, "depth": depth + 1, + "parent": new_node_id, "is_left": 1}) + + if depth > max_depth_seen: + max_depth_seen = depth + + if rc >= 0: + tree.max_depth = max_depth_seen + if rc == -1: + raise MemoryError("pruning tree") \ No newline at end of file diff --git a/ivy/functional/frontends/sklearn/_tree.pyx b/ivy/functional/frontends/sklearn/_tree.pyx new file mode 100644 index 0000000000000..f436c919f8bc2 --- /dev/null +++ b/ivy/functional/frontends/sklearn/_tree.pyx @@ -0,0 +1,1833 @@ +# Authors: Gilles Louppe +# Peter Prettenhofer +# Brian Holt +# Noel Dawe +# Satrajit Gosh +# Lars Buitinck +# Arnaud Joly +# Joel Nothman +# Fares Hedayati +# Jacob Schreiber +# Nelson Liu +# +# License: BSD 3 clause + +from cpython cimport Py_INCREF, PyObject, PyTypeObject + +from libc.stdlib cimport free +from libc.string cimport memcpy +from libc.string cimport memset +from libc.stdint cimport INTPTR_MAX +from libc.math cimport isnan +from libcpp.vector cimport vector +from libcpp.algorithm cimport pop_heap +from libcpp.algorithm cimport push_heap +from libcpp cimport bool + +import struct + +import numpy as np +cimport numpy as cnp +cnp.import_array() + +from scipy.sparse import issparse +from scipy.sparse import csr_matrix +from scipy.sparse import isspmatrix_csr + +from ._utils cimport safe_realloc +from ._utils cimport sizet_ptr_to_ndarray + +cdef extern from "numpy/arrayobject.h": + object PyArray_NewFromDescr(PyTypeObject* subtype, cnp.dtype descr, + int nd, cnp.npy_intp* dims, + cnp.npy_intp* strides, + void* data, int flags, object obj) + int PyArray_SetBaseObject(cnp.ndarray arr, PyObject* obj) + +cdef extern from "" namespace "std" nogil: + cdef cppclass stack[T]: + ctypedef T value_type + stack() except + + bint empty() + void pop() + void push(T&) except + # Raise c++ exception for bad_alloc -> MemoryError + T& top() + +# ============================================================================= +# Types and constants +# ============================================================================= + +from numpy import float32 as DTYPE +from numpy import float64 as DOUBLE + +cdef double INFINITY = np.inf +cdef double EPSILON = np.finfo('double').eps + +# Some handy constants (BestFirstTreeBuilder) +cdef int IS_FIRST = 1 +cdef int IS_NOT_FIRST = 0 +cdef int IS_LEFT = 1 +cdef int IS_NOT_LEFT = 0 + +TREE_LEAF = -1 +TREE_UNDEFINED = -2 +cdef SIZE_t _TREE_LEAF = TREE_LEAF +cdef SIZE_t _TREE_UNDEFINED = TREE_UNDEFINED + +# Build the corresponding numpy dtype for Node. +# This works by casting `dummy` to an array of Node of length 1, which numpy +# can construct a `dtype`-object for. See https://stackoverflow.com/q/62448946 +# for a more detailed explanation. +cdef Node dummy +NODE_DTYPE = np.asarray((&dummy)).dtype + +# ============================================================================= +# TreeBuilder +# ============================================================================= + +cdef class TreeBuilder: + """Interface for different tree building strategies.""" + + cpdef build( + self, + Tree tree, + object X, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight=None, + const unsigned char[::1] missing_values_in_feature_mask=None, + ): + """Build a decision tree from the training set (X, y).""" + pass + + cdef inline _check_input( + self, + object X, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight, + ): + """Check input dtype, layout and format""" + if issparse(X): + X = X.tocsc() + X.sort_indices() + + if X.data.dtype != DTYPE: + X.data = np.ascontiguousarray(X.data, dtype=DTYPE) + + if X.indices.dtype != np.int32 or X.indptr.dtype != np.int32: + raise ValueError("No support for np.int64 index based " + "sparse matrices") + + elif X.dtype != DTYPE: + # since we have to copy we will make it fortran for efficiency + X = np.asfortranarray(X, dtype=DTYPE) + + # TODO: This check for y seems to be redundant, as it is also + # present in the BaseDecisionTree's fit method, and therefore + # can be removed. + if y.base.dtype != DOUBLE or not y.base.flags.contiguous: + y = np.ascontiguousarray(y, dtype=DOUBLE) + + if ( + sample_weight is not None and + ( + sample_weight.base.dtype != DOUBLE or + not sample_weight.base.flags.contiguous + ) + ): + sample_weight = np.asarray(sample_weight, dtype=DOUBLE, order="C") + + return X, y, sample_weight + +# Depth first builder --------------------------------------------------------- +# A record on the stack for depth-first tree growing +cdef struct StackRecord: + SIZE_t start + SIZE_t end + SIZE_t depth + SIZE_t parent + bint is_left + double impurity + SIZE_t n_constant_features + + + + + + + +cdef class DepthFirstTreeBuilder(TreeBuilder): + """Build a decision tree in depth-first fashion.""" + + def __cinit__(self, Splitter splitter, SIZE_t min_samples_split, + SIZE_t min_samples_leaf, double min_weight_leaf, + SIZE_t max_depth, double min_impurity_decrease): + self.splitter = splitter + self.min_samples_split = min_samples_split + self.min_samples_leaf = min_samples_leaf + self.min_weight_leaf = min_weight_leaf + self.max_depth = max_depth + self.min_impurity_decrease = min_impurity_decrease + + cpdef build( + self, + Tree tree, + object X, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight=None, + const unsigned char[::1] missing_values_in_feature_mask=None, + ): + """Build a decision tree from the training set (X, y).""" + + # check input + X, y, sample_weight = self._check_input(X, y, sample_weight) + + # Initial capacity + cdef int init_capacity + + if tree.max_depth <= 10: + init_capacity = (2 ** (tree.max_depth + 1)) - 1 + else: + init_capacity = 2047 + + tree._resize(init_capacity) + + # Parameters + cdef Splitter splitter = self.splitter + cdef SIZE_t max_depth = self.max_depth + cdef SIZE_t min_samples_leaf = self.min_samples_leaf + cdef double min_weight_leaf = self.min_weight_leaf + cdef SIZE_t min_samples_split = self.min_samples_split + cdef double min_impurity_decrease = self.min_impurity_decrease + + # Recursive partition (without actual recursion) + splitter.init(X, y, sample_weight, missing_values_in_feature_mask) + + cdef SIZE_t start + cdef SIZE_t end + cdef SIZE_t depth + cdef SIZE_t parent + cdef bint is_left + cdef SIZE_t n_node_samples = splitter.n_samples + cdef double weighted_n_node_samples + cdef SplitRecord split + cdef SIZE_t node_id + + cdef double impurity = INFINITY + cdef SIZE_t n_constant_features + cdef bint is_leaf + cdef bint first = 1 + cdef SIZE_t max_depth_seen = -1 + cdef int rc = 0 + + cdef stack[StackRecord] builder_stack + cdef StackRecord stack_record + + with nogil: + # push root node onto stack + builder_stack.push({ + "start": 0, + "end": n_node_samples, + "depth": 0, + "parent": _TREE_UNDEFINED, + "is_left": 0, + "impurity": INFINITY, + "n_constant_features": 0}) + + while not builder_stack.empty(): + stack_record = builder_stack.top() + builder_stack.pop() + + start = stack_record.start + end = stack_record.end + depth = stack_record.depth + parent = stack_record.parent + is_left = stack_record.is_left + impurity = stack_record.impurity + n_constant_features = stack_record.n_constant_features + + n_node_samples = end - start + splitter.node_reset(start, end, &weighted_n_node_samples) + + is_leaf = (depth >= max_depth or + n_node_samples < min_samples_split or + n_node_samples < 2 * min_samples_leaf or + weighted_n_node_samples < 2 * min_weight_leaf) + + if first: + impurity = splitter.node_impurity() + first = 0 + + # impurity == 0 with tolerance due to rounding errors + is_leaf = is_leaf or impurity <= EPSILON + + if not is_leaf: + splitter.node_split(impurity, &split, &n_constant_features) + # If EPSILON=0 in the below comparison, float precision + # issues stop splitting, producing trees that are + # dissimilar to v0.18 + is_leaf = (is_leaf or split.pos >= end or + (split.improvement + EPSILON < + min_impurity_decrease)) + + node_id = tree._add_node(parent, is_left, is_leaf, split.feature, + split.threshold, impurity, n_node_samples, + weighted_n_node_samples, + split.missing_go_to_left) + + if node_id == INTPTR_MAX: + rc = -1 + break + + # Store value for all nodes, to facilitate tree/model + # inspection and interpretation + splitter.node_value(tree.value + node_id * tree.value_stride) + + if not is_leaf: + # Push right child on stack + builder_stack.push({ + "start": split.pos, + "end": end, + "depth": depth + 1, + "parent": node_id, + "is_left": 0, + "impurity": split.impurity_right, + "n_constant_features": n_constant_features}) + + # Push left child on stack + builder_stack.push({ + "start": start, + "end": split.pos, + "depth": depth + 1, + "parent": node_id, + "is_left": 1, + "impurity": split.impurity_left, + "n_constant_features": n_constant_features}) + + if depth > max_depth_seen: + max_depth_seen = depth + + if rc >= 0: + rc = tree._resize_c(tree.node_count) + + if rc >= 0: + tree.max_depth = max_depth_seen + if rc == -1: + raise MemoryError() + + +# Best first builder ---------------------------------------------------------- +cdef struct FrontierRecord: + # Record of information of a Node, the frontier for a split. Those records are + # maintained in a heap to access the Node with the best improvement in impurity, + # allowing growing trees greedily on this improvement. + SIZE_t node_id + SIZE_t start + SIZE_t end + SIZE_t pos + SIZE_t depth + bint is_leaf + double impurity + double impurity_left + double impurity_right + double improvement + +cdef inline bool _compare_records( + const FrontierRecord& left, + const FrontierRecord& right, +): + return left.improvement < right.improvement + +cdef inline void _add_to_frontier( + FrontierRecord rec, + vector[FrontierRecord]& frontier, +) noexcept nogil: + """Adds record `rec` to the priority queue `frontier`.""" + frontier.push_back(rec) + push_heap(frontier.begin(), frontier.end(), &_compare_records) + + +cdef class BestFirstTreeBuilder(TreeBuilder): + """Build a decision tree in best-first fashion. + + The best node to expand is given by the node at the frontier that has the + highest impurity improvement. + """ + cdef SIZE_t max_leaf_nodes + + def __cinit__(self, Splitter splitter, SIZE_t min_samples_split, + SIZE_t min_samples_leaf, min_weight_leaf, + SIZE_t max_depth, SIZE_t max_leaf_nodes, + double min_impurity_decrease): + self.splitter = splitter + self.min_samples_split = min_samples_split + self.min_samples_leaf = min_samples_leaf + self.min_weight_leaf = min_weight_leaf + self.max_depth = max_depth + self.max_leaf_nodes = max_leaf_nodes + self.min_impurity_decrease = min_impurity_decrease + + cpdef build( + self, + Tree tree, + object X, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight=None, + const unsigned char[::1] missing_values_in_feature_mask=None, + ): + """Build a decision tree from the training set (X, y).""" + + # check input + X, y, sample_weight = self._check_input(X, y, sample_weight) + + # Parameters + cdef Splitter splitter = self.splitter + cdef SIZE_t max_leaf_nodes = self.max_leaf_nodes + + # Recursive partition (without actual recursion) + splitter.init(X, y, sample_weight, missing_values_in_feature_mask) + + cdef vector[FrontierRecord] frontier + cdef FrontierRecord record + cdef FrontierRecord split_node_left + cdef FrontierRecord split_node_right + + cdef SIZE_t n_node_samples = splitter.n_samples + cdef SIZE_t max_split_nodes = max_leaf_nodes - 1 + cdef bint is_leaf + cdef SIZE_t max_depth_seen = -1 + cdef int rc = 0 + cdef Node* node + + # Initial capacity + cdef SIZE_t init_capacity = max_split_nodes + max_leaf_nodes + tree._resize(init_capacity) + + with nogil: + # add root to frontier + rc = self._add_split_node(splitter, tree, 0, n_node_samples, + INFINITY, IS_FIRST, IS_LEFT, NULL, 0, + &split_node_left) + if rc >= 0: + _add_to_frontier(split_node_left, frontier) + + while not frontier.empty(): + pop_heap(frontier.begin(), frontier.end(), &_compare_records) + record = frontier.back() + frontier.pop_back() + + node = &tree.nodes[record.node_id] + is_leaf = (record.is_leaf or max_split_nodes <= 0) + + if is_leaf: + # Node is not expandable; set node as leaf + node.left_child = _TREE_LEAF + node.right_child = _TREE_LEAF + node.feature = _TREE_UNDEFINED + node.threshold = _TREE_UNDEFINED + + else: + # Node is expandable + + # Decrement number of split nodes available + max_split_nodes -= 1 + + # Compute left split node + rc = self._add_split_node(splitter, tree, + record.start, record.pos, + record.impurity_left, + IS_NOT_FIRST, IS_LEFT, node, + record.depth + 1, + &split_node_left) + if rc == -1: + break + + # tree.nodes may have changed + node = &tree.nodes[record.node_id] + + # Compute right split node + rc = self._add_split_node(splitter, tree, record.pos, + record.end, + record.impurity_right, + IS_NOT_FIRST, IS_NOT_LEFT, node, + record.depth + 1, + &split_node_right) + if rc == -1: + break + + # Add nodes to queue + _add_to_frontier(split_node_left, frontier) + _add_to_frontier(split_node_right, frontier) + + if record.depth > max_depth_seen: + max_depth_seen = record.depth + + if rc >= 0: + rc = tree._resize_c(tree.node_count) + + if rc >= 0: + tree.max_depth = max_depth_seen + + if rc == -1: + raise MemoryError() + + cdef inline int _add_split_node(self, Splitter splitter, Tree tree, + SIZE_t start, SIZE_t end, double impurity, + bint is_first, bint is_left, Node* parent, + SIZE_t depth, + FrontierRecord* res) except -1 nogil: + """Adds node w/ partition ``[start, end)`` to the frontier. """ + cdef SplitRecord split + cdef SIZE_t node_id + cdef SIZE_t n_node_samples + cdef SIZE_t n_constant_features = 0 + cdef double min_impurity_decrease = self.min_impurity_decrease + cdef double weighted_n_node_samples + cdef bint is_leaf + + splitter.node_reset(start, end, &weighted_n_node_samples) + + if is_first: + impurity = splitter.node_impurity() + + n_node_samples = end - start + is_leaf = (depth >= self.max_depth or + n_node_samples < self.min_samples_split or + n_node_samples < 2 * self.min_samples_leaf or + weighted_n_node_samples < 2 * self.min_weight_leaf or + impurity <= EPSILON # impurity == 0 with tolerance + ) + + if not is_leaf: + splitter.node_split(impurity, &split, &n_constant_features) + # If EPSILON=0 in the below comparison, float precision issues stop + # splitting early, producing trees that are dissimilar to v0.18 + is_leaf = (is_leaf or split.pos >= end or + split.improvement + EPSILON < min_impurity_decrease) + + node_id = tree._add_node(parent - tree.nodes + if parent != NULL + else _TREE_UNDEFINED, + is_left, is_leaf, + split.feature, split.threshold, impurity, n_node_samples, + weighted_n_node_samples, + split.missing_go_to_left) + if node_id == INTPTR_MAX: + return -1 + + # compute values also for split nodes (might become leafs later). + splitter.node_value(tree.value + node_id * tree.value_stride) + + res.node_id = node_id + res.start = start + res.end = end + res.depth = depth + res.impurity = impurity + + if not is_leaf: + # is split node + res.pos = split.pos + res.is_leaf = 0 + res.improvement = split.improvement + res.impurity_left = split.impurity_left + res.impurity_right = split.impurity_right + + else: + # is leaf => 0 improvement + res.pos = end + res.is_leaf = 1 + res.improvement = 0.0 + res.impurity_left = impurity + res.impurity_right = impurity + + return 0 + + +# ============================================================================= +# Tree +# ============================================================================= + +cdef class Tree: + """Array-based representation of a binary decision tree. + + The binary tree is represented as a number of parallel arrays. The i-th + element of each array holds information about the node `i`. Node 0 is the + tree's root. You can find a detailed description of all arrays in + `_tree.pxd`. NOTE: Some of the arrays only apply to either leaves or split + nodes, resp. In this case the values of nodes of the other type are + arbitrary! + + Attributes + ---------- + node_count : int + The number of nodes (internal nodes + leaves) in the tree. + + capacity : int + The current capacity (i.e., size) of the arrays, which is at least as + great as `node_count`. + + max_depth : int + The depth of the tree, i.e. the maximum depth of its leaves. + + children_left : array of int, shape [node_count] + children_left[i] holds the node id of the left child of node i. + For leaves, children_left[i] == TREE_LEAF. Otherwise, + children_left[i] > i. This child handles the case where + X[:, feature[i]] <= threshold[i]. + + children_right : array of int, shape [node_count] + children_right[i] holds the node id of the right child of node i. + For leaves, children_right[i] == TREE_LEAF. Otherwise, + children_right[i] > i. This child handles the case where + X[:, feature[i]] > threshold[i]. + + feature : array of int, shape [node_count] + feature[i] holds the feature to split on, for the internal node i. + + threshold : array of double, shape [node_count] + threshold[i] holds the threshold for the internal node i. + + value : array of double, shape [node_count, n_outputs, max_n_classes] + Contains the constant prediction value of each node. + + impurity : array of double, shape [node_count] + impurity[i] holds the impurity (i.e., the value of the splitting + criterion) at node i. + + n_node_samples : array of int, shape [node_count] + n_node_samples[i] holds the number of training samples reaching node i. + + weighted_n_node_samples : array of double, shape [node_count] + weighted_n_node_samples[i] holds the weighted number of training samples + reaching node i. + """ + # Wrap for outside world. + # WARNING: these reference the current `nodes` and `value` buffers, which + # must not be freed by a subsequent memory allocation. + # (i.e. through `_resize` or `__setstate__`) + @property + def n_classes(self): + return sizet_ptr_to_ndarray(self.n_classes, self.n_outputs) + + @property + def children_left(self): + return self._get_node_ndarray()['left_child'][:self.node_count] + + @property + def children_right(self): + return self._get_node_ndarray()['right_child'][:self.node_count] + + @property + def n_leaves(self): + return np.sum(np.logical_and( + self.children_left == -1, + self.children_right == -1)) + + @property + def feature(self): + return self._get_node_ndarray()['feature'][:self.node_count] + + @property + def threshold(self): + return self._get_node_ndarray()['threshold'][:self.node_count] + + @property + def impurity(self): + return self._get_node_ndarray()['impurity'][:self.node_count] + + @property + def n_node_samples(self): + return self._get_node_ndarray()['n_node_samples'][:self.node_count] + + @property + def weighted_n_node_samples(self): + return self._get_node_ndarray()['weighted_n_node_samples'][:self.node_count] + + @property + def missing_go_to_left(self): + return self._get_node_ndarray()['missing_go_to_left'][:self.node_count] + + @property + def value(self): + return self._get_value_ndarray()[:self.node_count] + + # TODO: Convert n_classes to cython.integral memory view once + # https://github.com/cython/cython/issues/5243 is fixed + def __cinit__(self, int n_features, cnp.ndarray n_classes, int n_outputs): + """Constructor.""" + cdef SIZE_t dummy = 0 + size_t_dtype = np.array(dummy).dtype + + n_classes = _check_n_classes(n_classes, size_t_dtype) + + # Input/Output layout + self.n_features = n_features + self.n_outputs = n_outputs + self.n_classes = NULL + safe_realloc(&self.n_classes, n_outputs) + + self.max_n_classes = np.max(n_classes) + self.value_stride = n_outputs * self.max_n_classes + + cdef SIZE_t k + for k in range(n_outputs): + self.n_classes[k] = n_classes[k] + + # Inner structures + self.max_depth = 0 + self.node_count = 0 + self.capacity = 0 + self.value = NULL + self.nodes = NULL + + def __dealloc__(self): + """Destructor.""" + # Free all inner structures + free(self.n_classes) + free(self.value) + free(self.nodes) + + def __reduce__(self): + """Reduce re-implementation, for pickling.""" + return (Tree, (self.n_features, + sizet_ptr_to_ndarray(self.n_classes, self.n_outputs), + self.n_outputs), self.__getstate__()) + + def __getstate__(self): + """Getstate re-implementation, for pickling.""" + d = {} + # capacity is inferred during the __setstate__ using nodes + d["max_depth"] = self.max_depth + d["node_count"] = self.node_count + d["nodes"] = self._get_node_ndarray() + d["values"] = self._get_value_ndarray() + return d + + def __setstate__(self, d): + """Setstate re-implementation, for unpickling.""" + self.max_depth = d["max_depth"] + self.node_count = d["node_count"] + + if 'nodes' not in d: + raise ValueError('You have loaded Tree version which ' + 'cannot be imported') + + node_ndarray = d['nodes'] + value_ndarray = d['values'] + + value_shape = (node_ndarray.shape[0], self.n_outputs, + self.max_n_classes) + + node_ndarray = _check_node_ndarray(node_ndarray, expected_dtype=NODE_DTYPE) + value_ndarray = _check_value_ndarray( + value_ndarray, + expected_dtype=np.dtype(np.float64), + expected_shape=value_shape + ) + + self.capacity = node_ndarray.shape[0] + if self._resize_c(self.capacity) != 0: + raise MemoryError("resizing tree to %d" % self.capacity) + + memcpy(self.nodes, cnp.PyArray_DATA(node_ndarray), + self.capacity * sizeof(Node)) + memcpy(self.value, cnp.PyArray_DATA(value_ndarray), + self.capacity * self.value_stride * sizeof(double)) + + cdef int _resize(self, SIZE_t capacity) except -1 nogil: + """Resize all inner arrays to `capacity`, if `capacity` == -1, then + double the size of the inner arrays. + + Returns -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + """ + if self._resize_c(capacity) != 0: + # Acquire gil only if we need to raise + with gil: + raise MemoryError() + + cdef int _resize_c(self, SIZE_t capacity=INTPTR_MAX) except -1 nogil: + """Guts of _resize + + Returns -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + """ + if capacity == self.capacity and self.nodes != NULL: + return 0 + + if capacity == INTPTR_MAX: + if self.capacity == 0: + capacity = 3 # default initial value + else: + capacity = 2 * self.capacity + + safe_realloc(&self.nodes, capacity) + safe_realloc(&self.value, capacity * self.value_stride) + + # value memory is initialised to 0 to enable classifier argmax + if capacity > self.capacity: + memset((self.value + self.capacity * self.value_stride), 0, + (capacity - self.capacity) * self.value_stride * + sizeof(double)) + + # if capacity smaller than node_count, adjust the counter + if capacity < self.node_count: + self.node_count = capacity + + self.capacity = capacity + return 0 + + cdef SIZE_t _add_node(self, SIZE_t parent, bint is_left, bint is_leaf, + SIZE_t feature, double threshold, double impurity, + SIZE_t n_node_samples, + double weighted_n_node_samples, + unsigned char missing_go_to_left) except -1 nogil: + """Add a node to the tree. + + The new node registers itself as the child of its parent. + + Returns (size_t)(-1) on error. + """ + cdef SIZE_t node_id = self.node_count + + if node_id >= self.capacity: + if self._resize_c() != 0: + return INTPTR_MAX + + cdef Node* node = &self.nodes[node_id] + node.impurity = impurity + node.n_node_samples = n_node_samples + node.weighted_n_node_samples = weighted_n_node_samples + + if parent != _TREE_UNDEFINED: + if is_left: + self.nodes[parent].left_child = node_id + else: + self.nodes[parent].right_child = node_id + + if is_leaf: + node.left_child = _TREE_LEAF + node.right_child = _TREE_LEAF + node.feature = _TREE_UNDEFINED + node.threshold = _TREE_UNDEFINED + + else: + # left_child and right_child will be set later + node.feature = feature + node.threshold = threshold + node.missing_go_to_left = missing_go_to_left + + self.node_count += 1 + + return node_id + + cpdef cnp.ndarray predict(self, object X): + """Predict target for X.""" + out = self._get_value_ndarray().take(self.apply(X), axis=0, + mode='clip') + if self.n_outputs == 1: + out = out.reshape(X.shape[0], self.max_n_classes) + return out + + cpdef cnp.ndarray apply(self, object X): + """Finds the terminal region (=leaf node) for each sample in X.""" + if issparse(X): + return self._apply_sparse_csr(X) + else: + return self._apply_dense(X) + + cdef inline cnp.ndarray _apply_dense(self, object X): + """Finds the terminal region (=leaf node) for each sample in X.""" + + # Check input + if not isinstance(X, np.ndarray): + raise ValueError("X should be in np.ndarray format, got %s" + % type(X)) + + if X.dtype != DTYPE: + raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + + # Extract input + cdef const DTYPE_t[:, :] X_ndarray = X + cdef SIZE_t n_samples = X.shape[0] + cdef DTYPE_t X_i_node_feature + + # Initialize output + cdef SIZE_t[:] out = np.zeros(n_samples, dtype=np.intp) + + # Initialize auxiliary data-structure + cdef Node* node = NULL + cdef SIZE_t i = 0 + + with nogil: + for i in range(n_samples): + node = self.nodes + # While node not a leaf + while node.left_child != _TREE_LEAF: + X_i_node_feature = X_ndarray[i, node.feature] + # ... and node.right_child != _TREE_LEAF: + if isnan(X_i_node_feature): + if node.missing_go_to_left: + node = &self.nodes[node.left_child] + else: + node = &self.nodes[node.right_child] + elif X_i_node_feature <= node.threshold: + node = &self.nodes[node.left_child] + else: + node = &self.nodes[node.right_child] + + out[i] = (node - self.nodes) # node offset + + return np.asarray(out) + + cdef inline cnp.ndarray _apply_sparse_csr(self, object X): + """Finds the terminal region (=leaf node) for each sample in sparse X. + """ + # Check input + if not isspmatrix_csr(X): + raise ValueError("X should be in csr_matrix format, got %s" + % type(X)) + + if X.dtype != DTYPE: + raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + + # Extract input + cdef const DTYPE_t[:] X_data = X.data + cdef const INT32_t[:] X_indices = X.indices + cdef const INT32_t[:] X_indptr = X.indptr + + cdef SIZE_t n_samples = X.shape[0] + cdef SIZE_t n_features = X.shape[1] + + # Initialize output + cdef SIZE_t[:] out = np.zeros(n_samples, dtype=np.intp) + + # Initialize auxiliary data-structure + cdef DTYPE_t feature_value = 0. + cdef Node* node = NULL + cdef DTYPE_t* X_sample = NULL + cdef SIZE_t i = 0 + cdef INT32_t k = 0 + + # feature_to_sample as a data structure records the last seen sample + # for each feature; functionally, it is an efficient way to identify + # which features are nonzero in the present sample. + cdef SIZE_t* feature_to_sample = NULL + + safe_realloc(&X_sample, n_features) + safe_realloc(&feature_to_sample, n_features) + + with nogil: + memset(feature_to_sample, -1, n_features * sizeof(SIZE_t)) + + for i in range(n_samples): + node = self.nodes + + for k in range(X_indptr[i], X_indptr[i + 1]): + feature_to_sample[X_indices[k]] = i + X_sample[X_indices[k]] = X_data[k] + + # While node not a leaf + while node.left_child != _TREE_LEAF: + # ... and node.right_child != _TREE_LEAF: + if feature_to_sample[node.feature] == i: + feature_value = X_sample[node.feature] + + else: + feature_value = 0. + + if feature_value <= node.threshold: + node = &self.nodes[node.left_child] + else: + node = &self.nodes[node.right_child] + + out[i] = (node - self.nodes) # node offset + + # Free auxiliary arrays + free(X_sample) + free(feature_to_sample) + + return np.asarray(out) + + cpdef object decision_path(self, object X): + """Finds the decision path (=node) for each sample in X.""" + if issparse(X): + return self._decision_path_sparse_csr(X) + else: + return self._decision_path_dense(X) + + cdef inline object _decision_path_dense(self, object X): + """Finds the decision path (=node) for each sample in X.""" + + # Check input + if not isinstance(X, np.ndarray): + raise ValueError("X should be in np.ndarray format, got %s" + % type(X)) + + if X.dtype != DTYPE: + raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + + # Extract input + cdef const DTYPE_t[:, :] X_ndarray = X + cdef SIZE_t n_samples = X.shape[0] + + # Initialize output + cdef SIZE_t[:] indptr = np.zeros(n_samples + 1, dtype=np.intp) + cdef SIZE_t[:] indices = np.zeros( + n_samples * (1 + self.max_depth), dtype=np.intp + ) + + # Initialize auxiliary data-structure + cdef Node* node = NULL + cdef SIZE_t i = 0 + + with nogil: + for i in range(n_samples): + node = self.nodes + indptr[i + 1] = indptr[i] + + # Add all external nodes + while node.left_child != _TREE_LEAF: + # ... and node.right_child != _TREE_LEAF: + indices[indptr[i + 1]] = (node - self.nodes) + indptr[i + 1] += 1 + + if X_ndarray[i, node.feature] <= node.threshold: + node = &self.nodes[node.left_child] + else: + node = &self.nodes[node.right_child] + + # Add the leave node + indices[indptr[i + 1]] = (node - self.nodes) + indptr[i + 1] += 1 + + indices = indices[:indptr[n_samples]] + cdef SIZE_t[:] data = np.ones(shape=len(indices), dtype=np.intp) + out = csr_matrix((data, indices, indptr), + shape=(n_samples, self.node_count)) + + return out + + cdef inline object _decision_path_sparse_csr(self, object X): + """Finds the decision path (=node) for each sample in X.""" + + # Check input + if not isspmatrix_csr(X): + raise ValueError("X should be in csr_matrix format, got %s" + % type(X)) + + if X.dtype != DTYPE: + raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + + # Extract input + cdef const DTYPE_t[:] X_data = X.data + cdef const INT32_t[:] X_indices = X.indices + cdef const INT32_t[:] X_indptr = X.indptr + + cdef SIZE_t n_samples = X.shape[0] + cdef SIZE_t n_features = X.shape[1] + + # Initialize output + cdef SIZE_t[:] indptr = np.zeros(n_samples + 1, dtype=np.intp) + cdef SIZE_t[:] indices = np.zeros( + n_samples * (1 + self.max_depth), dtype=np.intp + ) + + # Initialize auxiliary data-structure + cdef DTYPE_t feature_value = 0. + cdef Node* node = NULL + cdef DTYPE_t* X_sample = NULL + cdef SIZE_t i = 0 + cdef INT32_t k = 0 + + # feature_to_sample as a data structure records the last seen sample + # for each feature; functionally, it is an efficient way to identify + # which features are nonzero in the present sample. + cdef SIZE_t* feature_to_sample = NULL + + safe_realloc(&X_sample, n_features) + safe_realloc(&feature_to_sample, n_features) + + with nogil: + memset(feature_to_sample, -1, n_features * sizeof(SIZE_t)) + + for i in range(n_samples): + node = self.nodes + indptr[i + 1] = indptr[i] + + for k in range(X_indptr[i], X_indptr[i + 1]): + feature_to_sample[X_indices[k]] = i + X_sample[X_indices[k]] = X_data[k] + + # While node not a leaf + while node.left_child != _TREE_LEAF: + # ... and node.right_child != _TREE_LEAF: + + indices[indptr[i + 1]] = (node - self.nodes) + indptr[i + 1] += 1 + + if feature_to_sample[node.feature] == i: + feature_value = X_sample[node.feature] + + else: + feature_value = 0. + + if feature_value <= node.threshold: + node = &self.nodes[node.left_child] + else: + node = &self.nodes[node.right_child] + + # Add the leave node + indices[indptr[i + 1]] = (node - self.nodes) + indptr[i + 1] += 1 + + # Free auxiliary arrays + free(X_sample) + free(feature_to_sample) + + indices = indices[:indptr[n_samples]] + cdef SIZE_t[:] data = np.ones(shape=len(indices), dtype=np.intp) + out = csr_matrix((data, indices, indptr), + shape=(n_samples, self.node_count)) + + return out + + cpdef compute_node_depths(self): + """Compute the depth of each node in a tree. + + .. versionadded:: 1.3 + + Returns + ------- + depths : ndarray of shape (self.node_count,), dtype=np.int64 + The depth of each node in the tree. + """ + cdef: + cnp.int64_t[::1] depths = np.empty(self.node_count, dtype=np.int64) + cnp.npy_intp[:] children_left = self.children_left + cnp.npy_intp[:] children_right = self.children_right + cnp.npy_intp node_id + cnp.npy_intp node_count = self.node_count + cnp.int64_t depth + + depths[0] = 1 # init root node + for node_id in range(node_count): + if children_left[node_id] != _TREE_LEAF: + depth = depths[node_id] + 1 + depths[children_left[node_id]] = depth + depths[children_right[node_id]] = depth + + return depths.base + + cpdef compute_feature_importances(self, normalize=True): + """Computes the importance of each feature (aka variable).""" + cdef Node* left + cdef Node* right + cdef Node* nodes = self.nodes + cdef Node* node = nodes + cdef Node* end_node = node + self.node_count + + cdef double normalizer = 0. + + cdef cnp.float64_t[:] importances = np.zeros(self.n_features) + + with nogil: + while node != end_node: + if node.left_child != _TREE_LEAF: + # ... and node.right_child != _TREE_LEAF: + left = &nodes[node.left_child] + right = &nodes[node.right_child] + + importances[node.feature] += ( + node.weighted_n_node_samples * node.impurity - + left.weighted_n_node_samples * left.impurity - + right.weighted_n_node_samples * right.impurity) + node += 1 + + for i in range(self.n_features): + importances[i] /= nodes[0].weighted_n_node_samples + + if normalize: + normalizer = np.sum(importances) + + if normalizer > 0.0: + # Avoid dividing by zero (e.g., when root is pure) + for i in range(self.n_features): + importances[i] /= normalizer + + return np.asarray(importances) + + cdef cnp.ndarray _get_value_ndarray(self): + """Wraps value as a 3-d NumPy array. + + The array keeps a reference to this Tree, which manages the underlying + memory. + """ + cdef cnp.npy_intp shape[3] + shape[0] = self.node_count + shape[1] = self.n_outputs + shape[2] = self.max_n_classes + cdef cnp.ndarray arr + arr = cnp.PyArray_SimpleNewFromData(3, shape, cnp.NPY_DOUBLE, self.value) + Py_INCREF(self) + if PyArray_SetBaseObject(arr, self) < 0: + raise ValueError("Can't initialize array.") + return arr + + cdef cnp.ndarray _get_node_ndarray(self): + """Wraps nodes as a NumPy struct array. + + The array keeps a reference to this Tree, which manages the underlying + memory. Individual fields are publicly accessible as properties of the + Tree. + """ + cdef cnp.npy_intp shape[1] + shape[0] = self.node_count + cdef cnp.npy_intp strides[1] + strides[0] = sizeof(Node) + cdef cnp.ndarray arr + Py_INCREF(NODE_DTYPE) + arr = PyArray_NewFromDescr( cnp.ndarray, + NODE_DTYPE, 1, shape, + strides, self.nodes, + cnp.NPY_ARRAY_DEFAULT, None) + Py_INCREF(self) + if PyArray_SetBaseObject(arr, self) < 0: + raise ValueError("Can't initialize array.") + return arr + + def compute_partial_dependence(self, DTYPE_t[:, ::1] X, + int[::1] target_features, + double[::1] out): + """Partial dependence of the response on the ``target_feature`` set. + + For each sample in ``X`` a tree traversal is performed. + Each traversal starts from the root with weight 1.0. + + At each non-leaf node that splits on a target feature, either + the left child or the right child is visited based on the feature + value of the current sample, and the weight is not modified. + At each non-leaf node that splits on a complementary feature, + both children are visited and the weight is multiplied by the fraction + of training samples which went to each child. + + At each leaf, the value of the node is multiplied by the current + weight (weights sum to 1 for all visited terminal nodes). + + Parameters + ---------- + X : view on 2d ndarray, shape (n_samples, n_target_features) + The grid points on which the partial dependence should be + evaluated. + target_features : view on 1d ndarray, shape (n_target_features) + The set of target features for which the partial dependence + should be evaluated. + out : view on 1d ndarray, shape (n_samples) + The value of the partial dependence function on each grid + point. + """ + cdef: + double[::1] weight_stack = np.zeros(self.node_count, + dtype=np.float64) + SIZE_t[::1] node_idx_stack = np.zeros(self.node_count, + dtype=np.intp) + SIZE_t sample_idx + SIZE_t feature_idx + int stack_size + double left_sample_frac + double current_weight + double total_weight # used for sanity check only + Node *current_node # use a pointer to avoid copying attributes + SIZE_t current_node_idx + bint is_target_feature + SIZE_t _TREE_LEAF = TREE_LEAF # to avoid python interactions + + for sample_idx in range(X.shape[0]): + # init stacks for current sample + stack_size = 1 + node_idx_stack[0] = 0 # root node + weight_stack[0] = 1 # all the samples are in the root node + total_weight = 0 + + while stack_size > 0: + # pop the stack + stack_size -= 1 + current_node_idx = node_idx_stack[stack_size] + current_node = &self.nodes[current_node_idx] + + if current_node.left_child == _TREE_LEAF: + # leaf node + out[sample_idx] += (weight_stack[stack_size] * + self.value[current_node_idx]) + total_weight += weight_stack[stack_size] + else: + # non-leaf node + + # determine if the split feature is a target feature + is_target_feature = False + for feature_idx in range(target_features.shape[0]): + if target_features[feature_idx] == current_node.feature: + is_target_feature = True + break + + if is_target_feature: + # In this case, we push left or right child on stack + if X[sample_idx, feature_idx] <= current_node.threshold: + node_idx_stack[stack_size] = current_node.left_child + else: + node_idx_stack[stack_size] = current_node.right_child + stack_size += 1 + else: + # In this case, we push both children onto the stack, + # and give a weight proportional to the number of + # samples going through each branch. + + # push left child + node_idx_stack[stack_size] = current_node.left_child + left_sample_frac = ( + self.nodes[current_node.left_child].weighted_n_node_samples / + current_node.weighted_n_node_samples) + current_weight = weight_stack[stack_size] + weight_stack[stack_size] = current_weight * left_sample_frac + stack_size += 1 + + # push right child + node_idx_stack[stack_size] = current_node.right_child + weight_stack[stack_size] = ( + current_weight * (1 - left_sample_frac)) + stack_size += 1 + + # Sanity check. Should never happen. + if not (0.999 < total_weight < 1.001): + raise ValueError("Total weight should be 1.0 but was %.9f" % + total_weight) + + +def _check_n_classes(n_classes, expected_dtype): + if n_classes.ndim != 1: + raise ValueError( + f"Wrong dimensions for n_classes from the pickle: " + f"expected 1, got {n_classes.ndim}" + ) + + if n_classes.dtype == expected_dtype: + return n_classes + + # Handles both different endianness and different bitness + if n_classes.dtype.kind == "i" and n_classes.dtype.itemsize in [4, 8]: + return n_classes.astype(expected_dtype, casting="same_kind") + + raise ValueError( + "n_classes from the pickle has an incompatible dtype:\n" + f"- expected: {expected_dtype}\n" + f"- got: {n_classes.dtype}" + ) + + +def _check_value_ndarray(value_ndarray, expected_dtype, expected_shape): + if value_ndarray.shape != expected_shape: + raise ValueError( + "Wrong shape for value array from the pickle: " + f"expected {expected_shape}, got {value_ndarray.shape}" + ) + + if not value_ndarray.flags.c_contiguous: + raise ValueError( + "value array from the pickle should be a C-contiguous array" + ) + + if value_ndarray.dtype == expected_dtype: + return value_ndarray + + # Handles different endianness + if value_ndarray.dtype.str.endswith('f8'): + return value_ndarray.astype(expected_dtype, casting='equiv') + + raise ValueError( + "value array from the pickle has an incompatible dtype:\n" + f"- expected: {expected_dtype}\n" + f"- got: {value_ndarray.dtype}" + ) + + +def _dtype_to_dict(dtype): + return {name: dt.str for name, (dt, *rest) in dtype.fields.items()} + + +def _dtype_dict_with_modified_bitness(dtype_dict): + # field names in Node struct with SIZE_t types (see sklearn/tree/_tree.pxd) + indexing_field_names = ["left_child", "right_child", "feature", "n_node_samples"] + + expected_dtype_size = str(struct.calcsize("P")) + allowed_dtype_size = "8" if expected_dtype_size == "4" else "4" + + allowed_dtype_dict = dtype_dict.copy() + for name in indexing_field_names: + allowed_dtype_dict[name] = allowed_dtype_dict[name].replace( + expected_dtype_size, allowed_dtype_size + ) + + return allowed_dtype_dict + + +def _all_compatible_dtype_dicts(dtype): + # The Cython code for decision trees uses platform-specific SIZE_t + # typed indexing fields that correspond to either i4 or i8 dtypes for + # the matching fields in the numpy array depending on the bitness of + # the platform (32 bit or 64 bit respectively). + # + # We need to cast the indexing fields of the NODE_DTYPE-dtyped array at + # pickle load time to enable cross-bitness deployment scenarios. We + # typically want to make it possible to run the expensive fit method of + # a tree estimator on a 64 bit server platform, pickle the estimator + # for deployment and run the predict method of a low power 32 bit edge + # platform. + # + # A similar thing happens for endianness, the machine where the pickle was + # saved can have a different endianness than the machine where the pickle + # is loaded + + dtype_dict = _dtype_to_dict(dtype) + dtype_dict_with_modified_bitness = _dtype_dict_with_modified_bitness(dtype_dict) + dtype_dict_with_modified_endianness = _dtype_to_dict(dtype.newbyteorder()) + dtype_dict_with_modified_bitness_and_endianness = _dtype_dict_with_modified_bitness( + dtype_dict_with_modified_endianness + ) + + return [ + dtype_dict, + dtype_dict_with_modified_bitness, + dtype_dict_with_modified_endianness, + dtype_dict_with_modified_bitness_and_endianness, + ] + + +def _check_node_ndarray(node_ndarray, expected_dtype): + if node_ndarray.ndim != 1: + raise ValueError( + "Wrong dimensions for node array from the pickle: " + f"expected 1, got {node_ndarray.ndim}" + ) + + if not node_ndarray.flags.c_contiguous: + raise ValueError( + "node array from the pickle should be a C-contiguous array" + ) + + node_ndarray_dtype = node_ndarray.dtype + if node_ndarray_dtype == expected_dtype: + return node_ndarray + + node_ndarray_dtype_dict = _dtype_to_dict(node_ndarray_dtype) + all_compatible_dtype_dicts = _all_compatible_dtype_dicts(expected_dtype) + + if node_ndarray_dtype_dict not in all_compatible_dtype_dicts: + raise ValueError( + "node array from the pickle has an incompatible dtype:\n" + f"- expected: {expected_dtype}\n" + f"- got : {node_ndarray_dtype}" + ) + + return node_ndarray.astype(expected_dtype, casting="same_kind") + + +# ============================================================================= +# Build Pruned Tree +# ============================================================================= + + +cdef class _CCPPruneController: + """Base class used by build_pruned_tree_ccp and ccp_pruning_path + to control pruning. + """ + cdef bint stop_pruning(self, DOUBLE_t effective_alpha) noexcept nogil: + """Return 1 to stop pruning and 0 to continue pruning""" + return 0 + + cdef void save_metrics(self, DOUBLE_t effective_alpha, + DOUBLE_t subtree_impurities) noexcept nogil: + """Save metrics when pruning""" + pass + + cdef void after_pruning(self, unsigned char[:] in_subtree) noexcept nogil: + """Called after pruning""" + pass + + +cdef class _AlphaPruner(_CCPPruneController): + """Use alpha to control when to stop pruning.""" + cdef DOUBLE_t ccp_alpha + cdef SIZE_t capacity + + def __cinit__(self, DOUBLE_t ccp_alpha): + self.ccp_alpha = ccp_alpha + self.capacity = 0 + + cdef bint stop_pruning(self, DOUBLE_t effective_alpha) noexcept nogil: + # The subtree on the previous iteration has the greatest ccp_alpha + # less than or equal to self.ccp_alpha + return self.ccp_alpha < effective_alpha + + cdef void after_pruning(self, unsigned char[:] in_subtree) noexcept nogil: + """Updates the number of leaves in subtree""" + for i in range(in_subtree.shape[0]): + if in_subtree[i]: + self.capacity += 1 + + +cdef class _PathFinder(_CCPPruneController): + """Record metrics used to return the cost complexity path.""" + cdef DOUBLE_t[:] ccp_alphas + cdef DOUBLE_t[:] impurities + cdef UINT32_t count + + def __cinit__(self, int node_count): + self.ccp_alphas = np.zeros(shape=(node_count), dtype=np.float64) + self.impurities = np.zeros(shape=(node_count), dtype=np.float64) + self.count = 0 + + cdef void save_metrics(self, + DOUBLE_t effective_alpha, + DOUBLE_t subtree_impurities) noexcept nogil: + self.ccp_alphas[self.count] = effective_alpha + self.impurities[self.count] = subtree_impurities + self.count += 1 + + +cdef struct CostComplexityPruningRecord: + SIZE_t node_idx + SIZE_t parent + +cdef _cost_complexity_prune(unsigned char[:] leaves_in_subtree, # OUT + Tree orig_tree, + _CCPPruneController controller): + """Perform cost complexity pruning. + + This function takes an already grown tree, `orig_tree` and outputs a + boolean mask `leaves_in_subtree` which are the leaves in the pruned tree. + During the pruning process, the controller is passed the effective alpha and + the subtree impurities. Furthermore, the controller signals when to stop + pruning. + + Parameters + ---------- + leaves_in_subtree : unsigned char[:] + Output for leaves of subtree + orig_tree : Tree + Original tree + ccp_controller : _CCPPruneController + Cost complexity controller + """ + + cdef: + SIZE_t i + SIZE_t n_nodes = orig_tree.node_count + # prior probability using weighted samples + DOUBLE_t[:] weighted_n_node_samples = orig_tree.weighted_n_node_samples + DOUBLE_t total_sum_weights = weighted_n_node_samples[0] + DOUBLE_t[:] impurity = orig_tree.impurity + # weighted impurity of each node + DOUBLE_t[:] r_node = np.empty(shape=n_nodes, dtype=np.float64) + + SIZE_t[:] child_l = orig_tree.children_left + SIZE_t[:] child_r = orig_tree.children_right + SIZE_t[:] parent = np.zeros(shape=n_nodes, dtype=np.intp) + + stack[CostComplexityPruningRecord] ccp_stack + CostComplexityPruningRecord stack_record + SIZE_t node_idx + stack[SIZE_t] node_indices_stack + + SIZE_t[:] n_leaves = np.zeros(shape=n_nodes, dtype=np.intp) + DOUBLE_t[:] r_branch = np.zeros(shape=n_nodes, dtype=np.float64) + DOUBLE_t current_r + SIZE_t leaf_idx + SIZE_t parent_idx + + # candidate nodes that can be pruned + unsigned char[:] candidate_nodes = np.zeros(shape=n_nodes, + dtype=np.uint8) + # nodes in subtree + unsigned char[:] in_subtree = np.ones(shape=n_nodes, dtype=np.uint8) + SIZE_t pruned_branch_node_idx + DOUBLE_t subtree_alpha + DOUBLE_t effective_alpha + SIZE_t n_pruned_leaves + DOUBLE_t r_diff + DOUBLE_t max_float64 = np.finfo(np.float64).max + + # find parent node ids and leaves + with nogil: + + for i in range(r_node.shape[0]): + r_node[i] = ( + weighted_n_node_samples[i] * impurity[i] / total_sum_weights) + + # Push the root node + ccp_stack.push({"node_idx": 0, "parent": _TREE_UNDEFINED}) + + while not ccp_stack.empty(): + stack_record = ccp_stack.top() + ccp_stack.pop() + + node_idx = stack_record.node_idx + parent[node_idx] = stack_record.parent + + if child_l[node_idx] == _TREE_LEAF: + # ... and child_r[node_idx] == _TREE_LEAF: + leaves_in_subtree[node_idx] = 1 + else: + ccp_stack.push({"node_idx": child_l[node_idx], "parent": node_idx}) + ccp_stack.push({"node_idx": child_r[node_idx], "parent": node_idx}) + + # computes number of leaves in all branches and the overall impurity of + # the branch. The overall impurity is the sum of r_node in its leaves. + for leaf_idx in range(leaves_in_subtree.shape[0]): + if not leaves_in_subtree[leaf_idx]: + continue + r_branch[leaf_idx] = r_node[leaf_idx] + + # bubble up values to ancestor nodes + current_r = r_node[leaf_idx] + while leaf_idx != 0: + parent_idx = parent[leaf_idx] + r_branch[parent_idx] += current_r + n_leaves[parent_idx] += 1 + leaf_idx = parent_idx + + for i in range(leaves_in_subtree.shape[0]): + candidate_nodes[i] = not leaves_in_subtree[i] + + # save metrics before pruning + controller.save_metrics(0.0, r_branch[0]) + + # while root node is not a leaf + while candidate_nodes[0]: + + # computes ccp_alpha for subtrees and finds the minimal alpha + effective_alpha = max_float64 + for i in range(n_nodes): + if not candidate_nodes[i]: + continue + subtree_alpha = (r_node[i] - r_branch[i]) / (n_leaves[i] - 1) + if subtree_alpha < effective_alpha: + effective_alpha = subtree_alpha + pruned_branch_node_idx = i + + if controller.stop_pruning(effective_alpha): + break + + node_indices_stack.push(pruned_branch_node_idx) + + # descendants of branch are not in subtree + while not node_indices_stack.empty(): + node_idx = node_indices_stack.top() + node_indices_stack.pop() + + if not in_subtree[node_idx]: + continue # branch has already been marked for pruning + candidate_nodes[node_idx] = 0 + leaves_in_subtree[node_idx] = 0 + in_subtree[node_idx] = 0 + + if child_l[node_idx] != _TREE_LEAF: + # ... and child_r[node_idx] != _TREE_LEAF: + node_indices_stack.push(child_l[node_idx]) + node_indices_stack.push(child_r[node_idx]) + leaves_in_subtree[pruned_branch_node_idx] = 1 + in_subtree[pruned_branch_node_idx] = 1 + + # updates number of leaves + n_pruned_leaves = n_leaves[pruned_branch_node_idx] - 1 + n_leaves[pruned_branch_node_idx] = 0 + + # computes the increase in r_branch to bubble up + r_diff = r_node[pruned_branch_node_idx] - r_branch[pruned_branch_node_idx] + r_branch[pruned_branch_node_idx] = r_node[pruned_branch_node_idx] + + # bubble up values to ancestors + node_idx = parent[pruned_branch_node_idx] + while node_idx != _TREE_UNDEFINED: + n_leaves[node_idx] -= n_pruned_leaves + r_branch[node_idx] += r_diff + node_idx = parent[node_idx] + + controller.save_metrics(effective_alpha, r_branch[0]) + + controller.after_pruning(in_subtree) + + +def _build_pruned_tree_ccp( + Tree tree, # OUT + Tree orig_tree, + DOUBLE_t ccp_alpha +): + """Build a pruned tree from the original tree using cost complexity + pruning. + + The values and nodes from the original tree are copied into the pruned + tree. + + Parameters + ---------- + tree : Tree + Location to place the pruned tree + orig_tree : Tree + Original tree + ccp_alpha : positive double + Complexity parameter. The subtree with the largest cost complexity + that is smaller than ``ccp_alpha`` will be chosen. By default, + no pruning is performed. + """ + + cdef: + SIZE_t n_nodes = orig_tree.node_count + unsigned char[:] leaves_in_subtree = np.zeros( + shape=n_nodes, dtype=np.uint8) + + pruning_controller = _AlphaPruner(ccp_alpha=ccp_alpha) + + _cost_complexity_prune(leaves_in_subtree, orig_tree, pruning_controller) + + _build_pruned_tree(tree, orig_tree, leaves_in_subtree, + pruning_controller.capacity) + + +def ccp_pruning_path(Tree orig_tree): + """Computes the cost complexity pruning path. + + Parameters + ---------- + tree : Tree + Original tree. + + Returns + ------- + path_info : dict + Information about pruning path with attributes: + + ccp_alphas : ndarray + Effective alphas of subtree during pruning. + + impurities : ndarray + Sum of the impurities of the subtree leaves for the + corresponding alpha value in ``ccp_alphas``. + """ + cdef: + unsigned char[:] leaves_in_subtree = np.zeros( + shape=orig_tree.node_count, dtype=np.uint8) + + path_finder = _PathFinder(orig_tree.node_count) + + _cost_complexity_prune(leaves_in_subtree, orig_tree, path_finder) + + cdef: + UINT32_t total_items = path_finder.count + DOUBLE_t[:] ccp_alphas = np.empty(shape=total_items, dtype=np.float64) + DOUBLE_t[:] impurities = np.empty(shape=total_items, dtype=np.float64) + UINT32_t count = 0 + + while count < total_items: + ccp_alphas[count] = path_finder.ccp_alphas[count] + impurities[count] = path_finder.impurities[count] + count += 1 + + return { + 'ccp_alphas': np.asarray(ccp_alphas), + 'impurities': np.asarray(impurities), + } + + +cdef struct BuildPrunedRecord: + SIZE_t start + SIZE_t depth + SIZE_t parent + bint is_left + +cdef _build_pruned_tree( + Tree tree, # OUT + Tree orig_tree, + const unsigned char[:] leaves_in_subtree, + SIZE_t capacity +): + """Build a pruned tree. + + Build a pruned tree from the original tree by transforming the nodes in + ``leaves_in_subtree`` into leaves. + + Parameters + ---------- + tree : Tree + Location to place the pruned tree + orig_tree : Tree + Original tree + leaves_in_subtree : unsigned char memoryview, shape=(node_count, ) + Boolean mask for leaves to include in subtree + capacity : SIZE_t + Number of nodes to initially allocate in pruned tree + """ + tree._resize(capacity) + + cdef: + SIZE_t orig_node_id + SIZE_t new_node_id + SIZE_t depth + SIZE_t parent + bint is_left + bint is_leaf + + # value_stride for original tree and new tree are the same + SIZE_t value_stride = orig_tree.value_stride + SIZE_t max_depth_seen = -1 + int rc = 0 + Node* node + double* orig_value_ptr + double* new_value_ptr + + stack[BuildPrunedRecord] prune_stack + BuildPrunedRecord stack_record + + with nogil: + # push root node onto stack + prune_stack.push({"start": 0, "depth": 0, "parent": _TREE_UNDEFINED, "is_left": 0}) + + while not prune_stack.empty(): + stack_record = prune_stack.top() + prune_stack.pop() + + orig_node_id = stack_record.start + depth = stack_record.depth + parent = stack_record.parent + is_left = stack_record.is_left + + is_leaf = leaves_in_subtree[orig_node_id] + node = &orig_tree.nodes[orig_node_id] + + new_node_id = tree._add_node( + parent, is_left, is_leaf, node.feature, node.threshold, + node.impurity, node.n_node_samples, + node.weighted_n_node_samples, node.missing_go_to_left) + + if new_node_id == INTPTR_MAX: + rc = -1 + break + + # copy value from original tree to new tree + orig_value_ptr = orig_tree.value + value_stride * orig_node_id + new_value_ptr = tree.value + value_stride * new_node_id + memcpy(new_value_ptr, orig_value_ptr, sizeof(double) * value_stride) + + if not is_leaf: + # Push right child on stack + prune_stack.push({"start": node.right_child, "depth": depth + 1, + "parent": new_node_id, "is_left": 0}) + # push left child on stack + prune_stack.push({"start": node.left_child, "depth": depth + 1, + "parent": new_node_id, "is_left": 1}) + + if depth > max_depth_seen: + max_depth_seen = depth + + if rc >= 0: + tree.max_depth = max_depth_seen + if rc == -1: + raise MemoryError("pruning tree") \ No newline at end of file diff --git a/ivy/functional/frontends/sklearn/_tree_ivy copy.pyx b/ivy/functional/frontends/sklearn/_tree_ivy copy.pyx new file mode 100644 index 0000000000000..f436c919f8bc2 --- /dev/null +++ b/ivy/functional/frontends/sklearn/_tree_ivy copy.pyx @@ -0,0 +1,1833 @@ +# Authors: Gilles Louppe +# Peter Prettenhofer +# Brian Holt +# Noel Dawe +# Satrajit Gosh +# Lars Buitinck +# Arnaud Joly +# Joel Nothman +# Fares Hedayati +# Jacob Schreiber +# Nelson Liu +# +# License: BSD 3 clause + +from cpython cimport Py_INCREF, PyObject, PyTypeObject + +from libc.stdlib cimport free +from libc.string cimport memcpy +from libc.string cimport memset +from libc.stdint cimport INTPTR_MAX +from libc.math cimport isnan +from libcpp.vector cimport vector +from libcpp.algorithm cimport pop_heap +from libcpp.algorithm cimport push_heap +from libcpp cimport bool + +import struct + +import numpy as np +cimport numpy as cnp +cnp.import_array() + +from scipy.sparse import issparse +from scipy.sparse import csr_matrix +from scipy.sparse import isspmatrix_csr + +from ._utils cimport safe_realloc +from ._utils cimport sizet_ptr_to_ndarray + +cdef extern from "numpy/arrayobject.h": + object PyArray_NewFromDescr(PyTypeObject* subtype, cnp.dtype descr, + int nd, cnp.npy_intp* dims, + cnp.npy_intp* strides, + void* data, int flags, object obj) + int PyArray_SetBaseObject(cnp.ndarray arr, PyObject* obj) + +cdef extern from "" namespace "std" nogil: + cdef cppclass stack[T]: + ctypedef T value_type + stack() except + + bint empty() + void pop() + void push(T&) except + # Raise c++ exception for bad_alloc -> MemoryError + T& top() + +# ============================================================================= +# Types and constants +# ============================================================================= + +from numpy import float32 as DTYPE +from numpy import float64 as DOUBLE + +cdef double INFINITY = np.inf +cdef double EPSILON = np.finfo('double').eps + +# Some handy constants (BestFirstTreeBuilder) +cdef int IS_FIRST = 1 +cdef int IS_NOT_FIRST = 0 +cdef int IS_LEFT = 1 +cdef int IS_NOT_LEFT = 0 + +TREE_LEAF = -1 +TREE_UNDEFINED = -2 +cdef SIZE_t _TREE_LEAF = TREE_LEAF +cdef SIZE_t _TREE_UNDEFINED = TREE_UNDEFINED + +# Build the corresponding numpy dtype for Node. +# This works by casting `dummy` to an array of Node of length 1, which numpy +# can construct a `dtype`-object for. See https://stackoverflow.com/q/62448946 +# for a more detailed explanation. +cdef Node dummy +NODE_DTYPE = np.asarray((&dummy)).dtype + +# ============================================================================= +# TreeBuilder +# ============================================================================= + +cdef class TreeBuilder: + """Interface for different tree building strategies.""" + + cpdef build( + self, + Tree tree, + object X, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight=None, + const unsigned char[::1] missing_values_in_feature_mask=None, + ): + """Build a decision tree from the training set (X, y).""" + pass + + cdef inline _check_input( + self, + object X, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight, + ): + """Check input dtype, layout and format""" + if issparse(X): + X = X.tocsc() + X.sort_indices() + + if X.data.dtype != DTYPE: + X.data = np.ascontiguousarray(X.data, dtype=DTYPE) + + if X.indices.dtype != np.int32 or X.indptr.dtype != np.int32: + raise ValueError("No support for np.int64 index based " + "sparse matrices") + + elif X.dtype != DTYPE: + # since we have to copy we will make it fortran for efficiency + X = np.asfortranarray(X, dtype=DTYPE) + + # TODO: This check for y seems to be redundant, as it is also + # present in the BaseDecisionTree's fit method, and therefore + # can be removed. + if y.base.dtype != DOUBLE or not y.base.flags.contiguous: + y = np.ascontiguousarray(y, dtype=DOUBLE) + + if ( + sample_weight is not None and + ( + sample_weight.base.dtype != DOUBLE or + not sample_weight.base.flags.contiguous + ) + ): + sample_weight = np.asarray(sample_weight, dtype=DOUBLE, order="C") + + return X, y, sample_weight + +# Depth first builder --------------------------------------------------------- +# A record on the stack for depth-first tree growing +cdef struct StackRecord: + SIZE_t start + SIZE_t end + SIZE_t depth + SIZE_t parent + bint is_left + double impurity + SIZE_t n_constant_features + + + + + + + +cdef class DepthFirstTreeBuilder(TreeBuilder): + """Build a decision tree in depth-first fashion.""" + + def __cinit__(self, Splitter splitter, SIZE_t min_samples_split, + SIZE_t min_samples_leaf, double min_weight_leaf, + SIZE_t max_depth, double min_impurity_decrease): + self.splitter = splitter + self.min_samples_split = min_samples_split + self.min_samples_leaf = min_samples_leaf + self.min_weight_leaf = min_weight_leaf + self.max_depth = max_depth + self.min_impurity_decrease = min_impurity_decrease + + cpdef build( + self, + Tree tree, + object X, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight=None, + const unsigned char[::1] missing_values_in_feature_mask=None, + ): + """Build a decision tree from the training set (X, y).""" + + # check input + X, y, sample_weight = self._check_input(X, y, sample_weight) + + # Initial capacity + cdef int init_capacity + + if tree.max_depth <= 10: + init_capacity = (2 ** (tree.max_depth + 1)) - 1 + else: + init_capacity = 2047 + + tree._resize(init_capacity) + + # Parameters + cdef Splitter splitter = self.splitter + cdef SIZE_t max_depth = self.max_depth + cdef SIZE_t min_samples_leaf = self.min_samples_leaf + cdef double min_weight_leaf = self.min_weight_leaf + cdef SIZE_t min_samples_split = self.min_samples_split + cdef double min_impurity_decrease = self.min_impurity_decrease + + # Recursive partition (without actual recursion) + splitter.init(X, y, sample_weight, missing_values_in_feature_mask) + + cdef SIZE_t start + cdef SIZE_t end + cdef SIZE_t depth + cdef SIZE_t parent + cdef bint is_left + cdef SIZE_t n_node_samples = splitter.n_samples + cdef double weighted_n_node_samples + cdef SplitRecord split + cdef SIZE_t node_id + + cdef double impurity = INFINITY + cdef SIZE_t n_constant_features + cdef bint is_leaf + cdef bint first = 1 + cdef SIZE_t max_depth_seen = -1 + cdef int rc = 0 + + cdef stack[StackRecord] builder_stack + cdef StackRecord stack_record + + with nogil: + # push root node onto stack + builder_stack.push({ + "start": 0, + "end": n_node_samples, + "depth": 0, + "parent": _TREE_UNDEFINED, + "is_left": 0, + "impurity": INFINITY, + "n_constant_features": 0}) + + while not builder_stack.empty(): + stack_record = builder_stack.top() + builder_stack.pop() + + start = stack_record.start + end = stack_record.end + depth = stack_record.depth + parent = stack_record.parent + is_left = stack_record.is_left + impurity = stack_record.impurity + n_constant_features = stack_record.n_constant_features + + n_node_samples = end - start + splitter.node_reset(start, end, &weighted_n_node_samples) + + is_leaf = (depth >= max_depth or + n_node_samples < min_samples_split or + n_node_samples < 2 * min_samples_leaf or + weighted_n_node_samples < 2 * min_weight_leaf) + + if first: + impurity = splitter.node_impurity() + first = 0 + + # impurity == 0 with tolerance due to rounding errors + is_leaf = is_leaf or impurity <= EPSILON + + if not is_leaf: + splitter.node_split(impurity, &split, &n_constant_features) + # If EPSILON=0 in the below comparison, float precision + # issues stop splitting, producing trees that are + # dissimilar to v0.18 + is_leaf = (is_leaf or split.pos >= end or + (split.improvement + EPSILON < + min_impurity_decrease)) + + node_id = tree._add_node(parent, is_left, is_leaf, split.feature, + split.threshold, impurity, n_node_samples, + weighted_n_node_samples, + split.missing_go_to_left) + + if node_id == INTPTR_MAX: + rc = -1 + break + + # Store value for all nodes, to facilitate tree/model + # inspection and interpretation + splitter.node_value(tree.value + node_id * tree.value_stride) + + if not is_leaf: + # Push right child on stack + builder_stack.push({ + "start": split.pos, + "end": end, + "depth": depth + 1, + "parent": node_id, + "is_left": 0, + "impurity": split.impurity_right, + "n_constant_features": n_constant_features}) + + # Push left child on stack + builder_stack.push({ + "start": start, + "end": split.pos, + "depth": depth + 1, + "parent": node_id, + "is_left": 1, + "impurity": split.impurity_left, + "n_constant_features": n_constant_features}) + + if depth > max_depth_seen: + max_depth_seen = depth + + if rc >= 0: + rc = tree._resize_c(tree.node_count) + + if rc >= 0: + tree.max_depth = max_depth_seen + if rc == -1: + raise MemoryError() + + +# Best first builder ---------------------------------------------------------- +cdef struct FrontierRecord: + # Record of information of a Node, the frontier for a split. Those records are + # maintained in a heap to access the Node with the best improvement in impurity, + # allowing growing trees greedily on this improvement. + SIZE_t node_id + SIZE_t start + SIZE_t end + SIZE_t pos + SIZE_t depth + bint is_leaf + double impurity + double impurity_left + double impurity_right + double improvement + +cdef inline bool _compare_records( + const FrontierRecord& left, + const FrontierRecord& right, +): + return left.improvement < right.improvement + +cdef inline void _add_to_frontier( + FrontierRecord rec, + vector[FrontierRecord]& frontier, +) noexcept nogil: + """Adds record `rec` to the priority queue `frontier`.""" + frontier.push_back(rec) + push_heap(frontier.begin(), frontier.end(), &_compare_records) + + +cdef class BestFirstTreeBuilder(TreeBuilder): + """Build a decision tree in best-first fashion. + + The best node to expand is given by the node at the frontier that has the + highest impurity improvement. + """ + cdef SIZE_t max_leaf_nodes + + def __cinit__(self, Splitter splitter, SIZE_t min_samples_split, + SIZE_t min_samples_leaf, min_weight_leaf, + SIZE_t max_depth, SIZE_t max_leaf_nodes, + double min_impurity_decrease): + self.splitter = splitter + self.min_samples_split = min_samples_split + self.min_samples_leaf = min_samples_leaf + self.min_weight_leaf = min_weight_leaf + self.max_depth = max_depth + self.max_leaf_nodes = max_leaf_nodes + self.min_impurity_decrease = min_impurity_decrease + + cpdef build( + self, + Tree tree, + object X, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight=None, + const unsigned char[::1] missing_values_in_feature_mask=None, + ): + """Build a decision tree from the training set (X, y).""" + + # check input + X, y, sample_weight = self._check_input(X, y, sample_weight) + + # Parameters + cdef Splitter splitter = self.splitter + cdef SIZE_t max_leaf_nodes = self.max_leaf_nodes + + # Recursive partition (without actual recursion) + splitter.init(X, y, sample_weight, missing_values_in_feature_mask) + + cdef vector[FrontierRecord] frontier + cdef FrontierRecord record + cdef FrontierRecord split_node_left + cdef FrontierRecord split_node_right + + cdef SIZE_t n_node_samples = splitter.n_samples + cdef SIZE_t max_split_nodes = max_leaf_nodes - 1 + cdef bint is_leaf + cdef SIZE_t max_depth_seen = -1 + cdef int rc = 0 + cdef Node* node + + # Initial capacity + cdef SIZE_t init_capacity = max_split_nodes + max_leaf_nodes + tree._resize(init_capacity) + + with nogil: + # add root to frontier + rc = self._add_split_node(splitter, tree, 0, n_node_samples, + INFINITY, IS_FIRST, IS_LEFT, NULL, 0, + &split_node_left) + if rc >= 0: + _add_to_frontier(split_node_left, frontier) + + while not frontier.empty(): + pop_heap(frontier.begin(), frontier.end(), &_compare_records) + record = frontier.back() + frontier.pop_back() + + node = &tree.nodes[record.node_id] + is_leaf = (record.is_leaf or max_split_nodes <= 0) + + if is_leaf: + # Node is not expandable; set node as leaf + node.left_child = _TREE_LEAF + node.right_child = _TREE_LEAF + node.feature = _TREE_UNDEFINED + node.threshold = _TREE_UNDEFINED + + else: + # Node is expandable + + # Decrement number of split nodes available + max_split_nodes -= 1 + + # Compute left split node + rc = self._add_split_node(splitter, tree, + record.start, record.pos, + record.impurity_left, + IS_NOT_FIRST, IS_LEFT, node, + record.depth + 1, + &split_node_left) + if rc == -1: + break + + # tree.nodes may have changed + node = &tree.nodes[record.node_id] + + # Compute right split node + rc = self._add_split_node(splitter, tree, record.pos, + record.end, + record.impurity_right, + IS_NOT_FIRST, IS_NOT_LEFT, node, + record.depth + 1, + &split_node_right) + if rc == -1: + break + + # Add nodes to queue + _add_to_frontier(split_node_left, frontier) + _add_to_frontier(split_node_right, frontier) + + if record.depth > max_depth_seen: + max_depth_seen = record.depth + + if rc >= 0: + rc = tree._resize_c(tree.node_count) + + if rc >= 0: + tree.max_depth = max_depth_seen + + if rc == -1: + raise MemoryError() + + cdef inline int _add_split_node(self, Splitter splitter, Tree tree, + SIZE_t start, SIZE_t end, double impurity, + bint is_first, bint is_left, Node* parent, + SIZE_t depth, + FrontierRecord* res) except -1 nogil: + """Adds node w/ partition ``[start, end)`` to the frontier. """ + cdef SplitRecord split + cdef SIZE_t node_id + cdef SIZE_t n_node_samples + cdef SIZE_t n_constant_features = 0 + cdef double min_impurity_decrease = self.min_impurity_decrease + cdef double weighted_n_node_samples + cdef bint is_leaf + + splitter.node_reset(start, end, &weighted_n_node_samples) + + if is_first: + impurity = splitter.node_impurity() + + n_node_samples = end - start + is_leaf = (depth >= self.max_depth or + n_node_samples < self.min_samples_split or + n_node_samples < 2 * self.min_samples_leaf or + weighted_n_node_samples < 2 * self.min_weight_leaf or + impurity <= EPSILON # impurity == 0 with tolerance + ) + + if not is_leaf: + splitter.node_split(impurity, &split, &n_constant_features) + # If EPSILON=0 in the below comparison, float precision issues stop + # splitting early, producing trees that are dissimilar to v0.18 + is_leaf = (is_leaf or split.pos >= end or + split.improvement + EPSILON < min_impurity_decrease) + + node_id = tree._add_node(parent - tree.nodes + if parent != NULL + else _TREE_UNDEFINED, + is_left, is_leaf, + split.feature, split.threshold, impurity, n_node_samples, + weighted_n_node_samples, + split.missing_go_to_left) + if node_id == INTPTR_MAX: + return -1 + + # compute values also for split nodes (might become leafs later). + splitter.node_value(tree.value + node_id * tree.value_stride) + + res.node_id = node_id + res.start = start + res.end = end + res.depth = depth + res.impurity = impurity + + if not is_leaf: + # is split node + res.pos = split.pos + res.is_leaf = 0 + res.improvement = split.improvement + res.impurity_left = split.impurity_left + res.impurity_right = split.impurity_right + + else: + # is leaf => 0 improvement + res.pos = end + res.is_leaf = 1 + res.improvement = 0.0 + res.impurity_left = impurity + res.impurity_right = impurity + + return 0 + + +# ============================================================================= +# Tree +# ============================================================================= + +cdef class Tree: + """Array-based representation of a binary decision tree. + + The binary tree is represented as a number of parallel arrays. The i-th + element of each array holds information about the node `i`. Node 0 is the + tree's root. You can find a detailed description of all arrays in + `_tree.pxd`. NOTE: Some of the arrays only apply to either leaves or split + nodes, resp. In this case the values of nodes of the other type are + arbitrary! + + Attributes + ---------- + node_count : int + The number of nodes (internal nodes + leaves) in the tree. + + capacity : int + The current capacity (i.e., size) of the arrays, which is at least as + great as `node_count`. + + max_depth : int + The depth of the tree, i.e. the maximum depth of its leaves. + + children_left : array of int, shape [node_count] + children_left[i] holds the node id of the left child of node i. + For leaves, children_left[i] == TREE_LEAF. Otherwise, + children_left[i] > i. This child handles the case where + X[:, feature[i]] <= threshold[i]. + + children_right : array of int, shape [node_count] + children_right[i] holds the node id of the right child of node i. + For leaves, children_right[i] == TREE_LEAF. Otherwise, + children_right[i] > i. This child handles the case where + X[:, feature[i]] > threshold[i]. + + feature : array of int, shape [node_count] + feature[i] holds the feature to split on, for the internal node i. + + threshold : array of double, shape [node_count] + threshold[i] holds the threshold for the internal node i. + + value : array of double, shape [node_count, n_outputs, max_n_classes] + Contains the constant prediction value of each node. + + impurity : array of double, shape [node_count] + impurity[i] holds the impurity (i.e., the value of the splitting + criterion) at node i. + + n_node_samples : array of int, shape [node_count] + n_node_samples[i] holds the number of training samples reaching node i. + + weighted_n_node_samples : array of double, shape [node_count] + weighted_n_node_samples[i] holds the weighted number of training samples + reaching node i. + """ + # Wrap for outside world. + # WARNING: these reference the current `nodes` and `value` buffers, which + # must not be freed by a subsequent memory allocation. + # (i.e. through `_resize` or `__setstate__`) + @property + def n_classes(self): + return sizet_ptr_to_ndarray(self.n_classes, self.n_outputs) + + @property + def children_left(self): + return self._get_node_ndarray()['left_child'][:self.node_count] + + @property + def children_right(self): + return self._get_node_ndarray()['right_child'][:self.node_count] + + @property + def n_leaves(self): + return np.sum(np.logical_and( + self.children_left == -1, + self.children_right == -1)) + + @property + def feature(self): + return self._get_node_ndarray()['feature'][:self.node_count] + + @property + def threshold(self): + return self._get_node_ndarray()['threshold'][:self.node_count] + + @property + def impurity(self): + return self._get_node_ndarray()['impurity'][:self.node_count] + + @property + def n_node_samples(self): + return self._get_node_ndarray()['n_node_samples'][:self.node_count] + + @property + def weighted_n_node_samples(self): + return self._get_node_ndarray()['weighted_n_node_samples'][:self.node_count] + + @property + def missing_go_to_left(self): + return self._get_node_ndarray()['missing_go_to_left'][:self.node_count] + + @property + def value(self): + return self._get_value_ndarray()[:self.node_count] + + # TODO: Convert n_classes to cython.integral memory view once + # https://github.com/cython/cython/issues/5243 is fixed + def __cinit__(self, int n_features, cnp.ndarray n_classes, int n_outputs): + """Constructor.""" + cdef SIZE_t dummy = 0 + size_t_dtype = np.array(dummy).dtype + + n_classes = _check_n_classes(n_classes, size_t_dtype) + + # Input/Output layout + self.n_features = n_features + self.n_outputs = n_outputs + self.n_classes = NULL + safe_realloc(&self.n_classes, n_outputs) + + self.max_n_classes = np.max(n_classes) + self.value_stride = n_outputs * self.max_n_classes + + cdef SIZE_t k + for k in range(n_outputs): + self.n_classes[k] = n_classes[k] + + # Inner structures + self.max_depth = 0 + self.node_count = 0 + self.capacity = 0 + self.value = NULL + self.nodes = NULL + + def __dealloc__(self): + """Destructor.""" + # Free all inner structures + free(self.n_classes) + free(self.value) + free(self.nodes) + + def __reduce__(self): + """Reduce re-implementation, for pickling.""" + return (Tree, (self.n_features, + sizet_ptr_to_ndarray(self.n_classes, self.n_outputs), + self.n_outputs), self.__getstate__()) + + def __getstate__(self): + """Getstate re-implementation, for pickling.""" + d = {} + # capacity is inferred during the __setstate__ using nodes + d["max_depth"] = self.max_depth + d["node_count"] = self.node_count + d["nodes"] = self._get_node_ndarray() + d["values"] = self._get_value_ndarray() + return d + + def __setstate__(self, d): + """Setstate re-implementation, for unpickling.""" + self.max_depth = d["max_depth"] + self.node_count = d["node_count"] + + if 'nodes' not in d: + raise ValueError('You have loaded Tree version which ' + 'cannot be imported') + + node_ndarray = d['nodes'] + value_ndarray = d['values'] + + value_shape = (node_ndarray.shape[0], self.n_outputs, + self.max_n_classes) + + node_ndarray = _check_node_ndarray(node_ndarray, expected_dtype=NODE_DTYPE) + value_ndarray = _check_value_ndarray( + value_ndarray, + expected_dtype=np.dtype(np.float64), + expected_shape=value_shape + ) + + self.capacity = node_ndarray.shape[0] + if self._resize_c(self.capacity) != 0: + raise MemoryError("resizing tree to %d" % self.capacity) + + memcpy(self.nodes, cnp.PyArray_DATA(node_ndarray), + self.capacity * sizeof(Node)) + memcpy(self.value, cnp.PyArray_DATA(value_ndarray), + self.capacity * self.value_stride * sizeof(double)) + + cdef int _resize(self, SIZE_t capacity) except -1 nogil: + """Resize all inner arrays to `capacity`, if `capacity` == -1, then + double the size of the inner arrays. + + Returns -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + """ + if self._resize_c(capacity) != 0: + # Acquire gil only if we need to raise + with gil: + raise MemoryError() + + cdef int _resize_c(self, SIZE_t capacity=INTPTR_MAX) except -1 nogil: + """Guts of _resize + + Returns -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + """ + if capacity == self.capacity and self.nodes != NULL: + return 0 + + if capacity == INTPTR_MAX: + if self.capacity == 0: + capacity = 3 # default initial value + else: + capacity = 2 * self.capacity + + safe_realloc(&self.nodes, capacity) + safe_realloc(&self.value, capacity * self.value_stride) + + # value memory is initialised to 0 to enable classifier argmax + if capacity > self.capacity: + memset((self.value + self.capacity * self.value_stride), 0, + (capacity - self.capacity) * self.value_stride * + sizeof(double)) + + # if capacity smaller than node_count, adjust the counter + if capacity < self.node_count: + self.node_count = capacity + + self.capacity = capacity + return 0 + + cdef SIZE_t _add_node(self, SIZE_t parent, bint is_left, bint is_leaf, + SIZE_t feature, double threshold, double impurity, + SIZE_t n_node_samples, + double weighted_n_node_samples, + unsigned char missing_go_to_left) except -1 nogil: + """Add a node to the tree. + + The new node registers itself as the child of its parent. + + Returns (size_t)(-1) on error. + """ + cdef SIZE_t node_id = self.node_count + + if node_id >= self.capacity: + if self._resize_c() != 0: + return INTPTR_MAX + + cdef Node* node = &self.nodes[node_id] + node.impurity = impurity + node.n_node_samples = n_node_samples + node.weighted_n_node_samples = weighted_n_node_samples + + if parent != _TREE_UNDEFINED: + if is_left: + self.nodes[parent].left_child = node_id + else: + self.nodes[parent].right_child = node_id + + if is_leaf: + node.left_child = _TREE_LEAF + node.right_child = _TREE_LEAF + node.feature = _TREE_UNDEFINED + node.threshold = _TREE_UNDEFINED + + else: + # left_child and right_child will be set later + node.feature = feature + node.threshold = threshold + node.missing_go_to_left = missing_go_to_left + + self.node_count += 1 + + return node_id + + cpdef cnp.ndarray predict(self, object X): + """Predict target for X.""" + out = self._get_value_ndarray().take(self.apply(X), axis=0, + mode='clip') + if self.n_outputs == 1: + out = out.reshape(X.shape[0], self.max_n_classes) + return out + + cpdef cnp.ndarray apply(self, object X): + """Finds the terminal region (=leaf node) for each sample in X.""" + if issparse(X): + return self._apply_sparse_csr(X) + else: + return self._apply_dense(X) + + cdef inline cnp.ndarray _apply_dense(self, object X): + """Finds the terminal region (=leaf node) for each sample in X.""" + + # Check input + if not isinstance(X, np.ndarray): + raise ValueError("X should be in np.ndarray format, got %s" + % type(X)) + + if X.dtype != DTYPE: + raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + + # Extract input + cdef const DTYPE_t[:, :] X_ndarray = X + cdef SIZE_t n_samples = X.shape[0] + cdef DTYPE_t X_i_node_feature + + # Initialize output + cdef SIZE_t[:] out = np.zeros(n_samples, dtype=np.intp) + + # Initialize auxiliary data-structure + cdef Node* node = NULL + cdef SIZE_t i = 0 + + with nogil: + for i in range(n_samples): + node = self.nodes + # While node not a leaf + while node.left_child != _TREE_LEAF: + X_i_node_feature = X_ndarray[i, node.feature] + # ... and node.right_child != _TREE_LEAF: + if isnan(X_i_node_feature): + if node.missing_go_to_left: + node = &self.nodes[node.left_child] + else: + node = &self.nodes[node.right_child] + elif X_i_node_feature <= node.threshold: + node = &self.nodes[node.left_child] + else: + node = &self.nodes[node.right_child] + + out[i] = (node - self.nodes) # node offset + + return np.asarray(out) + + cdef inline cnp.ndarray _apply_sparse_csr(self, object X): + """Finds the terminal region (=leaf node) for each sample in sparse X. + """ + # Check input + if not isspmatrix_csr(X): + raise ValueError("X should be in csr_matrix format, got %s" + % type(X)) + + if X.dtype != DTYPE: + raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + + # Extract input + cdef const DTYPE_t[:] X_data = X.data + cdef const INT32_t[:] X_indices = X.indices + cdef const INT32_t[:] X_indptr = X.indptr + + cdef SIZE_t n_samples = X.shape[0] + cdef SIZE_t n_features = X.shape[1] + + # Initialize output + cdef SIZE_t[:] out = np.zeros(n_samples, dtype=np.intp) + + # Initialize auxiliary data-structure + cdef DTYPE_t feature_value = 0. + cdef Node* node = NULL + cdef DTYPE_t* X_sample = NULL + cdef SIZE_t i = 0 + cdef INT32_t k = 0 + + # feature_to_sample as a data structure records the last seen sample + # for each feature; functionally, it is an efficient way to identify + # which features are nonzero in the present sample. + cdef SIZE_t* feature_to_sample = NULL + + safe_realloc(&X_sample, n_features) + safe_realloc(&feature_to_sample, n_features) + + with nogil: + memset(feature_to_sample, -1, n_features * sizeof(SIZE_t)) + + for i in range(n_samples): + node = self.nodes + + for k in range(X_indptr[i], X_indptr[i + 1]): + feature_to_sample[X_indices[k]] = i + X_sample[X_indices[k]] = X_data[k] + + # While node not a leaf + while node.left_child != _TREE_LEAF: + # ... and node.right_child != _TREE_LEAF: + if feature_to_sample[node.feature] == i: + feature_value = X_sample[node.feature] + + else: + feature_value = 0. + + if feature_value <= node.threshold: + node = &self.nodes[node.left_child] + else: + node = &self.nodes[node.right_child] + + out[i] = (node - self.nodes) # node offset + + # Free auxiliary arrays + free(X_sample) + free(feature_to_sample) + + return np.asarray(out) + + cpdef object decision_path(self, object X): + """Finds the decision path (=node) for each sample in X.""" + if issparse(X): + return self._decision_path_sparse_csr(X) + else: + return self._decision_path_dense(X) + + cdef inline object _decision_path_dense(self, object X): + """Finds the decision path (=node) for each sample in X.""" + + # Check input + if not isinstance(X, np.ndarray): + raise ValueError("X should be in np.ndarray format, got %s" + % type(X)) + + if X.dtype != DTYPE: + raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + + # Extract input + cdef const DTYPE_t[:, :] X_ndarray = X + cdef SIZE_t n_samples = X.shape[0] + + # Initialize output + cdef SIZE_t[:] indptr = np.zeros(n_samples + 1, dtype=np.intp) + cdef SIZE_t[:] indices = np.zeros( + n_samples * (1 + self.max_depth), dtype=np.intp + ) + + # Initialize auxiliary data-structure + cdef Node* node = NULL + cdef SIZE_t i = 0 + + with nogil: + for i in range(n_samples): + node = self.nodes + indptr[i + 1] = indptr[i] + + # Add all external nodes + while node.left_child != _TREE_LEAF: + # ... and node.right_child != _TREE_LEAF: + indices[indptr[i + 1]] = (node - self.nodes) + indptr[i + 1] += 1 + + if X_ndarray[i, node.feature] <= node.threshold: + node = &self.nodes[node.left_child] + else: + node = &self.nodes[node.right_child] + + # Add the leave node + indices[indptr[i + 1]] = (node - self.nodes) + indptr[i + 1] += 1 + + indices = indices[:indptr[n_samples]] + cdef SIZE_t[:] data = np.ones(shape=len(indices), dtype=np.intp) + out = csr_matrix((data, indices, indptr), + shape=(n_samples, self.node_count)) + + return out + + cdef inline object _decision_path_sparse_csr(self, object X): + """Finds the decision path (=node) for each sample in X.""" + + # Check input + if not isspmatrix_csr(X): + raise ValueError("X should be in csr_matrix format, got %s" + % type(X)) + + if X.dtype != DTYPE: + raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + + # Extract input + cdef const DTYPE_t[:] X_data = X.data + cdef const INT32_t[:] X_indices = X.indices + cdef const INT32_t[:] X_indptr = X.indptr + + cdef SIZE_t n_samples = X.shape[0] + cdef SIZE_t n_features = X.shape[1] + + # Initialize output + cdef SIZE_t[:] indptr = np.zeros(n_samples + 1, dtype=np.intp) + cdef SIZE_t[:] indices = np.zeros( + n_samples * (1 + self.max_depth), dtype=np.intp + ) + + # Initialize auxiliary data-structure + cdef DTYPE_t feature_value = 0. + cdef Node* node = NULL + cdef DTYPE_t* X_sample = NULL + cdef SIZE_t i = 0 + cdef INT32_t k = 0 + + # feature_to_sample as a data structure records the last seen sample + # for each feature; functionally, it is an efficient way to identify + # which features are nonzero in the present sample. + cdef SIZE_t* feature_to_sample = NULL + + safe_realloc(&X_sample, n_features) + safe_realloc(&feature_to_sample, n_features) + + with nogil: + memset(feature_to_sample, -1, n_features * sizeof(SIZE_t)) + + for i in range(n_samples): + node = self.nodes + indptr[i + 1] = indptr[i] + + for k in range(X_indptr[i], X_indptr[i + 1]): + feature_to_sample[X_indices[k]] = i + X_sample[X_indices[k]] = X_data[k] + + # While node not a leaf + while node.left_child != _TREE_LEAF: + # ... and node.right_child != _TREE_LEAF: + + indices[indptr[i + 1]] = (node - self.nodes) + indptr[i + 1] += 1 + + if feature_to_sample[node.feature] == i: + feature_value = X_sample[node.feature] + + else: + feature_value = 0. + + if feature_value <= node.threshold: + node = &self.nodes[node.left_child] + else: + node = &self.nodes[node.right_child] + + # Add the leave node + indices[indptr[i + 1]] = (node - self.nodes) + indptr[i + 1] += 1 + + # Free auxiliary arrays + free(X_sample) + free(feature_to_sample) + + indices = indices[:indptr[n_samples]] + cdef SIZE_t[:] data = np.ones(shape=len(indices), dtype=np.intp) + out = csr_matrix((data, indices, indptr), + shape=(n_samples, self.node_count)) + + return out + + cpdef compute_node_depths(self): + """Compute the depth of each node in a tree. + + .. versionadded:: 1.3 + + Returns + ------- + depths : ndarray of shape (self.node_count,), dtype=np.int64 + The depth of each node in the tree. + """ + cdef: + cnp.int64_t[::1] depths = np.empty(self.node_count, dtype=np.int64) + cnp.npy_intp[:] children_left = self.children_left + cnp.npy_intp[:] children_right = self.children_right + cnp.npy_intp node_id + cnp.npy_intp node_count = self.node_count + cnp.int64_t depth + + depths[0] = 1 # init root node + for node_id in range(node_count): + if children_left[node_id] != _TREE_LEAF: + depth = depths[node_id] + 1 + depths[children_left[node_id]] = depth + depths[children_right[node_id]] = depth + + return depths.base + + cpdef compute_feature_importances(self, normalize=True): + """Computes the importance of each feature (aka variable).""" + cdef Node* left + cdef Node* right + cdef Node* nodes = self.nodes + cdef Node* node = nodes + cdef Node* end_node = node + self.node_count + + cdef double normalizer = 0. + + cdef cnp.float64_t[:] importances = np.zeros(self.n_features) + + with nogil: + while node != end_node: + if node.left_child != _TREE_LEAF: + # ... and node.right_child != _TREE_LEAF: + left = &nodes[node.left_child] + right = &nodes[node.right_child] + + importances[node.feature] += ( + node.weighted_n_node_samples * node.impurity - + left.weighted_n_node_samples * left.impurity - + right.weighted_n_node_samples * right.impurity) + node += 1 + + for i in range(self.n_features): + importances[i] /= nodes[0].weighted_n_node_samples + + if normalize: + normalizer = np.sum(importances) + + if normalizer > 0.0: + # Avoid dividing by zero (e.g., when root is pure) + for i in range(self.n_features): + importances[i] /= normalizer + + return np.asarray(importances) + + cdef cnp.ndarray _get_value_ndarray(self): + """Wraps value as a 3-d NumPy array. + + The array keeps a reference to this Tree, which manages the underlying + memory. + """ + cdef cnp.npy_intp shape[3] + shape[0] = self.node_count + shape[1] = self.n_outputs + shape[2] = self.max_n_classes + cdef cnp.ndarray arr + arr = cnp.PyArray_SimpleNewFromData(3, shape, cnp.NPY_DOUBLE, self.value) + Py_INCREF(self) + if PyArray_SetBaseObject(arr, self) < 0: + raise ValueError("Can't initialize array.") + return arr + + cdef cnp.ndarray _get_node_ndarray(self): + """Wraps nodes as a NumPy struct array. + + The array keeps a reference to this Tree, which manages the underlying + memory. Individual fields are publicly accessible as properties of the + Tree. + """ + cdef cnp.npy_intp shape[1] + shape[0] = self.node_count + cdef cnp.npy_intp strides[1] + strides[0] = sizeof(Node) + cdef cnp.ndarray arr + Py_INCREF(NODE_DTYPE) + arr = PyArray_NewFromDescr( cnp.ndarray, + NODE_DTYPE, 1, shape, + strides, self.nodes, + cnp.NPY_ARRAY_DEFAULT, None) + Py_INCREF(self) + if PyArray_SetBaseObject(arr, self) < 0: + raise ValueError("Can't initialize array.") + return arr + + def compute_partial_dependence(self, DTYPE_t[:, ::1] X, + int[::1] target_features, + double[::1] out): + """Partial dependence of the response on the ``target_feature`` set. + + For each sample in ``X`` a tree traversal is performed. + Each traversal starts from the root with weight 1.0. + + At each non-leaf node that splits on a target feature, either + the left child or the right child is visited based on the feature + value of the current sample, and the weight is not modified. + At each non-leaf node that splits on a complementary feature, + both children are visited and the weight is multiplied by the fraction + of training samples which went to each child. + + At each leaf, the value of the node is multiplied by the current + weight (weights sum to 1 for all visited terminal nodes). + + Parameters + ---------- + X : view on 2d ndarray, shape (n_samples, n_target_features) + The grid points on which the partial dependence should be + evaluated. + target_features : view on 1d ndarray, shape (n_target_features) + The set of target features for which the partial dependence + should be evaluated. + out : view on 1d ndarray, shape (n_samples) + The value of the partial dependence function on each grid + point. + """ + cdef: + double[::1] weight_stack = np.zeros(self.node_count, + dtype=np.float64) + SIZE_t[::1] node_idx_stack = np.zeros(self.node_count, + dtype=np.intp) + SIZE_t sample_idx + SIZE_t feature_idx + int stack_size + double left_sample_frac + double current_weight + double total_weight # used for sanity check only + Node *current_node # use a pointer to avoid copying attributes + SIZE_t current_node_idx + bint is_target_feature + SIZE_t _TREE_LEAF = TREE_LEAF # to avoid python interactions + + for sample_idx in range(X.shape[0]): + # init stacks for current sample + stack_size = 1 + node_idx_stack[0] = 0 # root node + weight_stack[0] = 1 # all the samples are in the root node + total_weight = 0 + + while stack_size > 0: + # pop the stack + stack_size -= 1 + current_node_idx = node_idx_stack[stack_size] + current_node = &self.nodes[current_node_idx] + + if current_node.left_child == _TREE_LEAF: + # leaf node + out[sample_idx] += (weight_stack[stack_size] * + self.value[current_node_idx]) + total_weight += weight_stack[stack_size] + else: + # non-leaf node + + # determine if the split feature is a target feature + is_target_feature = False + for feature_idx in range(target_features.shape[0]): + if target_features[feature_idx] == current_node.feature: + is_target_feature = True + break + + if is_target_feature: + # In this case, we push left or right child on stack + if X[sample_idx, feature_idx] <= current_node.threshold: + node_idx_stack[stack_size] = current_node.left_child + else: + node_idx_stack[stack_size] = current_node.right_child + stack_size += 1 + else: + # In this case, we push both children onto the stack, + # and give a weight proportional to the number of + # samples going through each branch. + + # push left child + node_idx_stack[stack_size] = current_node.left_child + left_sample_frac = ( + self.nodes[current_node.left_child].weighted_n_node_samples / + current_node.weighted_n_node_samples) + current_weight = weight_stack[stack_size] + weight_stack[stack_size] = current_weight * left_sample_frac + stack_size += 1 + + # push right child + node_idx_stack[stack_size] = current_node.right_child + weight_stack[stack_size] = ( + current_weight * (1 - left_sample_frac)) + stack_size += 1 + + # Sanity check. Should never happen. + if not (0.999 < total_weight < 1.001): + raise ValueError("Total weight should be 1.0 but was %.9f" % + total_weight) + + +def _check_n_classes(n_classes, expected_dtype): + if n_classes.ndim != 1: + raise ValueError( + f"Wrong dimensions for n_classes from the pickle: " + f"expected 1, got {n_classes.ndim}" + ) + + if n_classes.dtype == expected_dtype: + return n_classes + + # Handles both different endianness and different bitness + if n_classes.dtype.kind == "i" and n_classes.dtype.itemsize in [4, 8]: + return n_classes.astype(expected_dtype, casting="same_kind") + + raise ValueError( + "n_classes from the pickle has an incompatible dtype:\n" + f"- expected: {expected_dtype}\n" + f"- got: {n_classes.dtype}" + ) + + +def _check_value_ndarray(value_ndarray, expected_dtype, expected_shape): + if value_ndarray.shape != expected_shape: + raise ValueError( + "Wrong shape for value array from the pickle: " + f"expected {expected_shape}, got {value_ndarray.shape}" + ) + + if not value_ndarray.flags.c_contiguous: + raise ValueError( + "value array from the pickle should be a C-contiguous array" + ) + + if value_ndarray.dtype == expected_dtype: + return value_ndarray + + # Handles different endianness + if value_ndarray.dtype.str.endswith('f8'): + return value_ndarray.astype(expected_dtype, casting='equiv') + + raise ValueError( + "value array from the pickle has an incompatible dtype:\n" + f"- expected: {expected_dtype}\n" + f"- got: {value_ndarray.dtype}" + ) + + +def _dtype_to_dict(dtype): + return {name: dt.str for name, (dt, *rest) in dtype.fields.items()} + + +def _dtype_dict_with_modified_bitness(dtype_dict): + # field names in Node struct with SIZE_t types (see sklearn/tree/_tree.pxd) + indexing_field_names = ["left_child", "right_child", "feature", "n_node_samples"] + + expected_dtype_size = str(struct.calcsize("P")) + allowed_dtype_size = "8" if expected_dtype_size == "4" else "4" + + allowed_dtype_dict = dtype_dict.copy() + for name in indexing_field_names: + allowed_dtype_dict[name] = allowed_dtype_dict[name].replace( + expected_dtype_size, allowed_dtype_size + ) + + return allowed_dtype_dict + + +def _all_compatible_dtype_dicts(dtype): + # The Cython code for decision trees uses platform-specific SIZE_t + # typed indexing fields that correspond to either i4 or i8 dtypes for + # the matching fields in the numpy array depending on the bitness of + # the platform (32 bit or 64 bit respectively). + # + # We need to cast the indexing fields of the NODE_DTYPE-dtyped array at + # pickle load time to enable cross-bitness deployment scenarios. We + # typically want to make it possible to run the expensive fit method of + # a tree estimator on a 64 bit server platform, pickle the estimator + # for deployment and run the predict method of a low power 32 bit edge + # platform. + # + # A similar thing happens for endianness, the machine where the pickle was + # saved can have a different endianness than the machine where the pickle + # is loaded + + dtype_dict = _dtype_to_dict(dtype) + dtype_dict_with_modified_bitness = _dtype_dict_with_modified_bitness(dtype_dict) + dtype_dict_with_modified_endianness = _dtype_to_dict(dtype.newbyteorder()) + dtype_dict_with_modified_bitness_and_endianness = _dtype_dict_with_modified_bitness( + dtype_dict_with_modified_endianness + ) + + return [ + dtype_dict, + dtype_dict_with_modified_bitness, + dtype_dict_with_modified_endianness, + dtype_dict_with_modified_bitness_and_endianness, + ] + + +def _check_node_ndarray(node_ndarray, expected_dtype): + if node_ndarray.ndim != 1: + raise ValueError( + "Wrong dimensions for node array from the pickle: " + f"expected 1, got {node_ndarray.ndim}" + ) + + if not node_ndarray.flags.c_contiguous: + raise ValueError( + "node array from the pickle should be a C-contiguous array" + ) + + node_ndarray_dtype = node_ndarray.dtype + if node_ndarray_dtype == expected_dtype: + return node_ndarray + + node_ndarray_dtype_dict = _dtype_to_dict(node_ndarray_dtype) + all_compatible_dtype_dicts = _all_compatible_dtype_dicts(expected_dtype) + + if node_ndarray_dtype_dict not in all_compatible_dtype_dicts: + raise ValueError( + "node array from the pickle has an incompatible dtype:\n" + f"- expected: {expected_dtype}\n" + f"- got : {node_ndarray_dtype}" + ) + + return node_ndarray.astype(expected_dtype, casting="same_kind") + + +# ============================================================================= +# Build Pruned Tree +# ============================================================================= + + +cdef class _CCPPruneController: + """Base class used by build_pruned_tree_ccp and ccp_pruning_path + to control pruning. + """ + cdef bint stop_pruning(self, DOUBLE_t effective_alpha) noexcept nogil: + """Return 1 to stop pruning and 0 to continue pruning""" + return 0 + + cdef void save_metrics(self, DOUBLE_t effective_alpha, + DOUBLE_t subtree_impurities) noexcept nogil: + """Save metrics when pruning""" + pass + + cdef void after_pruning(self, unsigned char[:] in_subtree) noexcept nogil: + """Called after pruning""" + pass + + +cdef class _AlphaPruner(_CCPPruneController): + """Use alpha to control when to stop pruning.""" + cdef DOUBLE_t ccp_alpha + cdef SIZE_t capacity + + def __cinit__(self, DOUBLE_t ccp_alpha): + self.ccp_alpha = ccp_alpha + self.capacity = 0 + + cdef bint stop_pruning(self, DOUBLE_t effective_alpha) noexcept nogil: + # The subtree on the previous iteration has the greatest ccp_alpha + # less than or equal to self.ccp_alpha + return self.ccp_alpha < effective_alpha + + cdef void after_pruning(self, unsigned char[:] in_subtree) noexcept nogil: + """Updates the number of leaves in subtree""" + for i in range(in_subtree.shape[0]): + if in_subtree[i]: + self.capacity += 1 + + +cdef class _PathFinder(_CCPPruneController): + """Record metrics used to return the cost complexity path.""" + cdef DOUBLE_t[:] ccp_alphas + cdef DOUBLE_t[:] impurities + cdef UINT32_t count + + def __cinit__(self, int node_count): + self.ccp_alphas = np.zeros(shape=(node_count), dtype=np.float64) + self.impurities = np.zeros(shape=(node_count), dtype=np.float64) + self.count = 0 + + cdef void save_metrics(self, + DOUBLE_t effective_alpha, + DOUBLE_t subtree_impurities) noexcept nogil: + self.ccp_alphas[self.count] = effective_alpha + self.impurities[self.count] = subtree_impurities + self.count += 1 + + +cdef struct CostComplexityPruningRecord: + SIZE_t node_idx + SIZE_t parent + +cdef _cost_complexity_prune(unsigned char[:] leaves_in_subtree, # OUT + Tree orig_tree, + _CCPPruneController controller): + """Perform cost complexity pruning. + + This function takes an already grown tree, `orig_tree` and outputs a + boolean mask `leaves_in_subtree` which are the leaves in the pruned tree. + During the pruning process, the controller is passed the effective alpha and + the subtree impurities. Furthermore, the controller signals when to stop + pruning. + + Parameters + ---------- + leaves_in_subtree : unsigned char[:] + Output for leaves of subtree + orig_tree : Tree + Original tree + ccp_controller : _CCPPruneController + Cost complexity controller + """ + + cdef: + SIZE_t i + SIZE_t n_nodes = orig_tree.node_count + # prior probability using weighted samples + DOUBLE_t[:] weighted_n_node_samples = orig_tree.weighted_n_node_samples + DOUBLE_t total_sum_weights = weighted_n_node_samples[0] + DOUBLE_t[:] impurity = orig_tree.impurity + # weighted impurity of each node + DOUBLE_t[:] r_node = np.empty(shape=n_nodes, dtype=np.float64) + + SIZE_t[:] child_l = orig_tree.children_left + SIZE_t[:] child_r = orig_tree.children_right + SIZE_t[:] parent = np.zeros(shape=n_nodes, dtype=np.intp) + + stack[CostComplexityPruningRecord] ccp_stack + CostComplexityPruningRecord stack_record + SIZE_t node_idx + stack[SIZE_t] node_indices_stack + + SIZE_t[:] n_leaves = np.zeros(shape=n_nodes, dtype=np.intp) + DOUBLE_t[:] r_branch = np.zeros(shape=n_nodes, dtype=np.float64) + DOUBLE_t current_r + SIZE_t leaf_idx + SIZE_t parent_idx + + # candidate nodes that can be pruned + unsigned char[:] candidate_nodes = np.zeros(shape=n_nodes, + dtype=np.uint8) + # nodes in subtree + unsigned char[:] in_subtree = np.ones(shape=n_nodes, dtype=np.uint8) + SIZE_t pruned_branch_node_idx + DOUBLE_t subtree_alpha + DOUBLE_t effective_alpha + SIZE_t n_pruned_leaves + DOUBLE_t r_diff + DOUBLE_t max_float64 = np.finfo(np.float64).max + + # find parent node ids and leaves + with nogil: + + for i in range(r_node.shape[0]): + r_node[i] = ( + weighted_n_node_samples[i] * impurity[i] / total_sum_weights) + + # Push the root node + ccp_stack.push({"node_idx": 0, "parent": _TREE_UNDEFINED}) + + while not ccp_stack.empty(): + stack_record = ccp_stack.top() + ccp_stack.pop() + + node_idx = stack_record.node_idx + parent[node_idx] = stack_record.parent + + if child_l[node_idx] == _TREE_LEAF: + # ... and child_r[node_idx] == _TREE_LEAF: + leaves_in_subtree[node_idx] = 1 + else: + ccp_stack.push({"node_idx": child_l[node_idx], "parent": node_idx}) + ccp_stack.push({"node_idx": child_r[node_idx], "parent": node_idx}) + + # computes number of leaves in all branches and the overall impurity of + # the branch. The overall impurity is the sum of r_node in its leaves. + for leaf_idx in range(leaves_in_subtree.shape[0]): + if not leaves_in_subtree[leaf_idx]: + continue + r_branch[leaf_idx] = r_node[leaf_idx] + + # bubble up values to ancestor nodes + current_r = r_node[leaf_idx] + while leaf_idx != 0: + parent_idx = parent[leaf_idx] + r_branch[parent_idx] += current_r + n_leaves[parent_idx] += 1 + leaf_idx = parent_idx + + for i in range(leaves_in_subtree.shape[0]): + candidate_nodes[i] = not leaves_in_subtree[i] + + # save metrics before pruning + controller.save_metrics(0.0, r_branch[0]) + + # while root node is not a leaf + while candidate_nodes[0]: + + # computes ccp_alpha for subtrees and finds the minimal alpha + effective_alpha = max_float64 + for i in range(n_nodes): + if not candidate_nodes[i]: + continue + subtree_alpha = (r_node[i] - r_branch[i]) / (n_leaves[i] - 1) + if subtree_alpha < effective_alpha: + effective_alpha = subtree_alpha + pruned_branch_node_idx = i + + if controller.stop_pruning(effective_alpha): + break + + node_indices_stack.push(pruned_branch_node_idx) + + # descendants of branch are not in subtree + while not node_indices_stack.empty(): + node_idx = node_indices_stack.top() + node_indices_stack.pop() + + if not in_subtree[node_idx]: + continue # branch has already been marked for pruning + candidate_nodes[node_idx] = 0 + leaves_in_subtree[node_idx] = 0 + in_subtree[node_idx] = 0 + + if child_l[node_idx] != _TREE_LEAF: + # ... and child_r[node_idx] != _TREE_LEAF: + node_indices_stack.push(child_l[node_idx]) + node_indices_stack.push(child_r[node_idx]) + leaves_in_subtree[pruned_branch_node_idx] = 1 + in_subtree[pruned_branch_node_idx] = 1 + + # updates number of leaves + n_pruned_leaves = n_leaves[pruned_branch_node_idx] - 1 + n_leaves[pruned_branch_node_idx] = 0 + + # computes the increase in r_branch to bubble up + r_diff = r_node[pruned_branch_node_idx] - r_branch[pruned_branch_node_idx] + r_branch[pruned_branch_node_idx] = r_node[pruned_branch_node_idx] + + # bubble up values to ancestors + node_idx = parent[pruned_branch_node_idx] + while node_idx != _TREE_UNDEFINED: + n_leaves[node_idx] -= n_pruned_leaves + r_branch[node_idx] += r_diff + node_idx = parent[node_idx] + + controller.save_metrics(effective_alpha, r_branch[0]) + + controller.after_pruning(in_subtree) + + +def _build_pruned_tree_ccp( + Tree tree, # OUT + Tree orig_tree, + DOUBLE_t ccp_alpha +): + """Build a pruned tree from the original tree using cost complexity + pruning. + + The values and nodes from the original tree are copied into the pruned + tree. + + Parameters + ---------- + tree : Tree + Location to place the pruned tree + orig_tree : Tree + Original tree + ccp_alpha : positive double + Complexity parameter. The subtree with the largest cost complexity + that is smaller than ``ccp_alpha`` will be chosen. By default, + no pruning is performed. + """ + + cdef: + SIZE_t n_nodes = orig_tree.node_count + unsigned char[:] leaves_in_subtree = np.zeros( + shape=n_nodes, dtype=np.uint8) + + pruning_controller = _AlphaPruner(ccp_alpha=ccp_alpha) + + _cost_complexity_prune(leaves_in_subtree, orig_tree, pruning_controller) + + _build_pruned_tree(tree, orig_tree, leaves_in_subtree, + pruning_controller.capacity) + + +def ccp_pruning_path(Tree orig_tree): + """Computes the cost complexity pruning path. + + Parameters + ---------- + tree : Tree + Original tree. + + Returns + ------- + path_info : dict + Information about pruning path with attributes: + + ccp_alphas : ndarray + Effective alphas of subtree during pruning. + + impurities : ndarray + Sum of the impurities of the subtree leaves for the + corresponding alpha value in ``ccp_alphas``. + """ + cdef: + unsigned char[:] leaves_in_subtree = np.zeros( + shape=orig_tree.node_count, dtype=np.uint8) + + path_finder = _PathFinder(orig_tree.node_count) + + _cost_complexity_prune(leaves_in_subtree, orig_tree, path_finder) + + cdef: + UINT32_t total_items = path_finder.count + DOUBLE_t[:] ccp_alphas = np.empty(shape=total_items, dtype=np.float64) + DOUBLE_t[:] impurities = np.empty(shape=total_items, dtype=np.float64) + UINT32_t count = 0 + + while count < total_items: + ccp_alphas[count] = path_finder.ccp_alphas[count] + impurities[count] = path_finder.impurities[count] + count += 1 + + return { + 'ccp_alphas': np.asarray(ccp_alphas), + 'impurities': np.asarray(impurities), + } + + +cdef struct BuildPrunedRecord: + SIZE_t start + SIZE_t depth + SIZE_t parent + bint is_left + +cdef _build_pruned_tree( + Tree tree, # OUT + Tree orig_tree, + const unsigned char[:] leaves_in_subtree, + SIZE_t capacity +): + """Build a pruned tree. + + Build a pruned tree from the original tree by transforming the nodes in + ``leaves_in_subtree`` into leaves. + + Parameters + ---------- + tree : Tree + Location to place the pruned tree + orig_tree : Tree + Original tree + leaves_in_subtree : unsigned char memoryview, shape=(node_count, ) + Boolean mask for leaves to include in subtree + capacity : SIZE_t + Number of nodes to initially allocate in pruned tree + """ + tree._resize(capacity) + + cdef: + SIZE_t orig_node_id + SIZE_t new_node_id + SIZE_t depth + SIZE_t parent + bint is_left + bint is_leaf + + # value_stride for original tree and new tree are the same + SIZE_t value_stride = orig_tree.value_stride + SIZE_t max_depth_seen = -1 + int rc = 0 + Node* node + double* orig_value_ptr + double* new_value_ptr + + stack[BuildPrunedRecord] prune_stack + BuildPrunedRecord stack_record + + with nogil: + # push root node onto stack + prune_stack.push({"start": 0, "depth": 0, "parent": _TREE_UNDEFINED, "is_left": 0}) + + while not prune_stack.empty(): + stack_record = prune_stack.top() + prune_stack.pop() + + orig_node_id = stack_record.start + depth = stack_record.depth + parent = stack_record.parent + is_left = stack_record.is_left + + is_leaf = leaves_in_subtree[orig_node_id] + node = &orig_tree.nodes[orig_node_id] + + new_node_id = tree._add_node( + parent, is_left, is_leaf, node.feature, node.threshold, + node.impurity, node.n_node_samples, + node.weighted_n_node_samples, node.missing_go_to_left) + + if new_node_id == INTPTR_MAX: + rc = -1 + break + + # copy value from original tree to new tree + orig_value_ptr = orig_tree.value + value_stride * orig_node_id + new_value_ptr = tree.value + value_stride * new_node_id + memcpy(new_value_ptr, orig_value_ptr, sizeof(double) * value_stride) + + if not is_leaf: + # Push right child on stack + prune_stack.push({"start": node.right_child, "depth": depth + 1, + "parent": new_node_id, "is_left": 0}) + # push left child on stack + prune_stack.push({"start": node.left_child, "depth": depth + 1, + "parent": new_node_id, "is_left": 1}) + + if depth > max_depth_seen: + max_depth_seen = depth + + if rc >= 0: + tree.max_depth = max_depth_seen + if rc == -1: + raise MemoryError("pruning tree") \ No newline at end of file diff --git a/ivy/functional/frontends/sklearn/_tree_ivy.pyx b/ivy/functional/frontends/sklearn/_tree_ivy.pyx new file mode 100644 index 0000000000000..f436c919f8bc2 --- /dev/null +++ b/ivy/functional/frontends/sklearn/_tree_ivy.pyx @@ -0,0 +1,1833 @@ +# Authors: Gilles Louppe +# Peter Prettenhofer +# Brian Holt +# Noel Dawe +# Satrajit Gosh +# Lars Buitinck +# Arnaud Joly +# Joel Nothman +# Fares Hedayati +# Jacob Schreiber +# Nelson Liu +# +# License: BSD 3 clause + +from cpython cimport Py_INCREF, PyObject, PyTypeObject + +from libc.stdlib cimport free +from libc.string cimport memcpy +from libc.string cimport memset +from libc.stdint cimport INTPTR_MAX +from libc.math cimport isnan +from libcpp.vector cimport vector +from libcpp.algorithm cimport pop_heap +from libcpp.algorithm cimport push_heap +from libcpp cimport bool + +import struct + +import numpy as np +cimport numpy as cnp +cnp.import_array() + +from scipy.sparse import issparse +from scipy.sparse import csr_matrix +from scipy.sparse import isspmatrix_csr + +from ._utils cimport safe_realloc +from ._utils cimport sizet_ptr_to_ndarray + +cdef extern from "numpy/arrayobject.h": + object PyArray_NewFromDescr(PyTypeObject* subtype, cnp.dtype descr, + int nd, cnp.npy_intp* dims, + cnp.npy_intp* strides, + void* data, int flags, object obj) + int PyArray_SetBaseObject(cnp.ndarray arr, PyObject* obj) + +cdef extern from "" namespace "std" nogil: + cdef cppclass stack[T]: + ctypedef T value_type + stack() except + + bint empty() + void pop() + void push(T&) except + # Raise c++ exception for bad_alloc -> MemoryError + T& top() + +# ============================================================================= +# Types and constants +# ============================================================================= + +from numpy import float32 as DTYPE +from numpy import float64 as DOUBLE + +cdef double INFINITY = np.inf +cdef double EPSILON = np.finfo('double').eps + +# Some handy constants (BestFirstTreeBuilder) +cdef int IS_FIRST = 1 +cdef int IS_NOT_FIRST = 0 +cdef int IS_LEFT = 1 +cdef int IS_NOT_LEFT = 0 + +TREE_LEAF = -1 +TREE_UNDEFINED = -2 +cdef SIZE_t _TREE_LEAF = TREE_LEAF +cdef SIZE_t _TREE_UNDEFINED = TREE_UNDEFINED + +# Build the corresponding numpy dtype for Node. +# This works by casting `dummy` to an array of Node of length 1, which numpy +# can construct a `dtype`-object for. See https://stackoverflow.com/q/62448946 +# for a more detailed explanation. +cdef Node dummy +NODE_DTYPE = np.asarray((&dummy)).dtype + +# ============================================================================= +# TreeBuilder +# ============================================================================= + +cdef class TreeBuilder: + """Interface for different tree building strategies.""" + + cpdef build( + self, + Tree tree, + object X, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight=None, + const unsigned char[::1] missing_values_in_feature_mask=None, + ): + """Build a decision tree from the training set (X, y).""" + pass + + cdef inline _check_input( + self, + object X, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight, + ): + """Check input dtype, layout and format""" + if issparse(X): + X = X.tocsc() + X.sort_indices() + + if X.data.dtype != DTYPE: + X.data = np.ascontiguousarray(X.data, dtype=DTYPE) + + if X.indices.dtype != np.int32 or X.indptr.dtype != np.int32: + raise ValueError("No support for np.int64 index based " + "sparse matrices") + + elif X.dtype != DTYPE: + # since we have to copy we will make it fortran for efficiency + X = np.asfortranarray(X, dtype=DTYPE) + + # TODO: This check for y seems to be redundant, as it is also + # present in the BaseDecisionTree's fit method, and therefore + # can be removed. + if y.base.dtype != DOUBLE or not y.base.flags.contiguous: + y = np.ascontiguousarray(y, dtype=DOUBLE) + + if ( + sample_weight is not None and + ( + sample_weight.base.dtype != DOUBLE or + not sample_weight.base.flags.contiguous + ) + ): + sample_weight = np.asarray(sample_weight, dtype=DOUBLE, order="C") + + return X, y, sample_weight + +# Depth first builder --------------------------------------------------------- +# A record on the stack for depth-first tree growing +cdef struct StackRecord: + SIZE_t start + SIZE_t end + SIZE_t depth + SIZE_t parent + bint is_left + double impurity + SIZE_t n_constant_features + + + + + + + +cdef class DepthFirstTreeBuilder(TreeBuilder): + """Build a decision tree in depth-first fashion.""" + + def __cinit__(self, Splitter splitter, SIZE_t min_samples_split, + SIZE_t min_samples_leaf, double min_weight_leaf, + SIZE_t max_depth, double min_impurity_decrease): + self.splitter = splitter + self.min_samples_split = min_samples_split + self.min_samples_leaf = min_samples_leaf + self.min_weight_leaf = min_weight_leaf + self.max_depth = max_depth + self.min_impurity_decrease = min_impurity_decrease + + cpdef build( + self, + Tree tree, + object X, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight=None, + const unsigned char[::1] missing_values_in_feature_mask=None, + ): + """Build a decision tree from the training set (X, y).""" + + # check input + X, y, sample_weight = self._check_input(X, y, sample_weight) + + # Initial capacity + cdef int init_capacity + + if tree.max_depth <= 10: + init_capacity = (2 ** (tree.max_depth + 1)) - 1 + else: + init_capacity = 2047 + + tree._resize(init_capacity) + + # Parameters + cdef Splitter splitter = self.splitter + cdef SIZE_t max_depth = self.max_depth + cdef SIZE_t min_samples_leaf = self.min_samples_leaf + cdef double min_weight_leaf = self.min_weight_leaf + cdef SIZE_t min_samples_split = self.min_samples_split + cdef double min_impurity_decrease = self.min_impurity_decrease + + # Recursive partition (without actual recursion) + splitter.init(X, y, sample_weight, missing_values_in_feature_mask) + + cdef SIZE_t start + cdef SIZE_t end + cdef SIZE_t depth + cdef SIZE_t parent + cdef bint is_left + cdef SIZE_t n_node_samples = splitter.n_samples + cdef double weighted_n_node_samples + cdef SplitRecord split + cdef SIZE_t node_id + + cdef double impurity = INFINITY + cdef SIZE_t n_constant_features + cdef bint is_leaf + cdef bint first = 1 + cdef SIZE_t max_depth_seen = -1 + cdef int rc = 0 + + cdef stack[StackRecord] builder_stack + cdef StackRecord stack_record + + with nogil: + # push root node onto stack + builder_stack.push({ + "start": 0, + "end": n_node_samples, + "depth": 0, + "parent": _TREE_UNDEFINED, + "is_left": 0, + "impurity": INFINITY, + "n_constant_features": 0}) + + while not builder_stack.empty(): + stack_record = builder_stack.top() + builder_stack.pop() + + start = stack_record.start + end = stack_record.end + depth = stack_record.depth + parent = stack_record.parent + is_left = stack_record.is_left + impurity = stack_record.impurity + n_constant_features = stack_record.n_constant_features + + n_node_samples = end - start + splitter.node_reset(start, end, &weighted_n_node_samples) + + is_leaf = (depth >= max_depth or + n_node_samples < min_samples_split or + n_node_samples < 2 * min_samples_leaf or + weighted_n_node_samples < 2 * min_weight_leaf) + + if first: + impurity = splitter.node_impurity() + first = 0 + + # impurity == 0 with tolerance due to rounding errors + is_leaf = is_leaf or impurity <= EPSILON + + if not is_leaf: + splitter.node_split(impurity, &split, &n_constant_features) + # If EPSILON=0 in the below comparison, float precision + # issues stop splitting, producing trees that are + # dissimilar to v0.18 + is_leaf = (is_leaf or split.pos >= end or + (split.improvement + EPSILON < + min_impurity_decrease)) + + node_id = tree._add_node(parent, is_left, is_leaf, split.feature, + split.threshold, impurity, n_node_samples, + weighted_n_node_samples, + split.missing_go_to_left) + + if node_id == INTPTR_MAX: + rc = -1 + break + + # Store value for all nodes, to facilitate tree/model + # inspection and interpretation + splitter.node_value(tree.value + node_id * tree.value_stride) + + if not is_leaf: + # Push right child on stack + builder_stack.push({ + "start": split.pos, + "end": end, + "depth": depth + 1, + "parent": node_id, + "is_left": 0, + "impurity": split.impurity_right, + "n_constant_features": n_constant_features}) + + # Push left child on stack + builder_stack.push({ + "start": start, + "end": split.pos, + "depth": depth + 1, + "parent": node_id, + "is_left": 1, + "impurity": split.impurity_left, + "n_constant_features": n_constant_features}) + + if depth > max_depth_seen: + max_depth_seen = depth + + if rc >= 0: + rc = tree._resize_c(tree.node_count) + + if rc >= 0: + tree.max_depth = max_depth_seen + if rc == -1: + raise MemoryError() + + +# Best first builder ---------------------------------------------------------- +cdef struct FrontierRecord: + # Record of information of a Node, the frontier for a split. Those records are + # maintained in a heap to access the Node with the best improvement in impurity, + # allowing growing trees greedily on this improvement. + SIZE_t node_id + SIZE_t start + SIZE_t end + SIZE_t pos + SIZE_t depth + bint is_leaf + double impurity + double impurity_left + double impurity_right + double improvement + +cdef inline bool _compare_records( + const FrontierRecord& left, + const FrontierRecord& right, +): + return left.improvement < right.improvement + +cdef inline void _add_to_frontier( + FrontierRecord rec, + vector[FrontierRecord]& frontier, +) noexcept nogil: + """Adds record `rec` to the priority queue `frontier`.""" + frontier.push_back(rec) + push_heap(frontier.begin(), frontier.end(), &_compare_records) + + +cdef class BestFirstTreeBuilder(TreeBuilder): + """Build a decision tree in best-first fashion. + + The best node to expand is given by the node at the frontier that has the + highest impurity improvement. + """ + cdef SIZE_t max_leaf_nodes + + def __cinit__(self, Splitter splitter, SIZE_t min_samples_split, + SIZE_t min_samples_leaf, min_weight_leaf, + SIZE_t max_depth, SIZE_t max_leaf_nodes, + double min_impurity_decrease): + self.splitter = splitter + self.min_samples_split = min_samples_split + self.min_samples_leaf = min_samples_leaf + self.min_weight_leaf = min_weight_leaf + self.max_depth = max_depth + self.max_leaf_nodes = max_leaf_nodes + self.min_impurity_decrease = min_impurity_decrease + + cpdef build( + self, + Tree tree, + object X, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight=None, + const unsigned char[::1] missing_values_in_feature_mask=None, + ): + """Build a decision tree from the training set (X, y).""" + + # check input + X, y, sample_weight = self._check_input(X, y, sample_weight) + + # Parameters + cdef Splitter splitter = self.splitter + cdef SIZE_t max_leaf_nodes = self.max_leaf_nodes + + # Recursive partition (without actual recursion) + splitter.init(X, y, sample_weight, missing_values_in_feature_mask) + + cdef vector[FrontierRecord] frontier + cdef FrontierRecord record + cdef FrontierRecord split_node_left + cdef FrontierRecord split_node_right + + cdef SIZE_t n_node_samples = splitter.n_samples + cdef SIZE_t max_split_nodes = max_leaf_nodes - 1 + cdef bint is_leaf + cdef SIZE_t max_depth_seen = -1 + cdef int rc = 0 + cdef Node* node + + # Initial capacity + cdef SIZE_t init_capacity = max_split_nodes + max_leaf_nodes + tree._resize(init_capacity) + + with nogil: + # add root to frontier + rc = self._add_split_node(splitter, tree, 0, n_node_samples, + INFINITY, IS_FIRST, IS_LEFT, NULL, 0, + &split_node_left) + if rc >= 0: + _add_to_frontier(split_node_left, frontier) + + while not frontier.empty(): + pop_heap(frontier.begin(), frontier.end(), &_compare_records) + record = frontier.back() + frontier.pop_back() + + node = &tree.nodes[record.node_id] + is_leaf = (record.is_leaf or max_split_nodes <= 0) + + if is_leaf: + # Node is not expandable; set node as leaf + node.left_child = _TREE_LEAF + node.right_child = _TREE_LEAF + node.feature = _TREE_UNDEFINED + node.threshold = _TREE_UNDEFINED + + else: + # Node is expandable + + # Decrement number of split nodes available + max_split_nodes -= 1 + + # Compute left split node + rc = self._add_split_node(splitter, tree, + record.start, record.pos, + record.impurity_left, + IS_NOT_FIRST, IS_LEFT, node, + record.depth + 1, + &split_node_left) + if rc == -1: + break + + # tree.nodes may have changed + node = &tree.nodes[record.node_id] + + # Compute right split node + rc = self._add_split_node(splitter, tree, record.pos, + record.end, + record.impurity_right, + IS_NOT_FIRST, IS_NOT_LEFT, node, + record.depth + 1, + &split_node_right) + if rc == -1: + break + + # Add nodes to queue + _add_to_frontier(split_node_left, frontier) + _add_to_frontier(split_node_right, frontier) + + if record.depth > max_depth_seen: + max_depth_seen = record.depth + + if rc >= 0: + rc = tree._resize_c(tree.node_count) + + if rc >= 0: + tree.max_depth = max_depth_seen + + if rc == -1: + raise MemoryError() + + cdef inline int _add_split_node(self, Splitter splitter, Tree tree, + SIZE_t start, SIZE_t end, double impurity, + bint is_first, bint is_left, Node* parent, + SIZE_t depth, + FrontierRecord* res) except -1 nogil: + """Adds node w/ partition ``[start, end)`` to the frontier. """ + cdef SplitRecord split + cdef SIZE_t node_id + cdef SIZE_t n_node_samples + cdef SIZE_t n_constant_features = 0 + cdef double min_impurity_decrease = self.min_impurity_decrease + cdef double weighted_n_node_samples + cdef bint is_leaf + + splitter.node_reset(start, end, &weighted_n_node_samples) + + if is_first: + impurity = splitter.node_impurity() + + n_node_samples = end - start + is_leaf = (depth >= self.max_depth or + n_node_samples < self.min_samples_split or + n_node_samples < 2 * self.min_samples_leaf or + weighted_n_node_samples < 2 * self.min_weight_leaf or + impurity <= EPSILON # impurity == 0 with tolerance + ) + + if not is_leaf: + splitter.node_split(impurity, &split, &n_constant_features) + # If EPSILON=0 in the below comparison, float precision issues stop + # splitting early, producing trees that are dissimilar to v0.18 + is_leaf = (is_leaf or split.pos >= end or + split.improvement + EPSILON < min_impurity_decrease) + + node_id = tree._add_node(parent - tree.nodes + if parent != NULL + else _TREE_UNDEFINED, + is_left, is_leaf, + split.feature, split.threshold, impurity, n_node_samples, + weighted_n_node_samples, + split.missing_go_to_left) + if node_id == INTPTR_MAX: + return -1 + + # compute values also for split nodes (might become leafs later). + splitter.node_value(tree.value + node_id * tree.value_stride) + + res.node_id = node_id + res.start = start + res.end = end + res.depth = depth + res.impurity = impurity + + if not is_leaf: + # is split node + res.pos = split.pos + res.is_leaf = 0 + res.improvement = split.improvement + res.impurity_left = split.impurity_left + res.impurity_right = split.impurity_right + + else: + # is leaf => 0 improvement + res.pos = end + res.is_leaf = 1 + res.improvement = 0.0 + res.impurity_left = impurity + res.impurity_right = impurity + + return 0 + + +# ============================================================================= +# Tree +# ============================================================================= + +cdef class Tree: + """Array-based representation of a binary decision tree. + + The binary tree is represented as a number of parallel arrays. The i-th + element of each array holds information about the node `i`. Node 0 is the + tree's root. You can find a detailed description of all arrays in + `_tree.pxd`. NOTE: Some of the arrays only apply to either leaves or split + nodes, resp. In this case the values of nodes of the other type are + arbitrary! + + Attributes + ---------- + node_count : int + The number of nodes (internal nodes + leaves) in the tree. + + capacity : int + The current capacity (i.e., size) of the arrays, which is at least as + great as `node_count`. + + max_depth : int + The depth of the tree, i.e. the maximum depth of its leaves. + + children_left : array of int, shape [node_count] + children_left[i] holds the node id of the left child of node i. + For leaves, children_left[i] == TREE_LEAF. Otherwise, + children_left[i] > i. This child handles the case where + X[:, feature[i]] <= threshold[i]. + + children_right : array of int, shape [node_count] + children_right[i] holds the node id of the right child of node i. + For leaves, children_right[i] == TREE_LEAF. Otherwise, + children_right[i] > i. This child handles the case where + X[:, feature[i]] > threshold[i]. + + feature : array of int, shape [node_count] + feature[i] holds the feature to split on, for the internal node i. + + threshold : array of double, shape [node_count] + threshold[i] holds the threshold for the internal node i. + + value : array of double, shape [node_count, n_outputs, max_n_classes] + Contains the constant prediction value of each node. + + impurity : array of double, shape [node_count] + impurity[i] holds the impurity (i.e., the value of the splitting + criterion) at node i. + + n_node_samples : array of int, shape [node_count] + n_node_samples[i] holds the number of training samples reaching node i. + + weighted_n_node_samples : array of double, shape [node_count] + weighted_n_node_samples[i] holds the weighted number of training samples + reaching node i. + """ + # Wrap for outside world. + # WARNING: these reference the current `nodes` and `value` buffers, which + # must not be freed by a subsequent memory allocation. + # (i.e. through `_resize` or `__setstate__`) + @property + def n_classes(self): + return sizet_ptr_to_ndarray(self.n_classes, self.n_outputs) + + @property + def children_left(self): + return self._get_node_ndarray()['left_child'][:self.node_count] + + @property + def children_right(self): + return self._get_node_ndarray()['right_child'][:self.node_count] + + @property + def n_leaves(self): + return np.sum(np.logical_and( + self.children_left == -1, + self.children_right == -1)) + + @property + def feature(self): + return self._get_node_ndarray()['feature'][:self.node_count] + + @property + def threshold(self): + return self._get_node_ndarray()['threshold'][:self.node_count] + + @property + def impurity(self): + return self._get_node_ndarray()['impurity'][:self.node_count] + + @property + def n_node_samples(self): + return self._get_node_ndarray()['n_node_samples'][:self.node_count] + + @property + def weighted_n_node_samples(self): + return self._get_node_ndarray()['weighted_n_node_samples'][:self.node_count] + + @property + def missing_go_to_left(self): + return self._get_node_ndarray()['missing_go_to_left'][:self.node_count] + + @property + def value(self): + return self._get_value_ndarray()[:self.node_count] + + # TODO: Convert n_classes to cython.integral memory view once + # https://github.com/cython/cython/issues/5243 is fixed + def __cinit__(self, int n_features, cnp.ndarray n_classes, int n_outputs): + """Constructor.""" + cdef SIZE_t dummy = 0 + size_t_dtype = np.array(dummy).dtype + + n_classes = _check_n_classes(n_classes, size_t_dtype) + + # Input/Output layout + self.n_features = n_features + self.n_outputs = n_outputs + self.n_classes = NULL + safe_realloc(&self.n_classes, n_outputs) + + self.max_n_classes = np.max(n_classes) + self.value_stride = n_outputs * self.max_n_classes + + cdef SIZE_t k + for k in range(n_outputs): + self.n_classes[k] = n_classes[k] + + # Inner structures + self.max_depth = 0 + self.node_count = 0 + self.capacity = 0 + self.value = NULL + self.nodes = NULL + + def __dealloc__(self): + """Destructor.""" + # Free all inner structures + free(self.n_classes) + free(self.value) + free(self.nodes) + + def __reduce__(self): + """Reduce re-implementation, for pickling.""" + return (Tree, (self.n_features, + sizet_ptr_to_ndarray(self.n_classes, self.n_outputs), + self.n_outputs), self.__getstate__()) + + def __getstate__(self): + """Getstate re-implementation, for pickling.""" + d = {} + # capacity is inferred during the __setstate__ using nodes + d["max_depth"] = self.max_depth + d["node_count"] = self.node_count + d["nodes"] = self._get_node_ndarray() + d["values"] = self._get_value_ndarray() + return d + + def __setstate__(self, d): + """Setstate re-implementation, for unpickling.""" + self.max_depth = d["max_depth"] + self.node_count = d["node_count"] + + if 'nodes' not in d: + raise ValueError('You have loaded Tree version which ' + 'cannot be imported') + + node_ndarray = d['nodes'] + value_ndarray = d['values'] + + value_shape = (node_ndarray.shape[0], self.n_outputs, + self.max_n_classes) + + node_ndarray = _check_node_ndarray(node_ndarray, expected_dtype=NODE_DTYPE) + value_ndarray = _check_value_ndarray( + value_ndarray, + expected_dtype=np.dtype(np.float64), + expected_shape=value_shape + ) + + self.capacity = node_ndarray.shape[0] + if self._resize_c(self.capacity) != 0: + raise MemoryError("resizing tree to %d" % self.capacity) + + memcpy(self.nodes, cnp.PyArray_DATA(node_ndarray), + self.capacity * sizeof(Node)) + memcpy(self.value, cnp.PyArray_DATA(value_ndarray), + self.capacity * self.value_stride * sizeof(double)) + + cdef int _resize(self, SIZE_t capacity) except -1 nogil: + """Resize all inner arrays to `capacity`, if `capacity` == -1, then + double the size of the inner arrays. + + Returns -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + """ + if self._resize_c(capacity) != 0: + # Acquire gil only if we need to raise + with gil: + raise MemoryError() + + cdef int _resize_c(self, SIZE_t capacity=INTPTR_MAX) except -1 nogil: + """Guts of _resize + + Returns -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + """ + if capacity == self.capacity and self.nodes != NULL: + return 0 + + if capacity == INTPTR_MAX: + if self.capacity == 0: + capacity = 3 # default initial value + else: + capacity = 2 * self.capacity + + safe_realloc(&self.nodes, capacity) + safe_realloc(&self.value, capacity * self.value_stride) + + # value memory is initialised to 0 to enable classifier argmax + if capacity > self.capacity: + memset((self.value + self.capacity * self.value_stride), 0, + (capacity - self.capacity) * self.value_stride * + sizeof(double)) + + # if capacity smaller than node_count, adjust the counter + if capacity < self.node_count: + self.node_count = capacity + + self.capacity = capacity + return 0 + + cdef SIZE_t _add_node(self, SIZE_t parent, bint is_left, bint is_leaf, + SIZE_t feature, double threshold, double impurity, + SIZE_t n_node_samples, + double weighted_n_node_samples, + unsigned char missing_go_to_left) except -1 nogil: + """Add a node to the tree. + + The new node registers itself as the child of its parent. + + Returns (size_t)(-1) on error. + """ + cdef SIZE_t node_id = self.node_count + + if node_id >= self.capacity: + if self._resize_c() != 0: + return INTPTR_MAX + + cdef Node* node = &self.nodes[node_id] + node.impurity = impurity + node.n_node_samples = n_node_samples + node.weighted_n_node_samples = weighted_n_node_samples + + if parent != _TREE_UNDEFINED: + if is_left: + self.nodes[parent].left_child = node_id + else: + self.nodes[parent].right_child = node_id + + if is_leaf: + node.left_child = _TREE_LEAF + node.right_child = _TREE_LEAF + node.feature = _TREE_UNDEFINED + node.threshold = _TREE_UNDEFINED + + else: + # left_child and right_child will be set later + node.feature = feature + node.threshold = threshold + node.missing_go_to_left = missing_go_to_left + + self.node_count += 1 + + return node_id + + cpdef cnp.ndarray predict(self, object X): + """Predict target for X.""" + out = self._get_value_ndarray().take(self.apply(X), axis=0, + mode='clip') + if self.n_outputs == 1: + out = out.reshape(X.shape[0], self.max_n_classes) + return out + + cpdef cnp.ndarray apply(self, object X): + """Finds the terminal region (=leaf node) for each sample in X.""" + if issparse(X): + return self._apply_sparse_csr(X) + else: + return self._apply_dense(X) + + cdef inline cnp.ndarray _apply_dense(self, object X): + """Finds the terminal region (=leaf node) for each sample in X.""" + + # Check input + if not isinstance(X, np.ndarray): + raise ValueError("X should be in np.ndarray format, got %s" + % type(X)) + + if X.dtype != DTYPE: + raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + + # Extract input + cdef const DTYPE_t[:, :] X_ndarray = X + cdef SIZE_t n_samples = X.shape[0] + cdef DTYPE_t X_i_node_feature + + # Initialize output + cdef SIZE_t[:] out = np.zeros(n_samples, dtype=np.intp) + + # Initialize auxiliary data-structure + cdef Node* node = NULL + cdef SIZE_t i = 0 + + with nogil: + for i in range(n_samples): + node = self.nodes + # While node not a leaf + while node.left_child != _TREE_LEAF: + X_i_node_feature = X_ndarray[i, node.feature] + # ... and node.right_child != _TREE_LEAF: + if isnan(X_i_node_feature): + if node.missing_go_to_left: + node = &self.nodes[node.left_child] + else: + node = &self.nodes[node.right_child] + elif X_i_node_feature <= node.threshold: + node = &self.nodes[node.left_child] + else: + node = &self.nodes[node.right_child] + + out[i] = (node - self.nodes) # node offset + + return np.asarray(out) + + cdef inline cnp.ndarray _apply_sparse_csr(self, object X): + """Finds the terminal region (=leaf node) for each sample in sparse X. + """ + # Check input + if not isspmatrix_csr(X): + raise ValueError("X should be in csr_matrix format, got %s" + % type(X)) + + if X.dtype != DTYPE: + raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + + # Extract input + cdef const DTYPE_t[:] X_data = X.data + cdef const INT32_t[:] X_indices = X.indices + cdef const INT32_t[:] X_indptr = X.indptr + + cdef SIZE_t n_samples = X.shape[0] + cdef SIZE_t n_features = X.shape[1] + + # Initialize output + cdef SIZE_t[:] out = np.zeros(n_samples, dtype=np.intp) + + # Initialize auxiliary data-structure + cdef DTYPE_t feature_value = 0. + cdef Node* node = NULL + cdef DTYPE_t* X_sample = NULL + cdef SIZE_t i = 0 + cdef INT32_t k = 0 + + # feature_to_sample as a data structure records the last seen sample + # for each feature; functionally, it is an efficient way to identify + # which features are nonzero in the present sample. + cdef SIZE_t* feature_to_sample = NULL + + safe_realloc(&X_sample, n_features) + safe_realloc(&feature_to_sample, n_features) + + with nogil: + memset(feature_to_sample, -1, n_features * sizeof(SIZE_t)) + + for i in range(n_samples): + node = self.nodes + + for k in range(X_indptr[i], X_indptr[i + 1]): + feature_to_sample[X_indices[k]] = i + X_sample[X_indices[k]] = X_data[k] + + # While node not a leaf + while node.left_child != _TREE_LEAF: + # ... and node.right_child != _TREE_LEAF: + if feature_to_sample[node.feature] == i: + feature_value = X_sample[node.feature] + + else: + feature_value = 0. + + if feature_value <= node.threshold: + node = &self.nodes[node.left_child] + else: + node = &self.nodes[node.right_child] + + out[i] = (node - self.nodes) # node offset + + # Free auxiliary arrays + free(X_sample) + free(feature_to_sample) + + return np.asarray(out) + + cpdef object decision_path(self, object X): + """Finds the decision path (=node) for each sample in X.""" + if issparse(X): + return self._decision_path_sparse_csr(X) + else: + return self._decision_path_dense(X) + + cdef inline object _decision_path_dense(self, object X): + """Finds the decision path (=node) for each sample in X.""" + + # Check input + if not isinstance(X, np.ndarray): + raise ValueError("X should be in np.ndarray format, got %s" + % type(X)) + + if X.dtype != DTYPE: + raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + + # Extract input + cdef const DTYPE_t[:, :] X_ndarray = X + cdef SIZE_t n_samples = X.shape[0] + + # Initialize output + cdef SIZE_t[:] indptr = np.zeros(n_samples + 1, dtype=np.intp) + cdef SIZE_t[:] indices = np.zeros( + n_samples * (1 + self.max_depth), dtype=np.intp + ) + + # Initialize auxiliary data-structure + cdef Node* node = NULL + cdef SIZE_t i = 0 + + with nogil: + for i in range(n_samples): + node = self.nodes + indptr[i + 1] = indptr[i] + + # Add all external nodes + while node.left_child != _TREE_LEAF: + # ... and node.right_child != _TREE_LEAF: + indices[indptr[i + 1]] = (node - self.nodes) + indptr[i + 1] += 1 + + if X_ndarray[i, node.feature] <= node.threshold: + node = &self.nodes[node.left_child] + else: + node = &self.nodes[node.right_child] + + # Add the leave node + indices[indptr[i + 1]] = (node - self.nodes) + indptr[i + 1] += 1 + + indices = indices[:indptr[n_samples]] + cdef SIZE_t[:] data = np.ones(shape=len(indices), dtype=np.intp) + out = csr_matrix((data, indices, indptr), + shape=(n_samples, self.node_count)) + + return out + + cdef inline object _decision_path_sparse_csr(self, object X): + """Finds the decision path (=node) for each sample in X.""" + + # Check input + if not isspmatrix_csr(X): + raise ValueError("X should be in csr_matrix format, got %s" + % type(X)) + + if X.dtype != DTYPE: + raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + + # Extract input + cdef const DTYPE_t[:] X_data = X.data + cdef const INT32_t[:] X_indices = X.indices + cdef const INT32_t[:] X_indptr = X.indptr + + cdef SIZE_t n_samples = X.shape[0] + cdef SIZE_t n_features = X.shape[1] + + # Initialize output + cdef SIZE_t[:] indptr = np.zeros(n_samples + 1, dtype=np.intp) + cdef SIZE_t[:] indices = np.zeros( + n_samples * (1 + self.max_depth), dtype=np.intp + ) + + # Initialize auxiliary data-structure + cdef DTYPE_t feature_value = 0. + cdef Node* node = NULL + cdef DTYPE_t* X_sample = NULL + cdef SIZE_t i = 0 + cdef INT32_t k = 0 + + # feature_to_sample as a data structure records the last seen sample + # for each feature; functionally, it is an efficient way to identify + # which features are nonzero in the present sample. + cdef SIZE_t* feature_to_sample = NULL + + safe_realloc(&X_sample, n_features) + safe_realloc(&feature_to_sample, n_features) + + with nogil: + memset(feature_to_sample, -1, n_features * sizeof(SIZE_t)) + + for i in range(n_samples): + node = self.nodes + indptr[i + 1] = indptr[i] + + for k in range(X_indptr[i], X_indptr[i + 1]): + feature_to_sample[X_indices[k]] = i + X_sample[X_indices[k]] = X_data[k] + + # While node not a leaf + while node.left_child != _TREE_LEAF: + # ... and node.right_child != _TREE_LEAF: + + indices[indptr[i + 1]] = (node - self.nodes) + indptr[i + 1] += 1 + + if feature_to_sample[node.feature] == i: + feature_value = X_sample[node.feature] + + else: + feature_value = 0. + + if feature_value <= node.threshold: + node = &self.nodes[node.left_child] + else: + node = &self.nodes[node.right_child] + + # Add the leave node + indices[indptr[i + 1]] = (node - self.nodes) + indptr[i + 1] += 1 + + # Free auxiliary arrays + free(X_sample) + free(feature_to_sample) + + indices = indices[:indptr[n_samples]] + cdef SIZE_t[:] data = np.ones(shape=len(indices), dtype=np.intp) + out = csr_matrix((data, indices, indptr), + shape=(n_samples, self.node_count)) + + return out + + cpdef compute_node_depths(self): + """Compute the depth of each node in a tree. + + .. versionadded:: 1.3 + + Returns + ------- + depths : ndarray of shape (self.node_count,), dtype=np.int64 + The depth of each node in the tree. + """ + cdef: + cnp.int64_t[::1] depths = np.empty(self.node_count, dtype=np.int64) + cnp.npy_intp[:] children_left = self.children_left + cnp.npy_intp[:] children_right = self.children_right + cnp.npy_intp node_id + cnp.npy_intp node_count = self.node_count + cnp.int64_t depth + + depths[0] = 1 # init root node + for node_id in range(node_count): + if children_left[node_id] != _TREE_LEAF: + depth = depths[node_id] + 1 + depths[children_left[node_id]] = depth + depths[children_right[node_id]] = depth + + return depths.base + + cpdef compute_feature_importances(self, normalize=True): + """Computes the importance of each feature (aka variable).""" + cdef Node* left + cdef Node* right + cdef Node* nodes = self.nodes + cdef Node* node = nodes + cdef Node* end_node = node + self.node_count + + cdef double normalizer = 0. + + cdef cnp.float64_t[:] importances = np.zeros(self.n_features) + + with nogil: + while node != end_node: + if node.left_child != _TREE_LEAF: + # ... and node.right_child != _TREE_LEAF: + left = &nodes[node.left_child] + right = &nodes[node.right_child] + + importances[node.feature] += ( + node.weighted_n_node_samples * node.impurity - + left.weighted_n_node_samples * left.impurity - + right.weighted_n_node_samples * right.impurity) + node += 1 + + for i in range(self.n_features): + importances[i] /= nodes[0].weighted_n_node_samples + + if normalize: + normalizer = np.sum(importances) + + if normalizer > 0.0: + # Avoid dividing by zero (e.g., when root is pure) + for i in range(self.n_features): + importances[i] /= normalizer + + return np.asarray(importances) + + cdef cnp.ndarray _get_value_ndarray(self): + """Wraps value as a 3-d NumPy array. + + The array keeps a reference to this Tree, which manages the underlying + memory. + """ + cdef cnp.npy_intp shape[3] + shape[0] = self.node_count + shape[1] = self.n_outputs + shape[2] = self.max_n_classes + cdef cnp.ndarray arr + arr = cnp.PyArray_SimpleNewFromData(3, shape, cnp.NPY_DOUBLE, self.value) + Py_INCREF(self) + if PyArray_SetBaseObject(arr, self) < 0: + raise ValueError("Can't initialize array.") + return arr + + cdef cnp.ndarray _get_node_ndarray(self): + """Wraps nodes as a NumPy struct array. + + The array keeps a reference to this Tree, which manages the underlying + memory. Individual fields are publicly accessible as properties of the + Tree. + """ + cdef cnp.npy_intp shape[1] + shape[0] = self.node_count + cdef cnp.npy_intp strides[1] + strides[0] = sizeof(Node) + cdef cnp.ndarray arr + Py_INCREF(NODE_DTYPE) + arr = PyArray_NewFromDescr( cnp.ndarray, + NODE_DTYPE, 1, shape, + strides, self.nodes, + cnp.NPY_ARRAY_DEFAULT, None) + Py_INCREF(self) + if PyArray_SetBaseObject(arr, self) < 0: + raise ValueError("Can't initialize array.") + return arr + + def compute_partial_dependence(self, DTYPE_t[:, ::1] X, + int[::1] target_features, + double[::1] out): + """Partial dependence of the response on the ``target_feature`` set. + + For each sample in ``X`` a tree traversal is performed. + Each traversal starts from the root with weight 1.0. + + At each non-leaf node that splits on a target feature, either + the left child or the right child is visited based on the feature + value of the current sample, and the weight is not modified. + At each non-leaf node that splits on a complementary feature, + both children are visited and the weight is multiplied by the fraction + of training samples which went to each child. + + At each leaf, the value of the node is multiplied by the current + weight (weights sum to 1 for all visited terminal nodes). + + Parameters + ---------- + X : view on 2d ndarray, shape (n_samples, n_target_features) + The grid points on which the partial dependence should be + evaluated. + target_features : view on 1d ndarray, shape (n_target_features) + The set of target features for which the partial dependence + should be evaluated. + out : view on 1d ndarray, shape (n_samples) + The value of the partial dependence function on each grid + point. + """ + cdef: + double[::1] weight_stack = np.zeros(self.node_count, + dtype=np.float64) + SIZE_t[::1] node_idx_stack = np.zeros(self.node_count, + dtype=np.intp) + SIZE_t sample_idx + SIZE_t feature_idx + int stack_size + double left_sample_frac + double current_weight + double total_weight # used for sanity check only + Node *current_node # use a pointer to avoid copying attributes + SIZE_t current_node_idx + bint is_target_feature + SIZE_t _TREE_LEAF = TREE_LEAF # to avoid python interactions + + for sample_idx in range(X.shape[0]): + # init stacks for current sample + stack_size = 1 + node_idx_stack[0] = 0 # root node + weight_stack[0] = 1 # all the samples are in the root node + total_weight = 0 + + while stack_size > 0: + # pop the stack + stack_size -= 1 + current_node_idx = node_idx_stack[stack_size] + current_node = &self.nodes[current_node_idx] + + if current_node.left_child == _TREE_LEAF: + # leaf node + out[sample_idx] += (weight_stack[stack_size] * + self.value[current_node_idx]) + total_weight += weight_stack[stack_size] + else: + # non-leaf node + + # determine if the split feature is a target feature + is_target_feature = False + for feature_idx in range(target_features.shape[0]): + if target_features[feature_idx] == current_node.feature: + is_target_feature = True + break + + if is_target_feature: + # In this case, we push left or right child on stack + if X[sample_idx, feature_idx] <= current_node.threshold: + node_idx_stack[stack_size] = current_node.left_child + else: + node_idx_stack[stack_size] = current_node.right_child + stack_size += 1 + else: + # In this case, we push both children onto the stack, + # and give a weight proportional to the number of + # samples going through each branch. + + # push left child + node_idx_stack[stack_size] = current_node.left_child + left_sample_frac = ( + self.nodes[current_node.left_child].weighted_n_node_samples / + current_node.weighted_n_node_samples) + current_weight = weight_stack[stack_size] + weight_stack[stack_size] = current_weight * left_sample_frac + stack_size += 1 + + # push right child + node_idx_stack[stack_size] = current_node.right_child + weight_stack[stack_size] = ( + current_weight * (1 - left_sample_frac)) + stack_size += 1 + + # Sanity check. Should never happen. + if not (0.999 < total_weight < 1.001): + raise ValueError("Total weight should be 1.0 but was %.9f" % + total_weight) + + +def _check_n_classes(n_classes, expected_dtype): + if n_classes.ndim != 1: + raise ValueError( + f"Wrong dimensions for n_classes from the pickle: " + f"expected 1, got {n_classes.ndim}" + ) + + if n_classes.dtype == expected_dtype: + return n_classes + + # Handles both different endianness and different bitness + if n_classes.dtype.kind == "i" and n_classes.dtype.itemsize in [4, 8]: + return n_classes.astype(expected_dtype, casting="same_kind") + + raise ValueError( + "n_classes from the pickle has an incompatible dtype:\n" + f"- expected: {expected_dtype}\n" + f"- got: {n_classes.dtype}" + ) + + +def _check_value_ndarray(value_ndarray, expected_dtype, expected_shape): + if value_ndarray.shape != expected_shape: + raise ValueError( + "Wrong shape for value array from the pickle: " + f"expected {expected_shape}, got {value_ndarray.shape}" + ) + + if not value_ndarray.flags.c_contiguous: + raise ValueError( + "value array from the pickle should be a C-contiguous array" + ) + + if value_ndarray.dtype == expected_dtype: + return value_ndarray + + # Handles different endianness + if value_ndarray.dtype.str.endswith('f8'): + return value_ndarray.astype(expected_dtype, casting='equiv') + + raise ValueError( + "value array from the pickle has an incompatible dtype:\n" + f"- expected: {expected_dtype}\n" + f"- got: {value_ndarray.dtype}" + ) + + +def _dtype_to_dict(dtype): + return {name: dt.str for name, (dt, *rest) in dtype.fields.items()} + + +def _dtype_dict_with_modified_bitness(dtype_dict): + # field names in Node struct with SIZE_t types (see sklearn/tree/_tree.pxd) + indexing_field_names = ["left_child", "right_child", "feature", "n_node_samples"] + + expected_dtype_size = str(struct.calcsize("P")) + allowed_dtype_size = "8" if expected_dtype_size == "4" else "4" + + allowed_dtype_dict = dtype_dict.copy() + for name in indexing_field_names: + allowed_dtype_dict[name] = allowed_dtype_dict[name].replace( + expected_dtype_size, allowed_dtype_size + ) + + return allowed_dtype_dict + + +def _all_compatible_dtype_dicts(dtype): + # The Cython code for decision trees uses platform-specific SIZE_t + # typed indexing fields that correspond to either i4 or i8 dtypes for + # the matching fields in the numpy array depending on the bitness of + # the platform (32 bit or 64 bit respectively). + # + # We need to cast the indexing fields of the NODE_DTYPE-dtyped array at + # pickle load time to enable cross-bitness deployment scenarios. We + # typically want to make it possible to run the expensive fit method of + # a tree estimator on a 64 bit server platform, pickle the estimator + # for deployment and run the predict method of a low power 32 bit edge + # platform. + # + # A similar thing happens for endianness, the machine where the pickle was + # saved can have a different endianness than the machine where the pickle + # is loaded + + dtype_dict = _dtype_to_dict(dtype) + dtype_dict_with_modified_bitness = _dtype_dict_with_modified_bitness(dtype_dict) + dtype_dict_with_modified_endianness = _dtype_to_dict(dtype.newbyteorder()) + dtype_dict_with_modified_bitness_and_endianness = _dtype_dict_with_modified_bitness( + dtype_dict_with_modified_endianness + ) + + return [ + dtype_dict, + dtype_dict_with_modified_bitness, + dtype_dict_with_modified_endianness, + dtype_dict_with_modified_bitness_and_endianness, + ] + + +def _check_node_ndarray(node_ndarray, expected_dtype): + if node_ndarray.ndim != 1: + raise ValueError( + "Wrong dimensions for node array from the pickle: " + f"expected 1, got {node_ndarray.ndim}" + ) + + if not node_ndarray.flags.c_contiguous: + raise ValueError( + "node array from the pickle should be a C-contiguous array" + ) + + node_ndarray_dtype = node_ndarray.dtype + if node_ndarray_dtype == expected_dtype: + return node_ndarray + + node_ndarray_dtype_dict = _dtype_to_dict(node_ndarray_dtype) + all_compatible_dtype_dicts = _all_compatible_dtype_dicts(expected_dtype) + + if node_ndarray_dtype_dict not in all_compatible_dtype_dicts: + raise ValueError( + "node array from the pickle has an incompatible dtype:\n" + f"- expected: {expected_dtype}\n" + f"- got : {node_ndarray_dtype}" + ) + + return node_ndarray.astype(expected_dtype, casting="same_kind") + + +# ============================================================================= +# Build Pruned Tree +# ============================================================================= + + +cdef class _CCPPruneController: + """Base class used by build_pruned_tree_ccp and ccp_pruning_path + to control pruning. + """ + cdef bint stop_pruning(self, DOUBLE_t effective_alpha) noexcept nogil: + """Return 1 to stop pruning and 0 to continue pruning""" + return 0 + + cdef void save_metrics(self, DOUBLE_t effective_alpha, + DOUBLE_t subtree_impurities) noexcept nogil: + """Save metrics when pruning""" + pass + + cdef void after_pruning(self, unsigned char[:] in_subtree) noexcept nogil: + """Called after pruning""" + pass + + +cdef class _AlphaPruner(_CCPPruneController): + """Use alpha to control when to stop pruning.""" + cdef DOUBLE_t ccp_alpha + cdef SIZE_t capacity + + def __cinit__(self, DOUBLE_t ccp_alpha): + self.ccp_alpha = ccp_alpha + self.capacity = 0 + + cdef bint stop_pruning(self, DOUBLE_t effective_alpha) noexcept nogil: + # The subtree on the previous iteration has the greatest ccp_alpha + # less than or equal to self.ccp_alpha + return self.ccp_alpha < effective_alpha + + cdef void after_pruning(self, unsigned char[:] in_subtree) noexcept nogil: + """Updates the number of leaves in subtree""" + for i in range(in_subtree.shape[0]): + if in_subtree[i]: + self.capacity += 1 + + +cdef class _PathFinder(_CCPPruneController): + """Record metrics used to return the cost complexity path.""" + cdef DOUBLE_t[:] ccp_alphas + cdef DOUBLE_t[:] impurities + cdef UINT32_t count + + def __cinit__(self, int node_count): + self.ccp_alphas = np.zeros(shape=(node_count), dtype=np.float64) + self.impurities = np.zeros(shape=(node_count), dtype=np.float64) + self.count = 0 + + cdef void save_metrics(self, + DOUBLE_t effective_alpha, + DOUBLE_t subtree_impurities) noexcept nogil: + self.ccp_alphas[self.count] = effective_alpha + self.impurities[self.count] = subtree_impurities + self.count += 1 + + +cdef struct CostComplexityPruningRecord: + SIZE_t node_idx + SIZE_t parent + +cdef _cost_complexity_prune(unsigned char[:] leaves_in_subtree, # OUT + Tree orig_tree, + _CCPPruneController controller): + """Perform cost complexity pruning. + + This function takes an already grown tree, `orig_tree` and outputs a + boolean mask `leaves_in_subtree` which are the leaves in the pruned tree. + During the pruning process, the controller is passed the effective alpha and + the subtree impurities. Furthermore, the controller signals when to stop + pruning. + + Parameters + ---------- + leaves_in_subtree : unsigned char[:] + Output for leaves of subtree + orig_tree : Tree + Original tree + ccp_controller : _CCPPruneController + Cost complexity controller + """ + + cdef: + SIZE_t i + SIZE_t n_nodes = orig_tree.node_count + # prior probability using weighted samples + DOUBLE_t[:] weighted_n_node_samples = orig_tree.weighted_n_node_samples + DOUBLE_t total_sum_weights = weighted_n_node_samples[0] + DOUBLE_t[:] impurity = orig_tree.impurity + # weighted impurity of each node + DOUBLE_t[:] r_node = np.empty(shape=n_nodes, dtype=np.float64) + + SIZE_t[:] child_l = orig_tree.children_left + SIZE_t[:] child_r = orig_tree.children_right + SIZE_t[:] parent = np.zeros(shape=n_nodes, dtype=np.intp) + + stack[CostComplexityPruningRecord] ccp_stack + CostComplexityPruningRecord stack_record + SIZE_t node_idx + stack[SIZE_t] node_indices_stack + + SIZE_t[:] n_leaves = np.zeros(shape=n_nodes, dtype=np.intp) + DOUBLE_t[:] r_branch = np.zeros(shape=n_nodes, dtype=np.float64) + DOUBLE_t current_r + SIZE_t leaf_idx + SIZE_t parent_idx + + # candidate nodes that can be pruned + unsigned char[:] candidate_nodes = np.zeros(shape=n_nodes, + dtype=np.uint8) + # nodes in subtree + unsigned char[:] in_subtree = np.ones(shape=n_nodes, dtype=np.uint8) + SIZE_t pruned_branch_node_idx + DOUBLE_t subtree_alpha + DOUBLE_t effective_alpha + SIZE_t n_pruned_leaves + DOUBLE_t r_diff + DOUBLE_t max_float64 = np.finfo(np.float64).max + + # find parent node ids and leaves + with nogil: + + for i in range(r_node.shape[0]): + r_node[i] = ( + weighted_n_node_samples[i] * impurity[i] / total_sum_weights) + + # Push the root node + ccp_stack.push({"node_idx": 0, "parent": _TREE_UNDEFINED}) + + while not ccp_stack.empty(): + stack_record = ccp_stack.top() + ccp_stack.pop() + + node_idx = stack_record.node_idx + parent[node_idx] = stack_record.parent + + if child_l[node_idx] == _TREE_LEAF: + # ... and child_r[node_idx] == _TREE_LEAF: + leaves_in_subtree[node_idx] = 1 + else: + ccp_stack.push({"node_idx": child_l[node_idx], "parent": node_idx}) + ccp_stack.push({"node_idx": child_r[node_idx], "parent": node_idx}) + + # computes number of leaves in all branches and the overall impurity of + # the branch. The overall impurity is the sum of r_node in its leaves. + for leaf_idx in range(leaves_in_subtree.shape[0]): + if not leaves_in_subtree[leaf_idx]: + continue + r_branch[leaf_idx] = r_node[leaf_idx] + + # bubble up values to ancestor nodes + current_r = r_node[leaf_idx] + while leaf_idx != 0: + parent_idx = parent[leaf_idx] + r_branch[parent_idx] += current_r + n_leaves[parent_idx] += 1 + leaf_idx = parent_idx + + for i in range(leaves_in_subtree.shape[0]): + candidate_nodes[i] = not leaves_in_subtree[i] + + # save metrics before pruning + controller.save_metrics(0.0, r_branch[0]) + + # while root node is not a leaf + while candidate_nodes[0]: + + # computes ccp_alpha for subtrees and finds the minimal alpha + effective_alpha = max_float64 + for i in range(n_nodes): + if not candidate_nodes[i]: + continue + subtree_alpha = (r_node[i] - r_branch[i]) / (n_leaves[i] - 1) + if subtree_alpha < effective_alpha: + effective_alpha = subtree_alpha + pruned_branch_node_idx = i + + if controller.stop_pruning(effective_alpha): + break + + node_indices_stack.push(pruned_branch_node_idx) + + # descendants of branch are not in subtree + while not node_indices_stack.empty(): + node_idx = node_indices_stack.top() + node_indices_stack.pop() + + if not in_subtree[node_idx]: + continue # branch has already been marked for pruning + candidate_nodes[node_idx] = 0 + leaves_in_subtree[node_idx] = 0 + in_subtree[node_idx] = 0 + + if child_l[node_idx] != _TREE_LEAF: + # ... and child_r[node_idx] != _TREE_LEAF: + node_indices_stack.push(child_l[node_idx]) + node_indices_stack.push(child_r[node_idx]) + leaves_in_subtree[pruned_branch_node_idx] = 1 + in_subtree[pruned_branch_node_idx] = 1 + + # updates number of leaves + n_pruned_leaves = n_leaves[pruned_branch_node_idx] - 1 + n_leaves[pruned_branch_node_idx] = 0 + + # computes the increase in r_branch to bubble up + r_diff = r_node[pruned_branch_node_idx] - r_branch[pruned_branch_node_idx] + r_branch[pruned_branch_node_idx] = r_node[pruned_branch_node_idx] + + # bubble up values to ancestors + node_idx = parent[pruned_branch_node_idx] + while node_idx != _TREE_UNDEFINED: + n_leaves[node_idx] -= n_pruned_leaves + r_branch[node_idx] += r_diff + node_idx = parent[node_idx] + + controller.save_metrics(effective_alpha, r_branch[0]) + + controller.after_pruning(in_subtree) + + +def _build_pruned_tree_ccp( + Tree tree, # OUT + Tree orig_tree, + DOUBLE_t ccp_alpha +): + """Build a pruned tree from the original tree using cost complexity + pruning. + + The values and nodes from the original tree are copied into the pruned + tree. + + Parameters + ---------- + tree : Tree + Location to place the pruned tree + orig_tree : Tree + Original tree + ccp_alpha : positive double + Complexity parameter. The subtree with the largest cost complexity + that is smaller than ``ccp_alpha`` will be chosen. By default, + no pruning is performed. + """ + + cdef: + SIZE_t n_nodes = orig_tree.node_count + unsigned char[:] leaves_in_subtree = np.zeros( + shape=n_nodes, dtype=np.uint8) + + pruning_controller = _AlphaPruner(ccp_alpha=ccp_alpha) + + _cost_complexity_prune(leaves_in_subtree, orig_tree, pruning_controller) + + _build_pruned_tree(tree, orig_tree, leaves_in_subtree, + pruning_controller.capacity) + + +def ccp_pruning_path(Tree orig_tree): + """Computes the cost complexity pruning path. + + Parameters + ---------- + tree : Tree + Original tree. + + Returns + ------- + path_info : dict + Information about pruning path with attributes: + + ccp_alphas : ndarray + Effective alphas of subtree during pruning. + + impurities : ndarray + Sum of the impurities of the subtree leaves for the + corresponding alpha value in ``ccp_alphas``. + """ + cdef: + unsigned char[:] leaves_in_subtree = np.zeros( + shape=orig_tree.node_count, dtype=np.uint8) + + path_finder = _PathFinder(orig_tree.node_count) + + _cost_complexity_prune(leaves_in_subtree, orig_tree, path_finder) + + cdef: + UINT32_t total_items = path_finder.count + DOUBLE_t[:] ccp_alphas = np.empty(shape=total_items, dtype=np.float64) + DOUBLE_t[:] impurities = np.empty(shape=total_items, dtype=np.float64) + UINT32_t count = 0 + + while count < total_items: + ccp_alphas[count] = path_finder.ccp_alphas[count] + impurities[count] = path_finder.impurities[count] + count += 1 + + return { + 'ccp_alphas': np.asarray(ccp_alphas), + 'impurities': np.asarray(impurities), + } + + +cdef struct BuildPrunedRecord: + SIZE_t start + SIZE_t depth + SIZE_t parent + bint is_left + +cdef _build_pruned_tree( + Tree tree, # OUT + Tree orig_tree, + const unsigned char[:] leaves_in_subtree, + SIZE_t capacity +): + """Build a pruned tree. + + Build a pruned tree from the original tree by transforming the nodes in + ``leaves_in_subtree`` into leaves. + + Parameters + ---------- + tree : Tree + Location to place the pruned tree + orig_tree : Tree + Original tree + leaves_in_subtree : unsigned char memoryview, shape=(node_count, ) + Boolean mask for leaves to include in subtree + capacity : SIZE_t + Number of nodes to initially allocate in pruned tree + """ + tree._resize(capacity) + + cdef: + SIZE_t orig_node_id + SIZE_t new_node_id + SIZE_t depth + SIZE_t parent + bint is_left + bint is_leaf + + # value_stride for original tree and new tree are the same + SIZE_t value_stride = orig_tree.value_stride + SIZE_t max_depth_seen = -1 + int rc = 0 + Node* node + double* orig_value_ptr + double* new_value_ptr + + stack[BuildPrunedRecord] prune_stack + BuildPrunedRecord stack_record + + with nogil: + # push root node onto stack + prune_stack.push({"start": 0, "depth": 0, "parent": _TREE_UNDEFINED, "is_left": 0}) + + while not prune_stack.empty(): + stack_record = prune_stack.top() + prune_stack.pop() + + orig_node_id = stack_record.start + depth = stack_record.depth + parent = stack_record.parent + is_left = stack_record.is_left + + is_leaf = leaves_in_subtree[orig_node_id] + node = &orig_tree.nodes[orig_node_id] + + new_node_id = tree._add_node( + parent, is_left, is_leaf, node.feature, node.threshold, + node.impurity, node.n_node_samples, + node.weighted_n_node_samples, node.missing_go_to_left) + + if new_node_id == INTPTR_MAX: + rc = -1 + break + + # copy value from original tree to new tree + orig_value_ptr = orig_tree.value + value_stride * orig_node_id + new_value_ptr = tree.value + value_stride * new_node_id + memcpy(new_value_ptr, orig_value_ptr, sizeof(double) * value_stride) + + if not is_leaf: + # Push right child on stack + prune_stack.push({"start": node.right_child, "depth": depth + 1, + "parent": new_node_id, "is_left": 0}) + # push left child on stack + prune_stack.push({"start": node.left_child, "depth": depth + 1, + "parent": new_node_id, "is_left": 1}) + + if depth > max_depth_seen: + max_depth_seen = depth + + if rc >= 0: + tree.max_depth = max_depth_seen + if rc == -1: + raise MemoryError("pruning tree") \ No newline at end of file diff --git a/ivy/functional/frontends/sklearn/tree copy.py b/ivy/functional/frontends/sklearn/tree copy.py new file mode 100644 index 0000000000000..55ab869bd01f7 --- /dev/null +++ b/ivy/functional/frontends/sklearn/tree copy.py @@ -0,0 +1,822 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +import numpy as np + + + +from scipy.sparse import issparse +from scipy.sparse import csr_matrix +from scipy.sparse import isspmatrix_csr + + + + + + + + + + + + + + + + + + + + +# ============================================================================= +# Types and constants +# ============================================================================= + +from numpy import float32 as DTYPE +from numpy import float64 as DOUBLE + + +# Define constants +INFINITY = np.inf +EPSILON = np.finfo(np.double).eps + +# Some handy constants (BestFirstTreeBuilder) +IS_FIRST = 1 +IS_NOT_FIRST = 0 +IS_LEFT = 1 +IS_NOT_LEFT = 0 + +TREE_LEAF = -1 +TREE_UNDEFINED = -2 +_TREE_LEAF = TREE_LEAF +_TREE_UNDEFINED = TREE_UNDEFINED + +# Since you're dealing with Cython-specific types and features, +# it's important to provide a dummy definition for Node. +class Node: + def __init__(self): + self.left_child = None + self.right_child = None + self.feature = None + self.threshold = None + self.impurity = None + self.n_node_samples = None + self.weighted_n_node_samples = None + self.missing_go_to_left = None + +dummy = Node() +# Create a numpy dtype for Node using the dummy object +NODE_DTYPE = np.asarray([dummy], dtype=object).dtype +# ============================================================================= +# TreeBuilder +# ============================================================================= + +class TreeBuilder: + """Interface for different tree building strategies.""" + def __init__(self): + self.splitter = None + self.min_samples_split = None + self.min_samples_leaf = None + self.min_weight_leaf = None + self.max_depth = None + self.min_impurity_decrease = None + + def build( + self, + tree, + X, + y, + sample_weight=None, + missing_values_in_feature_mask=None, + ): + """Build a decision tree from the training set (X, y).""" + pass + + def _check_input( + self, + X, + y, + sample_weight, + ): + """Check input dtype, layout, and format""" + if issparse(X): + X = X.tocsc() #tocsc() is a method provided by the scipy.sparse module in the SciPy library. It's used to convert a sparse matrix to the Compressed Sparse Column (CSC) format. + X.sort_indices() #This is done to ensure that the indices of non-zero elements within the matrix are sorted in ascending order. + + if X.data.dtype != DTYPE: + X.data = np.ascontiguousarray(X.data, dtype=DTYPE) + + if X.indices.dtype != np.int32 or X.indptr.dtype != np.int32: + raise ValueError("No support for np.int64 index-based sparse matrices") + + elif X.dtype != DTYPE: + # since we have to copy, we will make it Fortran for efficiency + X = np.asfortranarray(X, dtype=DTYPE) + + if y.base.dtype != DTYPE or not y.base.flags.contiguous: + y = np.ascontiguousarray(y, dtype=DTYPE) + + if ( + sample_weight is not None and + ( + sample_weight.base.dtype != DOUBLE or + not sample_weight.base.flags.contiguous + ) + ): + sample_weight = np.asarray(sample_weight, dtype=DOUBLE, order="C") + + return X, y, sample_weight + + + + +# Depth first builder --------------------------------------------------------- +# A record on the stack for depth-first tree growing +class StackRecord: + def __init__(self, start, end, depth, parent, is_left, impurity, n_constant_features): + self.start = start + self.end = end + self.depth = depth + self.parent = parent + self.is_left = is_left + self.impurity = impurity + self.n_constant_features = n_constant_features + + + + + + + + +class DepthFirstTreeBuilder(TreeBuilder): + """Build a decision tree in depth-first fashion.""" + + def __init__( + self, splitter, min_samples_split, + min_samples_leaf, min_weight_leaf, + max_depth, min_impurity_decrease + ): + self.splitter = splitter + self.min_samples_split = min_samples_split + self.min_samples_leaf = min_samples_leaf + self.min_weight_leaf = min_weight_leaf + self.max_depth = max_depth + self.min_impurity_decrease = min_impurity_decrease + + def build( + self, + tree, + X, + y, + sample_weight=None, + missing_values_in_feature_mask=None + ): + """Build a decision tree from the training set (X, y).""" + + # Check input + X, y, sample_weight = self._check_input(X, y, sample_weight) + + # Initial capacity + init_capacity = (2 ** (tree.max_depth + 1)) - 1 if tree.max_depth <= 10 else 2047 + + tree._resize(init_capacity) + + # Parameters + splitter = self.splitter + max_depth = self.max_depth + min_samples_leaf = self.min_samples_leaf + min_weight_leaf = self.min_weight_leaf + min_samples_split = self.min_samples_split + min_impurity_decrease = self.min_impurity_decrease + + # Recursive partition (without actual recursion) + splitter.init(X, y, sample_weight, missing_values_in_feature_mask) + + stack = [] + + # Push root node onto stack + stack.append( + StackRecord( + start=0, + end=splitter.n_samples, + depth=0, + parent=_TREE_UNDEFINED, + is_left=False, + impurity=INFINITY, + n_constant_features=0 + ) + ) + weighted_n_node_samples = np.zeros(1, dtype=np.double) + while stack: + stack_record = stack.pop() + + start = stack_record.start + end = stack_record.end + depth = stack_record.depth + parent = stack_record.parent + is_left = stack_record.is_left + impurity = stack_record.impurity + n_constant_features = stack_record.n_constant_features + + n_node_samples = end - start + splitter.node_reset(start, end, weighted_n_node_samples) + + is_leaf = ( + depth >= max_depth + or n_node_samples < min_samples_split + or n_node_samples < 2 * min_samples_leaf + or np.sum(sample_weight[start:end]) < 2 * min_weight_leaf + ) + + if is_left: + impurity = splitter.node_impurity() + + is_leaf = is_leaf or impurity <= EPSILON + + if not is_leaf: + split = SplitRecord() # No idea what is SplitRecord in original code. Maybe this never gets called, not sure + splitter.node_split(impurity, split, n_constant_features) + is_leaf = ( + is_leaf + or split.pos >= end + or (split.improvement + EPSILON < min_impurity_decrease) + ) + + node_id = tree._add_node( + parent, + is_left, + is_leaf, + split.feature if not is_leaf else 0, + split.threshold if not is_leaf else 0, + impurity, + n_node_samples, + np.sum(sample_weight[start:end]), + split.missing_go_to_left, + ) + + if node_id == np.iinfo(np.intp).max: + raise MemoryError() + + splitter.node_value(tree.value + node_id * tree.value_stride) + + if not is_leaf: + # Push right child on stack + stack.append( + StackRecord( + start=split.pos, + end=end, + depth=depth + 1, + parent=node_id, + is_left=False, + impurity=split.impurity_right, + n_constant_features=n_constant_features, + ) + ) + # Push left child on stack + stack.append( + StackRecord( + start=start, + end=split.pos, + depth=depth + 1, + parent=node_id, + is_left=True, + impurity=split.impurity_left, + n_constant_features=n_constant_features, + ) + ) + + +class Tree: + def __init__(self, n_features, n_classes, n_outputs): + """Constructor.""" + self.n_features = None + self.n_classes = None + self.n_outputs = None + self.max_n_classes = None + self.max_depth = None + self.node_count = None + self.capacity = None + self.nodes = [] #replaced it with array since this array will contain nodes + self.value = None + self.value_stride = None + + dummy = 0 + size_t_dtype = np.array(dummy).dtype + + n_classes = _check_n_classes(n_classes, size_t_dtype) + + # Input/Output layout + self.n_features = n_features + self.n_outputs = n_outputs + self.n_classes = np.zeros(n_outputs, dtype=size_t_dtype) + + self.max_n_classes = np.max(n_classes) + self.value_stride = n_outputs * self.max_n_classes + + for k in range(n_outputs): + self.n_classes[k] = n_classes[k] + + # Inner structures + self.max_depth = 0 + self.node_count = 0 + self.capacity = 0 + self.value = None + self.nodes = None + + def __del__(self): + """Destructor.""" + # Free all inner structures + self.n_classes = None + self.value = None + self.nodes = None + + def __reduce__(self): + """Reduce re-implementation, for pickling.""" + raise NotImplementedError + + def __getstate__(self): + """Getstate re-implementation, for pickling.""" + d = {} + # capacity is inferred during the __setstate__ using nodes + d["max_depth"] = self.max_depth + d["node_count"] = self.node_count + d["nodes"] = self._get_node_ndarray() + d["values"] = self._get_value_ndarray() + return d + + def __setstate__(self, d): + """Setstate re-implementation, for unpickling.""" + raise NotImplementedError + + def _resize(self, capacity): + """ + Resize all inner arrays to `capacity`. If `capacity` is -1, then double the size of the inner arrays. + Returns -1 in case of failure to allocate memory (and raise MemoryError), or 0 otherwise. + """ + if self._resize_c(capacity) != 0: + # Raise MemoryError if resizing fails + raise MemoryError() + + def _resize_c(self, capacity=float('inf')): + # """ + # Guts of _resize + # Returns -1 in case of failure to allocate memory (and raise MemoryError), + # or 0 otherwise. + # """ + # if capacity == self.capacity and self.nodes is not None: + # return 0 + + # if capacity == float('inf'): + # if self.capacity == 0: + # capacity = 3 # default initial value + # else: + # capacity = 2 * self.capacity + + # # This section is relevant if the code is dealing with C arrays. + # # In Python, resizing arrays is handled automatically by lists or numpy arrays. + # # You won't need to explicitly reallocate memory or initialize values like this. + # self.nodes = [None] * capacity #doubtfull either the Node classes get reallocated or something else happends + # self.value = [0.0] * (capacity * self.value_stride) #doubtfull either the value classes get reallocated or something else happends + + # # value memory is initialized to 0 to enable classifier argmax + # if capacity > self.capacity: + # self.value += [0.0] * ((capacity - self.capacity) * self.value_stride) + + # # if capacity smaller than node_count, adjust the counter + # if capacity < self.node_count: + # self.node_count = capacity + + # self.capacity = capacity + # return 0 + raise NotImplementedError + + + def _add_node(self, parent, is_left, is_leaf, feature, threshold, impurity, + n_node_samples, weighted_n_node_samples, missing_go_to_left): + """ + Add a node to the tree. + + The new node registers itself as the child of its parent. + + Returns -1 on error. + """ + node_id = self.node_count + + #no need to resize since python reallocates lists dynamically + # if node_id >= self.capacity: + # if self._resize_c() != 0: + # return -1 #throw error if resize not possible + + node = Node() #self.nodes contains a list of nodes, it returns the node at node_id location + self.nodes.append(node) + node.impurity = impurity + node.n_node_samples = n_node_samples + node.weighted_n_node_samples = weighted_n_node_samples + + if parent != _TREE_UNDEFINED: + if is_left: + self.nodes[parent].left_child = node_id + else: + self.nodes[parent].right_child = node_id + + if is_leaf: + node.left_child = _TREE_LEAF + node.right_child = _TREE_LEAF + node.feature = _TREE_UNDEFINED + node.threshold = _TREE_UNDEFINED + + else: + # left_child and right_child will be set later + node.feature = feature + node.threshold = threshold + node.missing_go_to_left = missing_go_to_left + + self.node_count += 1 + + return node_id + + def predict(self, X): + # Apply the model to the input data X + predictions = self.apply(X) + # Get the internal data as a NumPy array + internal_data = self._get_value_ndarray() + # Use the predictions to index the internal data + out = internal_data[predictions] #not sure if this accurately translates to .take(self.apply(X), axis=0, mode='clip') + # Reshape the output if the model is single-output + if self.n_outputs == 1: + out = out.reshape(X.shape[0], self.max_n_classes) + return out + + def apply(self, X): + """Finds the terminal region (=leaf node) for each sample in X.""" + if issparse(X): + return self._apply_sparse_csr(X) + else: + return self._apply_dense(X) + + def _apply_dense(self, X): + if not isinstance(X, torch.Tensor): + raise ValueError("X should be a torch.Tensor, got %s" % type(X)) + + if X.dtype != torch.float32: + raise ValueError("X.dtype should be torch.float32, got %s" % X.dtype) + + X_tensor = X + n_samples = X.shape[0] + out = torch.zeros(n_samples, dtype=torch.int64) + + for i in range(n_samples): + node = self.nodes + + while node.left_child != _TREE_LEAF: + X_i_node_feature = X_tensor[i, node.feature] + + if torch.isnan(X_i_node_feature): + if node.missing_go_to_left: + node = self.nodes[node.left_child] + else: + node = self.nodes[node.right_child] + elif X_i_node_feature <= node.threshold: + node = self.nodes[node.left_child] + else: + node = self.nodes[node.right_child] + + out[i] = node - self.nodes + + return out + + def _apply_sparse_csr(self, X): + """Finds the terminal region (=leaf node) for each sample in sparse X.""" + if not isinstance(X, csr_matrix): + raise ValueError("X should be in csr_matrix format, got %s" % type(X)) + + if X.dtype != np.float32: + raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + + n_samples, n_features = X.shape + + # Initialize output + out = np.zeros(n_samples, dtype=np.intp) + + # Initialize auxiliary data structures + feature_to_sample = np.full(n_features, -1, dtype=np.intp) + X_sample = np.zeros(n_features, dtype=np.float32) + + for i in range(n_samples): + node = self.nodes + + for k in range(X.indptr[i], X.indptr[i + 1]): + feature_to_sample[X.indices[k]] = i + X_sample[X.indices[k]] = X.data[k] + + while node.left_child != _TREE_LEAF: + if feature_to_sample[node.feature] == i: + feature_value = X_sample[node.feature] + else: + feature_value = 0.0 + + if feature_value <= node.threshold: + node = self.nodes[node.left_child] + else: + node = self.nodes[node.right_child] + + out[i] = node - self.nodes # node offset + + return out + + def decision_path(self, X): + """Finds the decision path (=node) for each sample in X.""" + if issparse(X): + return self._decision_path_sparse_csr(X) + else: + return self._decision_path_dense(X) + + def _decision_path_dense(self, X): + """Finds the decision path (=node) for each sample in X.""" + + # Check input + if not isinstance(X, np.ndarray): + raise ValueError("X should be in np.ndarray format, got %s" % type(X)) + + if X.dtype != DTYPE: + raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + + # Extract input + X_ndarray = X + n_samples = X.shape[0] + + # Initialize output + indptr = np.zeros(n_samples + 1, dtype=np.intp) + indices = np.zeros(n_samples * (1 + self.max_depth), dtype=np.intp) + + # Initialize auxiliary data-structure + node = None + i = 0 + + for i in range(n_samples): + node = self.nodes + indptr[i + 1] = indptr[i] + + # Add all external nodes + while node.left_child != _TREE_LEAF: + # ... and node.right_child != _TREE_LEAF: + indices[indptr[i + 1]] = node - self.nodes + indptr[i + 1] += 1 + + if X_ndarray[i, node.feature] <= node.threshold: + node = self.nodes[node.left_child] + else: + node = self.nodes[node.right_child] + + # Add the leaf node + indices[indptr[i + 1]] = node - self.nodes + indptr[i + 1] += 1 + + indices = indices[:indptr[n_samples]] + data = np.ones(shape=len(indices), dtype=np.intp) + out = csr_matrix((data, indices, indptr), shape=(n_samples, self.node_count)) + + return out + + def _decision_path_sparse_csr(self, X): + """Finds the decision path (=node) for each sample in X.""" + + # Check input + if not isspmatrix_csr(X): + raise ValueError("X should be in csr_matrix format, got %s" % type(X)) + + if X.dtype != DTYPE: + raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + + # Extract input + X_data = X.data + X_indices = X.indices + X_indptr = X.indptr + + n_samples = X.shape[0] + n_features = X.shape[1] + + # Initialize output + indptr = np.zeros(n_samples + 1, dtype=np.intp) + indices = np.zeros(n_samples * (1 + self.max_depth), dtype=np.intp) + + # Initialize auxiliary data-structure + feature_value = 0.0 + node = None + X_sample = np.zeros(n_features, dtype=DTYPE) + feature_to_sample = np.full(n_features, -1, dtype=np.intp) + + for i in range(n_samples): + node = self.nodes + indptr[i + 1] = indptr[i] + + for k in range(X_indptr[i], X_indptr[i + 1]): + feature_to_sample[X_indices[k]] = i + X_sample[X_indices[k]] = X_data[k] + + # While node not a leaf + while node.left_child != _TREE_LEAF: + indices[indptr[i + 1]] = node - self.nodes + indptr[i + 1] += 1 + + if feature_to_sample[node.feature] == i: + feature_value = X_sample[node.feature] + else: + feature_value = 0.0 + + if feature_value <= node.threshold: + node = self.nodes[node.left_child] + else: + node = self.nodes[node.right_child] + + # Add the leaf node + indices[indptr[i + 1]] = node - self.nodes + indptr[i + 1] += 1 + + indices = indices[:indptr[n_samples]] + data = np.ones(shape=len(indices), dtype=np.intp) + out = csr_matrix((data, indices, indptr), shape=(n_samples, self.node_count)) + + return out + + def compute_node_depths(self): + """Compute the depth of each node in a tree. + + .. versionadded:: 1.3 + + Returns + ------- + depths : ndarray of shape (self.node_count,), dtype=np.int64 + The depth of each node in the tree. + """ + depths = np.empty(self.node_count, dtype=np.int64) + children_left = self.children_left + children_right = self.children_right + node_count = self.node_count + + depths[0] = 1 # init root node + for node_id in range(node_count): + if children_left[node_id] != _TREE_LEAF: + depth = depths[node_id] + 1 + depths[children_left[node_id]] = depth + depths[children_right[node_id]] = depth + + return depths.base + + def compute_feature_importances(self, normalize=True): + """Computes the importance of each feature (aka variable).""" + nodes = self.nodes + node = nodes + end_node = node + self.node_count + + importances = np.zeros(self.n_features, dtype=np.float64) + + while node != end_node: + if node.left_child != _TREE_LEAF: + left = nodes[node.left_child] + right = nodes[node.right_child] + + importances[node.feature] += ( + node.weighted_n_node_samples * node.impurity - + left.weighted_n_node_samples * left.impurity - + right.weighted_n_node_samples * right.impurity) + node += 1 + + for i in range(self.n_features): + importances[i] /= nodes[0].weighted_n_node_samples + + if normalize: + normalizer = np.sum(importances) + + if normalizer > 0.0: + # Avoid dividing by zero (e.g., when root is pure) + importances /= normalizer + + return importances + + def _get_value_ndarray(self): + """Wraps value as a 3-d NumPy array. + + The array keeps a reference to this Tree, which manages the underlying + memory. + """ + shape = (self.node_count, self.n_outputs, self.max_n_classes) + arr = np.ndarray(shape, dtype=np.float64, buffer=self.value) + arr.base = self + return arr + + def _get_node_ndarray(self): + """Wraps nodes as a NumPy struct array. + + The array keeps a reference to this Tree, which manages the underlying + memory. Individual fields are publicly accessible as properties of the + Tree. + """ + shape = (self.node_count,) + dtype = np.dtype([ + ('left_child', np.intp), + ('right_child', np.intp), + ('feature', np.intp), + ('threshold', np.float64), + ('impurity', np.float64), + ('n_node_samples', np.intp), + ('weighted_n_node_samples', np.float64), + ('missing_go_to_left', np.uint8) + ]) + arr = np.ndarray(shape, dtype=dtype, buffer=self.nodes) + arr.base = self + return arr + + def compute_partial_dependence(self, X, target_features, out): + out.fill(0.0) # Initialize the output array + + _TREE_LEAF = self._TREE_LEAF # The value for leaf nodes + + for sample_idx in range(X.shape[0]): + stack_size = 1 + node_idx_stack = [0] # root node + weight_stack = [1.0] # all samples are in the root node + total_weight = 0.0 + + while stack_size > 0: + stack_size -= 1 + current_node_idx = node_idx_stack[stack_size] + current_node = self.nodes[current_node_idx] + + if current_node.left_child == _TREE_LEAF: + # Leaf node + out[sample_idx] += weight_stack[stack_size] * self.value[current_node_idx] + total_weight += weight_stack[stack_size] + else: + is_target_feature = any(target_feature == current_node.feature for target_feature in target_features) + if is_target_feature: + if X[sample_idx, current_node.feature] <= current_node.threshold: + node_idx_stack.append(current_node.left_child) + weight_stack.append(weight_stack[stack_size]) + stack_size += 1 + else: + node_idx_stack.append(current_node.right_child) + weight_stack.append(weight_stack[stack_size]) + stack_size += 1 + else: + left_sample_frac = self.nodes[current_node.left_child].weighted_n_node_samples / current_node.weighted_n_node_samples + current_weight = weight_stack[stack_size] + node_idx_stack.extend([current_node.left_child, current_node.right_child]) + weight_stack.extend([current_weight * left_sample_frac, current_weight * (1 - left_sample_frac)]) + stack_size += 2 + + if not (0.999 < total_weight < 1.001): + raise ValueError(f"Total weight should be 1.0 but was {total_weight:.9f}") + + +def _check_n_classes(n_classes, expected_dtype): + if n_classes.ndim != 1: + raise ValueError( + f"Wrong dimensions for n_classes from the pickle: " + f"expected 1, got {n_classes.ndim}" + ) + + if n_classes.dtype == expected_dtype: + return n_classes + + # Handles both different endianness and different bitness + if n_classes.dtype.kind == "i" and n_classes.dtype.itemsize in [4, 8]: + return n_classes.astype(expected_dtype, casting="same_kind") + + raise ValueError( + "n_classes from the pickle has an incompatible dtype:\n" + f"- expected: {expected_dtype}\n" + f"- got: {n_classes.dtype}" + ) + + + + + + diff --git a/ivy/functional/frontends/sklearn/tree ivy copy.py b/ivy/functional/frontends/sklearn/tree ivy copy.py new file mode 100644 index 0000000000000..fa547997eb761 --- /dev/null +++ b/ivy/functional/frontends/sklearn/tree ivy copy.py @@ -0,0 +1,840 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +import numpy as np + + + +from scipy.sparse import issparse +from scipy.sparse import csr_matrix +from scipy.sparse import isspmatrix_csr + + + + + + + + + + + + + + + + + + + + +# ============================================================================= +# Types and constants +# ============================================================================= + +from numpy import float32 as DTYPE +from numpy import float64 as DOUBLE + + +# Define constants +INFINITY = np.inf +EPSILON = np.finfo(np.double).eps + +# Some handy constants (BestFirstTreeBuilder) +IS_FIRST = 1 +IS_NOT_FIRST = 0 +IS_LEFT = 1 +IS_NOT_LEFT = 0 + +TREE_LEAF = -1 +TREE_UNDEFINED = -2 +_TREE_LEAF = TREE_LEAF +_TREE_UNDEFINED = TREE_UNDEFINED + +# Since you're dealing with Cython-specific types and features, +# it's important to provide a dummy definition for Node. +class Node: + def __init__(self): + self.left_child = None + self.right_child = None + self.feature = None + self.threshold = None + self.impurity = None + self.n_node_samples = None + self.weighted_n_node_samples = None + self.missing_go_to_left = None + +dummy = Node() +# Create a numpy dtype for Node using the dummy object +NODE_DTYPE = np.asarray([dummy], dtype=object).dtype +# ============================================================================= +# TreeBuilder +# ============================================================================= + +class TreeBuilder: + """Interface for different tree building strategies.""" + def __init__(self): + self.splitter = None + self.min_samples_split = None + self.min_samples_leaf = None + self.min_weight_leaf = None + self.max_depth = None + self.min_impurity_decrease = None + + def build( + self, + tree, + X, + y, + sample_weight=None, + missing_values_in_feature_mask=None, + ): + """Build a decision tree from the training set (X, y).""" + pass + + def _check_input( + self, + X, + y, + sample_weight, + ): + """Check input dtype, layout, and format""" + if issparse(X): + X = X.tocsc() #tocsc() is a method provided by the scipy.sparse module in the SciPy library. It's used to convert a sparse matrix to the Compressed Sparse Column (CSC) format. + X.sort_indices() #This is done to ensure that the indices of non-zero elements within the matrix are sorted in ascending order. + + if X.data.dtype != DTYPE: + X.data = np.ascontiguousarray(X.data, dtype=DTYPE) + + if X.indices.dtype != np.int32 or X.indptr.dtype != np.int32: + raise ValueError("No support for np.int64 index-based sparse matrices") + + elif X.dtype != DTYPE: + # since we have to copy, we will make it Fortran for efficiency + X = np.asfortranarray(X, dtype=DTYPE) + + if y.base.dtype != DTYPE or not y.base.flags.contiguous: + y = np.ascontiguousarray(y, dtype=DTYPE) + + if ( + sample_weight is not None and + ( + sample_weight.base.dtype != DOUBLE or + not sample_weight.base.flags.contiguous + ) + ): + sample_weight = np.asarray(sample_weight, dtype=DOUBLE, order="C") + + return X, y, sample_weight + + + + +# Depth first builder --------------------------------------------------------- +# A record on the stack for depth-first tree growing +class StackRecord: + def __init__(self, start, end, depth, parent, is_left, impurity, n_constant_features): + self.start = start + self.end = end + self.depth = depth + self.parent = parent + self.is_left = is_left + self.impurity = impurity + self.n_constant_features = n_constant_features + + +class SplitRecord: + def __init__(self, feature, pos, threshold, improvement, + impurity_left, impurity_right, missing_go_to_left, + n_missing): + self.feature = feature + self.pos = pos + self.threshold = threshold + self.improvement = improvement + self.impurity_left = impurity_left + self.impurity_right = impurity_right + self.missing_go_to_left = missing_go_to_left + self.n_missing = n_missing + + + + + +class DepthFirstTreeBuilder(TreeBuilder): + """Build a decision tree in depth-first fashion.""" + + def __init__( + self, splitter, min_samples_split, + min_samples_leaf, min_weight_leaf, + max_depth, min_impurity_decrease + ): + self.splitter = splitter + self.min_samples_split = min_samples_split + self.min_samples_leaf = min_samples_leaf + self.min_weight_leaf = min_weight_leaf + self.max_depth = max_depth + self.min_impurity_decrease = min_impurity_decrease + + def build( + self, + tree, + X, + y, + sample_weight=None, + missing_values_in_feature_mask=None + ): + """Build a decision tree from the training set (X, y).""" + + # Check input + X, y, sample_weight = self._check_input(X, y, sample_weight) + + # Initial capacity + init_capacity = (2 ** (tree.max_depth + 1)) - 1 if tree.max_depth <= 10 else 2047 + + tree._resize(init_capacity) + + # Parameters + splitter = self.splitter + max_depth = self.max_depth + min_samples_leaf = self.min_samples_leaf + min_weight_leaf = self.min_weight_leaf + min_samples_split = self.min_samples_split + min_impurity_decrease = self.min_impurity_decrease + + # Recursive partition (without actual recursion) + splitter.init(X, y, sample_weight, missing_values_in_feature_mask) + + stack = [] + + # Push root node onto stack + stack.append( + StackRecord( + start=0, + end=splitter.n_samples, + depth=0, + parent=_TREE_UNDEFINED, + is_left=False, + impurity=INFINITY, + n_constant_features=0 + ) + ) + weighted_n_node_samples = np.zeros(1, dtype=np.double) + while stack: + stack_record = stack.pop() + + start = stack_record.start + end = stack_record.end + depth = stack_record.depth + parent = stack_record.parent + is_left = stack_record.is_left + impurity = stack_record.impurity + n_constant_features = stack_record.n_constant_features + + n_node_samples = end - start + splitter.node_reset(start, end, weighted_n_node_samples) + + is_leaf = ( + depth >= max_depth + or n_node_samples < min_samples_split + or n_node_samples < 2 * min_samples_leaf + or np.sum(sample_weight[start:end]) < 2 * min_weight_leaf + ) + + if is_left: + impurity = splitter.node_impurity() + + is_leaf = is_leaf or impurity <= EPSILON + + if not is_leaf: + split = SplitRecord() # No idea what is SplitRecord in original code. Maybe this never gets called, not sure + splitter.node_split(impurity, split, n_constant_features) + is_leaf = ( + is_leaf + or split.pos >= end + or (split.improvement + EPSILON < min_impurity_decrease) + ) + + node_id = tree._add_node( + parent, + is_left, + is_leaf, + split.feature if not is_leaf else 0, + split.threshold if not is_leaf else 0, + impurity, + n_node_samples, + np.sum(sample_weight[start:end]), + split.missing_go_to_left, + ) + + if node_id == np.iinfo(np.intp).max: + raise MemoryError() + + splitter.node_value(tree.value + node_id * tree.value_stride) + + if not is_leaf: + # Push right child on stack + stack.append( + StackRecord( + start=split.pos, + end=end, + depth=depth + 1, + parent=node_id, + is_left=False, + impurity=split.impurity_right, + n_constant_features=n_constant_features, + ) + ) + # Push left child on stack + stack.append( + StackRecord( + start=start, + end=split.pos, + depth=depth + 1, + parent=node_id, + is_left=True, + impurity=split.impurity_left, + n_constant_features=n_constant_features, + ) + ) + + +class Tree: + def __init__(self, n_features, n_classes, n_outputs): + """Constructor.""" + self.n_features = None + self.n_outputs = None + self.n_classes = None + self.max_n_classes = None + self.max_depth = None + self.node_count = None + self.capacity = None + self.nodes = None + self.value = None + self.value_stride = None + + dummy = 0 + size_t_dtype = np.array(dummy).dtype + + n_classes = _check_n_classes(n_classes, size_t_dtype) + + # Input/Output layout + self.n_features = n_features + self.n_outputs = n_outputs + self.n_classes = np.zeros(n_outputs, dtype=size_t_dtype) + + self.max_n_classes = np.max(n_classes) + self.value_stride = n_outputs * self.max_n_classes + + for k in range(n_outputs): + self.n_classes[k] = n_classes[k] + + # Inner structures + self.max_depth = 0 + self.node_count = 0 + self.capacity = 0 + self.value = None + self.nodes = None + + def __del__(self): + """Destructor.""" + # Free all inner structures + self.n_classes = None + self.value = None + self.nodes = None + + #NOT CONSIDERING PICKINLING FOR NOW + def __reduce__(self): + """Reduce re-implementation, for pickling.""" + raise NotImplementedError + #NOT CONSIDERING PICKINLING FOR NOW + def __getstate__(self): + """Getstate re-implementation, for pickling.""" + d = {} + # capacity is inferred during the __setstate__ using nodes + d["max_depth"] = self.max_depth + d["node_count"] = self.node_count + d["nodes"] = self._get_node_ndarray() + d["values"] = self._get_value_ndarray() + return d + #NOT CONSIDERING PICKINLING FOR NOW + def __setstate__(self, d): + """Setstate re-implementation, for unpickling.""" + raise NotImplementedError + + def _resize(self, capacity): + """ + Resize all inner arrays to `capacity`. If `capacity` is -1, then double the size of the inner arrays. + Returns -1 in case of failure to allocate memory (and raise MemoryError), or 0 otherwise. + """ + if self._resize_c(capacity) != 0: + # Raise MemoryError if resizing fails + raise MemoryError() + + def _resize_c(self, capacity=float('inf')): + """ + Guts of _resize + Returns -1 in case of failure to allocate memory (and raise MemoryError), + or 0 otherwise. + """ + if capacity == self.capacity and self.nodes is not None: + return 0 + + if capacity == float('inf'): + if self.capacity == 0: + capacity = 3 # default initial value + else: + capacity = 2 * self.capacity + + # This section is relevant if the code is dealing with C arrays. + # In Python, resizing arrays is handled automatically by lists or numpy arrays. + # You won't need to explicitly reallocate memory or initialize values like this. + + # replaced safe_realloc(&self.nodes, capacity) with the following + new_nodes = np.empty(capacity, dtype=object) + for i in range(len(self.nodes)): + new_nodes[i] = self.nodes[i] + self.nodes = new_nodes + + # replaced safe_realloc(&self.value, capacity * self.value_stride) with the following + new_value = np.empty(capacity * self.value_stride, dtype=object) + for i in range(len(self.value)): + new_value[i] = self.value[i] + self.value = new_value + + # if capacity smaller than node_count, adjust the counter + if capacity < self.node_count: + self.node_count = capacity + + self.capacity = capacity + return 0 + + + def _add_node(self, parent, is_left, is_leaf, feature, threshold, impurity, + n_node_samples, weighted_n_node_samples, missing_go_to_left): + """ + Add a node to the tree. + + The new node registers itself as the child of its parent. + + Returns -1 on error. + """ + node_id = self.node_count + + if node_id >= self.capacity: + if self._resize_c() != 0: + return -1 + + node = self.nodes[node_id] + node.impurity = impurity + node.n_node_samples = n_node_samples + node.weighted_n_node_samples = weighted_n_node_samples + + if parent != _TREE_UNDEFINED: + if is_left: + self.nodes[parent].left_child = node_id + else: + self.nodes[parent].right_child = node_id + + if is_leaf: + node.left_child = _TREE_LEAF + node.right_child = _TREE_LEAF + node.feature = _TREE_UNDEFINED + node.threshold = _TREE_UNDEFINED + + else: + # left_child and right_child will be set later + node.feature = feature + node.threshold = threshold + node.missing_go_to_left = missing_go_to_left + + self.node_count += 1 + + return node_id + + def predict(self, X): + """Predict target for X.""" + out = self._get_value_ndarray()[self.apply(X), :, :] + + if self.n_outputs == 1: + out = out.reshape(X.shape[0], self.max_n_classes) + + return out + + def apply(self, X): + """Finds the terminal region (=leaf node) for each sample in X.""" + if issparse(X): + return self._apply_sparse_csr(X) + else: + return self._apply_dense(X) + + def _apply_dense(self, X): + """Finds the terminal region (=leaf node) for each sample in X.""" + + # Check input + if not isinstance(X, np.ndarray): + raise ValueError("X should be in np.ndarray format, got %s" % type(X)) + + if X.dtype != np.float32: + raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + + # Extract input + n_samples, n_features = X.shape + + # Initialize output + out = np.zeros(n_samples, dtype=np.intp) + + with np.nditer(X, flags=['c_index', 'multi_index'], op_flags=['readonly'], order='C') as it: + for x_value, index in it: + node = self.nodes + # While node not a leaf + while node.left_child != _TREE_LEAF: + X_i_node_feature = x_value + # ... and node.right_child != _TREE_LEAF: + if np.isnan(X_i_node_feature): + if node.missing_go_to_left: + node = self.nodes[node.left_child] + else: + node = self.nodes[node.right_child] + elif X_i_node_feature <= node.threshold: + node = self.nodes[node.left_child] + else: + node = self.nodes[node.right_child] + + out[index[0]] = node - self.nodes # node offset + + return out + + def _apply_sparse_csr(self, X): + """Finds the terminal region (=leaf node) for each sample in sparse X.""" + if not isinstance(X, csr_matrix): + raise ValueError("X should be in csr_matrix format, got %s" % type(X)) + + if X.dtype != np.float32: + raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + + n_samples, n_features = X.shape + + # Initialize output + out = np.zeros(n_samples, dtype=np.intp) + + # Initialize auxiliary data structures + feature_to_sample = np.full(n_features, -1, dtype=np.intp) + X_sample = np.zeros(n_features, dtype=np.float32) + + for i in range(n_samples): + node = self.nodes + + for k in range(X.indptr[i], X.indptr[i + 1]): + feature_to_sample[X.indices[k]] = i + X_sample[X.indices[k]] = X.data[k] + + while node.left_child != _TREE_LEAF: + if feature_to_sample[node.feature] == i: + feature_value = X_sample[node.feature] + else: + feature_value = 0.0 + + if feature_value <= node.threshold: + node = self.nodes[node.left_child] + else: + node = self.nodes[node.right_child] + + out[i] = node - self.nodes # node offset + + return out + + def decision_path(self, X): + """Finds the decision path (=node) for each sample in X.""" + if issparse(X): + return self._decision_path_sparse_csr(X) + else: + return self._decision_path_dense(X) + + def _decision_path_dense(self, X): + """Finds the decision path (=node) for each sample in X.""" + + # Check input + if not isinstance(X, np.ndarray): + raise ValueError("X should be in np.ndarray format, got %s" % type(X)) + + if X.dtype != DTYPE: + raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + + # Extract input + X_ndarray = X + n_samples = X.shape[0] + + # Initialize output + indptr = np.zeros(n_samples + 1, dtype=np.intp) + indices = np.zeros(n_samples * (1 + self.max_depth), dtype=np.intp) + + # Initialize auxiliary data-structure + node = None + i = 0 + + for i in range(n_samples): + node = self.nodes + indptr[i + 1] = indptr[i] + + # Add all external nodes + while node.left_child != _TREE_LEAF: + # ... and node.right_child != _TREE_LEAF: + indices[indptr[i + 1]] = node - self.nodes + indptr[i + 1] += 1 + + if X_ndarray[i, node.feature] <= node.threshold: + node = self.nodes[node.left_child] + else: + node = self.nodes[node.right_child] + + # Add the leaf node + indices[indptr[i + 1]] = node - self.nodes + indptr[i + 1] += 1 + + indices = indices[:indptr[n_samples]] + data = np.ones(shape=len(indices), dtype=np.intp) + out = csr_matrix((data, indices, indptr), shape=(n_samples, self.node_count)) + + return out + + def _decision_path_sparse_csr(self, X): + """Finds the decision path (=node) for each sample in X.""" + + # Check input + if not isspmatrix_csr(X): + raise ValueError("X should be in csr_matrix format, got %s" % type(X)) + + if X.dtype != DTYPE: + raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + + # Extract input + X_data = X.data + X_indices = X.indices + X_indptr = X.indptr + + n_samples = X.shape[0] + n_features = X.shape[1] + + # Initialize output + indptr = np.zeros(n_samples + 1, dtype=np.intp) + indices = np.zeros(n_samples * (1 + self.max_depth), dtype=np.intp) + + # Initialize auxiliary data-structure + feature_value = 0.0 + node = None + X_sample = np.zeros(n_features, dtype=DTYPE) + feature_to_sample = np.full(n_features, -1, dtype=np.intp) + + for i in range(n_samples): + node = self.nodes + indptr[i + 1] = indptr[i] + + for k in range(X_indptr[i], X_indptr[i + 1]): + feature_to_sample[X_indices[k]] = i + X_sample[X_indices[k]] = X_data[k] + + # While node not a leaf + while node.left_child != _TREE_LEAF: + indices[indptr[i + 1]] = node - self.nodes + indptr[i + 1] += 1 + + if feature_to_sample[node.feature] == i: + feature_value = X_sample[node.feature] + else: + feature_value = 0.0 + + if feature_value <= node.threshold: + node = self.nodes[node.left_child] + else: + node = self.nodes[node.right_child] + + # Add the leaf node + indices[indptr[i + 1]] = node - self.nodes + indptr[i + 1] += 1 + + indices = indices[:indptr[n_samples]] + data = np.ones(shape=len(indices), dtype=np.intp) + out = csr_matrix((data, indices, indptr), shape=(n_samples, self.node_count)) + + return out + + def compute_node_depths(self): + """Compute the depth of each node in a tree. + + .. versionadded:: 1.3 + + Returns + ------- + depths : ndarray of shape (self.node_count,), dtype=np.int64 + The depth of each node in the tree. + """ + depths = np.empty(self.node_count, dtype=np.int64) + children_left = self.children_left + children_right = self.children_right + node_count = self.node_count + + depths[0] = 1 # init root node + for node_id in range(node_count): + if children_left[node_id] != _TREE_LEAF: + depth = depths[node_id] + 1 + depths[children_left[node_id]] = depth + depths[children_right[node_id]] = depth + + return depths.base + + def compute_feature_importances(self, normalize=True): + """Computes the importance of each feature (aka variable).""" + nodes = self.nodes + node = nodes + end_node = node + self.node_count + + importances = np.zeros(self.n_features, dtype=np.float64) + + while node != end_node: + if node.left_child != _TREE_LEAF: + left = nodes[node.left_child] + right = nodes[node.right_child] + + importances[node.feature] += ( + node.weighted_n_node_samples * node.impurity - + left.weighted_n_node_samples * left.impurity - + right.weighted_n_node_samples * right.impurity) + node += 1 + + for i in range(self.n_features): + importances[i] /= nodes[0].weighted_n_node_samples + + if normalize: + normalizer = np.sum(importances) + + if normalizer > 0.0: + # Avoid dividing by zero (e.g., when root is pure) + importances /= normalizer + + return importances + + def _get_value_ndarray(self): + """Wraps value as a 3-d NumPy array. + + The array keeps a reference to this Tree, which manages the underlying + memory. + """ + shape = (self.node_count, self.n_outputs, self.max_n_classes) + arr = np.ndarray(shape, dtype=np.float64, buffer=self.value) + arr.base = self + return arr + + def _get_node_ndarray(self): + """Wraps nodes as a NumPy struct array. + + The array keeps a reference to this Tree, which manages the underlying + memory. Individual fields are publicly accessible as properties of the + Tree. + """ + shape = (self.node_count,) + dtype = np.dtype([ + ('left_child', np.intp), + ('right_child', np.intp), + ('feature', np.intp), + ('threshold', np.float64), + ('impurity', np.float64), + ('n_node_samples', np.intp), + ('weighted_n_node_samples', np.float64), + ('missing_go_to_left', np.uint8) + ]) + arr = np.ndarray(shape, dtype=dtype, buffer=self.nodes) + arr.base = self + return arr + + def compute_partial_dependence(self, X, target_features, out): + out.fill(0.0) # Initialize the output array + + _TREE_LEAF = self._TREE_LEAF # The value for leaf nodes + + for sample_idx in range(X.shape[0]): + stack_size = 1 + node_idx_stack = [0] # root node + weight_stack = [1.0] # all samples are in the root node + total_weight = 0.0 + + while stack_size > 0: + stack_size -= 1 + current_node_idx = node_idx_stack[stack_size] + current_node = self.nodes[current_node_idx] + + if current_node.left_child == _TREE_LEAF: + # Leaf node + out[sample_idx] += weight_stack[stack_size] * self.value[current_node_idx] + total_weight += weight_stack[stack_size] + else: + is_target_feature = any(target_feature == current_node.feature for target_feature in target_features) + if is_target_feature: + if X[sample_idx, current_node.feature] <= current_node.threshold: + node_idx_stack.append(current_node.left_child) + weight_stack.append(weight_stack[stack_size]) + stack_size += 1 + else: + node_idx_stack.append(current_node.right_child) + weight_stack.append(weight_stack[stack_size]) + stack_size += 1 + else: + left_sample_frac = self.nodes[current_node.left_child].weighted_n_node_samples / current_node.weighted_n_node_samples + current_weight = weight_stack[stack_size] + node_idx_stack.extend([current_node.left_child, current_node.right_child]) + weight_stack.extend([current_weight * left_sample_frac, current_weight * (1 - left_sample_frac)]) + stack_size += 2 + + if not (0.999 < total_weight < 1.001): + raise ValueError(f"Total weight should be 1.0 but was {total_weight:.9f}") + + +def _check_n_classes(n_classes, expected_dtype): + if n_classes.ndim != 1: + raise ValueError( + f"Wrong dimensions for n_classes from the pickle: " + f"expected 1, got {n_classes.ndim}" + ) + + if n_classes.dtype == expected_dtype: + return n_classes + + # Handles both different endianness and different bitness + if n_classes.dtype.kind == "i" and n_classes.dtype.itemsize in [4, 8]: + return n_classes.astype(expected_dtype, casting="same_kind") + + raise ValueError( + "n_classes from the pickle has an incompatible dtype:\n" + f"- expected: {expected_dtype}\n" + f"- got: {n_classes.dtype}" + ) + + + + + + diff --git a/ivy/functional/frontends/sklearn/tree ivy.py b/ivy/functional/frontends/sklearn/tree ivy.py new file mode 100644 index 0000000000000..fa547997eb761 --- /dev/null +++ b/ivy/functional/frontends/sklearn/tree ivy.py @@ -0,0 +1,840 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +import numpy as np + + + +from scipy.sparse import issparse +from scipy.sparse import csr_matrix +from scipy.sparse import isspmatrix_csr + + + + + + + + + + + + + + + + + + + + +# ============================================================================= +# Types and constants +# ============================================================================= + +from numpy import float32 as DTYPE +from numpy import float64 as DOUBLE + + +# Define constants +INFINITY = np.inf +EPSILON = np.finfo(np.double).eps + +# Some handy constants (BestFirstTreeBuilder) +IS_FIRST = 1 +IS_NOT_FIRST = 0 +IS_LEFT = 1 +IS_NOT_LEFT = 0 + +TREE_LEAF = -1 +TREE_UNDEFINED = -2 +_TREE_LEAF = TREE_LEAF +_TREE_UNDEFINED = TREE_UNDEFINED + +# Since you're dealing with Cython-specific types and features, +# it's important to provide a dummy definition for Node. +class Node: + def __init__(self): + self.left_child = None + self.right_child = None + self.feature = None + self.threshold = None + self.impurity = None + self.n_node_samples = None + self.weighted_n_node_samples = None + self.missing_go_to_left = None + +dummy = Node() +# Create a numpy dtype for Node using the dummy object +NODE_DTYPE = np.asarray([dummy], dtype=object).dtype +# ============================================================================= +# TreeBuilder +# ============================================================================= + +class TreeBuilder: + """Interface for different tree building strategies.""" + def __init__(self): + self.splitter = None + self.min_samples_split = None + self.min_samples_leaf = None + self.min_weight_leaf = None + self.max_depth = None + self.min_impurity_decrease = None + + def build( + self, + tree, + X, + y, + sample_weight=None, + missing_values_in_feature_mask=None, + ): + """Build a decision tree from the training set (X, y).""" + pass + + def _check_input( + self, + X, + y, + sample_weight, + ): + """Check input dtype, layout, and format""" + if issparse(X): + X = X.tocsc() #tocsc() is a method provided by the scipy.sparse module in the SciPy library. It's used to convert a sparse matrix to the Compressed Sparse Column (CSC) format. + X.sort_indices() #This is done to ensure that the indices of non-zero elements within the matrix are sorted in ascending order. + + if X.data.dtype != DTYPE: + X.data = np.ascontiguousarray(X.data, dtype=DTYPE) + + if X.indices.dtype != np.int32 or X.indptr.dtype != np.int32: + raise ValueError("No support for np.int64 index-based sparse matrices") + + elif X.dtype != DTYPE: + # since we have to copy, we will make it Fortran for efficiency + X = np.asfortranarray(X, dtype=DTYPE) + + if y.base.dtype != DTYPE or not y.base.flags.contiguous: + y = np.ascontiguousarray(y, dtype=DTYPE) + + if ( + sample_weight is not None and + ( + sample_weight.base.dtype != DOUBLE or + not sample_weight.base.flags.contiguous + ) + ): + sample_weight = np.asarray(sample_weight, dtype=DOUBLE, order="C") + + return X, y, sample_weight + + + + +# Depth first builder --------------------------------------------------------- +# A record on the stack for depth-first tree growing +class StackRecord: + def __init__(self, start, end, depth, parent, is_left, impurity, n_constant_features): + self.start = start + self.end = end + self.depth = depth + self.parent = parent + self.is_left = is_left + self.impurity = impurity + self.n_constant_features = n_constant_features + + +class SplitRecord: + def __init__(self, feature, pos, threshold, improvement, + impurity_left, impurity_right, missing_go_to_left, + n_missing): + self.feature = feature + self.pos = pos + self.threshold = threshold + self.improvement = improvement + self.impurity_left = impurity_left + self.impurity_right = impurity_right + self.missing_go_to_left = missing_go_to_left + self.n_missing = n_missing + + + + + +class DepthFirstTreeBuilder(TreeBuilder): + """Build a decision tree in depth-first fashion.""" + + def __init__( + self, splitter, min_samples_split, + min_samples_leaf, min_weight_leaf, + max_depth, min_impurity_decrease + ): + self.splitter = splitter + self.min_samples_split = min_samples_split + self.min_samples_leaf = min_samples_leaf + self.min_weight_leaf = min_weight_leaf + self.max_depth = max_depth + self.min_impurity_decrease = min_impurity_decrease + + def build( + self, + tree, + X, + y, + sample_weight=None, + missing_values_in_feature_mask=None + ): + """Build a decision tree from the training set (X, y).""" + + # Check input + X, y, sample_weight = self._check_input(X, y, sample_weight) + + # Initial capacity + init_capacity = (2 ** (tree.max_depth + 1)) - 1 if tree.max_depth <= 10 else 2047 + + tree._resize(init_capacity) + + # Parameters + splitter = self.splitter + max_depth = self.max_depth + min_samples_leaf = self.min_samples_leaf + min_weight_leaf = self.min_weight_leaf + min_samples_split = self.min_samples_split + min_impurity_decrease = self.min_impurity_decrease + + # Recursive partition (without actual recursion) + splitter.init(X, y, sample_weight, missing_values_in_feature_mask) + + stack = [] + + # Push root node onto stack + stack.append( + StackRecord( + start=0, + end=splitter.n_samples, + depth=0, + parent=_TREE_UNDEFINED, + is_left=False, + impurity=INFINITY, + n_constant_features=0 + ) + ) + weighted_n_node_samples = np.zeros(1, dtype=np.double) + while stack: + stack_record = stack.pop() + + start = stack_record.start + end = stack_record.end + depth = stack_record.depth + parent = stack_record.parent + is_left = stack_record.is_left + impurity = stack_record.impurity + n_constant_features = stack_record.n_constant_features + + n_node_samples = end - start + splitter.node_reset(start, end, weighted_n_node_samples) + + is_leaf = ( + depth >= max_depth + or n_node_samples < min_samples_split + or n_node_samples < 2 * min_samples_leaf + or np.sum(sample_weight[start:end]) < 2 * min_weight_leaf + ) + + if is_left: + impurity = splitter.node_impurity() + + is_leaf = is_leaf or impurity <= EPSILON + + if not is_leaf: + split = SplitRecord() # No idea what is SplitRecord in original code. Maybe this never gets called, not sure + splitter.node_split(impurity, split, n_constant_features) + is_leaf = ( + is_leaf + or split.pos >= end + or (split.improvement + EPSILON < min_impurity_decrease) + ) + + node_id = tree._add_node( + parent, + is_left, + is_leaf, + split.feature if not is_leaf else 0, + split.threshold if not is_leaf else 0, + impurity, + n_node_samples, + np.sum(sample_weight[start:end]), + split.missing_go_to_left, + ) + + if node_id == np.iinfo(np.intp).max: + raise MemoryError() + + splitter.node_value(tree.value + node_id * tree.value_stride) + + if not is_leaf: + # Push right child on stack + stack.append( + StackRecord( + start=split.pos, + end=end, + depth=depth + 1, + parent=node_id, + is_left=False, + impurity=split.impurity_right, + n_constant_features=n_constant_features, + ) + ) + # Push left child on stack + stack.append( + StackRecord( + start=start, + end=split.pos, + depth=depth + 1, + parent=node_id, + is_left=True, + impurity=split.impurity_left, + n_constant_features=n_constant_features, + ) + ) + + +class Tree: + def __init__(self, n_features, n_classes, n_outputs): + """Constructor.""" + self.n_features = None + self.n_outputs = None + self.n_classes = None + self.max_n_classes = None + self.max_depth = None + self.node_count = None + self.capacity = None + self.nodes = None + self.value = None + self.value_stride = None + + dummy = 0 + size_t_dtype = np.array(dummy).dtype + + n_classes = _check_n_classes(n_classes, size_t_dtype) + + # Input/Output layout + self.n_features = n_features + self.n_outputs = n_outputs + self.n_classes = np.zeros(n_outputs, dtype=size_t_dtype) + + self.max_n_classes = np.max(n_classes) + self.value_stride = n_outputs * self.max_n_classes + + for k in range(n_outputs): + self.n_classes[k] = n_classes[k] + + # Inner structures + self.max_depth = 0 + self.node_count = 0 + self.capacity = 0 + self.value = None + self.nodes = None + + def __del__(self): + """Destructor.""" + # Free all inner structures + self.n_classes = None + self.value = None + self.nodes = None + + #NOT CONSIDERING PICKINLING FOR NOW + def __reduce__(self): + """Reduce re-implementation, for pickling.""" + raise NotImplementedError + #NOT CONSIDERING PICKINLING FOR NOW + def __getstate__(self): + """Getstate re-implementation, for pickling.""" + d = {} + # capacity is inferred during the __setstate__ using nodes + d["max_depth"] = self.max_depth + d["node_count"] = self.node_count + d["nodes"] = self._get_node_ndarray() + d["values"] = self._get_value_ndarray() + return d + #NOT CONSIDERING PICKINLING FOR NOW + def __setstate__(self, d): + """Setstate re-implementation, for unpickling.""" + raise NotImplementedError + + def _resize(self, capacity): + """ + Resize all inner arrays to `capacity`. If `capacity` is -1, then double the size of the inner arrays. + Returns -1 in case of failure to allocate memory (and raise MemoryError), or 0 otherwise. + """ + if self._resize_c(capacity) != 0: + # Raise MemoryError if resizing fails + raise MemoryError() + + def _resize_c(self, capacity=float('inf')): + """ + Guts of _resize + Returns -1 in case of failure to allocate memory (and raise MemoryError), + or 0 otherwise. + """ + if capacity == self.capacity and self.nodes is not None: + return 0 + + if capacity == float('inf'): + if self.capacity == 0: + capacity = 3 # default initial value + else: + capacity = 2 * self.capacity + + # This section is relevant if the code is dealing with C arrays. + # In Python, resizing arrays is handled automatically by lists or numpy arrays. + # You won't need to explicitly reallocate memory or initialize values like this. + + # replaced safe_realloc(&self.nodes, capacity) with the following + new_nodes = np.empty(capacity, dtype=object) + for i in range(len(self.nodes)): + new_nodes[i] = self.nodes[i] + self.nodes = new_nodes + + # replaced safe_realloc(&self.value, capacity * self.value_stride) with the following + new_value = np.empty(capacity * self.value_stride, dtype=object) + for i in range(len(self.value)): + new_value[i] = self.value[i] + self.value = new_value + + # if capacity smaller than node_count, adjust the counter + if capacity < self.node_count: + self.node_count = capacity + + self.capacity = capacity + return 0 + + + def _add_node(self, parent, is_left, is_leaf, feature, threshold, impurity, + n_node_samples, weighted_n_node_samples, missing_go_to_left): + """ + Add a node to the tree. + + The new node registers itself as the child of its parent. + + Returns -1 on error. + """ + node_id = self.node_count + + if node_id >= self.capacity: + if self._resize_c() != 0: + return -1 + + node = self.nodes[node_id] + node.impurity = impurity + node.n_node_samples = n_node_samples + node.weighted_n_node_samples = weighted_n_node_samples + + if parent != _TREE_UNDEFINED: + if is_left: + self.nodes[parent].left_child = node_id + else: + self.nodes[parent].right_child = node_id + + if is_leaf: + node.left_child = _TREE_LEAF + node.right_child = _TREE_LEAF + node.feature = _TREE_UNDEFINED + node.threshold = _TREE_UNDEFINED + + else: + # left_child and right_child will be set later + node.feature = feature + node.threshold = threshold + node.missing_go_to_left = missing_go_to_left + + self.node_count += 1 + + return node_id + + def predict(self, X): + """Predict target for X.""" + out = self._get_value_ndarray()[self.apply(X), :, :] + + if self.n_outputs == 1: + out = out.reshape(X.shape[0], self.max_n_classes) + + return out + + def apply(self, X): + """Finds the terminal region (=leaf node) for each sample in X.""" + if issparse(X): + return self._apply_sparse_csr(X) + else: + return self._apply_dense(X) + + def _apply_dense(self, X): + """Finds the terminal region (=leaf node) for each sample in X.""" + + # Check input + if not isinstance(X, np.ndarray): + raise ValueError("X should be in np.ndarray format, got %s" % type(X)) + + if X.dtype != np.float32: + raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + + # Extract input + n_samples, n_features = X.shape + + # Initialize output + out = np.zeros(n_samples, dtype=np.intp) + + with np.nditer(X, flags=['c_index', 'multi_index'], op_flags=['readonly'], order='C') as it: + for x_value, index in it: + node = self.nodes + # While node not a leaf + while node.left_child != _TREE_LEAF: + X_i_node_feature = x_value + # ... and node.right_child != _TREE_LEAF: + if np.isnan(X_i_node_feature): + if node.missing_go_to_left: + node = self.nodes[node.left_child] + else: + node = self.nodes[node.right_child] + elif X_i_node_feature <= node.threshold: + node = self.nodes[node.left_child] + else: + node = self.nodes[node.right_child] + + out[index[0]] = node - self.nodes # node offset + + return out + + def _apply_sparse_csr(self, X): + """Finds the terminal region (=leaf node) for each sample in sparse X.""" + if not isinstance(X, csr_matrix): + raise ValueError("X should be in csr_matrix format, got %s" % type(X)) + + if X.dtype != np.float32: + raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + + n_samples, n_features = X.shape + + # Initialize output + out = np.zeros(n_samples, dtype=np.intp) + + # Initialize auxiliary data structures + feature_to_sample = np.full(n_features, -1, dtype=np.intp) + X_sample = np.zeros(n_features, dtype=np.float32) + + for i in range(n_samples): + node = self.nodes + + for k in range(X.indptr[i], X.indptr[i + 1]): + feature_to_sample[X.indices[k]] = i + X_sample[X.indices[k]] = X.data[k] + + while node.left_child != _TREE_LEAF: + if feature_to_sample[node.feature] == i: + feature_value = X_sample[node.feature] + else: + feature_value = 0.0 + + if feature_value <= node.threshold: + node = self.nodes[node.left_child] + else: + node = self.nodes[node.right_child] + + out[i] = node - self.nodes # node offset + + return out + + def decision_path(self, X): + """Finds the decision path (=node) for each sample in X.""" + if issparse(X): + return self._decision_path_sparse_csr(X) + else: + return self._decision_path_dense(X) + + def _decision_path_dense(self, X): + """Finds the decision path (=node) for each sample in X.""" + + # Check input + if not isinstance(X, np.ndarray): + raise ValueError("X should be in np.ndarray format, got %s" % type(X)) + + if X.dtype != DTYPE: + raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + + # Extract input + X_ndarray = X + n_samples = X.shape[0] + + # Initialize output + indptr = np.zeros(n_samples + 1, dtype=np.intp) + indices = np.zeros(n_samples * (1 + self.max_depth), dtype=np.intp) + + # Initialize auxiliary data-structure + node = None + i = 0 + + for i in range(n_samples): + node = self.nodes + indptr[i + 1] = indptr[i] + + # Add all external nodes + while node.left_child != _TREE_LEAF: + # ... and node.right_child != _TREE_LEAF: + indices[indptr[i + 1]] = node - self.nodes + indptr[i + 1] += 1 + + if X_ndarray[i, node.feature] <= node.threshold: + node = self.nodes[node.left_child] + else: + node = self.nodes[node.right_child] + + # Add the leaf node + indices[indptr[i + 1]] = node - self.nodes + indptr[i + 1] += 1 + + indices = indices[:indptr[n_samples]] + data = np.ones(shape=len(indices), dtype=np.intp) + out = csr_matrix((data, indices, indptr), shape=(n_samples, self.node_count)) + + return out + + def _decision_path_sparse_csr(self, X): + """Finds the decision path (=node) for each sample in X.""" + + # Check input + if not isspmatrix_csr(X): + raise ValueError("X should be in csr_matrix format, got %s" % type(X)) + + if X.dtype != DTYPE: + raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + + # Extract input + X_data = X.data + X_indices = X.indices + X_indptr = X.indptr + + n_samples = X.shape[0] + n_features = X.shape[1] + + # Initialize output + indptr = np.zeros(n_samples + 1, dtype=np.intp) + indices = np.zeros(n_samples * (1 + self.max_depth), dtype=np.intp) + + # Initialize auxiliary data-structure + feature_value = 0.0 + node = None + X_sample = np.zeros(n_features, dtype=DTYPE) + feature_to_sample = np.full(n_features, -1, dtype=np.intp) + + for i in range(n_samples): + node = self.nodes + indptr[i + 1] = indptr[i] + + for k in range(X_indptr[i], X_indptr[i + 1]): + feature_to_sample[X_indices[k]] = i + X_sample[X_indices[k]] = X_data[k] + + # While node not a leaf + while node.left_child != _TREE_LEAF: + indices[indptr[i + 1]] = node - self.nodes + indptr[i + 1] += 1 + + if feature_to_sample[node.feature] == i: + feature_value = X_sample[node.feature] + else: + feature_value = 0.0 + + if feature_value <= node.threshold: + node = self.nodes[node.left_child] + else: + node = self.nodes[node.right_child] + + # Add the leaf node + indices[indptr[i + 1]] = node - self.nodes + indptr[i + 1] += 1 + + indices = indices[:indptr[n_samples]] + data = np.ones(shape=len(indices), dtype=np.intp) + out = csr_matrix((data, indices, indptr), shape=(n_samples, self.node_count)) + + return out + + def compute_node_depths(self): + """Compute the depth of each node in a tree. + + .. versionadded:: 1.3 + + Returns + ------- + depths : ndarray of shape (self.node_count,), dtype=np.int64 + The depth of each node in the tree. + """ + depths = np.empty(self.node_count, dtype=np.int64) + children_left = self.children_left + children_right = self.children_right + node_count = self.node_count + + depths[0] = 1 # init root node + for node_id in range(node_count): + if children_left[node_id] != _TREE_LEAF: + depth = depths[node_id] + 1 + depths[children_left[node_id]] = depth + depths[children_right[node_id]] = depth + + return depths.base + + def compute_feature_importances(self, normalize=True): + """Computes the importance of each feature (aka variable).""" + nodes = self.nodes + node = nodes + end_node = node + self.node_count + + importances = np.zeros(self.n_features, dtype=np.float64) + + while node != end_node: + if node.left_child != _TREE_LEAF: + left = nodes[node.left_child] + right = nodes[node.right_child] + + importances[node.feature] += ( + node.weighted_n_node_samples * node.impurity - + left.weighted_n_node_samples * left.impurity - + right.weighted_n_node_samples * right.impurity) + node += 1 + + for i in range(self.n_features): + importances[i] /= nodes[0].weighted_n_node_samples + + if normalize: + normalizer = np.sum(importances) + + if normalizer > 0.0: + # Avoid dividing by zero (e.g., when root is pure) + importances /= normalizer + + return importances + + def _get_value_ndarray(self): + """Wraps value as a 3-d NumPy array. + + The array keeps a reference to this Tree, which manages the underlying + memory. + """ + shape = (self.node_count, self.n_outputs, self.max_n_classes) + arr = np.ndarray(shape, dtype=np.float64, buffer=self.value) + arr.base = self + return arr + + def _get_node_ndarray(self): + """Wraps nodes as a NumPy struct array. + + The array keeps a reference to this Tree, which manages the underlying + memory. Individual fields are publicly accessible as properties of the + Tree. + """ + shape = (self.node_count,) + dtype = np.dtype([ + ('left_child', np.intp), + ('right_child', np.intp), + ('feature', np.intp), + ('threshold', np.float64), + ('impurity', np.float64), + ('n_node_samples', np.intp), + ('weighted_n_node_samples', np.float64), + ('missing_go_to_left', np.uint8) + ]) + arr = np.ndarray(shape, dtype=dtype, buffer=self.nodes) + arr.base = self + return arr + + def compute_partial_dependence(self, X, target_features, out): + out.fill(0.0) # Initialize the output array + + _TREE_LEAF = self._TREE_LEAF # The value for leaf nodes + + for sample_idx in range(X.shape[0]): + stack_size = 1 + node_idx_stack = [0] # root node + weight_stack = [1.0] # all samples are in the root node + total_weight = 0.0 + + while stack_size > 0: + stack_size -= 1 + current_node_idx = node_idx_stack[stack_size] + current_node = self.nodes[current_node_idx] + + if current_node.left_child == _TREE_LEAF: + # Leaf node + out[sample_idx] += weight_stack[stack_size] * self.value[current_node_idx] + total_weight += weight_stack[stack_size] + else: + is_target_feature = any(target_feature == current_node.feature for target_feature in target_features) + if is_target_feature: + if X[sample_idx, current_node.feature] <= current_node.threshold: + node_idx_stack.append(current_node.left_child) + weight_stack.append(weight_stack[stack_size]) + stack_size += 1 + else: + node_idx_stack.append(current_node.right_child) + weight_stack.append(weight_stack[stack_size]) + stack_size += 1 + else: + left_sample_frac = self.nodes[current_node.left_child].weighted_n_node_samples / current_node.weighted_n_node_samples + current_weight = weight_stack[stack_size] + node_idx_stack.extend([current_node.left_child, current_node.right_child]) + weight_stack.extend([current_weight * left_sample_frac, current_weight * (1 - left_sample_frac)]) + stack_size += 2 + + if not (0.999 < total_weight < 1.001): + raise ValueError(f"Total weight should be 1.0 but was {total_weight:.9f}") + + +def _check_n_classes(n_classes, expected_dtype): + if n_classes.ndim != 1: + raise ValueError( + f"Wrong dimensions for n_classes from the pickle: " + f"expected 1, got {n_classes.ndim}" + ) + + if n_classes.dtype == expected_dtype: + return n_classes + + # Handles both different endianness and different bitness + if n_classes.dtype.kind == "i" and n_classes.dtype.itemsize in [4, 8]: + return n_classes.astype(expected_dtype, casting="same_kind") + + raise ValueError( + "n_classes from the pickle has an incompatible dtype:\n" + f"- expected: {expected_dtype}\n" + f"- got: {n_classes.dtype}" + ) + + + + + + diff --git a/ivy/functional/frontends/sklearn/tree.py b/ivy/functional/frontends/sklearn/tree.py new file mode 100644 index 0000000000000..55ab869bd01f7 --- /dev/null +++ b/ivy/functional/frontends/sklearn/tree.py @@ -0,0 +1,822 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +import numpy as np + + + +from scipy.sparse import issparse +from scipy.sparse import csr_matrix +from scipy.sparse import isspmatrix_csr + + + + + + + + + + + + + + + + + + + + +# ============================================================================= +# Types and constants +# ============================================================================= + +from numpy import float32 as DTYPE +from numpy import float64 as DOUBLE + + +# Define constants +INFINITY = np.inf +EPSILON = np.finfo(np.double).eps + +# Some handy constants (BestFirstTreeBuilder) +IS_FIRST = 1 +IS_NOT_FIRST = 0 +IS_LEFT = 1 +IS_NOT_LEFT = 0 + +TREE_LEAF = -1 +TREE_UNDEFINED = -2 +_TREE_LEAF = TREE_LEAF +_TREE_UNDEFINED = TREE_UNDEFINED + +# Since you're dealing with Cython-specific types and features, +# it's important to provide a dummy definition for Node. +class Node: + def __init__(self): + self.left_child = None + self.right_child = None + self.feature = None + self.threshold = None + self.impurity = None + self.n_node_samples = None + self.weighted_n_node_samples = None + self.missing_go_to_left = None + +dummy = Node() +# Create a numpy dtype for Node using the dummy object +NODE_DTYPE = np.asarray([dummy], dtype=object).dtype +# ============================================================================= +# TreeBuilder +# ============================================================================= + +class TreeBuilder: + """Interface for different tree building strategies.""" + def __init__(self): + self.splitter = None + self.min_samples_split = None + self.min_samples_leaf = None + self.min_weight_leaf = None + self.max_depth = None + self.min_impurity_decrease = None + + def build( + self, + tree, + X, + y, + sample_weight=None, + missing_values_in_feature_mask=None, + ): + """Build a decision tree from the training set (X, y).""" + pass + + def _check_input( + self, + X, + y, + sample_weight, + ): + """Check input dtype, layout, and format""" + if issparse(X): + X = X.tocsc() #tocsc() is a method provided by the scipy.sparse module in the SciPy library. It's used to convert a sparse matrix to the Compressed Sparse Column (CSC) format. + X.sort_indices() #This is done to ensure that the indices of non-zero elements within the matrix are sorted in ascending order. + + if X.data.dtype != DTYPE: + X.data = np.ascontiguousarray(X.data, dtype=DTYPE) + + if X.indices.dtype != np.int32 or X.indptr.dtype != np.int32: + raise ValueError("No support for np.int64 index-based sparse matrices") + + elif X.dtype != DTYPE: + # since we have to copy, we will make it Fortran for efficiency + X = np.asfortranarray(X, dtype=DTYPE) + + if y.base.dtype != DTYPE or not y.base.flags.contiguous: + y = np.ascontiguousarray(y, dtype=DTYPE) + + if ( + sample_weight is not None and + ( + sample_weight.base.dtype != DOUBLE or + not sample_weight.base.flags.contiguous + ) + ): + sample_weight = np.asarray(sample_weight, dtype=DOUBLE, order="C") + + return X, y, sample_weight + + + + +# Depth first builder --------------------------------------------------------- +# A record on the stack for depth-first tree growing +class StackRecord: + def __init__(self, start, end, depth, parent, is_left, impurity, n_constant_features): + self.start = start + self.end = end + self.depth = depth + self.parent = parent + self.is_left = is_left + self.impurity = impurity + self.n_constant_features = n_constant_features + + + + + + + + +class DepthFirstTreeBuilder(TreeBuilder): + """Build a decision tree in depth-first fashion.""" + + def __init__( + self, splitter, min_samples_split, + min_samples_leaf, min_weight_leaf, + max_depth, min_impurity_decrease + ): + self.splitter = splitter + self.min_samples_split = min_samples_split + self.min_samples_leaf = min_samples_leaf + self.min_weight_leaf = min_weight_leaf + self.max_depth = max_depth + self.min_impurity_decrease = min_impurity_decrease + + def build( + self, + tree, + X, + y, + sample_weight=None, + missing_values_in_feature_mask=None + ): + """Build a decision tree from the training set (X, y).""" + + # Check input + X, y, sample_weight = self._check_input(X, y, sample_weight) + + # Initial capacity + init_capacity = (2 ** (tree.max_depth + 1)) - 1 if tree.max_depth <= 10 else 2047 + + tree._resize(init_capacity) + + # Parameters + splitter = self.splitter + max_depth = self.max_depth + min_samples_leaf = self.min_samples_leaf + min_weight_leaf = self.min_weight_leaf + min_samples_split = self.min_samples_split + min_impurity_decrease = self.min_impurity_decrease + + # Recursive partition (without actual recursion) + splitter.init(X, y, sample_weight, missing_values_in_feature_mask) + + stack = [] + + # Push root node onto stack + stack.append( + StackRecord( + start=0, + end=splitter.n_samples, + depth=0, + parent=_TREE_UNDEFINED, + is_left=False, + impurity=INFINITY, + n_constant_features=0 + ) + ) + weighted_n_node_samples = np.zeros(1, dtype=np.double) + while stack: + stack_record = stack.pop() + + start = stack_record.start + end = stack_record.end + depth = stack_record.depth + parent = stack_record.parent + is_left = stack_record.is_left + impurity = stack_record.impurity + n_constant_features = stack_record.n_constant_features + + n_node_samples = end - start + splitter.node_reset(start, end, weighted_n_node_samples) + + is_leaf = ( + depth >= max_depth + or n_node_samples < min_samples_split + or n_node_samples < 2 * min_samples_leaf + or np.sum(sample_weight[start:end]) < 2 * min_weight_leaf + ) + + if is_left: + impurity = splitter.node_impurity() + + is_leaf = is_leaf or impurity <= EPSILON + + if not is_leaf: + split = SplitRecord() # No idea what is SplitRecord in original code. Maybe this never gets called, not sure + splitter.node_split(impurity, split, n_constant_features) + is_leaf = ( + is_leaf + or split.pos >= end + or (split.improvement + EPSILON < min_impurity_decrease) + ) + + node_id = tree._add_node( + parent, + is_left, + is_leaf, + split.feature if not is_leaf else 0, + split.threshold if not is_leaf else 0, + impurity, + n_node_samples, + np.sum(sample_weight[start:end]), + split.missing_go_to_left, + ) + + if node_id == np.iinfo(np.intp).max: + raise MemoryError() + + splitter.node_value(tree.value + node_id * tree.value_stride) + + if not is_leaf: + # Push right child on stack + stack.append( + StackRecord( + start=split.pos, + end=end, + depth=depth + 1, + parent=node_id, + is_left=False, + impurity=split.impurity_right, + n_constant_features=n_constant_features, + ) + ) + # Push left child on stack + stack.append( + StackRecord( + start=start, + end=split.pos, + depth=depth + 1, + parent=node_id, + is_left=True, + impurity=split.impurity_left, + n_constant_features=n_constant_features, + ) + ) + + +class Tree: + def __init__(self, n_features, n_classes, n_outputs): + """Constructor.""" + self.n_features = None + self.n_classes = None + self.n_outputs = None + self.max_n_classes = None + self.max_depth = None + self.node_count = None + self.capacity = None + self.nodes = [] #replaced it with array since this array will contain nodes + self.value = None + self.value_stride = None + + dummy = 0 + size_t_dtype = np.array(dummy).dtype + + n_classes = _check_n_classes(n_classes, size_t_dtype) + + # Input/Output layout + self.n_features = n_features + self.n_outputs = n_outputs + self.n_classes = np.zeros(n_outputs, dtype=size_t_dtype) + + self.max_n_classes = np.max(n_classes) + self.value_stride = n_outputs * self.max_n_classes + + for k in range(n_outputs): + self.n_classes[k] = n_classes[k] + + # Inner structures + self.max_depth = 0 + self.node_count = 0 + self.capacity = 0 + self.value = None + self.nodes = None + + def __del__(self): + """Destructor.""" + # Free all inner structures + self.n_classes = None + self.value = None + self.nodes = None + + def __reduce__(self): + """Reduce re-implementation, for pickling.""" + raise NotImplementedError + + def __getstate__(self): + """Getstate re-implementation, for pickling.""" + d = {} + # capacity is inferred during the __setstate__ using nodes + d["max_depth"] = self.max_depth + d["node_count"] = self.node_count + d["nodes"] = self._get_node_ndarray() + d["values"] = self._get_value_ndarray() + return d + + def __setstate__(self, d): + """Setstate re-implementation, for unpickling.""" + raise NotImplementedError + + def _resize(self, capacity): + """ + Resize all inner arrays to `capacity`. If `capacity` is -1, then double the size of the inner arrays. + Returns -1 in case of failure to allocate memory (and raise MemoryError), or 0 otherwise. + """ + if self._resize_c(capacity) != 0: + # Raise MemoryError if resizing fails + raise MemoryError() + + def _resize_c(self, capacity=float('inf')): + # """ + # Guts of _resize + # Returns -1 in case of failure to allocate memory (and raise MemoryError), + # or 0 otherwise. + # """ + # if capacity == self.capacity and self.nodes is not None: + # return 0 + + # if capacity == float('inf'): + # if self.capacity == 0: + # capacity = 3 # default initial value + # else: + # capacity = 2 * self.capacity + + # # This section is relevant if the code is dealing with C arrays. + # # In Python, resizing arrays is handled automatically by lists or numpy arrays. + # # You won't need to explicitly reallocate memory or initialize values like this. + # self.nodes = [None] * capacity #doubtfull either the Node classes get reallocated or something else happends + # self.value = [0.0] * (capacity * self.value_stride) #doubtfull either the value classes get reallocated or something else happends + + # # value memory is initialized to 0 to enable classifier argmax + # if capacity > self.capacity: + # self.value += [0.0] * ((capacity - self.capacity) * self.value_stride) + + # # if capacity smaller than node_count, adjust the counter + # if capacity < self.node_count: + # self.node_count = capacity + + # self.capacity = capacity + # return 0 + raise NotImplementedError + + + def _add_node(self, parent, is_left, is_leaf, feature, threshold, impurity, + n_node_samples, weighted_n_node_samples, missing_go_to_left): + """ + Add a node to the tree. + + The new node registers itself as the child of its parent. + + Returns -1 on error. + """ + node_id = self.node_count + + #no need to resize since python reallocates lists dynamically + # if node_id >= self.capacity: + # if self._resize_c() != 0: + # return -1 #throw error if resize not possible + + node = Node() #self.nodes contains a list of nodes, it returns the node at node_id location + self.nodes.append(node) + node.impurity = impurity + node.n_node_samples = n_node_samples + node.weighted_n_node_samples = weighted_n_node_samples + + if parent != _TREE_UNDEFINED: + if is_left: + self.nodes[parent].left_child = node_id + else: + self.nodes[parent].right_child = node_id + + if is_leaf: + node.left_child = _TREE_LEAF + node.right_child = _TREE_LEAF + node.feature = _TREE_UNDEFINED + node.threshold = _TREE_UNDEFINED + + else: + # left_child and right_child will be set later + node.feature = feature + node.threshold = threshold + node.missing_go_to_left = missing_go_to_left + + self.node_count += 1 + + return node_id + + def predict(self, X): + # Apply the model to the input data X + predictions = self.apply(X) + # Get the internal data as a NumPy array + internal_data = self._get_value_ndarray() + # Use the predictions to index the internal data + out = internal_data[predictions] #not sure if this accurately translates to .take(self.apply(X), axis=0, mode='clip') + # Reshape the output if the model is single-output + if self.n_outputs == 1: + out = out.reshape(X.shape[0], self.max_n_classes) + return out + + def apply(self, X): + """Finds the terminal region (=leaf node) for each sample in X.""" + if issparse(X): + return self._apply_sparse_csr(X) + else: + return self._apply_dense(X) + + def _apply_dense(self, X): + if not isinstance(X, torch.Tensor): + raise ValueError("X should be a torch.Tensor, got %s" % type(X)) + + if X.dtype != torch.float32: + raise ValueError("X.dtype should be torch.float32, got %s" % X.dtype) + + X_tensor = X + n_samples = X.shape[0] + out = torch.zeros(n_samples, dtype=torch.int64) + + for i in range(n_samples): + node = self.nodes + + while node.left_child != _TREE_LEAF: + X_i_node_feature = X_tensor[i, node.feature] + + if torch.isnan(X_i_node_feature): + if node.missing_go_to_left: + node = self.nodes[node.left_child] + else: + node = self.nodes[node.right_child] + elif X_i_node_feature <= node.threshold: + node = self.nodes[node.left_child] + else: + node = self.nodes[node.right_child] + + out[i] = node - self.nodes + + return out + + def _apply_sparse_csr(self, X): + """Finds the terminal region (=leaf node) for each sample in sparse X.""" + if not isinstance(X, csr_matrix): + raise ValueError("X should be in csr_matrix format, got %s" % type(X)) + + if X.dtype != np.float32: + raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + + n_samples, n_features = X.shape + + # Initialize output + out = np.zeros(n_samples, dtype=np.intp) + + # Initialize auxiliary data structures + feature_to_sample = np.full(n_features, -1, dtype=np.intp) + X_sample = np.zeros(n_features, dtype=np.float32) + + for i in range(n_samples): + node = self.nodes + + for k in range(X.indptr[i], X.indptr[i + 1]): + feature_to_sample[X.indices[k]] = i + X_sample[X.indices[k]] = X.data[k] + + while node.left_child != _TREE_LEAF: + if feature_to_sample[node.feature] == i: + feature_value = X_sample[node.feature] + else: + feature_value = 0.0 + + if feature_value <= node.threshold: + node = self.nodes[node.left_child] + else: + node = self.nodes[node.right_child] + + out[i] = node - self.nodes # node offset + + return out + + def decision_path(self, X): + """Finds the decision path (=node) for each sample in X.""" + if issparse(X): + return self._decision_path_sparse_csr(X) + else: + return self._decision_path_dense(X) + + def _decision_path_dense(self, X): + """Finds the decision path (=node) for each sample in X.""" + + # Check input + if not isinstance(X, np.ndarray): + raise ValueError("X should be in np.ndarray format, got %s" % type(X)) + + if X.dtype != DTYPE: + raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + + # Extract input + X_ndarray = X + n_samples = X.shape[0] + + # Initialize output + indptr = np.zeros(n_samples + 1, dtype=np.intp) + indices = np.zeros(n_samples * (1 + self.max_depth), dtype=np.intp) + + # Initialize auxiliary data-structure + node = None + i = 0 + + for i in range(n_samples): + node = self.nodes + indptr[i + 1] = indptr[i] + + # Add all external nodes + while node.left_child != _TREE_LEAF: + # ... and node.right_child != _TREE_LEAF: + indices[indptr[i + 1]] = node - self.nodes + indptr[i + 1] += 1 + + if X_ndarray[i, node.feature] <= node.threshold: + node = self.nodes[node.left_child] + else: + node = self.nodes[node.right_child] + + # Add the leaf node + indices[indptr[i + 1]] = node - self.nodes + indptr[i + 1] += 1 + + indices = indices[:indptr[n_samples]] + data = np.ones(shape=len(indices), dtype=np.intp) + out = csr_matrix((data, indices, indptr), shape=(n_samples, self.node_count)) + + return out + + def _decision_path_sparse_csr(self, X): + """Finds the decision path (=node) for each sample in X.""" + + # Check input + if not isspmatrix_csr(X): + raise ValueError("X should be in csr_matrix format, got %s" % type(X)) + + if X.dtype != DTYPE: + raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + + # Extract input + X_data = X.data + X_indices = X.indices + X_indptr = X.indptr + + n_samples = X.shape[0] + n_features = X.shape[1] + + # Initialize output + indptr = np.zeros(n_samples + 1, dtype=np.intp) + indices = np.zeros(n_samples * (1 + self.max_depth), dtype=np.intp) + + # Initialize auxiliary data-structure + feature_value = 0.0 + node = None + X_sample = np.zeros(n_features, dtype=DTYPE) + feature_to_sample = np.full(n_features, -1, dtype=np.intp) + + for i in range(n_samples): + node = self.nodes + indptr[i + 1] = indptr[i] + + for k in range(X_indptr[i], X_indptr[i + 1]): + feature_to_sample[X_indices[k]] = i + X_sample[X_indices[k]] = X_data[k] + + # While node not a leaf + while node.left_child != _TREE_LEAF: + indices[indptr[i + 1]] = node - self.nodes + indptr[i + 1] += 1 + + if feature_to_sample[node.feature] == i: + feature_value = X_sample[node.feature] + else: + feature_value = 0.0 + + if feature_value <= node.threshold: + node = self.nodes[node.left_child] + else: + node = self.nodes[node.right_child] + + # Add the leaf node + indices[indptr[i + 1]] = node - self.nodes + indptr[i + 1] += 1 + + indices = indices[:indptr[n_samples]] + data = np.ones(shape=len(indices), dtype=np.intp) + out = csr_matrix((data, indices, indptr), shape=(n_samples, self.node_count)) + + return out + + def compute_node_depths(self): + """Compute the depth of each node in a tree. + + .. versionadded:: 1.3 + + Returns + ------- + depths : ndarray of shape (self.node_count,), dtype=np.int64 + The depth of each node in the tree. + """ + depths = np.empty(self.node_count, dtype=np.int64) + children_left = self.children_left + children_right = self.children_right + node_count = self.node_count + + depths[0] = 1 # init root node + for node_id in range(node_count): + if children_left[node_id] != _TREE_LEAF: + depth = depths[node_id] + 1 + depths[children_left[node_id]] = depth + depths[children_right[node_id]] = depth + + return depths.base + + def compute_feature_importances(self, normalize=True): + """Computes the importance of each feature (aka variable).""" + nodes = self.nodes + node = nodes + end_node = node + self.node_count + + importances = np.zeros(self.n_features, dtype=np.float64) + + while node != end_node: + if node.left_child != _TREE_LEAF: + left = nodes[node.left_child] + right = nodes[node.right_child] + + importances[node.feature] += ( + node.weighted_n_node_samples * node.impurity - + left.weighted_n_node_samples * left.impurity - + right.weighted_n_node_samples * right.impurity) + node += 1 + + for i in range(self.n_features): + importances[i] /= nodes[0].weighted_n_node_samples + + if normalize: + normalizer = np.sum(importances) + + if normalizer > 0.0: + # Avoid dividing by zero (e.g., when root is pure) + importances /= normalizer + + return importances + + def _get_value_ndarray(self): + """Wraps value as a 3-d NumPy array. + + The array keeps a reference to this Tree, which manages the underlying + memory. + """ + shape = (self.node_count, self.n_outputs, self.max_n_classes) + arr = np.ndarray(shape, dtype=np.float64, buffer=self.value) + arr.base = self + return arr + + def _get_node_ndarray(self): + """Wraps nodes as a NumPy struct array. + + The array keeps a reference to this Tree, which manages the underlying + memory. Individual fields are publicly accessible as properties of the + Tree. + """ + shape = (self.node_count,) + dtype = np.dtype([ + ('left_child', np.intp), + ('right_child', np.intp), + ('feature', np.intp), + ('threshold', np.float64), + ('impurity', np.float64), + ('n_node_samples', np.intp), + ('weighted_n_node_samples', np.float64), + ('missing_go_to_left', np.uint8) + ]) + arr = np.ndarray(shape, dtype=dtype, buffer=self.nodes) + arr.base = self + return arr + + def compute_partial_dependence(self, X, target_features, out): + out.fill(0.0) # Initialize the output array + + _TREE_LEAF = self._TREE_LEAF # The value for leaf nodes + + for sample_idx in range(X.shape[0]): + stack_size = 1 + node_idx_stack = [0] # root node + weight_stack = [1.0] # all samples are in the root node + total_weight = 0.0 + + while stack_size > 0: + stack_size -= 1 + current_node_idx = node_idx_stack[stack_size] + current_node = self.nodes[current_node_idx] + + if current_node.left_child == _TREE_LEAF: + # Leaf node + out[sample_idx] += weight_stack[stack_size] * self.value[current_node_idx] + total_weight += weight_stack[stack_size] + else: + is_target_feature = any(target_feature == current_node.feature for target_feature in target_features) + if is_target_feature: + if X[sample_idx, current_node.feature] <= current_node.threshold: + node_idx_stack.append(current_node.left_child) + weight_stack.append(weight_stack[stack_size]) + stack_size += 1 + else: + node_idx_stack.append(current_node.right_child) + weight_stack.append(weight_stack[stack_size]) + stack_size += 1 + else: + left_sample_frac = self.nodes[current_node.left_child].weighted_n_node_samples / current_node.weighted_n_node_samples + current_weight = weight_stack[stack_size] + node_idx_stack.extend([current_node.left_child, current_node.right_child]) + weight_stack.extend([current_weight * left_sample_frac, current_weight * (1 - left_sample_frac)]) + stack_size += 2 + + if not (0.999 < total_weight < 1.001): + raise ValueError(f"Total weight should be 1.0 but was {total_weight:.9f}") + + +def _check_n_classes(n_classes, expected_dtype): + if n_classes.ndim != 1: + raise ValueError( + f"Wrong dimensions for n_classes from the pickle: " + f"expected 1, got {n_classes.ndim}" + ) + + if n_classes.dtype == expected_dtype: + return n_classes + + # Handles both different endianness and different bitness + if n_classes.dtype.kind == "i" and n_classes.dtype.itemsize in [4, 8]: + return n_classes.astype(expected_dtype, casting="same_kind") + + raise ValueError( + "n_classes from the pickle has an incompatible dtype:\n" + f"- expected: {expected_dtype}\n" + f"- got: {n_classes.dtype}" + ) + + + + + + diff --git a/ivy/functional/frontends/sklearn/tree/_criterion.pyx b/ivy/functional/frontends/sklearn/tree/_criterion.pyx new file mode 100644 index 0000000000000..df2da34a5303d --- /dev/null +++ b/ivy/functional/frontends/sklearn/tree/_criterion.pyx @@ -0,0 +1,1548 @@ +# Authors: Gilles Louppe +# Peter Prettenhofer +# Brian Holt +# Noel Dawe +# Satrajit Gosh +# Lars Buitinck +# Arnaud Joly +# Joel Nothman +# Fares Hedayati +# Jacob Schreiber +# Nelson Liu +# +# License: BSD 3 clause + +from libc.string cimport memcpy +from libc.string cimport memset +from libc.math cimport fabs, INFINITY + +import numpy as np +cimport numpy as cnp +cnp.import_array() + +from scipy.special.cython_special cimport xlogy + +from ._utils cimport log +from ._utils cimport WeightedMedianCalculator + +# EPSILON is used in the Poisson criterion +cdef double EPSILON = 10 * np.finfo('double').eps + +cdef class Criterion: + """Interface for impurity criteria. + + This object stores methods on how to calculate how good a split is using + different metrics. + """ + def __getstate__(self): + return {} + + def __setstate__(self, d): + pass + + cdef int init( + self, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight, + double weighted_n_samples, + const SIZE_t[:] sample_indices, + SIZE_t start, + SIZE_t end, + ) except -1 nogil: + """Placeholder for a method which will initialize the criterion. + + Returns -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + + Parameters + ---------- + y : ndarray, dtype=DOUBLE_t + y is a buffer that can store values for n_outputs target variables + stored as a Cython memoryview. + sample_weight : ndarray, dtype=DOUBLE_t + The weight of each sample stored as a Cython memoryview. + weighted_n_samples : double + The total weight of the samples being considered + sample_indices : ndarray, dtype=SIZE_t + A mask on the samples. Indices of the samples in X and y we want to use, + where sample_indices[start:end] correspond to the samples in this node. + start : SIZE_t + The first sample to be used on this node + end : SIZE_t + The last sample used on this node + + """ + pass + + cdef void init_missing(self, SIZE_t n_missing) noexcept nogil: + """Initialize sum_missing if there are missing values. + + This method assumes that caller placed the missing samples in + self.sample_indices[-n_missing:] + + Parameters + ---------- + n_missing: SIZE_t + Number of missing values for specific feature. + """ + pass + + cdef int reset(self) except -1 nogil: + """Reset the criterion at pos=start. + + This method must be implemented by the subclass. + """ + pass + + cdef int reverse_reset(self) except -1 nogil: + """Reset the criterion at pos=end. + + This method must be implemented by the subclass. + """ + pass + + cdef int update(self, SIZE_t new_pos) except -1 nogil: + """Updated statistics by moving sample_indices[pos:new_pos] to the left child. + + This updates the collected statistics by moving sample_indices[pos:new_pos] + from the right child to the left child. It must be implemented by + the subclass. + + Parameters + ---------- + new_pos : SIZE_t + New starting index position of the sample_indices in the right child + """ + pass + + cdef double node_impurity(self) noexcept nogil: + """Placeholder for calculating the impurity of the node. + + Placeholder for a method which will evaluate the impurity of + the current node, i.e. the impurity of sample_indices[start:end]. This is the + primary function of the criterion class. The smaller the impurity the + better. + """ + pass + + cdef void children_impurity(self, double* impurity_left, + double* impurity_right) noexcept nogil: + """Placeholder for calculating the impurity of children. + + Placeholder for a method which evaluates the impurity in + children nodes, i.e. the impurity of sample_indices[start:pos] + the impurity + of sample_indices[pos:end]. + + Parameters + ---------- + impurity_left : double pointer + The memory address where the impurity of the left child should be + stored. + impurity_right : double pointer + The memory address where the impurity of the right child should be + stored + """ + pass + + cdef void node_value(self, double* dest) noexcept nogil: + """Placeholder for storing the node value. + + Placeholder for a method which will compute the node value + of sample_indices[start:end] and save the value into dest. + + Parameters + ---------- + dest : double pointer + The memory address where the node value should be stored. + """ + pass + + cdef double proxy_impurity_improvement(self) noexcept nogil: + """Compute a proxy of the impurity reduction. + + This method is used to speed up the search for the best split. + It is a proxy quantity such that the split that maximizes this value + also maximizes the impurity improvement. It neglects all constant terms + of the impurity decrease for a given split. + + The absolute impurity improvement is only computed by the + impurity_improvement method once the best split has been found. + """ + cdef double impurity_left + cdef double impurity_right + self.children_impurity(&impurity_left, &impurity_right) + + return (- self.weighted_n_right * impurity_right + - self.weighted_n_left * impurity_left) + + cdef double impurity_improvement(self, double impurity_parent, + double impurity_left, + double impurity_right) noexcept nogil: + """Compute the improvement in impurity. + + This method computes the improvement in impurity when a split occurs. + The weighted impurity improvement equation is the following: + + N_t / N * (impurity - N_t_R / N_t * right_impurity + - N_t_L / N_t * left_impurity) + + where N is the total number of samples, N_t is the number of samples + at the current node, N_t_L is the number of samples in the left child, + and N_t_R is the number of samples in the right child, + + Parameters + ---------- + impurity_parent : double + The initial impurity of the parent node before the split + + impurity_left : double + The impurity of the left child + + impurity_right : double + The impurity of the right child + + Return + ------ + double : improvement in impurity after the split occurs + """ + return ((self.weighted_n_node_samples / self.weighted_n_samples) * + (impurity_parent - (self.weighted_n_right / + self.weighted_n_node_samples * impurity_right) + - (self.weighted_n_left / + self.weighted_n_node_samples * impurity_left))) + + cdef void init_sum_missing(self): + """Init sum_missing to hold sums for missing values.""" + +cdef inline void _move_sums_classification( + ClassificationCriterion criterion, + double[:, ::1] sum_1, + double[:, ::1] sum_2, + double* weighted_n_1, + double* weighted_n_2, + bint put_missing_in_1, +) noexcept nogil: + """Distribute sum_total and sum_missing into sum_1 and sum_2. + + If there are missing values and: + - put_missing_in_1 is True, then missing values to go sum_1. Specifically: + sum_1 = sum_missing + sum_2 = sum_total - sum_missing + + - put_missing_in_1 is False, then missing values go to sum_2. Specifically: + sum_1 = 0 + sum_2 = sum_total + """ + cdef SIZE_t k, c, n_bytes + if criterion.n_missing != 0 and put_missing_in_1: + for k in range(criterion.n_outputs): + n_bytes = criterion.n_classes[k] * sizeof(double) + memcpy(&sum_1[k, 0], &criterion.sum_missing[k, 0], n_bytes) + + for k in range(criterion.n_outputs): + for c in range(criterion.n_classes[k]): + sum_2[k, c] = criterion.sum_total[k, c] - criterion.sum_missing[k, c] + + weighted_n_1[0] = criterion.weighted_n_missing + weighted_n_2[0] = criterion.weighted_n_node_samples - criterion.weighted_n_missing + else: + # Assigning sum_2 = sum_total for all outputs. + for k in range(criterion.n_outputs): + n_bytes = criterion.n_classes[k] * sizeof(double) + memset(&sum_1[k, 0], 0, n_bytes) + memcpy(&sum_2[k, 0], &criterion.sum_total[k, 0], n_bytes) + + weighted_n_1[0] = 0.0 + weighted_n_2[0] = criterion.weighted_n_node_samples + + +cdef class ClassificationCriterion(Criterion): + """Abstract criterion for classification.""" + + def __cinit__(self, SIZE_t n_outputs, + cnp.ndarray[SIZE_t, ndim=1] n_classes): + """Initialize attributes for this criterion. + + Parameters + ---------- + n_outputs : SIZE_t + The number of targets, the dimensionality of the prediction + n_classes : numpy.ndarray, dtype=SIZE_t + The number of unique classes in each target + """ + self.start = 0 + self.pos = 0 + self.end = 0 + self.missing_go_to_left = 0 + + self.n_outputs = n_outputs + self.n_samples = 0 + self.n_node_samples = 0 + self.weighted_n_node_samples = 0.0 + self.weighted_n_left = 0.0 + self.weighted_n_right = 0.0 + self.weighted_n_missing = 0.0 + + self.n_classes = np.empty(n_outputs, dtype=np.intp) + + cdef SIZE_t k = 0 + cdef SIZE_t max_n_classes = 0 + + # For each target, set the number of unique classes in that target, + # and also compute the maximal stride of all targets + for k in range(n_outputs): + self.n_classes[k] = n_classes[k] + + if n_classes[k] > max_n_classes: + max_n_classes = n_classes[k] + + self.max_n_classes = max_n_classes + + # Count labels for each output + self.sum_total = np.zeros((n_outputs, max_n_classes), dtype=np.float64) + self.sum_left = np.zeros((n_outputs, max_n_classes), dtype=np.float64) + self.sum_right = np.zeros((n_outputs, max_n_classes), dtype=np.float64) + + def __reduce__(self): + return (type(self), + (self.n_outputs, np.asarray(self.n_classes)), self.__getstate__()) + + cdef int init( + self, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight, + double weighted_n_samples, + const SIZE_t[:] sample_indices, + SIZE_t start, + SIZE_t end + ) except -1 nogil: + """Initialize the criterion. + + This initializes the criterion at node sample_indices[start:end] and children + sample_indices[start:start] and sample_indices[start:end]. + + Returns -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + + Parameters + ---------- + y : ndarray, dtype=DOUBLE_t + The target stored as a buffer for memory efficiency. + sample_weight : ndarray, dtype=DOUBLE_t + The weight of each sample stored as a Cython memoryview. + weighted_n_samples : double + The total weight of all samples + sample_indices : ndarray, dtype=SIZE_t + A mask on the samples. Indices of the samples in X and y we want to use, + where sample_indices[start:end] correspond to the samples in this node. + start : SIZE_t + The first sample to use in the mask + end : SIZE_t + The last sample to use in the mask + """ + self.y = y + self.sample_weight = sample_weight + self.sample_indices = sample_indices + self.start = start + self.end = end + self.n_node_samples = end - start + self.weighted_n_samples = weighted_n_samples + self.weighted_n_node_samples = 0.0 + + cdef SIZE_t i + cdef SIZE_t p + cdef SIZE_t k + cdef SIZE_t c + cdef DOUBLE_t w = 1.0 + + for k in range(self.n_outputs): + memset(&self.sum_total[k, 0], 0, self.n_classes[k] * sizeof(double)) + + for p in range(start, end): + i = sample_indices[p] + + # w is originally set to be 1.0, meaning that if no sample weights + # are given, the default weight of each sample is 1.0. + if sample_weight is not None: + w = sample_weight[i] + + # Count weighted class frequency for each target + for k in range(self.n_outputs): + c = self.y[i, k] + self.sum_total[k, c] += w + + self.weighted_n_node_samples += w + + # Reset to pos=start + self.reset() + return 0 + + cdef void init_sum_missing(self): + """Init sum_missing to hold sums for missing values.""" + self.sum_missing = np.zeros((self.n_outputs, self.max_n_classes), dtype=np.float64) + + cdef void init_missing(self, SIZE_t n_missing) noexcept nogil: + """Initialize sum_missing if there are missing values. + + This method assumes that caller placed the missing samples in + self.sample_indices[-n_missing:] + """ + cdef SIZE_t i, p, k, c + cdef DOUBLE_t w = 1.0 + + self.n_missing = n_missing + if n_missing == 0: + return + + memset(&self.sum_missing[0, 0], 0, self.max_n_classes * self.n_outputs * sizeof(double)) + + self.weighted_n_missing = 0.0 + + # The missing samples are assumed to be in self.sample_indices[-n_missing:] + for p in range(self.end - n_missing, self.end): + i = self.sample_indices[p] + if self.sample_weight is not None: + w = self.sample_weight[i] + + for k in range(self.n_outputs): + c = self.y[i, k] + self.sum_missing[k, c] += w + + self.weighted_n_missing += w + + cdef int reset(self) except -1 nogil: + """Reset the criterion at pos=start. + + Returns -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + """ + self.pos = self.start + _move_sums_classification( + self, + self.sum_left, + self.sum_right, + &self.weighted_n_left, + &self.weighted_n_right, + self.missing_go_to_left, + ) + return 0 + + cdef int reverse_reset(self) except -1 nogil: + """Reset the criterion at pos=end. + + Returns -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + """ + self.pos = self.end + _move_sums_classification( + self, + self.sum_right, + self.sum_left, + &self.weighted_n_right, + &self.weighted_n_left, + not self.missing_go_to_left + ) + return 0 + + cdef int update(self, SIZE_t new_pos) except -1 nogil: + """Updated statistics by moving sample_indices[pos:new_pos] to the left child. + + Returns -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + + Parameters + ---------- + new_pos : SIZE_t + The new ending position for which to move sample_indices from the right + child to the left child. + """ + cdef SIZE_t pos = self.pos + # The missing samples are assumed to be in + # self.sample_indices[-self.n_missing:] that is + # self.sample_indices[end_non_missing:self.end]. + cdef SIZE_t end_non_missing = self.end - self.n_missing + + cdef const SIZE_t[:] sample_indices = self.sample_indices + cdef const DOUBLE_t[:] sample_weight = self.sample_weight + + cdef SIZE_t i + cdef SIZE_t p + cdef SIZE_t k + cdef SIZE_t c + cdef DOUBLE_t w = 1.0 + + # Update statistics up to new_pos + # + # Given that + # sum_left[x] + sum_right[x] = sum_total[x] + # and that sum_total is known, we are going to update + # sum_left from the direction that require the least amount + # of computations, i.e. from pos to new_pos or from end to new_po. + if (new_pos - pos) <= (end_non_missing - new_pos): + for p in range(pos, new_pos): + i = sample_indices[p] + + if sample_weight is not None: + w = sample_weight[i] + + for k in range(self.n_outputs): + self.sum_left[k, self.y[i, k]] += w + + self.weighted_n_left += w + + else: + self.reverse_reset() + + for p in range(end_non_missing - 1, new_pos - 1, -1): + i = sample_indices[p] + + if sample_weight is not None: + w = sample_weight[i] + + for k in range(self.n_outputs): + self.sum_left[k, self.y[i, k]] -= w + + self.weighted_n_left -= w + + # Update right part statistics + self.weighted_n_right = self.weighted_n_node_samples - self.weighted_n_left + for k in range(self.n_outputs): + for c in range(self.n_classes[k]): + self.sum_right[k, c] = self.sum_total[k, c] - self.sum_left[k, c] + + self.pos = new_pos + return 0 + + cdef double node_impurity(self) noexcept nogil: + pass + + cdef void children_impurity(self, double* impurity_left, + double* impurity_right) noexcept nogil: + pass + + cdef void node_value(self, double* dest) noexcept nogil: + """Compute the node value of sample_indices[start:end] and save it into dest. + + Parameters + ---------- + dest : double pointer + The memory address which we will save the node value into. + """ + cdef SIZE_t k + + for k in range(self.n_outputs): + memcpy(dest, &self.sum_total[k, 0], self.n_classes[k] * sizeof(double)) + dest += self.max_n_classes + + +cdef class Entropy(ClassificationCriterion): + r"""Cross Entropy impurity criterion. + + This handles cases where the target is a classification taking values + 0, 1, ... K-2, K-1. If node m represents a region Rm with Nm observations, + then let + + count_k = 1 / Nm \sum_{x_i in Rm} I(yi = k) + + be the proportion of class k observations in node m. + + The cross-entropy is then defined as + + cross-entropy = -\sum_{k=0}^{K-1} count_k log(count_k) + """ + + cdef double node_impurity(self) noexcept nogil: + """Evaluate the impurity of the current node. + + Evaluate the cross-entropy criterion as impurity of the current node, + i.e. the impurity of sample_indices[start:end]. The smaller the impurity the + better. + """ + cdef double entropy = 0.0 + cdef double count_k + cdef SIZE_t k + cdef SIZE_t c + + for k in range(self.n_outputs): + for c in range(self.n_classes[k]): + count_k = self.sum_total[k, c] + if count_k > 0.0: + count_k /= self.weighted_n_node_samples + entropy -= count_k * log(count_k) + + return entropy / self.n_outputs + + cdef void children_impurity(self, double* impurity_left, + double* impurity_right) noexcept nogil: + """Evaluate the impurity in children nodes. + + i.e. the impurity of the left child (sample_indices[start:pos]) and the + impurity the right child (sample_indices[pos:end]). + + Parameters + ---------- + impurity_left : double pointer + The memory address to save the impurity of the left node + impurity_right : double pointer + The memory address to save the impurity of the right node + """ + cdef double entropy_left = 0.0 + cdef double entropy_right = 0.0 + cdef double count_k + cdef SIZE_t k + cdef SIZE_t c + + for k in range(self.n_outputs): + for c in range(self.n_classes[k]): + count_k = self.sum_left[k, c] + if count_k > 0.0: + count_k /= self.weighted_n_left + entropy_left -= count_k * log(count_k) + + count_k = self.sum_right[k, c] + if count_k > 0.0: + count_k /= self.weighted_n_right + entropy_right -= count_k * log(count_k) + + impurity_left[0] = entropy_left / self.n_outputs + impurity_right[0] = entropy_right / self.n_outputs + + +cdef class Gini(ClassificationCriterion): + r"""Gini Index impurity criterion. + + This handles cases where the target is a classification taking values + 0, 1, ... K-2, K-1. If node m represents a region Rm with Nm observations, + then let + + count_k = 1/ Nm \sum_{x_i in Rm} I(yi = k) + + be the proportion of class k observations in node m. + + The Gini Index is then defined as: + + index = \sum_{k=0}^{K-1} count_k (1 - count_k) + = 1 - \sum_{k=0}^{K-1} count_k ** 2 + """ + + cdef double node_impurity(self) noexcept nogil: + """Evaluate the impurity of the current node. + + Evaluate the Gini criterion as impurity of the current node, + i.e. the impurity of sample_indices[start:end]. The smaller the impurity the + better. + """ + cdef double gini = 0.0 + cdef double sq_count + cdef double count_k + cdef SIZE_t k + cdef SIZE_t c + + for k in range(self.n_outputs): + sq_count = 0.0 + + for c in range(self.n_classes[k]): + count_k = self.sum_total[k, c] + sq_count += count_k * count_k + + gini += 1.0 - sq_count / (self.weighted_n_node_samples * + self.weighted_n_node_samples) + + return gini / self.n_outputs + + cdef void children_impurity(self, double* impurity_left, + double* impurity_right) noexcept nogil: + """Evaluate the impurity in children nodes. + + i.e. the impurity of the left child (sample_indices[start:pos]) and the + impurity the right child (sample_indices[pos:end]) using the Gini index. + + Parameters + ---------- + impurity_left : double pointer + The memory address to save the impurity of the left node to + impurity_right : double pointer + The memory address to save the impurity of the right node to + """ + cdef double gini_left = 0.0 + cdef double gini_right = 0.0 + cdef double sq_count_left + cdef double sq_count_right + cdef double count_k + cdef SIZE_t k + cdef SIZE_t c + + for k in range(self.n_outputs): + sq_count_left = 0.0 + sq_count_right = 0.0 + + for c in range(self.n_classes[k]): + count_k = self.sum_left[k, c] + sq_count_left += count_k * count_k + + count_k = self.sum_right[k, c] + sq_count_right += count_k * count_k + + gini_left += 1.0 - sq_count_left / (self.weighted_n_left * + self.weighted_n_left) + + gini_right += 1.0 - sq_count_right / (self.weighted_n_right * + self.weighted_n_right) + + impurity_left[0] = gini_left / self.n_outputs + impurity_right[0] = gini_right / self.n_outputs + + +cdef inline void _move_sums_regression( + RegressionCriterion criterion, + double[::1] sum_1, + double[::1] sum_2, + double* weighted_n_1, + double* weighted_n_2, + bint put_missing_in_1, +) noexcept nogil: + """Distribute sum_total and sum_missing into sum_1 and sum_2. + + If there are missing values and: + - put_missing_in_1 is True, then missing values to go sum_1. Specifically: + sum_1 = sum_missing + sum_2 = sum_total - sum_missing + + - put_missing_in_1 is False, then missing values go to sum_2. Specifically: + sum_1 = 0 + sum_2 = sum_total + """ + cdef: + SIZE_t i + SIZE_t n_bytes = criterion.n_outputs * sizeof(double) + bint has_missing = criterion.n_missing != 0 + + if has_missing and put_missing_in_1: + memcpy(&sum_1[0], &criterion.sum_missing[0], n_bytes) + for i in range(criterion.n_outputs): + sum_2[i] = criterion.sum_total[i] - criterion.sum_missing[i] + weighted_n_1[0] = criterion.weighted_n_missing + weighted_n_2[0] = criterion.weighted_n_node_samples - criterion.weighted_n_missing + else: + memset(&sum_1[0], 0, n_bytes) + # Assigning sum_2 = sum_total for all outputs. + memcpy(&sum_2[0], &criterion.sum_total[0], n_bytes) + weighted_n_1[0] = 0.0 + weighted_n_2[0] = criterion.weighted_n_node_samples + + +cdef class RegressionCriterion(Criterion): + r"""Abstract regression criterion. + + This handles cases where the target is a continuous value, and is + evaluated by computing the variance of the target values left and right + of the split point. The computation takes linear time with `n_samples` + by using :: + + var = \sum_i^n (y_i - y_bar) ** 2 + = (\sum_i^n y_i ** 2) - n_samples * y_bar ** 2 + """ + + def __cinit__(self, SIZE_t n_outputs, SIZE_t n_samples): + """Initialize parameters for this criterion. + + Parameters + ---------- + n_outputs : SIZE_t + The number of targets to be predicted + + n_samples : SIZE_t + The total number of samples to fit on + """ + # Default values + self.start = 0 + self.pos = 0 + self.end = 0 + + self.n_outputs = n_outputs + self.n_samples = n_samples + self.n_node_samples = 0 + self.weighted_n_node_samples = 0.0 + self.weighted_n_left = 0.0 + self.weighted_n_right = 0.0 + self.weighted_n_missing = 0.0 + + self.sq_sum_total = 0.0 + + self.sum_total = np.zeros(n_outputs, dtype=np.float64) + self.sum_left = np.zeros(n_outputs, dtype=np.float64) + self.sum_right = np.zeros(n_outputs, dtype=np.float64) + + def __reduce__(self): + return (type(self), (self.n_outputs, self.n_samples), self.__getstate__()) + + cdef int init( + self, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight, + double weighted_n_samples, + const SIZE_t[:] sample_indices, + SIZE_t start, + SIZE_t end, + ) except -1 nogil: + """Initialize the criterion. + + This initializes the criterion at node sample_indices[start:end] and children + sample_indices[start:start] and sample_indices[start:end]. + """ + # Initialize fields + self.y = y + self.sample_weight = sample_weight + self.sample_indices = sample_indices + self.start = start + self.end = end + self.n_node_samples = end - start + self.weighted_n_samples = weighted_n_samples + self.weighted_n_node_samples = 0. + + cdef SIZE_t i + cdef SIZE_t p + cdef SIZE_t k + cdef DOUBLE_t y_ik + cdef DOUBLE_t w_y_ik + cdef DOUBLE_t w = 1.0 + self.sq_sum_total = 0.0 + memset(&self.sum_total[0], 0, self.n_outputs * sizeof(double)) + + for p in range(start, end): + i = sample_indices[p] + + if sample_weight is not None: + w = sample_weight[i] + + for k in range(self.n_outputs): + y_ik = self.y[i, k] + w_y_ik = w * y_ik + self.sum_total[k] += w_y_ik + self.sq_sum_total += w_y_ik * y_ik + + self.weighted_n_node_samples += w + + # Reset to pos=start + self.reset() + return 0 + + cdef void init_sum_missing(self): + """Init sum_missing to hold sums for missing values.""" + self.sum_missing = np.zeros(self.n_outputs, dtype=np.float64) + + cdef void init_missing(self, SIZE_t n_missing) noexcept nogil: + """Initialize sum_missing if there are missing values. + + This method assumes that caller placed the missing samples in + self.sample_indices[-n_missing:] + """ + cdef SIZE_t i, p, k + cdef DOUBLE_t y_ik + cdef DOUBLE_t w_y_ik + cdef DOUBLE_t w = 1.0 + + self.n_missing = n_missing + if n_missing == 0: + return + + memset(&self.sum_missing[0], 0, self.n_outputs * sizeof(double)) + + self.weighted_n_missing = 0.0 + + # The missing samples are assumed to be in self.sample_indices[-n_missing:] + for p in range(self.end - n_missing, self.end): + i = self.sample_indices[p] + if self.sample_weight is not None: + w = self.sample_weight[i] + + for k in range(self.n_outputs): + y_ik = self.y[i, k] + w_y_ik = w * y_ik + self.sum_missing[k] += w_y_ik + + self.weighted_n_missing += w + + cdef int reset(self) except -1 nogil: + """Reset the criterion at pos=start.""" + self.pos = self.start + _move_sums_regression( + self, + self.sum_left, + self.sum_right, + &self.weighted_n_left, + &self.weighted_n_right, + self.missing_go_to_left + ) + return 0 + + cdef int reverse_reset(self) except -1 nogil: + """Reset the criterion at pos=end.""" + self.pos = self.end + _move_sums_regression( + self, + self.sum_right, + self.sum_left, + &self.weighted_n_right, + &self.weighted_n_left, + not self.missing_go_to_left + ) + return 0 + + cdef int update(self, SIZE_t new_pos) except -1 nogil: + """Updated statistics by moving sample_indices[pos:new_pos] to the left.""" + cdef const DOUBLE_t[:] sample_weight = self.sample_weight + cdef const SIZE_t[:] sample_indices = self.sample_indices + + cdef SIZE_t pos = self.pos + + # The missing samples are assumed to be in + # self.sample_indices[-self.n_missing:] that is + # self.sample_indices[end_non_missing:self.end]. + cdef SIZE_t end_non_missing = self.end - self.n_missing + cdef SIZE_t i + cdef SIZE_t p + cdef SIZE_t k + cdef DOUBLE_t w = 1.0 + + # Update statistics up to new_pos + # + # Given that + # sum_left[x] + sum_right[x] = sum_total[x] + # and that sum_total is known, we are going to update + # sum_left from the direction that require the least amount + # of computations, i.e. from pos to new_pos or from end to new_pos. + if (new_pos - pos) <= (end_non_missing - new_pos): + for p in range(pos, new_pos): + i = sample_indices[p] + + if sample_weight is not None: + w = sample_weight[i] + + for k in range(self.n_outputs): + self.sum_left[k] += w * self.y[i, k] + + self.weighted_n_left += w + else: + self.reverse_reset() + + for p in range(end_non_missing - 1, new_pos - 1, -1): + i = sample_indices[p] + + if sample_weight is not None: + w = sample_weight[i] + + for k in range(self.n_outputs): + self.sum_left[k] -= w * self.y[i, k] + + self.weighted_n_left -= w + + self.weighted_n_right = (self.weighted_n_node_samples - + self.weighted_n_left) + for k in range(self.n_outputs): + self.sum_right[k] = self.sum_total[k] - self.sum_left[k] + + self.pos = new_pos + return 0 + + cdef double node_impurity(self) noexcept nogil: + pass + + cdef void children_impurity(self, double* impurity_left, + double* impurity_right) noexcept nogil: + pass + + cdef void node_value(self, double* dest) noexcept nogil: + """Compute the node value of sample_indices[start:end] into dest.""" + cdef SIZE_t k + + for k in range(self.n_outputs): + dest[k] = self.sum_total[k] / self.weighted_n_node_samples + + +cdef class MSE(RegressionCriterion): + """Mean squared error impurity criterion. + + MSE = var_left + var_right + """ + + cdef double node_impurity(self) noexcept nogil: + """Evaluate the impurity of the current node. + + Evaluate the MSE criterion as impurity of the current node, + i.e. the impurity of sample_indices[start:end]. The smaller the impurity the + better. + """ + cdef double impurity + cdef SIZE_t k + + impurity = self.sq_sum_total / self.weighted_n_node_samples + for k in range(self.n_outputs): + impurity -= (self.sum_total[k] / self.weighted_n_node_samples)**2.0 + + return impurity / self.n_outputs + + cdef double proxy_impurity_improvement(self) noexcept nogil: + """Compute a proxy of the impurity reduction. + + This method is used to speed up the search for the best split. + It is a proxy quantity such that the split that maximizes this value + also maximizes the impurity improvement. It neglects all constant terms + of the impurity decrease for a given split. + + The absolute impurity improvement is only computed by the + impurity_improvement method once the best split has been found. + + The MSE proxy is derived from + + sum_{i left}(y_i - y_pred_L)^2 + sum_{i right}(y_i - y_pred_R)^2 + = sum(y_i^2) - n_L * mean_{i left}(y_i)^2 - n_R * mean_{i right}(y_i)^2 + + Neglecting constant terms, this gives: + + - 1/n_L * sum_{i left}(y_i)^2 - 1/n_R * sum_{i right}(y_i)^2 + """ + cdef SIZE_t k + cdef double proxy_impurity_left = 0.0 + cdef double proxy_impurity_right = 0.0 + + for k in range(self.n_outputs): + proxy_impurity_left += self.sum_left[k] * self.sum_left[k] + proxy_impurity_right += self.sum_right[k] * self.sum_right[k] + + return (proxy_impurity_left / self.weighted_n_left + + proxy_impurity_right / self.weighted_n_right) + + cdef void children_impurity(self, double* impurity_left, + double* impurity_right) noexcept nogil: + """Evaluate the impurity in children nodes. + + i.e. the impurity of the left child (sample_indices[start:pos]) and the + impurity the right child (sample_indices[pos:end]). + """ + cdef const DOUBLE_t[:] sample_weight = self.sample_weight + cdef const SIZE_t[:] sample_indices = self.sample_indices + cdef SIZE_t pos = self.pos + cdef SIZE_t start = self.start + + cdef DOUBLE_t y_ik + + cdef double sq_sum_left = 0.0 + cdef double sq_sum_right + + cdef SIZE_t i + cdef SIZE_t p + cdef SIZE_t k + cdef DOUBLE_t w = 1.0 + + for p in range(start, pos): + i = sample_indices[p] + + if sample_weight is not None: + w = sample_weight[i] + + for k in range(self.n_outputs): + y_ik = self.y[i, k] + sq_sum_left += w * y_ik * y_ik + + sq_sum_right = self.sq_sum_total - sq_sum_left + + impurity_left[0] = sq_sum_left / self.weighted_n_left + impurity_right[0] = sq_sum_right / self.weighted_n_right + + for k in range(self.n_outputs): + impurity_left[0] -= (self.sum_left[k] / self.weighted_n_left) ** 2.0 + impurity_right[0] -= (self.sum_right[k] / self.weighted_n_right) ** 2.0 + + impurity_left[0] /= self.n_outputs + impurity_right[0] /= self.n_outputs + + +cdef class MAE(RegressionCriterion): + r"""Mean absolute error impurity criterion. + + MAE = (1 / n)*(\sum_i |y_i - f_i|), where y_i is the true + value and f_i is the predicted value.""" + + cdef cnp.ndarray left_child + cdef cnp.ndarray right_child + cdef void** left_child_ptr + cdef void** right_child_ptr + cdef DOUBLE_t[::1] node_medians + + def __cinit__(self, SIZE_t n_outputs, SIZE_t n_samples): + """Initialize parameters for this criterion. + + Parameters + ---------- + n_outputs : SIZE_t + The number of targets to be predicted + + n_samples : SIZE_t + The total number of samples to fit on + """ + # Default values + self.start = 0 + self.pos = 0 + self.end = 0 + + self.n_outputs = n_outputs + self.n_samples = n_samples + self.n_node_samples = 0 + self.weighted_n_node_samples = 0.0 + self.weighted_n_left = 0.0 + self.weighted_n_right = 0.0 + + self.node_medians = np.zeros(n_outputs, dtype=np.float64) + + self.left_child = np.empty(n_outputs, dtype='object') + self.right_child = np.empty(n_outputs, dtype='object') + # initialize WeightedMedianCalculators + for k in range(n_outputs): + self.left_child[k] = WeightedMedianCalculator(n_samples) + self.right_child[k] = WeightedMedianCalculator(n_samples) + + self.left_child_ptr = cnp.PyArray_DATA(self.left_child) + self.right_child_ptr = cnp.PyArray_DATA(self.right_child) + + cdef int init( + self, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight, + double weighted_n_samples, + const SIZE_t[:] sample_indices, + SIZE_t start, + SIZE_t end, + ) except -1 nogil: + """Initialize the criterion. + + This initializes the criterion at node sample_indices[start:end] and children + sample_indices[start:start] and sample_indices[start:end]. + """ + cdef SIZE_t i, p, k + cdef DOUBLE_t w = 1.0 + + # Initialize fields + self.y = y + self.sample_weight = sample_weight + self.sample_indices = sample_indices + self.start = start + self.end = end + self.n_node_samples = end - start + self.weighted_n_samples = weighted_n_samples + self.weighted_n_node_samples = 0. + + cdef void** left_child = self.left_child_ptr + cdef void** right_child = self.right_child_ptr + + for k in range(self.n_outputs): + ( left_child[k]).reset() + ( right_child[k]).reset() + + for p in range(start, end): + i = sample_indices[p] + + if sample_weight is not None: + w = sample_weight[i] + + for k in range(self.n_outputs): + # push method ends up calling safe_realloc, hence `except -1` + # push all values to the right side, + # since pos = start initially anyway + ( right_child[k]).push(self.y[i, k], w) + + self.weighted_n_node_samples += w + # calculate the node medians + for k in range(self.n_outputs): + self.node_medians[k] = ( right_child[k]).get_median() + + # Reset to pos=start + self.reset() + return 0 + + cdef void init_missing(self, SIZE_t n_missing) noexcept nogil: + """Raise error if n_missing != 0.""" + if n_missing == 0: + return + with gil: + raise ValueError("missing values is not supported for MAE.") + + cdef int reset(self) except -1 nogil: + """Reset the criterion at pos=start. + + Returns -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + """ + cdef SIZE_t i, k + cdef DOUBLE_t value + cdef DOUBLE_t weight + + cdef void** left_child = self.left_child_ptr + cdef void** right_child = self.right_child_ptr + + self.weighted_n_left = 0.0 + self.weighted_n_right = self.weighted_n_node_samples + self.pos = self.start + + # reset the WeightedMedianCalculators, left should have no + # elements and right should have all elements. + + for k in range(self.n_outputs): + # if left has no elements, it's already reset + for i in range(( left_child[k]).size()): + # remove everything from left and put it into right + ( left_child[k]).pop(&value, + &weight) + # push method ends up calling safe_realloc, hence `except -1` + ( right_child[k]).push(value, + weight) + return 0 + + cdef int reverse_reset(self) except -1 nogil: + """Reset the criterion at pos=end. + + Returns -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + """ + self.weighted_n_right = 0.0 + self.weighted_n_left = self.weighted_n_node_samples + self.pos = self.end + + cdef DOUBLE_t value + cdef DOUBLE_t weight + cdef void** left_child = self.left_child_ptr + cdef void** right_child = self.right_child_ptr + + # reverse reset the WeightedMedianCalculators, right should have no + # elements and left should have all elements. + for k in range(self.n_outputs): + # if right has no elements, it's already reset + for i in range(( right_child[k]).size()): + # remove everything from right and put it into left + ( right_child[k]).pop(&value, + &weight) + # push method ends up calling safe_realloc, hence `except -1` + ( left_child[k]).push(value, + weight) + return 0 + + cdef int update(self, SIZE_t new_pos) except -1 nogil: + """Updated statistics by moving sample_indices[pos:new_pos] to the left. + + Returns -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + """ + cdef const DOUBLE_t[:] sample_weight = self.sample_weight + cdef const SIZE_t[:] sample_indices = self.sample_indices + + cdef void** left_child = self.left_child_ptr + cdef void** right_child = self.right_child_ptr + + cdef SIZE_t pos = self.pos + cdef SIZE_t end = self.end + cdef SIZE_t i, p, k + cdef DOUBLE_t w = 1.0 + + # Update statistics up to new_pos + # + # We are going to update right_child and left_child + # from the direction that require the least amount of + # computations, i.e. from pos to new_pos or from end to new_pos. + if (new_pos - pos) <= (end - new_pos): + for p in range(pos, new_pos): + i = sample_indices[p] + + if sample_weight is not None: + w = sample_weight[i] + + for k in range(self.n_outputs): + # remove y_ik and its weight w from right and add to left + ( right_child[k]).remove(self.y[i, k], w) + # push method ends up calling safe_realloc, hence except -1 + ( left_child[k]).push(self.y[i, k], w) + + self.weighted_n_left += w + else: + self.reverse_reset() + + for p in range(end - 1, new_pos - 1, -1): + i = sample_indices[p] + + if sample_weight is not None: + w = sample_weight[i] + + for k in range(self.n_outputs): + # remove y_ik and its weight w from left and add to right + ( left_child[k]).remove(self.y[i, k], w) + ( right_child[k]).push(self.y[i, k], w) + + self.weighted_n_left -= w + + self.weighted_n_right = (self.weighted_n_node_samples - + self.weighted_n_left) + self.pos = new_pos + return 0 + + cdef void node_value(self, double* dest) noexcept nogil: + """Computes the node value of sample_indices[start:end] into dest.""" + cdef SIZE_t k + for k in range(self.n_outputs): + dest[k] = self.node_medians[k] + + cdef double node_impurity(self) noexcept nogil: + """Evaluate the impurity of the current node. + + Evaluate the MAE criterion as impurity of the current node, + i.e. the impurity of sample_indices[start:end]. The smaller the impurity the + better. + """ + cdef const DOUBLE_t[:] sample_weight = self.sample_weight + cdef const SIZE_t[:] sample_indices = self.sample_indices + cdef SIZE_t i, p, k + cdef DOUBLE_t w = 1.0 + cdef DOUBLE_t impurity = 0.0 + + for k in range(self.n_outputs): + for p in range(self.start, self.end): + i = sample_indices[p] + + if sample_weight is not None: + w = sample_weight[i] + + impurity += fabs(self.y[i, k] - self.node_medians[k]) * w + + return impurity / (self.weighted_n_node_samples * self.n_outputs) + + cdef void children_impurity(self, double* p_impurity_left, + double* p_impurity_right) noexcept nogil: + """Evaluate the impurity in children nodes. + + i.e. the impurity of the left child (sample_indices[start:pos]) and the + impurity the right child (sample_indices[pos:end]). + """ + cdef const DOUBLE_t[:] sample_weight = self.sample_weight + cdef const SIZE_t[:] sample_indices = self.sample_indices + + cdef SIZE_t start = self.start + cdef SIZE_t pos = self.pos + cdef SIZE_t end = self.end + + cdef SIZE_t i, p, k + cdef DOUBLE_t median + cdef DOUBLE_t w = 1.0 + cdef DOUBLE_t impurity_left = 0.0 + cdef DOUBLE_t impurity_right = 0.0 + + cdef void** left_child = self.left_child_ptr + cdef void** right_child = self.right_child_ptr + + for k in range(self.n_outputs): + median = ( left_child[k]).get_median() + for p in range(start, pos): + i = sample_indices[p] + + if sample_weight is not None: + w = sample_weight[i] + + impurity_left += fabs(self.y[i, k] - median) * w + p_impurity_left[0] = impurity_left / (self.weighted_n_left * + self.n_outputs) + + for k in range(self.n_outputs): + median = ( right_child[k]).get_median() + for p in range(pos, end): + i = sample_indices[p] + + if sample_weight is not None: + w = sample_weight[i] + + impurity_right += fabs(self.y[i, k] - median) * w + p_impurity_right[0] = impurity_right / (self.weighted_n_right * + self.n_outputs) + + +cdef class FriedmanMSE(MSE): + """Mean squared error impurity criterion with improvement score by Friedman. + + Uses the formula (35) in Friedman's original Gradient Boosting paper: + + diff = mean_left - mean_right + improvement = n_left * n_right * diff^2 / (n_left + n_right) + """ + + cdef double proxy_impurity_improvement(self) noexcept nogil: + """Compute a proxy of the impurity reduction. + + This method is used to speed up the search for the best split. + It is a proxy quantity such that the split that maximizes this value + also maximizes the impurity improvement. It neglects all constant terms + of the impurity decrease for a given split. + + The absolute impurity improvement is only computed by the + impurity_improvement method once the best split has been found. + """ + cdef double total_sum_left = 0.0 + cdef double total_sum_right = 0.0 + + cdef SIZE_t k + cdef double diff = 0.0 + + for k in range(self.n_outputs): + total_sum_left += self.sum_left[k] + total_sum_right += self.sum_right[k] + + diff = (self.weighted_n_right * total_sum_left - + self.weighted_n_left * total_sum_right) + + return diff * diff / (self.weighted_n_left * self.weighted_n_right) + + cdef double impurity_improvement(self, double impurity_parent, double + impurity_left, double impurity_right) noexcept nogil: + # Note: none of the arguments are used here + cdef double total_sum_left = 0.0 + cdef double total_sum_right = 0.0 + + cdef SIZE_t k + cdef double diff = 0.0 + + for k in range(self.n_outputs): + total_sum_left += self.sum_left[k] + total_sum_right += self.sum_right[k] + + diff = (self.weighted_n_right * total_sum_left - + self.weighted_n_left * total_sum_right) / self.n_outputs + + return (diff * diff / (self.weighted_n_left * self.weighted_n_right * + self.weighted_n_node_samples)) + + +cdef class Poisson(RegressionCriterion): + """Half Poisson deviance as impurity criterion. + + Poisson deviance = 2/n * sum(y_true * log(y_true/y_pred) + y_pred - y_true) + + Note that the deviance is >= 0, and since we have `y_pred = mean(y_true)` + at the leaves, one always has `sum(y_pred - y_true) = 0`. It remains the + implemented impurity (factor 2 is skipped): + 1/n * sum(y_true * log(y_true/y_pred) + """ + # FIXME in 1.0: + # min_impurity_split with default = 0 forces us to use a non-negative + # impurity like the Poisson deviance. Without this restriction, one could + # throw away the 'constant' term sum(y_true * log(y_true)) and just use + # Poisson loss = - 1/n * sum(y_true * log(y_pred)) + # = - 1/n * sum(y_true * log(mean(y_true)) + # = - mean(y_true) * log(mean(y_true)) + # With this trick (used in proxy_impurity_improvement()), as for MSE, + # children_impurity would only need to go over left xor right split, not + # both. This could be faster. + + cdef double node_impurity(self) noexcept nogil: + """Evaluate the impurity of the current node. + + Evaluate the Poisson criterion as impurity of the current node, + i.e. the impurity of sample_indices[start:end]. The smaller the impurity the + better. + """ + return self.poisson_loss(self.start, self.end, self.sum_total, + self.weighted_n_node_samples) + + cdef double proxy_impurity_improvement(self) noexcept nogil: + """Compute a proxy of the impurity reduction. + + This method is used to speed up the search for the best split. + It is a proxy quantity such that the split that maximizes this value + also maximizes the impurity improvement. It neglects all constant terms + of the impurity decrease for a given split. + + The absolute impurity improvement is only computed by the + impurity_improvement method once the best split has been found. + + The Poisson proxy is derived from: + + sum_{i left }(y_i * log(y_i / y_pred_L)) + + sum_{i right}(y_i * log(y_i / y_pred_R)) + = sum(y_i * log(y_i) - n_L * mean_{i left}(y_i) * log(mean_{i left}(y_i)) + - n_R * mean_{i right}(y_i) * log(mean_{i right}(y_i)) + + Neglecting constant terms, this gives + + - sum{i left }(y_i) * log(mean{i left}(y_i)) + - sum{i right}(y_i) * log(mean{i right}(y_i)) + """ + cdef SIZE_t k + cdef double proxy_impurity_left = 0.0 + cdef double proxy_impurity_right = 0.0 + cdef double y_mean_left = 0. + cdef double y_mean_right = 0. + + for k in range(self.n_outputs): + if (self.sum_left[k] <= EPSILON) or (self.sum_right[k] <= EPSILON): + # Poisson loss does not allow non-positive predictions. We + # therefore forbid splits that have child nodes with + # sum(y_i) <= 0. + # Since sum_right = sum_total - sum_left, it can lead to + # floating point rounding error and will not give zero. Thus, + # we relax the above comparison to sum(y_i) <= EPSILON. + return -INFINITY + else: + y_mean_left = self.sum_left[k] / self.weighted_n_left + y_mean_right = self.sum_right[k] / self.weighted_n_right + proxy_impurity_left -= self.sum_left[k] * log(y_mean_left) + proxy_impurity_right -= self.sum_right[k] * log(y_mean_right) + + return - proxy_impurity_left - proxy_impurity_right + + cdef void children_impurity(self, double* impurity_left, + double* impurity_right) noexcept nogil: + """Evaluate the impurity in children nodes. + + i.e. the impurity of the left child (sample_indices[start:pos]) and the + impurity of the right child (sample_indices[pos:end]) for Poisson. + """ + cdef SIZE_t start = self.start + cdef SIZE_t pos = self.pos + cdef SIZE_t end = self.end + + impurity_left[0] = self.poisson_loss(start, pos, self.sum_left, + self.weighted_n_left) + + impurity_right[0] = self.poisson_loss(pos, end, self.sum_right, + self.weighted_n_right) + + cdef inline DOUBLE_t poisson_loss(self, + SIZE_t start, + SIZE_t end, + const double[::1] y_sum, + DOUBLE_t weight_sum) noexcept nogil: + """Helper function to compute Poisson loss (~deviance) of a given node. + """ + cdef const DOUBLE_t[:, ::1] y = self.y + cdef const DOUBLE_t[:] sample_weight = self.sample_weight + cdef const SIZE_t[:] sample_indices = self.sample_indices + + cdef DOUBLE_t y_mean = 0. + cdef DOUBLE_t poisson_loss = 0. + cdef DOUBLE_t w = 1.0 + cdef SIZE_t i, k, p + cdef SIZE_t n_outputs = self.n_outputs + + for k in range(n_outputs): + if y_sum[k] <= EPSILON: + # y_sum could be computed from the subtraction + # sum_right = sum_total - sum_left leading to a potential + # floating point rounding error. + # Thus, we relax the comparison y_sum <= 0 to + # y_sum <= EPSILON. + return INFINITY + + y_mean = y_sum[k] / weight_sum + + for p in range(start, end): + i = sample_indices[p] + + if sample_weight is not None: + w = sample_weight[i] + + poisson_loss += w * xlogy(y[i, k], y[i, k] / y_mean) + return poisson_loss / (weight_sum * n_outputs) \ No newline at end of file diff --git a/ivy/functional/frontends/sklearn/tree/_splitter.pyx b/ivy/functional/frontends/sklearn/tree/_splitter.pyx new file mode 100644 index 0000000000000..bbe67f15c9bb5 --- /dev/null +++ b/ivy/functional/frontends/sklearn/tree/_splitter.pyx @@ -0,0 +1,1531 @@ +# Authors: Gilles Louppe +# Peter Prettenhofer +# Brian Holt +# Noel Dawe +# Satrajit Gosh +# Lars Buitinck +# Arnaud Joly +# Joel Nothman +# Fares Hedayati +# Jacob Schreiber +# +# License: BSD 3 clause + +from ._criterion cimport Criterion + +from libc.stdlib cimport qsort +from libc.string cimport memcpy +from libc.math cimport isnan +from cython cimport final + +import numpy as np + +from scipy.sparse import isspmatrix_csc + +from ._utils cimport log +from ._utils cimport rand_int +from ._utils cimport rand_uniform +from ._utils cimport RAND_R_MAX + +cdef double INFINITY = np.inf + +# Mitigate precision differences between 32 bit and 64 bit +cdef DTYPE_t FEATURE_THRESHOLD = 1e-7 + +# Constant to switch between algorithm non zero value extract algorithm +# in SparsePartitioner +cdef DTYPE_t EXTRACT_NNZ_SWITCH = 0.1 + +cdef inline void _init_split(SplitRecord* self, SIZE_t start_pos) noexcept nogil: + self.impurity_left = INFINITY + self.impurity_right = INFINITY + self.pos = start_pos + self.feature = 0 + self.threshold = 0. + self.improvement = -INFINITY + self.missing_go_to_left = False + self.n_missing = 0 + +cdef class Splitter: + """Abstract splitter class. + + Splitters are called by tree builders to find the best splits on both + sparse and dense data, one split at a time. + """ + + def __cinit__(self, Criterion criterion, SIZE_t max_features, + SIZE_t min_samples_leaf, double min_weight_leaf, + object random_state): + """ + Parameters + ---------- + criterion : Criterion + The criterion to measure the quality of a split. + + max_features : SIZE_t + The maximal number of randomly selected features which can be + considered for a split. + + min_samples_leaf : SIZE_t + The minimal number of samples each leaf can have, where splits + which would result in having less samples in a leaf are not + considered. + + min_weight_leaf : double + The minimal weight each leaf can have, where the weight is the sum + of the weights of each sample in it. + + random_state : object + The user inputted random state to be used for pseudo-randomness + """ + + self.criterion = criterion + + self.n_samples = 0 + self.n_features = 0 + + self.max_features = max_features + self.min_samples_leaf = min_samples_leaf + self.min_weight_leaf = min_weight_leaf + self.random_state = random_state + + def __getstate__(self): + return {} + + def __setstate__(self, d): + pass + + def __reduce__(self): + return (type(self), (self.criterion, + self.max_features, + self.min_samples_leaf, + self.min_weight_leaf, + self.random_state), self.__getstate__()) + + cdef int init( + self, + object X, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight, + const unsigned char[::1] missing_values_in_feature_mask, + ) except -1: + """Initialize the splitter. + + Take in the input data X, the target Y, and optional sample weights. + + Returns -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + + Parameters + ---------- + X : object + This contains the inputs. Usually it is a 2d numpy array. + + y : ndarray, dtype=DOUBLE_t + This is the vector of targets, or true labels, for the samples represented + as a Cython memoryview. + + sample_weight : ndarray, dtype=DOUBLE_t + The weights of the samples, where higher weighted samples are fit + closer than lower weight samples. If not provided, all samples + are assumed to have uniform weight. This is represented + as a Cython memoryview. + + has_missing : bool + At least one missing values is in X. + """ + + self.rand_r_state = self.random_state.randint(0, RAND_R_MAX) + cdef SIZE_t n_samples = X.shape[0] + + # Create a new array which will be used to store nonzero + # samples from the feature of interest + self.samples = np.empty(n_samples, dtype=np.intp) + cdef SIZE_t[::1] samples = self.samples + + cdef SIZE_t i, j + cdef double weighted_n_samples = 0.0 + j = 0 + + for i in range(n_samples): + # Only work with positively weighted samples + if sample_weight is None or sample_weight[i] != 0.0: + samples[j] = i + j += 1 + + if sample_weight is not None: + weighted_n_samples += sample_weight[i] + else: + weighted_n_samples += 1.0 + + # Number of samples is number of positively weighted samples + self.n_samples = j + self.weighted_n_samples = weighted_n_samples + + cdef SIZE_t n_features = X.shape[1] + self.features = np.arange(n_features, dtype=np.intp) + self.n_features = n_features + + self.feature_values = np.empty(n_samples, dtype=np.float32) + self.constant_features = np.empty(n_features, dtype=np.intp) + + self.y = y + + self.sample_weight = sample_weight + if missing_values_in_feature_mask is not None: + self.criterion.init_sum_missing() + return 0 + + cdef int node_reset(self, SIZE_t start, SIZE_t end, + double* weighted_n_node_samples) except -1 nogil: + """Reset splitter on node samples[start:end]. + + Returns -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + + Parameters + ---------- + start : SIZE_t + The index of the first sample to consider + end : SIZE_t + The index of the last sample to consider + weighted_n_node_samples : ndarray, dtype=double pointer + The total weight of those samples + """ + + self.start = start + self.end = end + + self.criterion.init( + self.y, + self.sample_weight, + self.weighted_n_samples, + self.samples, + start, + end + ) + + weighted_n_node_samples[0] = self.criterion.weighted_n_node_samples + return 0 + + cdef int node_split(self, double impurity, SplitRecord* split, + SIZE_t* n_constant_features) except -1 nogil: + """Find the best split on node samples[start:end]. + + This is a placeholder method. The majority of computation will be done + here. + + It should return -1 upon errors. + """ + + pass + + cdef void node_value(self, double* dest) noexcept nogil: + """Copy the value of node samples[start:end] into dest.""" + + self.criterion.node_value(dest) + + cdef double node_impurity(self) noexcept nogil: + """Return the impurity of the current node.""" + + return self.criterion.node_impurity() + +cdef inline void shift_missing_values_to_left_if_required( + SplitRecord* best, + SIZE_t[::1] samples, + SIZE_t end, +) nogil: + cdef SIZE_t i, p, current_end + # The partitioner partitions the data such that the missing values are in + # samples[-n_missing:] for the criterion to consume. If the missing values + # are going to the right node, then the missing values are already in the + # correct position. If the missing values go left, then we move the missing + # values to samples[best.pos:best.pos+n_missing] and update `best.pos`. + if best.n_missing > 0 and best.missing_go_to_left: + for p in range(best.n_missing): + i = best.pos + p + current_end = end - 1 - p + samples[i], samples[current_end] = samples[current_end], samples[i] + best.pos += best.n_missing + +# Introduce a fused-class to make it possible to share the split implementation +# between the dense and sparse cases in the node_split_best and node_split_random +# functions. The alternative would have been to use inheritance-based polymorphism +# but it would have resulted in a ~10% overall tree fitting performance +# degradation caused by the overhead frequent virtual method lookups. +ctypedef fused Partitioner: + DensePartitioner + SparsePartitioner + +cdef inline int node_split_best( + Splitter splitter, + Partitioner partitioner, + Criterion criterion, + double impurity, + SplitRecord* split, + SIZE_t* n_constant_features, +) except -1 nogil: + """Find the best split on node samples[start:end] + + Returns -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + """ + # Find the best split + cdef SIZE_t start = splitter.start + cdef SIZE_t end = splitter.end + cdef SIZE_t end_non_missing + cdef SIZE_t n_missing = 0 + cdef bint has_missing = 0 + cdef SIZE_t n_searches + cdef SIZE_t n_left, n_right + cdef bint missing_go_to_left + + cdef SIZE_t[::1] samples = splitter.samples + cdef SIZE_t[::1] features = splitter.features + cdef SIZE_t[::1] constant_features = splitter.constant_features + cdef SIZE_t n_features = splitter.n_features + + cdef DTYPE_t[::1] feature_values = splitter.feature_values + cdef SIZE_t max_features = splitter.max_features + cdef SIZE_t min_samples_leaf = splitter.min_samples_leaf + cdef double min_weight_leaf = splitter.min_weight_leaf + cdef UINT32_t* random_state = &splitter.rand_r_state + + cdef SplitRecord best_split, current_split + cdef double current_proxy_improvement = -INFINITY + cdef double best_proxy_improvement = -INFINITY + + cdef SIZE_t f_i = n_features + cdef SIZE_t f_j + cdef SIZE_t p + cdef SIZE_t p_prev + + cdef SIZE_t n_visited_features = 0 + # Number of features discovered to be constant during the split search + cdef SIZE_t n_found_constants = 0 + # Number of features known to be constant and drawn without replacement + cdef SIZE_t n_drawn_constants = 0 + cdef SIZE_t n_known_constants = n_constant_features[0] + # n_total_constants = n_known_constants + n_found_constants + cdef SIZE_t n_total_constants = n_known_constants + + _init_split(&best_split, end) + + partitioner.init_node_split(start, end) + + # Sample up to max_features without replacement using a + # Fisher-Yates-based algorithm (using the local variables `f_i` and + # `f_j` to compute a permutation of the `features` array). + # + # Skip the CPU intensive evaluation of the impurity criterion for + # features that were already detected as constant (hence not suitable + # for good splitting) by ancestor nodes and save the information on + # newly discovered constant features to spare computation on descendant + # nodes. + while (f_i > n_total_constants and # Stop early if remaining features + # are constant + (n_visited_features < max_features or + # At least one drawn features must be non constant + n_visited_features <= n_found_constants + n_drawn_constants)): + + n_visited_features += 1 + + # Loop invariant: elements of features in + # - [:n_drawn_constant[ holds drawn and known constant features; + # - [n_drawn_constant:n_known_constant[ holds known constant + # features that haven't been drawn yet; + # - [n_known_constant:n_total_constant[ holds newly found constant + # features; + # - [n_total_constant:f_i[ holds features that haven't been drawn + # yet and aren't constant apriori. + # - [f_i:n_features[ holds features that have been drawn + # and aren't constant. + + # Draw a feature at random + f_j = rand_int(n_drawn_constants, f_i - n_found_constants, + random_state) + + if f_j < n_known_constants: + # f_j in the interval [n_drawn_constants, n_known_constants[ + features[n_drawn_constants], features[f_j] = features[f_j], features[n_drawn_constants] + + n_drawn_constants += 1 + continue + + # f_j in the interval [n_known_constants, f_i - n_found_constants[ + f_j += n_found_constants + # f_j in the interval [n_total_constants, f_i[ + current_split.feature = features[f_j] + partitioner.sort_samples_and_feature_values(current_split.feature) + n_missing = partitioner.n_missing + end_non_missing = end - n_missing + + if ( + # All values for this feature are missing, or + end_non_missing == start or + # This feature is considered constant (max - min <= FEATURE_THRESHOLD) + feature_values[end_non_missing - 1] <= feature_values[start] + FEATURE_THRESHOLD + ): + # We consider this feature constant in this case. + # Since finding a split among constant feature is not valuable, + # we do not consider this feature for splitting. + features[f_j], features[n_total_constants] = features[n_total_constants], features[f_j] + + n_found_constants += 1 + n_total_constants += 1 + continue + + f_i -= 1 + features[f_i], features[f_j] = features[f_j], features[f_i] + has_missing = n_missing != 0 + if has_missing: + criterion.init_missing(n_missing) + # Evaluate all splits + + # If there are missing values, then we search twice for the most optimal split. + # The first search will have all the missing values going to the right node. + # The second search will have all the missing values going to the left node. + # If there are no missing values, then we search only once for the most + # optimal split. + n_searches = 2 if has_missing else 1 + + for i in range(n_searches): + missing_go_to_left = i == 1 + criterion.missing_go_to_left = missing_go_to_left + criterion.reset() + + p = start + + while p < end_non_missing: + partitioner.next_p(&p_prev, &p) + + if p >= end_non_missing: + continue + + if missing_go_to_left: + n_left = p - start + n_missing + n_right = end_non_missing - p + else: + n_left = p - start + n_right = end_non_missing - p + n_missing + + # Reject if min_samples_leaf is not guaranteed + if n_left < min_samples_leaf or n_right < min_samples_leaf: + continue + + current_split.pos = p + criterion.update(current_split.pos) + + # Reject if min_weight_leaf is not satisfied + if ((criterion.weighted_n_left < min_weight_leaf) or + (criterion.weighted_n_right < min_weight_leaf)): + continue + + current_proxy_improvement = criterion.proxy_impurity_improvement() + + if current_proxy_improvement > best_proxy_improvement: + best_proxy_improvement = current_proxy_improvement + # sum of halves is used to avoid infinite value + current_split.threshold = ( + feature_values[p_prev] / 2.0 + feature_values[p] / 2.0 + ) + + if ( + current_split.threshold == feature_values[p] or + current_split.threshold == INFINITY or + current_split.threshold == -INFINITY + ): + current_split.threshold = feature_values[p_prev] + + current_split.n_missing = n_missing + if n_missing == 0: + current_split.missing_go_to_left = n_left > n_right + else: + current_split.missing_go_to_left = missing_go_to_left + + best_split = current_split # copy + + # Evaluate when there are missing values and all missing values goes + # to the right node and non-missing values goes to the left node. + if has_missing: + n_left, n_right = end - start - n_missing, n_missing + p = end - n_missing + missing_go_to_left = 0 + + if not (n_left < min_samples_leaf or n_right < min_samples_leaf): + criterion.missing_go_to_left = missing_go_to_left + criterion.update(p) + + if not ((criterion.weighted_n_left < min_weight_leaf) or + (criterion.weighted_n_right < min_weight_leaf)): + current_proxy_improvement = criterion.proxy_impurity_improvement() + + if current_proxy_improvement > best_proxy_improvement: + best_proxy_improvement = current_proxy_improvement + current_split.threshold = INFINITY + current_split.missing_go_to_left = missing_go_to_left + current_split.n_missing = n_missing + current_split.pos = p + best_split = current_split + + # Reorganize into samples[start:best_split.pos] + samples[best_split.pos:end] + if best_split.pos < end: + partitioner.partition_samples_final( + best_split.pos, + best_split.threshold, + best_split.feature, + best_split.n_missing + ) + if best_split.n_missing != 0: + criterion.init_missing(best_split.n_missing) + criterion.missing_go_to_left = best_split.missing_go_to_left + + criterion.reset() + criterion.update(best_split.pos) + criterion.children_impurity( + &best_split.impurity_left, &best_split.impurity_right + ) + best_split.improvement = criterion.impurity_improvement( + impurity, + best_split.impurity_left, + best_split.impurity_right + ) + + shift_missing_values_to_left_if_required(&best_split, samples, end) + + # Respect invariant for constant features: the original order of + # element in features[:n_known_constants] must be preserved for sibling + # and child nodes + memcpy(&features[0], &constant_features[0], sizeof(SIZE_t) * n_known_constants) + + # Copy newly found constant features + memcpy(&constant_features[n_known_constants], + &features[n_known_constants], + sizeof(SIZE_t) * n_found_constants) + + # Return values + split[0] = best_split + n_constant_features[0] = n_total_constants + return 0 + + +# Sort n-element arrays pointed to by feature_values and samples, simultaneously, +# by the values in feature_values. Algorithm: Introsort (Musser, SP&E, 1997). +cdef inline void sort(DTYPE_t* feature_values, SIZE_t* samples, SIZE_t n) noexcept nogil: + if n == 0: + return + cdef int maxd = 2 * log(n) + introsort(feature_values, samples, n, maxd) + + +cdef inline void swap(DTYPE_t* feature_values, SIZE_t* samples, + SIZE_t i, SIZE_t j) noexcept nogil: + # Helper for sort + feature_values[i], feature_values[j] = feature_values[j], feature_values[i] + samples[i], samples[j] = samples[j], samples[i] + + +cdef inline DTYPE_t median3(DTYPE_t* feature_values, SIZE_t n) noexcept nogil: + # Median of three pivot selection, after Bentley and McIlroy (1993). + # Engineering a sort function. SP&E. Requires 8/3 comparisons on average. + cdef DTYPE_t a = feature_values[0], b = feature_values[n / 2], c = feature_values[n - 1] + if a < b: + if b < c: + return b + elif a < c: + return c + else: + return a + elif b < c: + if a < c: + return a + else: + return c + else: + return b + + +# Introsort with median of 3 pivot selection and 3-way partition function +# (robust to repeated elements, e.g. lots of zero features). +cdef void introsort(DTYPE_t* feature_values, SIZE_t *samples, + SIZE_t n, int maxd) noexcept nogil: + cdef DTYPE_t pivot + cdef SIZE_t i, l, r + + while n > 1: + if maxd <= 0: # max depth limit exceeded ("gone quadratic") + heapsort(feature_values, samples, n) + return + maxd -= 1 + + pivot = median3(feature_values, n) + + # Three-way partition. + i = l = 0 + r = n + while i < r: + if feature_values[i] < pivot: + swap(feature_values, samples, i, l) + i += 1 + l += 1 + elif feature_values[i] > pivot: + r -= 1 + swap(feature_values, samples, i, r) + else: + i += 1 + + introsort(feature_values, samples, l, maxd) + feature_values += r + samples += r + n -= r + + +cdef inline void sift_down(DTYPE_t* feature_values, SIZE_t* samples, + SIZE_t start, SIZE_t end) noexcept nogil: + # Restore heap order in feature_values[start:end] by moving the max element to start. + cdef SIZE_t child, maxind, root + + root = start + while True: + child = root * 2 + 1 + + # find max of root, left child, right child + maxind = root + if child < end and feature_values[maxind] < feature_values[child]: + maxind = child + if child + 1 < end and feature_values[maxind] < feature_values[child + 1]: + maxind = child + 1 + + if maxind == root: + break + else: + swap(feature_values, samples, root, maxind) + root = maxind + + +cdef void heapsort(DTYPE_t* feature_values, SIZE_t* samples, SIZE_t n) noexcept nogil: + cdef SIZE_t start, end + + # heapify + start = (n - 2) / 2 + end = n + while True: + sift_down(feature_values, samples, start, end) + if start == 0: + break + start -= 1 + + # sort by shrinking the heap, putting the max element immediately after it + end = n - 1 + while end > 0: + swap(feature_values, samples, 0, end) + sift_down(feature_values, samples, 0, end) + end = end - 1 + +cdef inline int node_split_random( + Splitter splitter, + Partitioner partitioner, + Criterion criterion, + double impurity, + SplitRecord* split, + SIZE_t* n_constant_features +) except -1 nogil: + """Find the best random split on node samples[start:end] + + Returns -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + """ + # Draw random splits and pick the best + cdef SIZE_t start = splitter.start + cdef SIZE_t end = splitter.end + + cdef SIZE_t[::1] features = splitter.features + cdef SIZE_t[::1] constant_features = splitter.constant_features + cdef SIZE_t n_features = splitter.n_features + + cdef SIZE_t max_features = splitter.max_features + cdef SIZE_t min_samples_leaf = splitter.min_samples_leaf + cdef double min_weight_leaf = splitter.min_weight_leaf + cdef UINT32_t* random_state = &splitter.rand_r_state + + cdef SplitRecord best_split, current_split + cdef double current_proxy_improvement = - INFINITY + cdef double best_proxy_improvement = - INFINITY + + cdef SIZE_t f_i = n_features + cdef SIZE_t f_j + # Number of features discovered to be constant during the split search + cdef SIZE_t n_found_constants = 0 + # Number of features known to be constant and drawn without replacement + cdef SIZE_t n_drawn_constants = 0 + cdef SIZE_t n_known_constants = n_constant_features[0] + # n_total_constants = n_known_constants + n_found_constants + cdef SIZE_t n_total_constants = n_known_constants + cdef SIZE_t n_visited_features = 0 + cdef DTYPE_t min_feature_value + cdef DTYPE_t max_feature_value + + _init_split(&best_split, end) + + partitioner.init_node_split(start, end) + + # Sample up to max_features without replacement using a + # Fisher-Yates-based algorithm (using the local variables `f_i` and + # `f_j` to compute a permutation of the `features` array). + # + # Skip the CPU intensive evaluation of the impurity criterion for + # features that were already detected as constant (hence not suitable + # for good splitting) by ancestor nodes and save the information on + # newly discovered constant features to spare computation on descendant + # nodes. + while (f_i > n_total_constants and # Stop early if remaining features + # are constant + (n_visited_features < max_features or + # At least one drawn features must be non constant + n_visited_features <= n_found_constants + n_drawn_constants)): + n_visited_features += 1 + + # Loop invariant: elements of features in + # - [:n_drawn_constant[ holds drawn and known constant features; + # - [n_drawn_constant:n_known_constant[ holds known constant + # features that haven't been drawn yet; + # - [n_known_constant:n_total_constant[ holds newly found constant + # features; + # - [n_total_constant:f_i[ holds features that haven't been drawn + # yet and aren't constant apriori. + # - [f_i:n_features[ holds features that have been drawn + # and aren't constant. + + # Draw a feature at random + f_j = rand_int(n_drawn_constants, f_i - n_found_constants, + random_state) + + if f_j < n_known_constants: + # f_j in the interval [n_drawn_constants, n_known_constants[ + features[n_drawn_constants], features[f_j] = features[f_j], features[n_drawn_constants] + n_drawn_constants += 1 + continue + + # f_j in the interval [n_known_constants, f_i - n_found_constants[ + f_j += n_found_constants + # f_j in the interval [n_total_constants, f_i[ + + current_split.feature = features[f_j] + + # Find min, max + partitioner.find_min_max( + current_split.feature, &min_feature_value, &max_feature_value + ) + + if max_feature_value <= min_feature_value + FEATURE_THRESHOLD: + features[f_j], features[n_total_constants] = features[n_total_constants], current_split.feature + + n_found_constants += 1 + n_total_constants += 1 + continue + + f_i -= 1 + features[f_i], features[f_j] = features[f_j], features[f_i] + + # Draw a random threshold + current_split.threshold = rand_uniform( + min_feature_value, + max_feature_value, + random_state, + ) + + if current_split.threshold == max_feature_value: + current_split.threshold = min_feature_value + + # Partition + current_split.pos = partitioner.partition_samples(current_split.threshold) + + # Reject if min_samples_leaf is not guaranteed + if (((current_split.pos - start) < min_samples_leaf) or + ((end - current_split.pos) < min_samples_leaf)): + continue + + # Evaluate split + # At this point, the criterion has a view into the samples that was partitioned + # by the partitioner. The criterion will use the partition to evaluating the split. + criterion.reset() + criterion.update(current_split.pos) + + # Reject if min_weight_leaf is not satisfied + if ((criterion.weighted_n_left < min_weight_leaf) or + (criterion.weighted_n_right < min_weight_leaf)): + continue + + current_proxy_improvement = criterion.proxy_impurity_improvement() + + if current_proxy_improvement > best_proxy_improvement: + best_proxy_improvement = current_proxy_improvement + best_split = current_split # copy + + # Reorganize into samples[start:best.pos] + samples[best.pos:end] + if best_split.pos < end: + if current_split.feature != best_split.feature: + # TODO: Pass in best.n_missing when random splitter supports missing values. + partitioner.partition_samples_final( + best_split.pos, best_split.threshold, best_split.feature, 0 + ) + + criterion.reset() + criterion.update(best_split.pos) + criterion.children_impurity( + &best_split.impurity_left, &best_split.impurity_right + ) + best_split.improvement = criterion.impurity_improvement( + impurity, best_split.impurity_left, best_split.impurity_right + ) + + # Respect invariant for constant features: the original order of + # element in features[:n_known_constants] must be preserved for sibling + # and child nodes + memcpy(&features[0], &constant_features[0], sizeof(SIZE_t) * n_known_constants) + + # Copy newly found constant features + memcpy(&constant_features[n_known_constants], + &features[n_known_constants], + sizeof(SIZE_t) * n_found_constants) + + # Return values + split[0] = best_split + n_constant_features[0] = n_total_constants + return 0 + + +@final +cdef class DensePartitioner: + """Partitioner specialized for dense data. + + Note that this partitioner is agnostic to the splitting strategy (best vs. random). + """ + cdef: + const DTYPE_t[:, :] X + cdef SIZE_t[::1] samples + cdef DTYPE_t[::1] feature_values + cdef SIZE_t start + cdef SIZE_t end + cdef SIZE_t n_missing + cdef const unsigned char[::1] missing_values_in_feature_mask + + def __init__( + self, + const DTYPE_t[:, :] X, + SIZE_t[::1] samples, + DTYPE_t[::1] feature_values, + const unsigned char[::1] missing_values_in_feature_mask, + ): + self.X = X + self.samples = samples + self.feature_values = feature_values + self.missing_values_in_feature_mask = missing_values_in_feature_mask + + cdef inline void init_node_split(self, SIZE_t start, SIZE_t end) noexcept nogil: + """Initialize splitter at the beginning of node_split.""" + self.start = start + self.end = end + self.n_missing = 0 + + cdef inline void sort_samples_and_feature_values( + self, SIZE_t current_feature + ) noexcept nogil: + """Simultaneously sort based on the feature_values. + + Missing values are stored at the end of feature_values. + The number of missing values observed in feature_values is stored + in self.n_missing. + """ + cdef: + SIZE_t i, current_end + DTYPE_t[::1] feature_values = self.feature_values + const DTYPE_t[:, :] X = self.X + SIZE_t[::1] samples = self.samples + SIZE_t n_missing = 0 + const unsigned char[::1] missing_values_in_feature_mask = self.missing_values_in_feature_mask + + # Sort samples along that feature; by + # copying the values into an array and + # sorting the array in a manner which utilizes the cache more + # effectively. + if missing_values_in_feature_mask is not None and missing_values_in_feature_mask[current_feature]: + i, current_end = self.start, self.end - 1 + # Missing values are placed at the end and do not participate in the sorting. + while i <= current_end: + # Finds the right-most value that is not missing so that + # it can be swapped with missing values at its left. + if isnan(X[samples[current_end], current_feature]): + n_missing += 1 + current_end -= 1 + continue + + # X[samples[current_end], current_feature] is a non-missing value + if isnan(X[samples[i], current_feature]): + samples[i], samples[current_end] = samples[current_end], samples[i] + n_missing += 1 + current_end -= 1 + + feature_values[i] = X[samples[i], current_feature] + i += 1 + else: + # When there are no missing values, we only need to copy the data into + # feature_values + for i in range(self.start, self.end): + feature_values[i] = X[samples[i], current_feature] + + sort(&feature_values[self.start], &samples[self.start], self.end - self.start - n_missing) + self.n_missing = n_missing + + cdef inline void find_min_max( + self, + SIZE_t current_feature, + DTYPE_t* min_feature_value_out, + DTYPE_t* max_feature_value_out, + ) noexcept nogil: + """Find the minimum and maximum value for current_feature.""" + cdef: + SIZE_t p + DTYPE_t current_feature_value + const DTYPE_t[:, :] X = self.X + SIZE_t[::1] samples = self.samples + DTYPE_t min_feature_value = X[samples[self.start], current_feature] + DTYPE_t max_feature_value = min_feature_value + DTYPE_t[::1] feature_values = self.feature_values + + feature_values[self.start] = min_feature_value + + for p in range(self.start + 1, self.end): + current_feature_value = X[samples[p], current_feature] + feature_values[p] = current_feature_value + + if current_feature_value < min_feature_value: + min_feature_value = current_feature_value + elif current_feature_value > max_feature_value: + max_feature_value = current_feature_value + + min_feature_value_out[0] = min_feature_value + max_feature_value_out[0] = max_feature_value + + cdef inline void next_p(self, SIZE_t* p_prev, SIZE_t* p) noexcept nogil: + """Compute the next p_prev and p for iteratiing over feature values. + + The missing values are not included when iterating through the feature values. + """ + cdef: + DTYPE_t[::1] feature_values = self.feature_values + SIZE_t end_non_missing = self.end - self.n_missing + + while ( + p[0] + 1 < end_non_missing and + feature_values[p[0] + 1] <= feature_values[p[0]] + FEATURE_THRESHOLD + ): + p[0] += 1 + + p_prev[0] = p[0] + + # By adding 1, we have + # (feature_values[p] >= end) or (feature_values[p] > feature_values[p - 1]) + p[0] += 1 + + cdef inline SIZE_t partition_samples(self, double current_threshold) noexcept nogil: + """Partition samples for feature_values at the current_threshold.""" + cdef: + SIZE_t p = self.start + SIZE_t partition_end = self.end + SIZE_t[::1] samples = self.samples + DTYPE_t[::1] feature_values = self.feature_values + + while p < partition_end: + if feature_values[p] <= current_threshold: + p += 1 + else: + partition_end -= 1 + + feature_values[p], feature_values[partition_end] = ( + feature_values[partition_end], feature_values[p] + ) + samples[p], samples[partition_end] = samples[partition_end], samples[p] + + return partition_end + + cdef inline void partition_samples_final( + self, + SIZE_t best_pos, + double best_threshold, + SIZE_t best_feature, + SIZE_t best_n_missing, + ) noexcept nogil: + """Partition samples for X at the best_threshold and best_feature. + + If missing values are present, this method partitions `samples` + so that the `best_n_missing` missing values' indices are in the + right-most end of `samples`, that is `samples[end_non_missing:end]`. + """ + cdef: + # Local invariance: start <= p <= partition_end <= end + SIZE_t start = self.start + SIZE_t p = start + SIZE_t end = self.end - 1 + SIZE_t partition_end = end - best_n_missing + SIZE_t[::1] samples = self.samples + const DTYPE_t[:, :] X = self.X + DTYPE_t current_value + + if best_n_missing != 0: + # Move samples with missing values to the end while partitioning the + # non-missing samples + while p < partition_end: + # Keep samples with missing values at the end + if isnan(X[samples[end], best_feature]): + end -= 1 + continue + + # Swap sample with missing values with the sample at the end + current_value = X[samples[p], best_feature] + if isnan(current_value): + samples[p], samples[end] = samples[end], samples[p] + end -= 1 + + # The swapped sample at the end is always a non-missing value, so + # we can continue the algorithm without checking for missingness. + current_value = X[samples[p], best_feature] + + # Partition the non-missing samples + if current_value <= best_threshold: + p += 1 + else: + samples[p], samples[partition_end] = samples[partition_end], samples[p] + partition_end -= 1 + else: + # Partitioning routine when there are no missing values + while p < partition_end: + if X[samples[p], best_feature] <= best_threshold: + p += 1 + else: + samples[p], samples[partition_end] = samples[partition_end], samples[p] + partition_end -= 1 + + +@final +cdef class SparsePartitioner: + """Partitioner specialized for sparse CSC data. + + Note that this partitioner is agnostic to the splitting strategy (best vs. random). + """ + cdef SIZE_t[::1] samples + cdef DTYPE_t[::1] feature_values + cdef SIZE_t start + cdef SIZE_t end + cdef SIZE_t n_missing + cdef const unsigned char[::1] missing_values_in_feature_mask + + cdef const DTYPE_t[::1] X_data + cdef const INT32_t[::1] X_indices + cdef const INT32_t[::1] X_indptr + + cdef SIZE_t n_total_samples + + cdef SIZE_t[::1] index_to_samples + cdef SIZE_t[::1] sorted_samples + + cdef SIZE_t start_positive + cdef SIZE_t end_negative + cdef bint is_samples_sorted + + def __init__( + self, + object X, + SIZE_t[::1] samples, + SIZE_t n_samples, + DTYPE_t[::1] feature_values, + const unsigned char[::1] missing_values_in_feature_mask, + ): + if not isspmatrix_csc(X): + raise ValueError("X should be in csc format") + + self.samples = samples + self.feature_values = feature_values + + # Initialize X + cdef SIZE_t n_total_samples = X.shape[0] + + self.X_data = X.data + self.X_indices = X.indices + self.X_indptr = X.indptr + self.n_total_samples = n_total_samples + + # Initialize auxiliary array used to perform split + self.index_to_samples = np.full(n_total_samples, fill_value=-1, dtype=np.intp) + self.sorted_samples = np.empty(n_samples, dtype=np.intp) + + cdef SIZE_t p + for p in range(n_samples): + self.index_to_samples[samples[p]] = p + + self.missing_values_in_feature_mask = missing_values_in_feature_mask + + cdef inline void init_node_split(self, SIZE_t start, SIZE_t end) noexcept nogil: + """Initialize splitter at the beginning of node_split.""" + self.start = start + self.end = end + self.is_samples_sorted = 0 + self.n_missing = 0 + + cdef inline void sort_samples_and_feature_values( + self, SIZE_t current_feature + ) noexcept nogil: + """Simultaneously sort based on the feature_values.""" + cdef: + DTYPE_t[::1] feature_values = self.feature_values + SIZE_t[::1] index_to_samples = self.index_to_samples + SIZE_t[::1] samples = self.samples + + self.extract_nnz(current_feature) + # Sort the positive and negative parts of `feature_values` + sort(&feature_values[self.start], &samples[self.start], self.end_negative - self.start) + if self.start_positive < self.end: + sort( + &feature_values[self.start_positive], + &samples[self.start_positive], + self.end - self.start_positive + ) + + # Update index_to_samples to take into account the sort + for p in range(self.start, self.end_negative): + index_to_samples[samples[p]] = p + for p in range(self.start_positive, self.end): + index_to_samples[samples[p]] = p + + # Add one or two zeros in feature_values, if there is any + if self.end_negative < self.start_positive: + self.start_positive -= 1 + feature_values[self.start_positive] = 0. + + if self.end_negative != self.start_positive: + feature_values[self.end_negative] = 0. + self.end_negative += 1 + + # XXX: When sparse supports missing values, this should be set to the + # number of missing values for current_feature + self.n_missing = 0 + + cdef inline void find_min_max( + self, + SIZE_t current_feature, + DTYPE_t* min_feature_value_out, + DTYPE_t* max_feature_value_out, + ) noexcept nogil: + """Find the minimum and maximum value for current_feature.""" + cdef: + SIZE_t p + DTYPE_t current_feature_value, min_feature_value, max_feature_value + DTYPE_t[::1] feature_values = self.feature_values + + self.extract_nnz(current_feature) + + if self.end_negative != self.start_positive: + # There is a zero + min_feature_value = 0 + max_feature_value = 0 + else: + min_feature_value = feature_values[self.start] + max_feature_value = min_feature_value + + # Find min, max in feature_values[start:end_negative] + for p in range(self.start, self.end_negative): + current_feature_value = feature_values[p] + + if current_feature_value < min_feature_value: + min_feature_value = current_feature_value + elif current_feature_value > max_feature_value: + max_feature_value = current_feature_value + + # Update min, max given feature_values[start_positive:end] + for p in range(self.start_positive, self.end): + current_feature_value = feature_values[p] + + if current_feature_value < min_feature_value: + min_feature_value = current_feature_value + elif current_feature_value > max_feature_value: + max_feature_value = current_feature_value + + min_feature_value_out[0] = min_feature_value + max_feature_value_out[0] = max_feature_value + + cdef inline void next_p(self, SIZE_t* p_prev, SIZE_t* p) noexcept nogil: + """Compute the next p_prev and p for iteratiing over feature values.""" + cdef: + SIZE_t p_next + DTYPE_t[::1] feature_values = self.feature_values + + if p[0] + 1 != self.end_negative: + p_next = p[0] + 1 + else: + p_next = self.start_positive + + while (p_next < self.end and + feature_values[p_next] <= feature_values[p[0]] + FEATURE_THRESHOLD): + p[0] = p_next + if p[0] + 1 != self.end_negative: + p_next = p[0] + 1 + else: + p_next = self.start_positive + + p_prev[0] = p[0] + p[0] = p_next + + cdef inline SIZE_t partition_samples(self, double current_threshold) noexcept nogil: + """Partition samples for feature_values at the current_threshold.""" + return self._partition(current_threshold, self.start_positive) + + cdef inline void partition_samples_final( + self, + SIZE_t best_pos, + double best_threshold, + SIZE_t best_feature, + SIZE_t n_missing, + ) noexcept nogil: + """Partition samples for X at the best_threshold and best_feature.""" + self.extract_nnz(best_feature) + self._partition(best_threshold, best_pos) + + cdef inline SIZE_t _partition(self, double threshold, SIZE_t zero_pos) noexcept nogil: + """Partition samples[start:end] based on threshold.""" + cdef: + SIZE_t p, partition_end + SIZE_t[::1] index_to_samples = self.index_to_samples + DTYPE_t[::1] feature_values = self.feature_values + SIZE_t[::1] samples = self.samples + + if threshold < 0.: + p = self.start + partition_end = self.end_negative + elif threshold > 0.: + p = self.start_positive + partition_end = self.end + else: + # Data are already split + return zero_pos + + while p < partition_end: + if feature_values[p] <= threshold: + p += 1 + + else: + partition_end -= 1 + + feature_values[p], feature_values[partition_end] = ( + feature_values[partition_end], feature_values[p] + ) + sparse_swap(index_to_samples, samples, p, partition_end) + + return partition_end + + cdef inline void extract_nnz(self, SIZE_t feature) noexcept nogil: + """Extract and partition values for a given feature. + + The extracted values are partitioned between negative values + feature_values[start:end_negative[0]] and positive values + feature_values[start_positive[0]:end]. + The samples and index_to_samples are modified according to this + partition. + + The extraction corresponds to the intersection between the arrays + X_indices[indptr_start:indptr_end] and samples[start:end]. + This is done efficiently using either an index_to_samples based approach + or binary search based approach. + + Parameters + ---------- + feature : SIZE_t, + Index of the feature we want to extract non zero value. + """ + cdef SIZE_t[::1] samples = self.samples + cdef DTYPE_t[::1] feature_values = self.feature_values + cdef SIZE_t indptr_start = self.X_indptr[feature], + cdef SIZE_t indptr_end = self.X_indptr[feature + 1] + cdef SIZE_t n_indices = (indptr_end - indptr_start) + cdef SIZE_t n_samples = self.end - self.start + cdef SIZE_t[::1] index_to_samples = self.index_to_samples + cdef SIZE_t[::1] sorted_samples = self.sorted_samples + cdef const INT32_t[::1] X_indices = self.X_indices + cdef const DTYPE_t[::1] X_data = self.X_data + + # Use binary search if n_samples * log(n_indices) < + # n_indices and index_to_samples approach otherwise. + # O(n_samples * log(n_indices)) is the running time of binary + # search and O(n_indices) is the running time of index_to_samples + # approach. + if ((1 - self.is_samples_sorted) * n_samples * log(n_samples) + + n_samples * log(n_indices) < EXTRACT_NNZ_SWITCH * n_indices): + extract_nnz_binary_search(X_indices, X_data, + indptr_start, indptr_end, + samples, self.start, self.end, + index_to_samples, + feature_values, + &self.end_negative, &self.start_positive, + sorted_samples, &self.is_samples_sorted) + + # Using an index to samples technique to extract non zero values + # index_to_samples is a mapping from X_indices to samples + else: + extract_nnz_index_to_samples(X_indices, X_data, + indptr_start, indptr_end, + samples, self.start, self.end, + index_to_samples, + feature_values, + &self.end_negative, &self.start_positive) + + +cdef int compare_SIZE_t(const void* a, const void* b) noexcept nogil: + """Comparison function for sort.""" + return ((a)[0] - (b)[0]) + + +cdef inline void binary_search(const INT32_t[::1] sorted_array, + INT32_t start, INT32_t end, + SIZE_t value, SIZE_t* index, + INT32_t* new_start) noexcept nogil: + """Return the index of value in the sorted array. + + If not found, return -1. new_start is the last pivot + 1 + """ + cdef INT32_t pivot + index[0] = -1 + while start < end: + pivot = start + (end - start) / 2 + + if sorted_array[pivot] == value: + index[0] = pivot + start = pivot + 1 + break + + if sorted_array[pivot] < value: + start = pivot + 1 + else: + end = pivot + new_start[0] = start + + +cdef inline void extract_nnz_index_to_samples(const INT32_t[::1] X_indices, + const DTYPE_t[::1] X_data, + INT32_t indptr_start, + INT32_t indptr_end, + SIZE_t[::1] samples, + SIZE_t start, + SIZE_t end, + SIZE_t[::1] index_to_samples, + DTYPE_t[::1] feature_values, + SIZE_t* end_negative, + SIZE_t* start_positive) noexcept nogil: + """Extract and partition values for a feature using index_to_samples. + + Complexity is O(indptr_end - indptr_start). + """ + cdef INT32_t k + cdef SIZE_t index + cdef SIZE_t end_negative_ = start + cdef SIZE_t start_positive_ = end + + for k in range(indptr_start, indptr_end): + if start <= index_to_samples[X_indices[k]] < end: + if X_data[k] > 0: + start_positive_ -= 1 + feature_values[start_positive_] = X_data[k] + index = index_to_samples[X_indices[k]] + sparse_swap(index_to_samples, samples, index, start_positive_) + + elif X_data[k] < 0: + feature_values[end_negative_] = X_data[k] + index = index_to_samples[X_indices[k]] + sparse_swap(index_to_samples, samples, index, end_negative_) + end_negative_ += 1 + + # Returned values + end_negative[0] = end_negative_ + start_positive[0] = start_positive_ + + +cdef inline void extract_nnz_binary_search(const INT32_t[::1] X_indices, + const DTYPE_t[::1] X_data, + INT32_t indptr_start, + INT32_t indptr_end, + SIZE_t[::1] samples, + SIZE_t start, + SIZE_t end, + SIZE_t[::1] index_to_samples, + DTYPE_t[::1] feature_values, + SIZE_t* end_negative, + SIZE_t* start_positive, + SIZE_t[::1] sorted_samples, + bint* is_samples_sorted) noexcept nogil: + """Extract and partition values for a given feature using binary search. + + If n_samples = end - start and n_indices = indptr_end - indptr_start, + the complexity is + + O((1 - is_samples_sorted[0]) * n_samples * log(n_samples) + + n_samples * log(n_indices)). + """ + cdef SIZE_t n_samples + + if not is_samples_sorted[0]: + n_samples = end - start + memcpy(&sorted_samples[start], &samples[start], + n_samples * sizeof(SIZE_t)) + qsort(&sorted_samples[start], n_samples, sizeof(SIZE_t), + compare_SIZE_t) + is_samples_sorted[0] = 1 + + while (indptr_start < indptr_end and + sorted_samples[start] > X_indices[indptr_start]): + indptr_start += 1 + + while (indptr_start < indptr_end and + sorted_samples[end - 1] < X_indices[indptr_end - 1]): + indptr_end -= 1 + + cdef SIZE_t p = start + cdef SIZE_t index + cdef SIZE_t k + cdef SIZE_t end_negative_ = start + cdef SIZE_t start_positive_ = end + + while (p < end and indptr_start < indptr_end): + # Find index of sorted_samples[p] in X_indices + binary_search(X_indices, indptr_start, indptr_end, + sorted_samples[p], &k, &indptr_start) + + if k != -1: + # If k != -1, we have found a non zero value + + if X_data[k] > 0: + start_positive_ -= 1 + feature_values[start_positive_] = X_data[k] + index = index_to_samples[X_indices[k]] + sparse_swap(index_to_samples, samples, index, start_positive_) + + elif X_data[k] < 0: + feature_values[end_negative_] = X_data[k] + index = index_to_samples[X_indices[k]] + sparse_swap(index_to_samples, samples, index, end_negative_) + end_negative_ += 1 + p += 1 + + # Returned values + end_negative[0] = end_negative_ + start_positive[0] = start_positive_ + + +cdef inline void sparse_swap(SIZE_t[::1] index_to_samples, SIZE_t[::1] samples, + SIZE_t pos_1, SIZE_t pos_2) noexcept nogil: + """Swap sample pos_1 and pos_2 preserving sparse invariant.""" + samples[pos_1], samples[pos_2] = samples[pos_2], samples[pos_1] + index_to_samples[samples[pos_1]] = pos_1 + index_to_samples[samples[pos_2]] = pos_2 + + +cdef class BestSplitter(Splitter): + """Splitter for finding the best split on dense data.""" + cdef DensePartitioner partitioner + cdef int init( + self, + object X, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight, + const unsigned char[::1] missing_values_in_feature_mask, + ) except -1: + Splitter.init(self, X, y, sample_weight, missing_values_in_feature_mask) + self.partitioner = DensePartitioner( + X, self.samples, self.feature_values, missing_values_in_feature_mask + ) + + cdef int node_split(self, double impurity, SplitRecord* split, + SIZE_t* n_constant_features) except -1 nogil: + return node_split_best( + self, + self.partitioner, + self.criterion, + impurity, + split, + n_constant_features, + ) + +cdef class BestSparseSplitter(Splitter): + """Splitter for finding the best split, using the sparse data.""" + cdef SparsePartitioner partitioner + cdef int init( + self, + object X, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight, + const unsigned char[::1] missing_values_in_feature_mask, + ) except -1: + Splitter.init(self, X, y, sample_weight, missing_values_in_feature_mask) + self.partitioner = SparsePartitioner( + X, self.samples, self.n_samples, self.feature_values, missing_values_in_feature_mask + ) + + cdef int node_split(self, double impurity, SplitRecord* split, + SIZE_t* n_constant_features) except -1 nogil: + return node_split_best( + self, + self.partitioner, + self.criterion, + impurity, + split, + n_constant_features, + ) + +cdef class RandomSplitter(Splitter): + """Splitter for finding the best random split on dense data.""" + cdef DensePartitioner partitioner + cdef int init( + self, + object X, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight, + const unsigned char[::1] missing_values_in_feature_mask, + ) except -1: + Splitter.init(self, X, y, sample_weight, missing_values_in_feature_mask) + self.partitioner = DensePartitioner( + X, self.samples, self.feature_values, missing_values_in_feature_mask + ) + + cdef int node_split(self, double impurity, SplitRecord* split, + SIZE_t* n_constant_features) except -1 nogil: + return node_split_random( + self, + self.partitioner, + self.criterion, + impurity, + split, + n_constant_features, + ) + +cdef class RandomSparseSplitter(Splitter): + """Splitter for finding the best random split, using the sparse data.""" + cdef SparsePartitioner partitioner + cdef int init( + self, + object X, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight, + const unsigned char[::1] missing_values_in_feature_mask, + ) except -1: + Splitter.init(self, X, y, sample_weight, missing_values_in_feature_mask) + self.partitioner = SparsePartitioner( + X, self.samples, self.n_samples, self.feature_values, missing_values_in_feature_mask + ) + + cdef int node_split(self, double impurity, SplitRecord* split, + SIZE_t* n_constant_features) except -1 nogil: + return node_split_random( + self, + self.partitioner, + self.criterion, + impurity, + split, + n_constant_features, + ) \ No newline at end of file diff --git a/ivy/functional/frontends/sklearn/tree/_tree.py b/ivy/functional/frontends/sklearn/tree/_tree.py new file mode 100644 index 0000000000000..4d532ebb66125 --- /dev/null +++ b/ivy/functional/frontends/sklearn/tree/_tree.py @@ -0,0 +1,822 @@ +import ivy + + + + + + + + + + + + + + + + + + + + + + + + + + + +import numpy as np + + + +from scipy.sparse import issparse +from scipy.sparse import csr_matrix +from scipy.sparse import isspmatrix_csr + + + + + + + + + + + + + + + + + + + + +# ============================================================================= +# Types and constants +# ============================================================================= + +from numpy import float32 as DTYPE +from numpy import float64 as DOUBLE + + +# Define constants +INFINITY = np.inf +EPSILON = np.finfo(np.double).eps + +# Some handy constants (BestFirstTreeBuilder) +IS_FIRST = 1 +IS_NOT_FIRST = 0 +IS_LEFT = 1 +IS_NOT_LEFT = 0 + +TREE_LEAF = -1 +TREE_UNDEFINED = -2 +_TREE_LEAF = TREE_LEAF +_TREE_UNDEFINED = TREE_UNDEFINED + +# Since you're dealing with Cython-specific types and features, +# it's important to provide a dummy definition for Node. +class Node: + def __init__(self): + self.left_child = None + self.right_child = None + self.feature = None + self.threshold = None + self.impurity = None + self.n_node_samples = None + self.weighted_n_node_samples = None + self.missing_go_to_left = None + +dummy = Node() +# Create a numpy dtype for Node using the dummy object +NODE_DTYPE = np.asarray([dummy], dtype=object).dtype +# ============================================================================= +# TreeBuilder +# ============================================================================= + +class TreeBuilder: + """Interface for different tree building strategies.""" + def __init__(self): + self.splitter = None + self.min_samples_split = None + self.min_samples_leaf = None + self.min_weight_leaf = None + self.max_depth = None + self.min_impurity_decrease = None + + def build( + self, + tree, + X, + y, + sample_weight=None, + missing_values_in_feature_mask=None, + ): + """Build a decision tree from the training set (X, y).""" + pass + + def _check_input( + self, + X, + y, + sample_weight, + ): + """Check input dtype, layout, and format""" + if issparse(X): + X = X.tocsc() #tocsc() is a method provided by the scipy.sparse module in the SciPy library. It's used to convert a sparse matrix to the Compressed Sparse Column (CSC) format. + X.sort_indices() #This is done to ensure that the indices of non-zero elements within the matrix are sorted in ascending order. + + if X.data.dtype != DTYPE: + X.data = np.ascontiguousarray(X.data, dtype=DTYPE) + + if X.indices.dtype != np.int32 or X.indptr.dtype != np.int32: + raise ValueError("No support for np.int64 index-based sparse matrices") + + elif X.dtype != DTYPE: + # since we have to copy, we will make it Fortran for efficiency + X = np.asfortranarray(X, dtype=DTYPE) + + if y.base.dtype != DTYPE or not y.base.flags.contiguous: + y = np.ascontiguousarray(y, dtype=DTYPE) + + if ( + sample_weight is not None and + ( + sample_weight.base.dtype != DOUBLE or + not sample_weight.base.flags.contiguous + ) + ): + sample_weight = np.asarray(sample_weight, dtype=DOUBLE, order="C") + + return X, y, sample_weight + + + + +# Depth first builder --------------------------------------------------------- +# A record on the stack for depth-first tree growing +class StackRecord: + def __init__(self, start, end, depth, parent, is_left, impurity, n_constant_features): + self.start = start + self.end = end + self.depth = depth + self.parent = parent + self.is_left = is_left + self.impurity = impurity + self.n_constant_features = n_constant_features + + + + + + + + +class DepthFirstTreeBuilder(TreeBuilder): + """Build a decision tree in depth-first fashion.""" + + def __init__( + self, splitter, min_samples_split, + min_samples_leaf, min_weight_leaf, + max_depth, min_impurity_decrease + ): + self.splitter = splitter + self.min_samples_split = min_samples_split + self.min_samples_leaf = min_samples_leaf + self.min_weight_leaf = min_weight_leaf + self.max_depth = max_depth + self.min_impurity_decrease = min_impurity_decrease + + def build( + self, + tree, + X, + y, + sample_weight=None, + missing_values_in_feature_mask=None + ): + """Build a decision tree from the training set (X, y).""" + + # Check input + X, y, sample_weight = self._check_input(X, y, sample_weight) + + # Initial capacity + init_capacity = (2 ** (tree.max_depth + 1)) - 1 if tree.max_depth <= 10 else 2047 + + tree._resize(init_capacity) + + # Parameters + splitter = self.splitter + max_depth = self.max_depth + min_samples_leaf = self.min_samples_leaf + min_weight_leaf = self.min_weight_leaf + min_samples_split = self.min_samples_split + min_impurity_decrease = self.min_impurity_decrease + + # Recursive partition (without actual recursion) + splitter.init(X, y, sample_weight, missing_values_in_feature_mask) + + stack = [] + + # Push root node onto stack + stack.append( + StackRecord( + start=0, + end=splitter.n_samples, + depth=0, + parent=_TREE_UNDEFINED, + is_left=False, + impurity=INFINITY, + n_constant_features=0 + ) + ) + weighted_n_node_samples = np.zeros(1, dtype=np.double) + while stack: + stack_record = stack.pop() + + start = stack_record.start + end = stack_record.end + depth = stack_record.depth + parent = stack_record.parent + is_left = stack_record.is_left + impurity = stack_record.impurity + n_constant_features = stack_record.n_constant_features + + n_node_samples = end - start + splitter.node_reset(start, end, weighted_n_node_samples) + + is_leaf = ( + depth >= max_depth + or n_node_samples < min_samples_split + or n_node_samples < 2 * min_samples_leaf + or np.sum(sample_weight[start:end]) < 2 * min_weight_leaf + ) + + if is_left: + impurity = splitter.node_impurity() + + is_leaf = is_leaf or impurity <= EPSILON + + if not is_leaf: + split = SplitRecord() # No idea what is SplitRecord in original code. Maybe this never gets called, not sure + splitter.node_split(impurity, split, n_constant_features) + is_leaf = ( + is_leaf + or split.pos >= end + or (split.improvement + EPSILON < min_impurity_decrease) + ) + + node_id = tree._add_node( + parent, + is_left, + is_leaf, + split.feature if not is_leaf else 0, + split.threshold if not is_leaf else 0, + impurity, + n_node_samples, + np.sum(sample_weight[start:end]), + split.missing_go_to_left, + ) + + if node_id == np.iinfo(np.intp).max: + raise MemoryError() + + splitter.node_value(tree.value + node_id * tree.value_stride) + + if not is_leaf: + # Push right child on stack + stack.append( + StackRecord( + start=split.pos, + end=end, + depth=depth + 1, + parent=node_id, + is_left=False, + impurity=split.impurity_right, + n_constant_features=n_constant_features, + ) + ) + # Push left child on stack + stack.append( + StackRecord( + start=start, + end=split.pos, + depth=depth + 1, + parent=node_id, + is_left=True, + impurity=split.impurity_left, + n_constant_features=n_constant_features, + ) + ) + + +class Tree: + def __init__(self, n_features, n_classes, n_outputs): + """Constructor.""" + self.n_features = None + self.n_classes = None + self.n_outputs = None + self.max_n_classes = None + self.max_depth = None + self.node_count = None + self.capacity = None + self.nodes = [] #replaced it with array since this array will contain nodes + self.value = None + self.value_stride = None + + dummy = 0 + size_t_dtype = np.array(dummy).dtype + + n_classes = _check_n_classes(n_classes, size_t_dtype) + + # Input/Output layout + self.n_features = n_features + self.n_outputs = n_outputs + self.n_classes = np.zeros(n_outputs, dtype=size_t_dtype) + + self.max_n_classes = np.max(n_classes) + self.value_stride = n_outputs * self.max_n_classes + + for k in range(n_outputs): + self.n_classes[k] = n_classes[k] + + # Inner structures + self.max_depth = 0 + self.node_count = 0 + self.capacity = 0 + self.value = None + self.nodes = None + + def __del__(self): + """Destructor.""" + # Free all inner structures + self.n_classes = None + self.value = None + self.nodes = None + + def __reduce__(self): + """Reduce re-implementation, for pickling.""" + raise NotImplementedError + + def __getstate__(self): + """Getstate re-implementation, for pickling.""" + d = {} + # capacity is inferred during the __setstate__ using nodes + d["max_depth"] = self.max_depth + d["node_count"] = self.node_count + d["nodes"] = self._get_node_ndarray() + d["values"] = self._get_value_ndarray() + return d + + def __setstate__(self, d): + """Setstate re-implementation, for unpickling.""" + raise NotImplementedError + + def _resize(self, capacity): + """ + Resize all inner arrays to `capacity`. If `capacity` is -1, then double the size of the inner arrays. + Returns -1 in case of failure to allocate memory (and raise MemoryError), or 0 otherwise. + """ + if self._resize_c(capacity) != 0: + # Raise MemoryError if resizing fails + raise MemoryError() + + def _resize_c(self, capacity=float('inf')): + # """ + # Guts of _resize + # Returns -1 in case of failure to allocate memory (and raise MemoryError), + # or 0 otherwise. + # """ + # if capacity == self.capacity and self.nodes is not None: + # return 0 + + # if capacity == float('inf'): + # if self.capacity == 0: + # capacity = 3 # default initial value + # else: + # capacity = 2 * self.capacity + + # # This section is relevant if the code is dealing with C arrays. + # # In Python, resizing arrays is handled automatically by lists or numpy arrays. + # # You won't need to explicitly reallocate memory or initialize values like this. + # self.nodes = [None] * capacity #doubtfull either the Node classes get reallocated or something else happends + # self.value = [0.0] * (capacity * self.value_stride) #doubtfull either the value classes get reallocated or something else happends + + # # value memory is initialized to 0 to enable classifier argmax + # if capacity > self.capacity: + # self.value += [0.0] * ((capacity - self.capacity) * self.value_stride) + + # # if capacity smaller than node_count, adjust the counter + # if capacity < self.node_count: + # self.node_count = capacity + + # self.capacity = capacity + # return 0 + raise NotImplementedError + + + def _add_node(self, parent, is_left, is_leaf, feature, threshold, impurity, + n_node_samples, weighted_n_node_samples, missing_go_to_left): + """ + Add a node to the tree. + + The new node registers itself as the child of its parent. + + Returns -1 on error. + """ + node_id = self.node_count + + #no need to resize since python reallocates lists dynamically + # if node_id >= self.capacity: + # if self._resize_c() != 0: + # return -1 #throw error if resize not possible + + node = Node() #self.nodes contains a list of nodes, it returns the node at node_id location + self.nodes.append(node) + node.impurity = impurity + node.n_node_samples = n_node_samples + node.weighted_n_node_samples = weighted_n_node_samples + + if parent != _TREE_UNDEFINED: + if is_left: + self.nodes[parent].left_child = node_id + else: + self.nodes[parent].right_child = node_id + + if is_leaf: + node.left_child = _TREE_LEAF + node.right_child = _TREE_LEAF + node.feature = _TREE_UNDEFINED + node.threshold = _TREE_UNDEFINED + + else: + # left_child and right_child will be set later + node.feature = feature + node.threshold = threshold + node.missing_go_to_left = missing_go_to_left + + self.node_count += 1 + + return node_id + + def predict(self, X): + # Apply the model to the input data X + predictions = self.apply(X) + # Get the internal data as a NumPy array + internal_data = self._get_value_ndarray() + # Use the predictions to index the internal data + out = internal_data[predictions] #not sure if this accurately translates to .take(self.apply(X), axis=0, mode='clip') + # Reshape the output if the model is single-output + if self.n_outputs == 1: + out = out.reshape(X.shape[0], self.max_n_classes) + return out + + def apply(self, X): + """Finds the terminal region (=leaf node) for each sample in X.""" + if issparse(X): + return self._apply_sparse_csr(X) + else: + return self._apply_dense(X) + + def _apply_dense(self, X): + if not isinstance(X, ivy.data_classes.array.array.Array): + raise ValueError("X should be a torch.Tensor, got %s" % type(X)) + + if X.dtype != torch.float32: + raise ValueError("X.dtype should be torch.float32, got %s" % X.dtype) + + X_tensor = X + n_samples = X.shape[0] + out = torch.zeros(n_samples, dtype=torch.int64) + + for i in range(n_samples): + node = self.nodes + + while node.left_child != _TREE_LEAF: + X_i_node_feature = X_tensor[i, node.feature] + + if torch.isnan(X_i_node_feature): + if node.missing_go_to_left: + node = self.nodes[node.left_child] + else: + node = self.nodes[node.right_child] + elif X_i_node_feature <= node.threshold: + node = self.nodes[node.left_child] + else: + node = self.nodes[node.right_child] + + out[i] = node - self.nodes + + return out + + def _apply_sparse_csr(self, X): + """Finds the terminal region (=leaf node) for each sample in sparse X.""" + if not isinstance(X, csr_matrix): + raise ValueError("X should be in csr_matrix format, got %s" % type(X)) + + if X.dtype != np.float32: + raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + + n_samples, n_features = X.shape + + # Initialize output + out = np.zeros(n_samples, dtype=np.intp) + + # Initialize auxiliary data structures + feature_to_sample = np.full(n_features, -1, dtype=np.intp) + X_sample = np.zeros(n_features, dtype=np.float32) + + for i in range(n_samples): + node = self.nodes + + for k in range(X.indptr[i], X.indptr[i + 1]): + feature_to_sample[X.indices[k]] = i + X_sample[X.indices[k]] = X.data[k] + + while node.left_child != _TREE_LEAF: + if feature_to_sample[node.feature] == i: + feature_value = X_sample[node.feature] + else: + feature_value = 0.0 + + if feature_value <= node.threshold: + node = self.nodes[node.left_child] + else: + node = self.nodes[node.right_child] + + out[i] = node - self.nodes # node offset + + return out + + def decision_path(self, X): + """Finds the decision path (=node) for each sample in X.""" + if issparse(X): + return self._decision_path_sparse_csr(X) + else: + return self._decision_path_dense(X) + + def _decision_path_dense(self, X): + """Finds the decision path (=node) for each sample in X.""" + + # Check input + if not isinstance(X, np.ndarray): + raise ValueError("X should be in np.ndarray format, got %s" % type(X)) + + if X.dtype != DTYPE: + raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + + # Extract input + X_ndarray = X + n_samples = X.shape[0] + + # Initialize output + indptr = np.zeros(n_samples + 1, dtype=np.intp) + indices = np.zeros(n_samples * (1 + self.max_depth), dtype=np.intp) + + # Initialize auxiliary data-structure + node = None + i = 0 + + for i in range(n_samples): + node = self.nodes + indptr[i + 1] = indptr[i] + + # Add all external nodes + while node.left_child != _TREE_LEAF: + # ... and node.right_child != _TREE_LEAF: + indices[indptr[i + 1]] = node - self.nodes + indptr[i + 1] += 1 + + if X_ndarray[i, node.feature] <= node.threshold: + node = self.nodes[node.left_child] + else: + node = self.nodes[node.right_child] + + # Add the leaf node + indices[indptr[i + 1]] = node - self.nodes + indptr[i + 1] += 1 + + indices = indices[:indptr[n_samples]] + data = np.ones(shape=len(indices), dtype=np.intp) + out = csr_matrix((data, indices, indptr), shape=(n_samples, self.node_count)) + + return out + + def _decision_path_sparse_csr(self, X): + """Finds the decision path (=node) for each sample in X.""" + + # Check input + if not isspmatrix_csr(X): + raise ValueError("X should be in csr_matrix format, got %s" % type(X)) + + if X.dtype != DTYPE: + raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + + # Extract input + X_data = X.data + X_indices = X.indices + X_indptr = X.indptr + + n_samples = X.shape[0] + n_features = X.shape[1] + + # Initialize output + indptr = np.zeros(n_samples + 1, dtype=np.intp) + indices = np.zeros(n_samples * (1 + self.max_depth), dtype=np.intp) + + # Initialize auxiliary data-structure + feature_value = 0.0 + node = None + X_sample = np.zeros(n_features, dtype=DTYPE) + feature_to_sample = np.full(n_features, -1, dtype=np.intp) + + for i in range(n_samples): + node = self.nodes + indptr[i + 1] = indptr[i] + + for k in range(X_indptr[i], X_indptr[i + 1]): + feature_to_sample[X_indices[k]] = i + X_sample[X_indices[k]] = X_data[k] + + # While node not a leaf + while node.left_child != _TREE_LEAF: + indices[indptr[i + 1]] = node - self.nodes + indptr[i + 1] += 1 + + if feature_to_sample[node.feature] == i: + feature_value = X_sample[node.feature] + else: + feature_value = 0.0 + + if feature_value <= node.threshold: + node = self.nodes[node.left_child] + else: + node = self.nodes[node.right_child] + + # Add the leaf node + indices[indptr[i + 1]] = node - self.nodes + indptr[i + 1] += 1 + + indices = indices[:indptr[n_samples]] + data = np.ones(shape=len(indices), dtype=np.intp) + out = csr_matrix((data, indices, indptr), shape=(n_samples, self.node_count)) + + return out + + def compute_node_depths(self): + """Compute the depth of each node in a tree. + + .. versionadded:: 1.3 + + Returns + ------- + depths : ndarray of shape (self.node_count,), dtype=np.int64 + The depth of each node in the tree. + """ + depths = np.empty(self.node_count, dtype=np.int64) + children_left = self.children_left + children_right = self.children_right + node_count = self.node_count + + depths[0] = 1 # init root node + for node_id in range(node_count): + if children_left[node_id] != _TREE_LEAF: + depth = depths[node_id] + 1 + depths[children_left[node_id]] = depth + depths[children_right[node_id]] = depth + + return depths.base + + def compute_feature_importances(self, normalize=True): + """Computes the importance of each feature (aka variable).""" + nodes = self.nodes + node = nodes + end_node = node + self.node_count + + importances = np.zeros(self.n_features, dtype=np.float64) + + while node != end_node: + if node.left_child != _TREE_LEAF: + left = nodes[node.left_child] + right = nodes[node.right_child] + + importances[node.feature] += ( + node.weighted_n_node_samples * node.impurity - + left.weighted_n_node_samples * left.impurity - + right.weighted_n_node_samples * right.impurity) + node += 1 + + for i in range(self.n_features): + importances[i] /= nodes[0].weighted_n_node_samples + + if normalize: + normalizer = np.sum(importances) + + if normalizer > 0.0: + # Avoid dividing by zero (e.g., when root is pure) + importances /= normalizer + + return importances + + def _get_value_ndarray(self): + """Wraps value as a 3-d NumPy array. + + The array keeps a reference to this Tree, which manages the underlying + memory. + """ + shape = (self.node_count, self.n_outputs, self.max_n_classes) + arr = np.ndarray(shape, dtype=np.float64, buffer=self.value) + arr.base = self + return arr + + def _get_node_ndarray(self): + """Wraps nodes as a NumPy struct array. + + The array keeps a reference to this Tree, which manages the underlying + memory. Individual fields are publicly accessible as properties of the + Tree. + """ + shape = (self.node_count,) + dtype = np.dtype([ + ('left_child', np.intp), + ('right_child', np.intp), + ('feature', np.intp), + ('threshold', np.float64), + ('impurity', np.float64), + ('n_node_samples', np.intp), + ('weighted_n_node_samples', np.float64), + ('missing_go_to_left', np.uint8) + ]) + arr = np.ndarray(shape, dtype=dtype, buffer=self.nodes) + arr.base = self + return arr + + def compute_partial_dependence(self, X, target_features, out): + out.fill(0.0) # Initialize the output array + + _TREE_LEAF = self._TREE_LEAF # The value for leaf nodes + + for sample_idx in range(X.shape[0]): + stack_size = 1 + node_idx_stack = [0] # root node + weight_stack = [1.0] # all samples are in the root node + total_weight = 0.0 + + while stack_size > 0: + stack_size -= 1 + current_node_idx = node_idx_stack[stack_size] + current_node = self.nodes[current_node_idx] + + if current_node.left_child == _TREE_LEAF: + # Leaf node + out[sample_idx] += weight_stack[stack_size] * self.value[current_node_idx] + total_weight += weight_stack[stack_size] + else: + is_target_feature = any(target_feature == current_node.feature for target_feature in target_features) + if is_target_feature: + if X[sample_idx, current_node.feature] <= current_node.threshold: + node_idx_stack.append(current_node.left_child) + weight_stack.append(weight_stack[stack_size]) + stack_size += 1 + else: + node_idx_stack.append(current_node.right_child) + weight_stack.append(weight_stack[stack_size]) + stack_size += 1 + else: + left_sample_frac = self.nodes[current_node.left_child].weighted_n_node_samples / current_node.weighted_n_node_samples + current_weight = weight_stack[stack_size] + node_idx_stack.extend([current_node.left_child, current_node.right_child]) + weight_stack.extend([current_weight * left_sample_frac, current_weight * (1 - left_sample_frac)]) + stack_size += 2 + + if not (0.999 < total_weight < 1.001): + raise ValueError(f"Total weight should be 1.0 but was {total_weight:.9f}") + + +def _check_n_classes(n_classes, expected_dtype): + if n_classes.ndim != 1: + raise ValueError( + f"Wrong dimensions for n_classes from the pickle: " + f"expected 1, got {n_classes.ndim}" + ) + + if n_classes.dtype == expected_dtype: + return n_classes + + # Handles both different endianness and different bitness + if n_classes.dtype.kind == "i" and n_classes.dtype.itemsize in [4, 8]: + return n_classes.astype(expected_dtype, casting="same_kind") + + raise ValueError( + "n_classes from the pickle has an incompatible dtype:\n" + f"- expected: {expected_dtype}\n" + f"- got: {n_classes.dtype}" + ) + + + + + + diff --git a/ivy/functional/frontends/sklearn/tree/_tree.pyx b/ivy/functional/frontends/sklearn/tree/_tree.pyx new file mode 100644 index 0000000000000..f436c919f8bc2 --- /dev/null +++ b/ivy/functional/frontends/sklearn/tree/_tree.pyx @@ -0,0 +1,1833 @@ +# Authors: Gilles Louppe +# Peter Prettenhofer +# Brian Holt +# Noel Dawe +# Satrajit Gosh +# Lars Buitinck +# Arnaud Joly +# Joel Nothman +# Fares Hedayati +# Jacob Schreiber +# Nelson Liu +# +# License: BSD 3 clause + +from cpython cimport Py_INCREF, PyObject, PyTypeObject + +from libc.stdlib cimport free +from libc.string cimport memcpy +from libc.string cimport memset +from libc.stdint cimport INTPTR_MAX +from libc.math cimport isnan +from libcpp.vector cimport vector +from libcpp.algorithm cimport pop_heap +from libcpp.algorithm cimport push_heap +from libcpp cimport bool + +import struct + +import numpy as np +cimport numpy as cnp +cnp.import_array() + +from scipy.sparse import issparse +from scipy.sparse import csr_matrix +from scipy.sparse import isspmatrix_csr + +from ._utils cimport safe_realloc +from ._utils cimport sizet_ptr_to_ndarray + +cdef extern from "numpy/arrayobject.h": + object PyArray_NewFromDescr(PyTypeObject* subtype, cnp.dtype descr, + int nd, cnp.npy_intp* dims, + cnp.npy_intp* strides, + void* data, int flags, object obj) + int PyArray_SetBaseObject(cnp.ndarray arr, PyObject* obj) + +cdef extern from "" namespace "std" nogil: + cdef cppclass stack[T]: + ctypedef T value_type + stack() except + + bint empty() + void pop() + void push(T&) except + # Raise c++ exception for bad_alloc -> MemoryError + T& top() + +# ============================================================================= +# Types and constants +# ============================================================================= + +from numpy import float32 as DTYPE +from numpy import float64 as DOUBLE + +cdef double INFINITY = np.inf +cdef double EPSILON = np.finfo('double').eps + +# Some handy constants (BestFirstTreeBuilder) +cdef int IS_FIRST = 1 +cdef int IS_NOT_FIRST = 0 +cdef int IS_LEFT = 1 +cdef int IS_NOT_LEFT = 0 + +TREE_LEAF = -1 +TREE_UNDEFINED = -2 +cdef SIZE_t _TREE_LEAF = TREE_LEAF +cdef SIZE_t _TREE_UNDEFINED = TREE_UNDEFINED + +# Build the corresponding numpy dtype for Node. +# This works by casting `dummy` to an array of Node of length 1, which numpy +# can construct a `dtype`-object for. See https://stackoverflow.com/q/62448946 +# for a more detailed explanation. +cdef Node dummy +NODE_DTYPE = np.asarray((&dummy)).dtype + +# ============================================================================= +# TreeBuilder +# ============================================================================= + +cdef class TreeBuilder: + """Interface for different tree building strategies.""" + + cpdef build( + self, + Tree tree, + object X, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight=None, + const unsigned char[::1] missing_values_in_feature_mask=None, + ): + """Build a decision tree from the training set (X, y).""" + pass + + cdef inline _check_input( + self, + object X, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight, + ): + """Check input dtype, layout and format""" + if issparse(X): + X = X.tocsc() + X.sort_indices() + + if X.data.dtype != DTYPE: + X.data = np.ascontiguousarray(X.data, dtype=DTYPE) + + if X.indices.dtype != np.int32 or X.indptr.dtype != np.int32: + raise ValueError("No support for np.int64 index based " + "sparse matrices") + + elif X.dtype != DTYPE: + # since we have to copy we will make it fortran for efficiency + X = np.asfortranarray(X, dtype=DTYPE) + + # TODO: This check for y seems to be redundant, as it is also + # present in the BaseDecisionTree's fit method, and therefore + # can be removed. + if y.base.dtype != DOUBLE or not y.base.flags.contiguous: + y = np.ascontiguousarray(y, dtype=DOUBLE) + + if ( + sample_weight is not None and + ( + sample_weight.base.dtype != DOUBLE or + not sample_weight.base.flags.contiguous + ) + ): + sample_weight = np.asarray(sample_weight, dtype=DOUBLE, order="C") + + return X, y, sample_weight + +# Depth first builder --------------------------------------------------------- +# A record on the stack for depth-first tree growing +cdef struct StackRecord: + SIZE_t start + SIZE_t end + SIZE_t depth + SIZE_t parent + bint is_left + double impurity + SIZE_t n_constant_features + + + + + + + +cdef class DepthFirstTreeBuilder(TreeBuilder): + """Build a decision tree in depth-first fashion.""" + + def __cinit__(self, Splitter splitter, SIZE_t min_samples_split, + SIZE_t min_samples_leaf, double min_weight_leaf, + SIZE_t max_depth, double min_impurity_decrease): + self.splitter = splitter + self.min_samples_split = min_samples_split + self.min_samples_leaf = min_samples_leaf + self.min_weight_leaf = min_weight_leaf + self.max_depth = max_depth + self.min_impurity_decrease = min_impurity_decrease + + cpdef build( + self, + Tree tree, + object X, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight=None, + const unsigned char[::1] missing_values_in_feature_mask=None, + ): + """Build a decision tree from the training set (X, y).""" + + # check input + X, y, sample_weight = self._check_input(X, y, sample_weight) + + # Initial capacity + cdef int init_capacity + + if tree.max_depth <= 10: + init_capacity = (2 ** (tree.max_depth + 1)) - 1 + else: + init_capacity = 2047 + + tree._resize(init_capacity) + + # Parameters + cdef Splitter splitter = self.splitter + cdef SIZE_t max_depth = self.max_depth + cdef SIZE_t min_samples_leaf = self.min_samples_leaf + cdef double min_weight_leaf = self.min_weight_leaf + cdef SIZE_t min_samples_split = self.min_samples_split + cdef double min_impurity_decrease = self.min_impurity_decrease + + # Recursive partition (without actual recursion) + splitter.init(X, y, sample_weight, missing_values_in_feature_mask) + + cdef SIZE_t start + cdef SIZE_t end + cdef SIZE_t depth + cdef SIZE_t parent + cdef bint is_left + cdef SIZE_t n_node_samples = splitter.n_samples + cdef double weighted_n_node_samples + cdef SplitRecord split + cdef SIZE_t node_id + + cdef double impurity = INFINITY + cdef SIZE_t n_constant_features + cdef bint is_leaf + cdef bint first = 1 + cdef SIZE_t max_depth_seen = -1 + cdef int rc = 0 + + cdef stack[StackRecord] builder_stack + cdef StackRecord stack_record + + with nogil: + # push root node onto stack + builder_stack.push({ + "start": 0, + "end": n_node_samples, + "depth": 0, + "parent": _TREE_UNDEFINED, + "is_left": 0, + "impurity": INFINITY, + "n_constant_features": 0}) + + while not builder_stack.empty(): + stack_record = builder_stack.top() + builder_stack.pop() + + start = stack_record.start + end = stack_record.end + depth = stack_record.depth + parent = stack_record.parent + is_left = stack_record.is_left + impurity = stack_record.impurity + n_constant_features = stack_record.n_constant_features + + n_node_samples = end - start + splitter.node_reset(start, end, &weighted_n_node_samples) + + is_leaf = (depth >= max_depth or + n_node_samples < min_samples_split or + n_node_samples < 2 * min_samples_leaf or + weighted_n_node_samples < 2 * min_weight_leaf) + + if first: + impurity = splitter.node_impurity() + first = 0 + + # impurity == 0 with tolerance due to rounding errors + is_leaf = is_leaf or impurity <= EPSILON + + if not is_leaf: + splitter.node_split(impurity, &split, &n_constant_features) + # If EPSILON=0 in the below comparison, float precision + # issues stop splitting, producing trees that are + # dissimilar to v0.18 + is_leaf = (is_leaf or split.pos >= end or + (split.improvement + EPSILON < + min_impurity_decrease)) + + node_id = tree._add_node(parent, is_left, is_leaf, split.feature, + split.threshold, impurity, n_node_samples, + weighted_n_node_samples, + split.missing_go_to_left) + + if node_id == INTPTR_MAX: + rc = -1 + break + + # Store value for all nodes, to facilitate tree/model + # inspection and interpretation + splitter.node_value(tree.value + node_id * tree.value_stride) + + if not is_leaf: + # Push right child on stack + builder_stack.push({ + "start": split.pos, + "end": end, + "depth": depth + 1, + "parent": node_id, + "is_left": 0, + "impurity": split.impurity_right, + "n_constant_features": n_constant_features}) + + # Push left child on stack + builder_stack.push({ + "start": start, + "end": split.pos, + "depth": depth + 1, + "parent": node_id, + "is_left": 1, + "impurity": split.impurity_left, + "n_constant_features": n_constant_features}) + + if depth > max_depth_seen: + max_depth_seen = depth + + if rc >= 0: + rc = tree._resize_c(tree.node_count) + + if rc >= 0: + tree.max_depth = max_depth_seen + if rc == -1: + raise MemoryError() + + +# Best first builder ---------------------------------------------------------- +cdef struct FrontierRecord: + # Record of information of a Node, the frontier for a split. Those records are + # maintained in a heap to access the Node with the best improvement in impurity, + # allowing growing trees greedily on this improvement. + SIZE_t node_id + SIZE_t start + SIZE_t end + SIZE_t pos + SIZE_t depth + bint is_leaf + double impurity + double impurity_left + double impurity_right + double improvement + +cdef inline bool _compare_records( + const FrontierRecord& left, + const FrontierRecord& right, +): + return left.improvement < right.improvement + +cdef inline void _add_to_frontier( + FrontierRecord rec, + vector[FrontierRecord]& frontier, +) noexcept nogil: + """Adds record `rec` to the priority queue `frontier`.""" + frontier.push_back(rec) + push_heap(frontier.begin(), frontier.end(), &_compare_records) + + +cdef class BestFirstTreeBuilder(TreeBuilder): + """Build a decision tree in best-first fashion. + + The best node to expand is given by the node at the frontier that has the + highest impurity improvement. + """ + cdef SIZE_t max_leaf_nodes + + def __cinit__(self, Splitter splitter, SIZE_t min_samples_split, + SIZE_t min_samples_leaf, min_weight_leaf, + SIZE_t max_depth, SIZE_t max_leaf_nodes, + double min_impurity_decrease): + self.splitter = splitter + self.min_samples_split = min_samples_split + self.min_samples_leaf = min_samples_leaf + self.min_weight_leaf = min_weight_leaf + self.max_depth = max_depth + self.max_leaf_nodes = max_leaf_nodes + self.min_impurity_decrease = min_impurity_decrease + + cpdef build( + self, + Tree tree, + object X, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight=None, + const unsigned char[::1] missing_values_in_feature_mask=None, + ): + """Build a decision tree from the training set (X, y).""" + + # check input + X, y, sample_weight = self._check_input(X, y, sample_weight) + + # Parameters + cdef Splitter splitter = self.splitter + cdef SIZE_t max_leaf_nodes = self.max_leaf_nodes + + # Recursive partition (without actual recursion) + splitter.init(X, y, sample_weight, missing_values_in_feature_mask) + + cdef vector[FrontierRecord] frontier + cdef FrontierRecord record + cdef FrontierRecord split_node_left + cdef FrontierRecord split_node_right + + cdef SIZE_t n_node_samples = splitter.n_samples + cdef SIZE_t max_split_nodes = max_leaf_nodes - 1 + cdef bint is_leaf + cdef SIZE_t max_depth_seen = -1 + cdef int rc = 0 + cdef Node* node + + # Initial capacity + cdef SIZE_t init_capacity = max_split_nodes + max_leaf_nodes + tree._resize(init_capacity) + + with nogil: + # add root to frontier + rc = self._add_split_node(splitter, tree, 0, n_node_samples, + INFINITY, IS_FIRST, IS_LEFT, NULL, 0, + &split_node_left) + if rc >= 0: + _add_to_frontier(split_node_left, frontier) + + while not frontier.empty(): + pop_heap(frontier.begin(), frontier.end(), &_compare_records) + record = frontier.back() + frontier.pop_back() + + node = &tree.nodes[record.node_id] + is_leaf = (record.is_leaf or max_split_nodes <= 0) + + if is_leaf: + # Node is not expandable; set node as leaf + node.left_child = _TREE_LEAF + node.right_child = _TREE_LEAF + node.feature = _TREE_UNDEFINED + node.threshold = _TREE_UNDEFINED + + else: + # Node is expandable + + # Decrement number of split nodes available + max_split_nodes -= 1 + + # Compute left split node + rc = self._add_split_node(splitter, tree, + record.start, record.pos, + record.impurity_left, + IS_NOT_FIRST, IS_LEFT, node, + record.depth + 1, + &split_node_left) + if rc == -1: + break + + # tree.nodes may have changed + node = &tree.nodes[record.node_id] + + # Compute right split node + rc = self._add_split_node(splitter, tree, record.pos, + record.end, + record.impurity_right, + IS_NOT_FIRST, IS_NOT_LEFT, node, + record.depth + 1, + &split_node_right) + if rc == -1: + break + + # Add nodes to queue + _add_to_frontier(split_node_left, frontier) + _add_to_frontier(split_node_right, frontier) + + if record.depth > max_depth_seen: + max_depth_seen = record.depth + + if rc >= 0: + rc = tree._resize_c(tree.node_count) + + if rc >= 0: + tree.max_depth = max_depth_seen + + if rc == -1: + raise MemoryError() + + cdef inline int _add_split_node(self, Splitter splitter, Tree tree, + SIZE_t start, SIZE_t end, double impurity, + bint is_first, bint is_left, Node* parent, + SIZE_t depth, + FrontierRecord* res) except -1 nogil: + """Adds node w/ partition ``[start, end)`` to the frontier. """ + cdef SplitRecord split + cdef SIZE_t node_id + cdef SIZE_t n_node_samples + cdef SIZE_t n_constant_features = 0 + cdef double min_impurity_decrease = self.min_impurity_decrease + cdef double weighted_n_node_samples + cdef bint is_leaf + + splitter.node_reset(start, end, &weighted_n_node_samples) + + if is_first: + impurity = splitter.node_impurity() + + n_node_samples = end - start + is_leaf = (depth >= self.max_depth or + n_node_samples < self.min_samples_split or + n_node_samples < 2 * self.min_samples_leaf or + weighted_n_node_samples < 2 * self.min_weight_leaf or + impurity <= EPSILON # impurity == 0 with tolerance + ) + + if not is_leaf: + splitter.node_split(impurity, &split, &n_constant_features) + # If EPSILON=0 in the below comparison, float precision issues stop + # splitting early, producing trees that are dissimilar to v0.18 + is_leaf = (is_leaf or split.pos >= end or + split.improvement + EPSILON < min_impurity_decrease) + + node_id = tree._add_node(parent - tree.nodes + if parent != NULL + else _TREE_UNDEFINED, + is_left, is_leaf, + split.feature, split.threshold, impurity, n_node_samples, + weighted_n_node_samples, + split.missing_go_to_left) + if node_id == INTPTR_MAX: + return -1 + + # compute values also for split nodes (might become leafs later). + splitter.node_value(tree.value + node_id * tree.value_stride) + + res.node_id = node_id + res.start = start + res.end = end + res.depth = depth + res.impurity = impurity + + if not is_leaf: + # is split node + res.pos = split.pos + res.is_leaf = 0 + res.improvement = split.improvement + res.impurity_left = split.impurity_left + res.impurity_right = split.impurity_right + + else: + # is leaf => 0 improvement + res.pos = end + res.is_leaf = 1 + res.improvement = 0.0 + res.impurity_left = impurity + res.impurity_right = impurity + + return 0 + + +# ============================================================================= +# Tree +# ============================================================================= + +cdef class Tree: + """Array-based representation of a binary decision tree. + + The binary tree is represented as a number of parallel arrays. The i-th + element of each array holds information about the node `i`. Node 0 is the + tree's root. You can find a detailed description of all arrays in + `_tree.pxd`. NOTE: Some of the arrays only apply to either leaves or split + nodes, resp. In this case the values of nodes of the other type are + arbitrary! + + Attributes + ---------- + node_count : int + The number of nodes (internal nodes + leaves) in the tree. + + capacity : int + The current capacity (i.e., size) of the arrays, which is at least as + great as `node_count`. + + max_depth : int + The depth of the tree, i.e. the maximum depth of its leaves. + + children_left : array of int, shape [node_count] + children_left[i] holds the node id of the left child of node i. + For leaves, children_left[i] == TREE_LEAF. Otherwise, + children_left[i] > i. This child handles the case where + X[:, feature[i]] <= threshold[i]. + + children_right : array of int, shape [node_count] + children_right[i] holds the node id of the right child of node i. + For leaves, children_right[i] == TREE_LEAF. Otherwise, + children_right[i] > i. This child handles the case where + X[:, feature[i]] > threshold[i]. + + feature : array of int, shape [node_count] + feature[i] holds the feature to split on, for the internal node i. + + threshold : array of double, shape [node_count] + threshold[i] holds the threshold for the internal node i. + + value : array of double, shape [node_count, n_outputs, max_n_classes] + Contains the constant prediction value of each node. + + impurity : array of double, shape [node_count] + impurity[i] holds the impurity (i.e., the value of the splitting + criterion) at node i. + + n_node_samples : array of int, shape [node_count] + n_node_samples[i] holds the number of training samples reaching node i. + + weighted_n_node_samples : array of double, shape [node_count] + weighted_n_node_samples[i] holds the weighted number of training samples + reaching node i. + """ + # Wrap for outside world. + # WARNING: these reference the current `nodes` and `value` buffers, which + # must not be freed by a subsequent memory allocation. + # (i.e. through `_resize` or `__setstate__`) + @property + def n_classes(self): + return sizet_ptr_to_ndarray(self.n_classes, self.n_outputs) + + @property + def children_left(self): + return self._get_node_ndarray()['left_child'][:self.node_count] + + @property + def children_right(self): + return self._get_node_ndarray()['right_child'][:self.node_count] + + @property + def n_leaves(self): + return np.sum(np.logical_and( + self.children_left == -1, + self.children_right == -1)) + + @property + def feature(self): + return self._get_node_ndarray()['feature'][:self.node_count] + + @property + def threshold(self): + return self._get_node_ndarray()['threshold'][:self.node_count] + + @property + def impurity(self): + return self._get_node_ndarray()['impurity'][:self.node_count] + + @property + def n_node_samples(self): + return self._get_node_ndarray()['n_node_samples'][:self.node_count] + + @property + def weighted_n_node_samples(self): + return self._get_node_ndarray()['weighted_n_node_samples'][:self.node_count] + + @property + def missing_go_to_left(self): + return self._get_node_ndarray()['missing_go_to_left'][:self.node_count] + + @property + def value(self): + return self._get_value_ndarray()[:self.node_count] + + # TODO: Convert n_classes to cython.integral memory view once + # https://github.com/cython/cython/issues/5243 is fixed + def __cinit__(self, int n_features, cnp.ndarray n_classes, int n_outputs): + """Constructor.""" + cdef SIZE_t dummy = 0 + size_t_dtype = np.array(dummy).dtype + + n_classes = _check_n_classes(n_classes, size_t_dtype) + + # Input/Output layout + self.n_features = n_features + self.n_outputs = n_outputs + self.n_classes = NULL + safe_realloc(&self.n_classes, n_outputs) + + self.max_n_classes = np.max(n_classes) + self.value_stride = n_outputs * self.max_n_classes + + cdef SIZE_t k + for k in range(n_outputs): + self.n_classes[k] = n_classes[k] + + # Inner structures + self.max_depth = 0 + self.node_count = 0 + self.capacity = 0 + self.value = NULL + self.nodes = NULL + + def __dealloc__(self): + """Destructor.""" + # Free all inner structures + free(self.n_classes) + free(self.value) + free(self.nodes) + + def __reduce__(self): + """Reduce re-implementation, for pickling.""" + return (Tree, (self.n_features, + sizet_ptr_to_ndarray(self.n_classes, self.n_outputs), + self.n_outputs), self.__getstate__()) + + def __getstate__(self): + """Getstate re-implementation, for pickling.""" + d = {} + # capacity is inferred during the __setstate__ using nodes + d["max_depth"] = self.max_depth + d["node_count"] = self.node_count + d["nodes"] = self._get_node_ndarray() + d["values"] = self._get_value_ndarray() + return d + + def __setstate__(self, d): + """Setstate re-implementation, for unpickling.""" + self.max_depth = d["max_depth"] + self.node_count = d["node_count"] + + if 'nodes' not in d: + raise ValueError('You have loaded Tree version which ' + 'cannot be imported') + + node_ndarray = d['nodes'] + value_ndarray = d['values'] + + value_shape = (node_ndarray.shape[0], self.n_outputs, + self.max_n_classes) + + node_ndarray = _check_node_ndarray(node_ndarray, expected_dtype=NODE_DTYPE) + value_ndarray = _check_value_ndarray( + value_ndarray, + expected_dtype=np.dtype(np.float64), + expected_shape=value_shape + ) + + self.capacity = node_ndarray.shape[0] + if self._resize_c(self.capacity) != 0: + raise MemoryError("resizing tree to %d" % self.capacity) + + memcpy(self.nodes, cnp.PyArray_DATA(node_ndarray), + self.capacity * sizeof(Node)) + memcpy(self.value, cnp.PyArray_DATA(value_ndarray), + self.capacity * self.value_stride * sizeof(double)) + + cdef int _resize(self, SIZE_t capacity) except -1 nogil: + """Resize all inner arrays to `capacity`, if `capacity` == -1, then + double the size of the inner arrays. + + Returns -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + """ + if self._resize_c(capacity) != 0: + # Acquire gil only if we need to raise + with gil: + raise MemoryError() + + cdef int _resize_c(self, SIZE_t capacity=INTPTR_MAX) except -1 nogil: + """Guts of _resize + + Returns -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + """ + if capacity == self.capacity and self.nodes != NULL: + return 0 + + if capacity == INTPTR_MAX: + if self.capacity == 0: + capacity = 3 # default initial value + else: + capacity = 2 * self.capacity + + safe_realloc(&self.nodes, capacity) + safe_realloc(&self.value, capacity * self.value_stride) + + # value memory is initialised to 0 to enable classifier argmax + if capacity > self.capacity: + memset((self.value + self.capacity * self.value_stride), 0, + (capacity - self.capacity) * self.value_stride * + sizeof(double)) + + # if capacity smaller than node_count, adjust the counter + if capacity < self.node_count: + self.node_count = capacity + + self.capacity = capacity + return 0 + + cdef SIZE_t _add_node(self, SIZE_t parent, bint is_left, bint is_leaf, + SIZE_t feature, double threshold, double impurity, + SIZE_t n_node_samples, + double weighted_n_node_samples, + unsigned char missing_go_to_left) except -1 nogil: + """Add a node to the tree. + + The new node registers itself as the child of its parent. + + Returns (size_t)(-1) on error. + """ + cdef SIZE_t node_id = self.node_count + + if node_id >= self.capacity: + if self._resize_c() != 0: + return INTPTR_MAX + + cdef Node* node = &self.nodes[node_id] + node.impurity = impurity + node.n_node_samples = n_node_samples + node.weighted_n_node_samples = weighted_n_node_samples + + if parent != _TREE_UNDEFINED: + if is_left: + self.nodes[parent].left_child = node_id + else: + self.nodes[parent].right_child = node_id + + if is_leaf: + node.left_child = _TREE_LEAF + node.right_child = _TREE_LEAF + node.feature = _TREE_UNDEFINED + node.threshold = _TREE_UNDEFINED + + else: + # left_child and right_child will be set later + node.feature = feature + node.threshold = threshold + node.missing_go_to_left = missing_go_to_left + + self.node_count += 1 + + return node_id + + cpdef cnp.ndarray predict(self, object X): + """Predict target for X.""" + out = self._get_value_ndarray().take(self.apply(X), axis=0, + mode='clip') + if self.n_outputs == 1: + out = out.reshape(X.shape[0], self.max_n_classes) + return out + + cpdef cnp.ndarray apply(self, object X): + """Finds the terminal region (=leaf node) for each sample in X.""" + if issparse(X): + return self._apply_sparse_csr(X) + else: + return self._apply_dense(X) + + cdef inline cnp.ndarray _apply_dense(self, object X): + """Finds the terminal region (=leaf node) for each sample in X.""" + + # Check input + if not isinstance(X, np.ndarray): + raise ValueError("X should be in np.ndarray format, got %s" + % type(X)) + + if X.dtype != DTYPE: + raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + + # Extract input + cdef const DTYPE_t[:, :] X_ndarray = X + cdef SIZE_t n_samples = X.shape[0] + cdef DTYPE_t X_i_node_feature + + # Initialize output + cdef SIZE_t[:] out = np.zeros(n_samples, dtype=np.intp) + + # Initialize auxiliary data-structure + cdef Node* node = NULL + cdef SIZE_t i = 0 + + with nogil: + for i in range(n_samples): + node = self.nodes + # While node not a leaf + while node.left_child != _TREE_LEAF: + X_i_node_feature = X_ndarray[i, node.feature] + # ... and node.right_child != _TREE_LEAF: + if isnan(X_i_node_feature): + if node.missing_go_to_left: + node = &self.nodes[node.left_child] + else: + node = &self.nodes[node.right_child] + elif X_i_node_feature <= node.threshold: + node = &self.nodes[node.left_child] + else: + node = &self.nodes[node.right_child] + + out[i] = (node - self.nodes) # node offset + + return np.asarray(out) + + cdef inline cnp.ndarray _apply_sparse_csr(self, object X): + """Finds the terminal region (=leaf node) for each sample in sparse X. + """ + # Check input + if not isspmatrix_csr(X): + raise ValueError("X should be in csr_matrix format, got %s" + % type(X)) + + if X.dtype != DTYPE: + raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + + # Extract input + cdef const DTYPE_t[:] X_data = X.data + cdef const INT32_t[:] X_indices = X.indices + cdef const INT32_t[:] X_indptr = X.indptr + + cdef SIZE_t n_samples = X.shape[0] + cdef SIZE_t n_features = X.shape[1] + + # Initialize output + cdef SIZE_t[:] out = np.zeros(n_samples, dtype=np.intp) + + # Initialize auxiliary data-structure + cdef DTYPE_t feature_value = 0. + cdef Node* node = NULL + cdef DTYPE_t* X_sample = NULL + cdef SIZE_t i = 0 + cdef INT32_t k = 0 + + # feature_to_sample as a data structure records the last seen sample + # for each feature; functionally, it is an efficient way to identify + # which features are nonzero in the present sample. + cdef SIZE_t* feature_to_sample = NULL + + safe_realloc(&X_sample, n_features) + safe_realloc(&feature_to_sample, n_features) + + with nogil: + memset(feature_to_sample, -1, n_features * sizeof(SIZE_t)) + + for i in range(n_samples): + node = self.nodes + + for k in range(X_indptr[i], X_indptr[i + 1]): + feature_to_sample[X_indices[k]] = i + X_sample[X_indices[k]] = X_data[k] + + # While node not a leaf + while node.left_child != _TREE_LEAF: + # ... and node.right_child != _TREE_LEAF: + if feature_to_sample[node.feature] == i: + feature_value = X_sample[node.feature] + + else: + feature_value = 0. + + if feature_value <= node.threshold: + node = &self.nodes[node.left_child] + else: + node = &self.nodes[node.right_child] + + out[i] = (node - self.nodes) # node offset + + # Free auxiliary arrays + free(X_sample) + free(feature_to_sample) + + return np.asarray(out) + + cpdef object decision_path(self, object X): + """Finds the decision path (=node) for each sample in X.""" + if issparse(X): + return self._decision_path_sparse_csr(X) + else: + return self._decision_path_dense(X) + + cdef inline object _decision_path_dense(self, object X): + """Finds the decision path (=node) for each sample in X.""" + + # Check input + if not isinstance(X, np.ndarray): + raise ValueError("X should be in np.ndarray format, got %s" + % type(X)) + + if X.dtype != DTYPE: + raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + + # Extract input + cdef const DTYPE_t[:, :] X_ndarray = X + cdef SIZE_t n_samples = X.shape[0] + + # Initialize output + cdef SIZE_t[:] indptr = np.zeros(n_samples + 1, dtype=np.intp) + cdef SIZE_t[:] indices = np.zeros( + n_samples * (1 + self.max_depth), dtype=np.intp + ) + + # Initialize auxiliary data-structure + cdef Node* node = NULL + cdef SIZE_t i = 0 + + with nogil: + for i in range(n_samples): + node = self.nodes + indptr[i + 1] = indptr[i] + + # Add all external nodes + while node.left_child != _TREE_LEAF: + # ... and node.right_child != _TREE_LEAF: + indices[indptr[i + 1]] = (node - self.nodes) + indptr[i + 1] += 1 + + if X_ndarray[i, node.feature] <= node.threshold: + node = &self.nodes[node.left_child] + else: + node = &self.nodes[node.right_child] + + # Add the leave node + indices[indptr[i + 1]] = (node - self.nodes) + indptr[i + 1] += 1 + + indices = indices[:indptr[n_samples]] + cdef SIZE_t[:] data = np.ones(shape=len(indices), dtype=np.intp) + out = csr_matrix((data, indices, indptr), + shape=(n_samples, self.node_count)) + + return out + + cdef inline object _decision_path_sparse_csr(self, object X): + """Finds the decision path (=node) for each sample in X.""" + + # Check input + if not isspmatrix_csr(X): + raise ValueError("X should be in csr_matrix format, got %s" + % type(X)) + + if X.dtype != DTYPE: + raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + + # Extract input + cdef const DTYPE_t[:] X_data = X.data + cdef const INT32_t[:] X_indices = X.indices + cdef const INT32_t[:] X_indptr = X.indptr + + cdef SIZE_t n_samples = X.shape[0] + cdef SIZE_t n_features = X.shape[1] + + # Initialize output + cdef SIZE_t[:] indptr = np.zeros(n_samples + 1, dtype=np.intp) + cdef SIZE_t[:] indices = np.zeros( + n_samples * (1 + self.max_depth), dtype=np.intp + ) + + # Initialize auxiliary data-structure + cdef DTYPE_t feature_value = 0. + cdef Node* node = NULL + cdef DTYPE_t* X_sample = NULL + cdef SIZE_t i = 0 + cdef INT32_t k = 0 + + # feature_to_sample as a data structure records the last seen sample + # for each feature; functionally, it is an efficient way to identify + # which features are nonzero in the present sample. + cdef SIZE_t* feature_to_sample = NULL + + safe_realloc(&X_sample, n_features) + safe_realloc(&feature_to_sample, n_features) + + with nogil: + memset(feature_to_sample, -1, n_features * sizeof(SIZE_t)) + + for i in range(n_samples): + node = self.nodes + indptr[i + 1] = indptr[i] + + for k in range(X_indptr[i], X_indptr[i + 1]): + feature_to_sample[X_indices[k]] = i + X_sample[X_indices[k]] = X_data[k] + + # While node not a leaf + while node.left_child != _TREE_LEAF: + # ... and node.right_child != _TREE_LEAF: + + indices[indptr[i + 1]] = (node - self.nodes) + indptr[i + 1] += 1 + + if feature_to_sample[node.feature] == i: + feature_value = X_sample[node.feature] + + else: + feature_value = 0. + + if feature_value <= node.threshold: + node = &self.nodes[node.left_child] + else: + node = &self.nodes[node.right_child] + + # Add the leave node + indices[indptr[i + 1]] = (node - self.nodes) + indptr[i + 1] += 1 + + # Free auxiliary arrays + free(X_sample) + free(feature_to_sample) + + indices = indices[:indptr[n_samples]] + cdef SIZE_t[:] data = np.ones(shape=len(indices), dtype=np.intp) + out = csr_matrix((data, indices, indptr), + shape=(n_samples, self.node_count)) + + return out + + cpdef compute_node_depths(self): + """Compute the depth of each node in a tree. + + .. versionadded:: 1.3 + + Returns + ------- + depths : ndarray of shape (self.node_count,), dtype=np.int64 + The depth of each node in the tree. + """ + cdef: + cnp.int64_t[::1] depths = np.empty(self.node_count, dtype=np.int64) + cnp.npy_intp[:] children_left = self.children_left + cnp.npy_intp[:] children_right = self.children_right + cnp.npy_intp node_id + cnp.npy_intp node_count = self.node_count + cnp.int64_t depth + + depths[0] = 1 # init root node + for node_id in range(node_count): + if children_left[node_id] != _TREE_LEAF: + depth = depths[node_id] + 1 + depths[children_left[node_id]] = depth + depths[children_right[node_id]] = depth + + return depths.base + + cpdef compute_feature_importances(self, normalize=True): + """Computes the importance of each feature (aka variable).""" + cdef Node* left + cdef Node* right + cdef Node* nodes = self.nodes + cdef Node* node = nodes + cdef Node* end_node = node + self.node_count + + cdef double normalizer = 0. + + cdef cnp.float64_t[:] importances = np.zeros(self.n_features) + + with nogil: + while node != end_node: + if node.left_child != _TREE_LEAF: + # ... and node.right_child != _TREE_LEAF: + left = &nodes[node.left_child] + right = &nodes[node.right_child] + + importances[node.feature] += ( + node.weighted_n_node_samples * node.impurity - + left.weighted_n_node_samples * left.impurity - + right.weighted_n_node_samples * right.impurity) + node += 1 + + for i in range(self.n_features): + importances[i] /= nodes[0].weighted_n_node_samples + + if normalize: + normalizer = np.sum(importances) + + if normalizer > 0.0: + # Avoid dividing by zero (e.g., when root is pure) + for i in range(self.n_features): + importances[i] /= normalizer + + return np.asarray(importances) + + cdef cnp.ndarray _get_value_ndarray(self): + """Wraps value as a 3-d NumPy array. + + The array keeps a reference to this Tree, which manages the underlying + memory. + """ + cdef cnp.npy_intp shape[3] + shape[0] = self.node_count + shape[1] = self.n_outputs + shape[2] = self.max_n_classes + cdef cnp.ndarray arr + arr = cnp.PyArray_SimpleNewFromData(3, shape, cnp.NPY_DOUBLE, self.value) + Py_INCREF(self) + if PyArray_SetBaseObject(arr, self) < 0: + raise ValueError("Can't initialize array.") + return arr + + cdef cnp.ndarray _get_node_ndarray(self): + """Wraps nodes as a NumPy struct array. + + The array keeps a reference to this Tree, which manages the underlying + memory. Individual fields are publicly accessible as properties of the + Tree. + """ + cdef cnp.npy_intp shape[1] + shape[0] = self.node_count + cdef cnp.npy_intp strides[1] + strides[0] = sizeof(Node) + cdef cnp.ndarray arr + Py_INCREF(NODE_DTYPE) + arr = PyArray_NewFromDescr( cnp.ndarray, + NODE_DTYPE, 1, shape, + strides, self.nodes, + cnp.NPY_ARRAY_DEFAULT, None) + Py_INCREF(self) + if PyArray_SetBaseObject(arr, self) < 0: + raise ValueError("Can't initialize array.") + return arr + + def compute_partial_dependence(self, DTYPE_t[:, ::1] X, + int[::1] target_features, + double[::1] out): + """Partial dependence of the response on the ``target_feature`` set. + + For each sample in ``X`` a tree traversal is performed. + Each traversal starts from the root with weight 1.0. + + At each non-leaf node that splits on a target feature, either + the left child or the right child is visited based on the feature + value of the current sample, and the weight is not modified. + At each non-leaf node that splits on a complementary feature, + both children are visited and the weight is multiplied by the fraction + of training samples which went to each child. + + At each leaf, the value of the node is multiplied by the current + weight (weights sum to 1 for all visited terminal nodes). + + Parameters + ---------- + X : view on 2d ndarray, shape (n_samples, n_target_features) + The grid points on which the partial dependence should be + evaluated. + target_features : view on 1d ndarray, shape (n_target_features) + The set of target features for which the partial dependence + should be evaluated. + out : view on 1d ndarray, shape (n_samples) + The value of the partial dependence function on each grid + point. + """ + cdef: + double[::1] weight_stack = np.zeros(self.node_count, + dtype=np.float64) + SIZE_t[::1] node_idx_stack = np.zeros(self.node_count, + dtype=np.intp) + SIZE_t sample_idx + SIZE_t feature_idx + int stack_size + double left_sample_frac + double current_weight + double total_weight # used for sanity check only + Node *current_node # use a pointer to avoid copying attributes + SIZE_t current_node_idx + bint is_target_feature + SIZE_t _TREE_LEAF = TREE_LEAF # to avoid python interactions + + for sample_idx in range(X.shape[0]): + # init stacks for current sample + stack_size = 1 + node_idx_stack[0] = 0 # root node + weight_stack[0] = 1 # all the samples are in the root node + total_weight = 0 + + while stack_size > 0: + # pop the stack + stack_size -= 1 + current_node_idx = node_idx_stack[stack_size] + current_node = &self.nodes[current_node_idx] + + if current_node.left_child == _TREE_LEAF: + # leaf node + out[sample_idx] += (weight_stack[stack_size] * + self.value[current_node_idx]) + total_weight += weight_stack[stack_size] + else: + # non-leaf node + + # determine if the split feature is a target feature + is_target_feature = False + for feature_idx in range(target_features.shape[0]): + if target_features[feature_idx] == current_node.feature: + is_target_feature = True + break + + if is_target_feature: + # In this case, we push left or right child on stack + if X[sample_idx, feature_idx] <= current_node.threshold: + node_idx_stack[stack_size] = current_node.left_child + else: + node_idx_stack[stack_size] = current_node.right_child + stack_size += 1 + else: + # In this case, we push both children onto the stack, + # and give a weight proportional to the number of + # samples going through each branch. + + # push left child + node_idx_stack[stack_size] = current_node.left_child + left_sample_frac = ( + self.nodes[current_node.left_child].weighted_n_node_samples / + current_node.weighted_n_node_samples) + current_weight = weight_stack[stack_size] + weight_stack[stack_size] = current_weight * left_sample_frac + stack_size += 1 + + # push right child + node_idx_stack[stack_size] = current_node.right_child + weight_stack[stack_size] = ( + current_weight * (1 - left_sample_frac)) + stack_size += 1 + + # Sanity check. Should never happen. + if not (0.999 < total_weight < 1.001): + raise ValueError("Total weight should be 1.0 but was %.9f" % + total_weight) + + +def _check_n_classes(n_classes, expected_dtype): + if n_classes.ndim != 1: + raise ValueError( + f"Wrong dimensions for n_classes from the pickle: " + f"expected 1, got {n_classes.ndim}" + ) + + if n_classes.dtype == expected_dtype: + return n_classes + + # Handles both different endianness and different bitness + if n_classes.dtype.kind == "i" and n_classes.dtype.itemsize in [4, 8]: + return n_classes.astype(expected_dtype, casting="same_kind") + + raise ValueError( + "n_classes from the pickle has an incompatible dtype:\n" + f"- expected: {expected_dtype}\n" + f"- got: {n_classes.dtype}" + ) + + +def _check_value_ndarray(value_ndarray, expected_dtype, expected_shape): + if value_ndarray.shape != expected_shape: + raise ValueError( + "Wrong shape for value array from the pickle: " + f"expected {expected_shape}, got {value_ndarray.shape}" + ) + + if not value_ndarray.flags.c_contiguous: + raise ValueError( + "value array from the pickle should be a C-contiguous array" + ) + + if value_ndarray.dtype == expected_dtype: + return value_ndarray + + # Handles different endianness + if value_ndarray.dtype.str.endswith('f8'): + return value_ndarray.astype(expected_dtype, casting='equiv') + + raise ValueError( + "value array from the pickle has an incompatible dtype:\n" + f"- expected: {expected_dtype}\n" + f"- got: {value_ndarray.dtype}" + ) + + +def _dtype_to_dict(dtype): + return {name: dt.str for name, (dt, *rest) in dtype.fields.items()} + + +def _dtype_dict_with_modified_bitness(dtype_dict): + # field names in Node struct with SIZE_t types (see sklearn/tree/_tree.pxd) + indexing_field_names = ["left_child", "right_child", "feature", "n_node_samples"] + + expected_dtype_size = str(struct.calcsize("P")) + allowed_dtype_size = "8" if expected_dtype_size == "4" else "4" + + allowed_dtype_dict = dtype_dict.copy() + for name in indexing_field_names: + allowed_dtype_dict[name] = allowed_dtype_dict[name].replace( + expected_dtype_size, allowed_dtype_size + ) + + return allowed_dtype_dict + + +def _all_compatible_dtype_dicts(dtype): + # The Cython code for decision trees uses platform-specific SIZE_t + # typed indexing fields that correspond to either i4 or i8 dtypes for + # the matching fields in the numpy array depending on the bitness of + # the platform (32 bit or 64 bit respectively). + # + # We need to cast the indexing fields of the NODE_DTYPE-dtyped array at + # pickle load time to enable cross-bitness deployment scenarios. We + # typically want to make it possible to run the expensive fit method of + # a tree estimator on a 64 bit server platform, pickle the estimator + # for deployment and run the predict method of a low power 32 bit edge + # platform. + # + # A similar thing happens for endianness, the machine where the pickle was + # saved can have a different endianness than the machine where the pickle + # is loaded + + dtype_dict = _dtype_to_dict(dtype) + dtype_dict_with_modified_bitness = _dtype_dict_with_modified_bitness(dtype_dict) + dtype_dict_with_modified_endianness = _dtype_to_dict(dtype.newbyteorder()) + dtype_dict_with_modified_bitness_and_endianness = _dtype_dict_with_modified_bitness( + dtype_dict_with_modified_endianness + ) + + return [ + dtype_dict, + dtype_dict_with_modified_bitness, + dtype_dict_with_modified_endianness, + dtype_dict_with_modified_bitness_and_endianness, + ] + + +def _check_node_ndarray(node_ndarray, expected_dtype): + if node_ndarray.ndim != 1: + raise ValueError( + "Wrong dimensions for node array from the pickle: " + f"expected 1, got {node_ndarray.ndim}" + ) + + if not node_ndarray.flags.c_contiguous: + raise ValueError( + "node array from the pickle should be a C-contiguous array" + ) + + node_ndarray_dtype = node_ndarray.dtype + if node_ndarray_dtype == expected_dtype: + return node_ndarray + + node_ndarray_dtype_dict = _dtype_to_dict(node_ndarray_dtype) + all_compatible_dtype_dicts = _all_compatible_dtype_dicts(expected_dtype) + + if node_ndarray_dtype_dict not in all_compatible_dtype_dicts: + raise ValueError( + "node array from the pickle has an incompatible dtype:\n" + f"- expected: {expected_dtype}\n" + f"- got : {node_ndarray_dtype}" + ) + + return node_ndarray.astype(expected_dtype, casting="same_kind") + + +# ============================================================================= +# Build Pruned Tree +# ============================================================================= + + +cdef class _CCPPruneController: + """Base class used by build_pruned_tree_ccp and ccp_pruning_path + to control pruning. + """ + cdef bint stop_pruning(self, DOUBLE_t effective_alpha) noexcept nogil: + """Return 1 to stop pruning and 0 to continue pruning""" + return 0 + + cdef void save_metrics(self, DOUBLE_t effective_alpha, + DOUBLE_t subtree_impurities) noexcept nogil: + """Save metrics when pruning""" + pass + + cdef void after_pruning(self, unsigned char[:] in_subtree) noexcept nogil: + """Called after pruning""" + pass + + +cdef class _AlphaPruner(_CCPPruneController): + """Use alpha to control when to stop pruning.""" + cdef DOUBLE_t ccp_alpha + cdef SIZE_t capacity + + def __cinit__(self, DOUBLE_t ccp_alpha): + self.ccp_alpha = ccp_alpha + self.capacity = 0 + + cdef bint stop_pruning(self, DOUBLE_t effective_alpha) noexcept nogil: + # The subtree on the previous iteration has the greatest ccp_alpha + # less than or equal to self.ccp_alpha + return self.ccp_alpha < effective_alpha + + cdef void after_pruning(self, unsigned char[:] in_subtree) noexcept nogil: + """Updates the number of leaves in subtree""" + for i in range(in_subtree.shape[0]): + if in_subtree[i]: + self.capacity += 1 + + +cdef class _PathFinder(_CCPPruneController): + """Record metrics used to return the cost complexity path.""" + cdef DOUBLE_t[:] ccp_alphas + cdef DOUBLE_t[:] impurities + cdef UINT32_t count + + def __cinit__(self, int node_count): + self.ccp_alphas = np.zeros(shape=(node_count), dtype=np.float64) + self.impurities = np.zeros(shape=(node_count), dtype=np.float64) + self.count = 0 + + cdef void save_metrics(self, + DOUBLE_t effective_alpha, + DOUBLE_t subtree_impurities) noexcept nogil: + self.ccp_alphas[self.count] = effective_alpha + self.impurities[self.count] = subtree_impurities + self.count += 1 + + +cdef struct CostComplexityPruningRecord: + SIZE_t node_idx + SIZE_t parent + +cdef _cost_complexity_prune(unsigned char[:] leaves_in_subtree, # OUT + Tree orig_tree, + _CCPPruneController controller): + """Perform cost complexity pruning. + + This function takes an already grown tree, `orig_tree` and outputs a + boolean mask `leaves_in_subtree` which are the leaves in the pruned tree. + During the pruning process, the controller is passed the effective alpha and + the subtree impurities. Furthermore, the controller signals when to stop + pruning. + + Parameters + ---------- + leaves_in_subtree : unsigned char[:] + Output for leaves of subtree + orig_tree : Tree + Original tree + ccp_controller : _CCPPruneController + Cost complexity controller + """ + + cdef: + SIZE_t i + SIZE_t n_nodes = orig_tree.node_count + # prior probability using weighted samples + DOUBLE_t[:] weighted_n_node_samples = orig_tree.weighted_n_node_samples + DOUBLE_t total_sum_weights = weighted_n_node_samples[0] + DOUBLE_t[:] impurity = orig_tree.impurity + # weighted impurity of each node + DOUBLE_t[:] r_node = np.empty(shape=n_nodes, dtype=np.float64) + + SIZE_t[:] child_l = orig_tree.children_left + SIZE_t[:] child_r = orig_tree.children_right + SIZE_t[:] parent = np.zeros(shape=n_nodes, dtype=np.intp) + + stack[CostComplexityPruningRecord] ccp_stack + CostComplexityPruningRecord stack_record + SIZE_t node_idx + stack[SIZE_t] node_indices_stack + + SIZE_t[:] n_leaves = np.zeros(shape=n_nodes, dtype=np.intp) + DOUBLE_t[:] r_branch = np.zeros(shape=n_nodes, dtype=np.float64) + DOUBLE_t current_r + SIZE_t leaf_idx + SIZE_t parent_idx + + # candidate nodes that can be pruned + unsigned char[:] candidate_nodes = np.zeros(shape=n_nodes, + dtype=np.uint8) + # nodes in subtree + unsigned char[:] in_subtree = np.ones(shape=n_nodes, dtype=np.uint8) + SIZE_t pruned_branch_node_idx + DOUBLE_t subtree_alpha + DOUBLE_t effective_alpha + SIZE_t n_pruned_leaves + DOUBLE_t r_diff + DOUBLE_t max_float64 = np.finfo(np.float64).max + + # find parent node ids and leaves + with nogil: + + for i in range(r_node.shape[0]): + r_node[i] = ( + weighted_n_node_samples[i] * impurity[i] / total_sum_weights) + + # Push the root node + ccp_stack.push({"node_idx": 0, "parent": _TREE_UNDEFINED}) + + while not ccp_stack.empty(): + stack_record = ccp_stack.top() + ccp_stack.pop() + + node_idx = stack_record.node_idx + parent[node_idx] = stack_record.parent + + if child_l[node_idx] == _TREE_LEAF: + # ... and child_r[node_idx] == _TREE_LEAF: + leaves_in_subtree[node_idx] = 1 + else: + ccp_stack.push({"node_idx": child_l[node_idx], "parent": node_idx}) + ccp_stack.push({"node_idx": child_r[node_idx], "parent": node_idx}) + + # computes number of leaves in all branches and the overall impurity of + # the branch. The overall impurity is the sum of r_node in its leaves. + for leaf_idx in range(leaves_in_subtree.shape[0]): + if not leaves_in_subtree[leaf_idx]: + continue + r_branch[leaf_idx] = r_node[leaf_idx] + + # bubble up values to ancestor nodes + current_r = r_node[leaf_idx] + while leaf_idx != 0: + parent_idx = parent[leaf_idx] + r_branch[parent_idx] += current_r + n_leaves[parent_idx] += 1 + leaf_idx = parent_idx + + for i in range(leaves_in_subtree.shape[0]): + candidate_nodes[i] = not leaves_in_subtree[i] + + # save metrics before pruning + controller.save_metrics(0.0, r_branch[0]) + + # while root node is not a leaf + while candidate_nodes[0]: + + # computes ccp_alpha for subtrees and finds the minimal alpha + effective_alpha = max_float64 + for i in range(n_nodes): + if not candidate_nodes[i]: + continue + subtree_alpha = (r_node[i] - r_branch[i]) / (n_leaves[i] - 1) + if subtree_alpha < effective_alpha: + effective_alpha = subtree_alpha + pruned_branch_node_idx = i + + if controller.stop_pruning(effective_alpha): + break + + node_indices_stack.push(pruned_branch_node_idx) + + # descendants of branch are not in subtree + while not node_indices_stack.empty(): + node_idx = node_indices_stack.top() + node_indices_stack.pop() + + if not in_subtree[node_idx]: + continue # branch has already been marked for pruning + candidate_nodes[node_idx] = 0 + leaves_in_subtree[node_idx] = 0 + in_subtree[node_idx] = 0 + + if child_l[node_idx] != _TREE_LEAF: + # ... and child_r[node_idx] != _TREE_LEAF: + node_indices_stack.push(child_l[node_idx]) + node_indices_stack.push(child_r[node_idx]) + leaves_in_subtree[pruned_branch_node_idx] = 1 + in_subtree[pruned_branch_node_idx] = 1 + + # updates number of leaves + n_pruned_leaves = n_leaves[pruned_branch_node_idx] - 1 + n_leaves[pruned_branch_node_idx] = 0 + + # computes the increase in r_branch to bubble up + r_diff = r_node[pruned_branch_node_idx] - r_branch[pruned_branch_node_idx] + r_branch[pruned_branch_node_idx] = r_node[pruned_branch_node_idx] + + # bubble up values to ancestors + node_idx = parent[pruned_branch_node_idx] + while node_idx != _TREE_UNDEFINED: + n_leaves[node_idx] -= n_pruned_leaves + r_branch[node_idx] += r_diff + node_idx = parent[node_idx] + + controller.save_metrics(effective_alpha, r_branch[0]) + + controller.after_pruning(in_subtree) + + +def _build_pruned_tree_ccp( + Tree tree, # OUT + Tree orig_tree, + DOUBLE_t ccp_alpha +): + """Build a pruned tree from the original tree using cost complexity + pruning. + + The values and nodes from the original tree are copied into the pruned + tree. + + Parameters + ---------- + tree : Tree + Location to place the pruned tree + orig_tree : Tree + Original tree + ccp_alpha : positive double + Complexity parameter. The subtree with the largest cost complexity + that is smaller than ``ccp_alpha`` will be chosen. By default, + no pruning is performed. + """ + + cdef: + SIZE_t n_nodes = orig_tree.node_count + unsigned char[:] leaves_in_subtree = np.zeros( + shape=n_nodes, dtype=np.uint8) + + pruning_controller = _AlphaPruner(ccp_alpha=ccp_alpha) + + _cost_complexity_prune(leaves_in_subtree, orig_tree, pruning_controller) + + _build_pruned_tree(tree, orig_tree, leaves_in_subtree, + pruning_controller.capacity) + + +def ccp_pruning_path(Tree orig_tree): + """Computes the cost complexity pruning path. + + Parameters + ---------- + tree : Tree + Original tree. + + Returns + ------- + path_info : dict + Information about pruning path with attributes: + + ccp_alphas : ndarray + Effective alphas of subtree during pruning. + + impurities : ndarray + Sum of the impurities of the subtree leaves for the + corresponding alpha value in ``ccp_alphas``. + """ + cdef: + unsigned char[:] leaves_in_subtree = np.zeros( + shape=orig_tree.node_count, dtype=np.uint8) + + path_finder = _PathFinder(orig_tree.node_count) + + _cost_complexity_prune(leaves_in_subtree, orig_tree, path_finder) + + cdef: + UINT32_t total_items = path_finder.count + DOUBLE_t[:] ccp_alphas = np.empty(shape=total_items, dtype=np.float64) + DOUBLE_t[:] impurities = np.empty(shape=total_items, dtype=np.float64) + UINT32_t count = 0 + + while count < total_items: + ccp_alphas[count] = path_finder.ccp_alphas[count] + impurities[count] = path_finder.impurities[count] + count += 1 + + return { + 'ccp_alphas': np.asarray(ccp_alphas), + 'impurities': np.asarray(impurities), + } + + +cdef struct BuildPrunedRecord: + SIZE_t start + SIZE_t depth + SIZE_t parent + bint is_left + +cdef _build_pruned_tree( + Tree tree, # OUT + Tree orig_tree, + const unsigned char[:] leaves_in_subtree, + SIZE_t capacity +): + """Build a pruned tree. + + Build a pruned tree from the original tree by transforming the nodes in + ``leaves_in_subtree`` into leaves. + + Parameters + ---------- + tree : Tree + Location to place the pruned tree + orig_tree : Tree + Original tree + leaves_in_subtree : unsigned char memoryview, shape=(node_count, ) + Boolean mask for leaves to include in subtree + capacity : SIZE_t + Number of nodes to initially allocate in pruned tree + """ + tree._resize(capacity) + + cdef: + SIZE_t orig_node_id + SIZE_t new_node_id + SIZE_t depth + SIZE_t parent + bint is_left + bint is_leaf + + # value_stride for original tree and new tree are the same + SIZE_t value_stride = orig_tree.value_stride + SIZE_t max_depth_seen = -1 + int rc = 0 + Node* node + double* orig_value_ptr + double* new_value_ptr + + stack[BuildPrunedRecord] prune_stack + BuildPrunedRecord stack_record + + with nogil: + # push root node onto stack + prune_stack.push({"start": 0, "depth": 0, "parent": _TREE_UNDEFINED, "is_left": 0}) + + while not prune_stack.empty(): + stack_record = prune_stack.top() + prune_stack.pop() + + orig_node_id = stack_record.start + depth = stack_record.depth + parent = stack_record.parent + is_left = stack_record.is_left + + is_leaf = leaves_in_subtree[orig_node_id] + node = &orig_tree.nodes[orig_node_id] + + new_node_id = tree._add_node( + parent, is_left, is_leaf, node.feature, node.threshold, + node.impurity, node.n_node_samples, + node.weighted_n_node_samples, node.missing_go_to_left) + + if new_node_id == INTPTR_MAX: + rc = -1 + break + + # copy value from original tree to new tree + orig_value_ptr = orig_tree.value + value_stride * orig_node_id + new_value_ptr = tree.value + value_stride * new_node_id + memcpy(new_value_ptr, orig_value_ptr, sizeof(double) * value_stride) + + if not is_leaf: + # Push right child on stack + prune_stack.push({"start": node.right_child, "depth": depth + 1, + "parent": new_node_id, "is_left": 0}) + # push left child on stack + prune_stack.push({"start": node.left_child, "depth": depth + 1, + "parent": new_node_id, "is_left": 1}) + + if depth > max_depth_seen: + max_depth_seen = depth + + if rc >= 0: + tree.max_depth = max_depth_seen + if rc == -1: + raise MemoryError("pruning tree") \ No newline at end of file diff --git a/ivy/functional/frontends/sklearn/tree/_tree_ivy.pyx b/ivy/functional/frontends/sklearn/tree/_tree_ivy.pyx new file mode 100644 index 0000000000000..f436c919f8bc2 --- /dev/null +++ b/ivy/functional/frontends/sklearn/tree/_tree_ivy.pyx @@ -0,0 +1,1833 @@ +# Authors: Gilles Louppe +# Peter Prettenhofer +# Brian Holt +# Noel Dawe +# Satrajit Gosh +# Lars Buitinck +# Arnaud Joly +# Joel Nothman +# Fares Hedayati +# Jacob Schreiber +# Nelson Liu +# +# License: BSD 3 clause + +from cpython cimport Py_INCREF, PyObject, PyTypeObject + +from libc.stdlib cimport free +from libc.string cimport memcpy +from libc.string cimport memset +from libc.stdint cimport INTPTR_MAX +from libc.math cimport isnan +from libcpp.vector cimport vector +from libcpp.algorithm cimport pop_heap +from libcpp.algorithm cimport push_heap +from libcpp cimport bool + +import struct + +import numpy as np +cimport numpy as cnp +cnp.import_array() + +from scipy.sparse import issparse +from scipy.sparse import csr_matrix +from scipy.sparse import isspmatrix_csr + +from ._utils cimport safe_realloc +from ._utils cimport sizet_ptr_to_ndarray + +cdef extern from "numpy/arrayobject.h": + object PyArray_NewFromDescr(PyTypeObject* subtype, cnp.dtype descr, + int nd, cnp.npy_intp* dims, + cnp.npy_intp* strides, + void* data, int flags, object obj) + int PyArray_SetBaseObject(cnp.ndarray arr, PyObject* obj) + +cdef extern from "" namespace "std" nogil: + cdef cppclass stack[T]: + ctypedef T value_type + stack() except + + bint empty() + void pop() + void push(T&) except + # Raise c++ exception for bad_alloc -> MemoryError + T& top() + +# ============================================================================= +# Types and constants +# ============================================================================= + +from numpy import float32 as DTYPE +from numpy import float64 as DOUBLE + +cdef double INFINITY = np.inf +cdef double EPSILON = np.finfo('double').eps + +# Some handy constants (BestFirstTreeBuilder) +cdef int IS_FIRST = 1 +cdef int IS_NOT_FIRST = 0 +cdef int IS_LEFT = 1 +cdef int IS_NOT_LEFT = 0 + +TREE_LEAF = -1 +TREE_UNDEFINED = -2 +cdef SIZE_t _TREE_LEAF = TREE_LEAF +cdef SIZE_t _TREE_UNDEFINED = TREE_UNDEFINED + +# Build the corresponding numpy dtype for Node. +# This works by casting `dummy` to an array of Node of length 1, which numpy +# can construct a `dtype`-object for. See https://stackoverflow.com/q/62448946 +# for a more detailed explanation. +cdef Node dummy +NODE_DTYPE = np.asarray((&dummy)).dtype + +# ============================================================================= +# TreeBuilder +# ============================================================================= + +cdef class TreeBuilder: + """Interface for different tree building strategies.""" + + cpdef build( + self, + Tree tree, + object X, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight=None, + const unsigned char[::1] missing_values_in_feature_mask=None, + ): + """Build a decision tree from the training set (X, y).""" + pass + + cdef inline _check_input( + self, + object X, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight, + ): + """Check input dtype, layout and format""" + if issparse(X): + X = X.tocsc() + X.sort_indices() + + if X.data.dtype != DTYPE: + X.data = np.ascontiguousarray(X.data, dtype=DTYPE) + + if X.indices.dtype != np.int32 or X.indptr.dtype != np.int32: + raise ValueError("No support for np.int64 index based " + "sparse matrices") + + elif X.dtype != DTYPE: + # since we have to copy we will make it fortran for efficiency + X = np.asfortranarray(X, dtype=DTYPE) + + # TODO: This check for y seems to be redundant, as it is also + # present in the BaseDecisionTree's fit method, and therefore + # can be removed. + if y.base.dtype != DOUBLE or not y.base.flags.contiguous: + y = np.ascontiguousarray(y, dtype=DOUBLE) + + if ( + sample_weight is not None and + ( + sample_weight.base.dtype != DOUBLE or + not sample_weight.base.flags.contiguous + ) + ): + sample_weight = np.asarray(sample_weight, dtype=DOUBLE, order="C") + + return X, y, sample_weight + +# Depth first builder --------------------------------------------------------- +# A record on the stack for depth-first tree growing +cdef struct StackRecord: + SIZE_t start + SIZE_t end + SIZE_t depth + SIZE_t parent + bint is_left + double impurity + SIZE_t n_constant_features + + + + + + + +cdef class DepthFirstTreeBuilder(TreeBuilder): + """Build a decision tree in depth-first fashion.""" + + def __cinit__(self, Splitter splitter, SIZE_t min_samples_split, + SIZE_t min_samples_leaf, double min_weight_leaf, + SIZE_t max_depth, double min_impurity_decrease): + self.splitter = splitter + self.min_samples_split = min_samples_split + self.min_samples_leaf = min_samples_leaf + self.min_weight_leaf = min_weight_leaf + self.max_depth = max_depth + self.min_impurity_decrease = min_impurity_decrease + + cpdef build( + self, + Tree tree, + object X, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight=None, + const unsigned char[::1] missing_values_in_feature_mask=None, + ): + """Build a decision tree from the training set (X, y).""" + + # check input + X, y, sample_weight = self._check_input(X, y, sample_weight) + + # Initial capacity + cdef int init_capacity + + if tree.max_depth <= 10: + init_capacity = (2 ** (tree.max_depth + 1)) - 1 + else: + init_capacity = 2047 + + tree._resize(init_capacity) + + # Parameters + cdef Splitter splitter = self.splitter + cdef SIZE_t max_depth = self.max_depth + cdef SIZE_t min_samples_leaf = self.min_samples_leaf + cdef double min_weight_leaf = self.min_weight_leaf + cdef SIZE_t min_samples_split = self.min_samples_split + cdef double min_impurity_decrease = self.min_impurity_decrease + + # Recursive partition (without actual recursion) + splitter.init(X, y, sample_weight, missing_values_in_feature_mask) + + cdef SIZE_t start + cdef SIZE_t end + cdef SIZE_t depth + cdef SIZE_t parent + cdef bint is_left + cdef SIZE_t n_node_samples = splitter.n_samples + cdef double weighted_n_node_samples + cdef SplitRecord split + cdef SIZE_t node_id + + cdef double impurity = INFINITY + cdef SIZE_t n_constant_features + cdef bint is_leaf + cdef bint first = 1 + cdef SIZE_t max_depth_seen = -1 + cdef int rc = 0 + + cdef stack[StackRecord] builder_stack + cdef StackRecord stack_record + + with nogil: + # push root node onto stack + builder_stack.push({ + "start": 0, + "end": n_node_samples, + "depth": 0, + "parent": _TREE_UNDEFINED, + "is_left": 0, + "impurity": INFINITY, + "n_constant_features": 0}) + + while not builder_stack.empty(): + stack_record = builder_stack.top() + builder_stack.pop() + + start = stack_record.start + end = stack_record.end + depth = stack_record.depth + parent = stack_record.parent + is_left = stack_record.is_left + impurity = stack_record.impurity + n_constant_features = stack_record.n_constant_features + + n_node_samples = end - start + splitter.node_reset(start, end, &weighted_n_node_samples) + + is_leaf = (depth >= max_depth or + n_node_samples < min_samples_split or + n_node_samples < 2 * min_samples_leaf or + weighted_n_node_samples < 2 * min_weight_leaf) + + if first: + impurity = splitter.node_impurity() + first = 0 + + # impurity == 0 with tolerance due to rounding errors + is_leaf = is_leaf or impurity <= EPSILON + + if not is_leaf: + splitter.node_split(impurity, &split, &n_constant_features) + # If EPSILON=0 in the below comparison, float precision + # issues stop splitting, producing trees that are + # dissimilar to v0.18 + is_leaf = (is_leaf or split.pos >= end or + (split.improvement + EPSILON < + min_impurity_decrease)) + + node_id = tree._add_node(parent, is_left, is_leaf, split.feature, + split.threshold, impurity, n_node_samples, + weighted_n_node_samples, + split.missing_go_to_left) + + if node_id == INTPTR_MAX: + rc = -1 + break + + # Store value for all nodes, to facilitate tree/model + # inspection and interpretation + splitter.node_value(tree.value + node_id * tree.value_stride) + + if not is_leaf: + # Push right child on stack + builder_stack.push({ + "start": split.pos, + "end": end, + "depth": depth + 1, + "parent": node_id, + "is_left": 0, + "impurity": split.impurity_right, + "n_constant_features": n_constant_features}) + + # Push left child on stack + builder_stack.push({ + "start": start, + "end": split.pos, + "depth": depth + 1, + "parent": node_id, + "is_left": 1, + "impurity": split.impurity_left, + "n_constant_features": n_constant_features}) + + if depth > max_depth_seen: + max_depth_seen = depth + + if rc >= 0: + rc = tree._resize_c(tree.node_count) + + if rc >= 0: + tree.max_depth = max_depth_seen + if rc == -1: + raise MemoryError() + + +# Best first builder ---------------------------------------------------------- +cdef struct FrontierRecord: + # Record of information of a Node, the frontier for a split. Those records are + # maintained in a heap to access the Node with the best improvement in impurity, + # allowing growing trees greedily on this improvement. + SIZE_t node_id + SIZE_t start + SIZE_t end + SIZE_t pos + SIZE_t depth + bint is_leaf + double impurity + double impurity_left + double impurity_right + double improvement + +cdef inline bool _compare_records( + const FrontierRecord& left, + const FrontierRecord& right, +): + return left.improvement < right.improvement + +cdef inline void _add_to_frontier( + FrontierRecord rec, + vector[FrontierRecord]& frontier, +) noexcept nogil: + """Adds record `rec` to the priority queue `frontier`.""" + frontier.push_back(rec) + push_heap(frontier.begin(), frontier.end(), &_compare_records) + + +cdef class BestFirstTreeBuilder(TreeBuilder): + """Build a decision tree in best-first fashion. + + The best node to expand is given by the node at the frontier that has the + highest impurity improvement. + """ + cdef SIZE_t max_leaf_nodes + + def __cinit__(self, Splitter splitter, SIZE_t min_samples_split, + SIZE_t min_samples_leaf, min_weight_leaf, + SIZE_t max_depth, SIZE_t max_leaf_nodes, + double min_impurity_decrease): + self.splitter = splitter + self.min_samples_split = min_samples_split + self.min_samples_leaf = min_samples_leaf + self.min_weight_leaf = min_weight_leaf + self.max_depth = max_depth + self.max_leaf_nodes = max_leaf_nodes + self.min_impurity_decrease = min_impurity_decrease + + cpdef build( + self, + Tree tree, + object X, + const DOUBLE_t[:, ::1] y, + const DOUBLE_t[:] sample_weight=None, + const unsigned char[::1] missing_values_in_feature_mask=None, + ): + """Build a decision tree from the training set (X, y).""" + + # check input + X, y, sample_weight = self._check_input(X, y, sample_weight) + + # Parameters + cdef Splitter splitter = self.splitter + cdef SIZE_t max_leaf_nodes = self.max_leaf_nodes + + # Recursive partition (without actual recursion) + splitter.init(X, y, sample_weight, missing_values_in_feature_mask) + + cdef vector[FrontierRecord] frontier + cdef FrontierRecord record + cdef FrontierRecord split_node_left + cdef FrontierRecord split_node_right + + cdef SIZE_t n_node_samples = splitter.n_samples + cdef SIZE_t max_split_nodes = max_leaf_nodes - 1 + cdef bint is_leaf + cdef SIZE_t max_depth_seen = -1 + cdef int rc = 0 + cdef Node* node + + # Initial capacity + cdef SIZE_t init_capacity = max_split_nodes + max_leaf_nodes + tree._resize(init_capacity) + + with nogil: + # add root to frontier + rc = self._add_split_node(splitter, tree, 0, n_node_samples, + INFINITY, IS_FIRST, IS_LEFT, NULL, 0, + &split_node_left) + if rc >= 0: + _add_to_frontier(split_node_left, frontier) + + while not frontier.empty(): + pop_heap(frontier.begin(), frontier.end(), &_compare_records) + record = frontier.back() + frontier.pop_back() + + node = &tree.nodes[record.node_id] + is_leaf = (record.is_leaf or max_split_nodes <= 0) + + if is_leaf: + # Node is not expandable; set node as leaf + node.left_child = _TREE_LEAF + node.right_child = _TREE_LEAF + node.feature = _TREE_UNDEFINED + node.threshold = _TREE_UNDEFINED + + else: + # Node is expandable + + # Decrement number of split nodes available + max_split_nodes -= 1 + + # Compute left split node + rc = self._add_split_node(splitter, tree, + record.start, record.pos, + record.impurity_left, + IS_NOT_FIRST, IS_LEFT, node, + record.depth + 1, + &split_node_left) + if rc == -1: + break + + # tree.nodes may have changed + node = &tree.nodes[record.node_id] + + # Compute right split node + rc = self._add_split_node(splitter, tree, record.pos, + record.end, + record.impurity_right, + IS_NOT_FIRST, IS_NOT_LEFT, node, + record.depth + 1, + &split_node_right) + if rc == -1: + break + + # Add nodes to queue + _add_to_frontier(split_node_left, frontier) + _add_to_frontier(split_node_right, frontier) + + if record.depth > max_depth_seen: + max_depth_seen = record.depth + + if rc >= 0: + rc = tree._resize_c(tree.node_count) + + if rc >= 0: + tree.max_depth = max_depth_seen + + if rc == -1: + raise MemoryError() + + cdef inline int _add_split_node(self, Splitter splitter, Tree tree, + SIZE_t start, SIZE_t end, double impurity, + bint is_first, bint is_left, Node* parent, + SIZE_t depth, + FrontierRecord* res) except -1 nogil: + """Adds node w/ partition ``[start, end)`` to the frontier. """ + cdef SplitRecord split + cdef SIZE_t node_id + cdef SIZE_t n_node_samples + cdef SIZE_t n_constant_features = 0 + cdef double min_impurity_decrease = self.min_impurity_decrease + cdef double weighted_n_node_samples + cdef bint is_leaf + + splitter.node_reset(start, end, &weighted_n_node_samples) + + if is_first: + impurity = splitter.node_impurity() + + n_node_samples = end - start + is_leaf = (depth >= self.max_depth or + n_node_samples < self.min_samples_split or + n_node_samples < 2 * self.min_samples_leaf or + weighted_n_node_samples < 2 * self.min_weight_leaf or + impurity <= EPSILON # impurity == 0 with tolerance + ) + + if not is_leaf: + splitter.node_split(impurity, &split, &n_constant_features) + # If EPSILON=0 in the below comparison, float precision issues stop + # splitting early, producing trees that are dissimilar to v0.18 + is_leaf = (is_leaf or split.pos >= end or + split.improvement + EPSILON < min_impurity_decrease) + + node_id = tree._add_node(parent - tree.nodes + if parent != NULL + else _TREE_UNDEFINED, + is_left, is_leaf, + split.feature, split.threshold, impurity, n_node_samples, + weighted_n_node_samples, + split.missing_go_to_left) + if node_id == INTPTR_MAX: + return -1 + + # compute values also for split nodes (might become leafs later). + splitter.node_value(tree.value + node_id * tree.value_stride) + + res.node_id = node_id + res.start = start + res.end = end + res.depth = depth + res.impurity = impurity + + if not is_leaf: + # is split node + res.pos = split.pos + res.is_leaf = 0 + res.improvement = split.improvement + res.impurity_left = split.impurity_left + res.impurity_right = split.impurity_right + + else: + # is leaf => 0 improvement + res.pos = end + res.is_leaf = 1 + res.improvement = 0.0 + res.impurity_left = impurity + res.impurity_right = impurity + + return 0 + + +# ============================================================================= +# Tree +# ============================================================================= + +cdef class Tree: + """Array-based representation of a binary decision tree. + + The binary tree is represented as a number of parallel arrays. The i-th + element of each array holds information about the node `i`. Node 0 is the + tree's root. You can find a detailed description of all arrays in + `_tree.pxd`. NOTE: Some of the arrays only apply to either leaves or split + nodes, resp. In this case the values of nodes of the other type are + arbitrary! + + Attributes + ---------- + node_count : int + The number of nodes (internal nodes + leaves) in the tree. + + capacity : int + The current capacity (i.e., size) of the arrays, which is at least as + great as `node_count`. + + max_depth : int + The depth of the tree, i.e. the maximum depth of its leaves. + + children_left : array of int, shape [node_count] + children_left[i] holds the node id of the left child of node i. + For leaves, children_left[i] == TREE_LEAF. Otherwise, + children_left[i] > i. This child handles the case where + X[:, feature[i]] <= threshold[i]. + + children_right : array of int, shape [node_count] + children_right[i] holds the node id of the right child of node i. + For leaves, children_right[i] == TREE_LEAF. Otherwise, + children_right[i] > i. This child handles the case where + X[:, feature[i]] > threshold[i]. + + feature : array of int, shape [node_count] + feature[i] holds the feature to split on, for the internal node i. + + threshold : array of double, shape [node_count] + threshold[i] holds the threshold for the internal node i. + + value : array of double, shape [node_count, n_outputs, max_n_classes] + Contains the constant prediction value of each node. + + impurity : array of double, shape [node_count] + impurity[i] holds the impurity (i.e., the value of the splitting + criterion) at node i. + + n_node_samples : array of int, shape [node_count] + n_node_samples[i] holds the number of training samples reaching node i. + + weighted_n_node_samples : array of double, shape [node_count] + weighted_n_node_samples[i] holds the weighted number of training samples + reaching node i. + """ + # Wrap for outside world. + # WARNING: these reference the current `nodes` and `value` buffers, which + # must not be freed by a subsequent memory allocation. + # (i.e. through `_resize` or `__setstate__`) + @property + def n_classes(self): + return sizet_ptr_to_ndarray(self.n_classes, self.n_outputs) + + @property + def children_left(self): + return self._get_node_ndarray()['left_child'][:self.node_count] + + @property + def children_right(self): + return self._get_node_ndarray()['right_child'][:self.node_count] + + @property + def n_leaves(self): + return np.sum(np.logical_and( + self.children_left == -1, + self.children_right == -1)) + + @property + def feature(self): + return self._get_node_ndarray()['feature'][:self.node_count] + + @property + def threshold(self): + return self._get_node_ndarray()['threshold'][:self.node_count] + + @property + def impurity(self): + return self._get_node_ndarray()['impurity'][:self.node_count] + + @property + def n_node_samples(self): + return self._get_node_ndarray()['n_node_samples'][:self.node_count] + + @property + def weighted_n_node_samples(self): + return self._get_node_ndarray()['weighted_n_node_samples'][:self.node_count] + + @property + def missing_go_to_left(self): + return self._get_node_ndarray()['missing_go_to_left'][:self.node_count] + + @property + def value(self): + return self._get_value_ndarray()[:self.node_count] + + # TODO: Convert n_classes to cython.integral memory view once + # https://github.com/cython/cython/issues/5243 is fixed + def __cinit__(self, int n_features, cnp.ndarray n_classes, int n_outputs): + """Constructor.""" + cdef SIZE_t dummy = 0 + size_t_dtype = np.array(dummy).dtype + + n_classes = _check_n_classes(n_classes, size_t_dtype) + + # Input/Output layout + self.n_features = n_features + self.n_outputs = n_outputs + self.n_classes = NULL + safe_realloc(&self.n_classes, n_outputs) + + self.max_n_classes = np.max(n_classes) + self.value_stride = n_outputs * self.max_n_classes + + cdef SIZE_t k + for k in range(n_outputs): + self.n_classes[k] = n_classes[k] + + # Inner structures + self.max_depth = 0 + self.node_count = 0 + self.capacity = 0 + self.value = NULL + self.nodes = NULL + + def __dealloc__(self): + """Destructor.""" + # Free all inner structures + free(self.n_classes) + free(self.value) + free(self.nodes) + + def __reduce__(self): + """Reduce re-implementation, for pickling.""" + return (Tree, (self.n_features, + sizet_ptr_to_ndarray(self.n_classes, self.n_outputs), + self.n_outputs), self.__getstate__()) + + def __getstate__(self): + """Getstate re-implementation, for pickling.""" + d = {} + # capacity is inferred during the __setstate__ using nodes + d["max_depth"] = self.max_depth + d["node_count"] = self.node_count + d["nodes"] = self._get_node_ndarray() + d["values"] = self._get_value_ndarray() + return d + + def __setstate__(self, d): + """Setstate re-implementation, for unpickling.""" + self.max_depth = d["max_depth"] + self.node_count = d["node_count"] + + if 'nodes' not in d: + raise ValueError('You have loaded Tree version which ' + 'cannot be imported') + + node_ndarray = d['nodes'] + value_ndarray = d['values'] + + value_shape = (node_ndarray.shape[0], self.n_outputs, + self.max_n_classes) + + node_ndarray = _check_node_ndarray(node_ndarray, expected_dtype=NODE_DTYPE) + value_ndarray = _check_value_ndarray( + value_ndarray, + expected_dtype=np.dtype(np.float64), + expected_shape=value_shape + ) + + self.capacity = node_ndarray.shape[0] + if self._resize_c(self.capacity) != 0: + raise MemoryError("resizing tree to %d" % self.capacity) + + memcpy(self.nodes, cnp.PyArray_DATA(node_ndarray), + self.capacity * sizeof(Node)) + memcpy(self.value, cnp.PyArray_DATA(value_ndarray), + self.capacity * self.value_stride * sizeof(double)) + + cdef int _resize(self, SIZE_t capacity) except -1 nogil: + """Resize all inner arrays to `capacity`, if `capacity` == -1, then + double the size of the inner arrays. + + Returns -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + """ + if self._resize_c(capacity) != 0: + # Acquire gil only if we need to raise + with gil: + raise MemoryError() + + cdef int _resize_c(self, SIZE_t capacity=INTPTR_MAX) except -1 nogil: + """Guts of _resize + + Returns -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + """ + if capacity == self.capacity and self.nodes != NULL: + return 0 + + if capacity == INTPTR_MAX: + if self.capacity == 0: + capacity = 3 # default initial value + else: + capacity = 2 * self.capacity + + safe_realloc(&self.nodes, capacity) + safe_realloc(&self.value, capacity * self.value_stride) + + # value memory is initialised to 0 to enable classifier argmax + if capacity > self.capacity: + memset((self.value + self.capacity * self.value_stride), 0, + (capacity - self.capacity) * self.value_stride * + sizeof(double)) + + # if capacity smaller than node_count, adjust the counter + if capacity < self.node_count: + self.node_count = capacity + + self.capacity = capacity + return 0 + + cdef SIZE_t _add_node(self, SIZE_t parent, bint is_left, bint is_leaf, + SIZE_t feature, double threshold, double impurity, + SIZE_t n_node_samples, + double weighted_n_node_samples, + unsigned char missing_go_to_left) except -1 nogil: + """Add a node to the tree. + + The new node registers itself as the child of its parent. + + Returns (size_t)(-1) on error. + """ + cdef SIZE_t node_id = self.node_count + + if node_id >= self.capacity: + if self._resize_c() != 0: + return INTPTR_MAX + + cdef Node* node = &self.nodes[node_id] + node.impurity = impurity + node.n_node_samples = n_node_samples + node.weighted_n_node_samples = weighted_n_node_samples + + if parent != _TREE_UNDEFINED: + if is_left: + self.nodes[parent].left_child = node_id + else: + self.nodes[parent].right_child = node_id + + if is_leaf: + node.left_child = _TREE_LEAF + node.right_child = _TREE_LEAF + node.feature = _TREE_UNDEFINED + node.threshold = _TREE_UNDEFINED + + else: + # left_child and right_child will be set later + node.feature = feature + node.threshold = threshold + node.missing_go_to_left = missing_go_to_left + + self.node_count += 1 + + return node_id + + cpdef cnp.ndarray predict(self, object X): + """Predict target for X.""" + out = self._get_value_ndarray().take(self.apply(X), axis=0, + mode='clip') + if self.n_outputs == 1: + out = out.reshape(X.shape[0], self.max_n_classes) + return out + + cpdef cnp.ndarray apply(self, object X): + """Finds the terminal region (=leaf node) for each sample in X.""" + if issparse(X): + return self._apply_sparse_csr(X) + else: + return self._apply_dense(X) + + cdef inline cnp.ndarray _apply_dense(self, object X): + """Finds the terminal region (=leaf node) for each sample in X.""" + + # Check input + if not isinstance(X, np.ndarray): + raise ValueError("X should be in np.ndarray format, got %s" + % type(X)) + + if X.dtype != DTYPE: + raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + + # Extract input + cdef const DTYPE_t[:, :] X_ndarray = X + cdef SIZE_t n_samples = X.shape[0] + cdef DTYPE_t X_i_node_feature + + # Initialize output + cdef SIZE_t[:] out = np.zeros(n_samples, dtype=np.intp) + + # Initialize auxiliary data-structure + cdef Node* node = NULL + cdef SIZE_t i = 0 + + with nogil: + for i in range(n_samples): + node = self.nodes + # While node not a leaf + while node.left_child != _TREE_LEAF: + X_i_node_feature = X_ndarray[i, node.feature] + # ... and node.right_child != _TREE_LEAF: + if isnan(X_i_node_feature): + if node.missing_go_to_left: + node = &self.nodes[node.left_child] + else: + node = &self.nodes[node.right_child] + elif X_i_node_feature <= node.threshold: + node = &self.nodes[node.left_child] + else: + node = &self.nodes[node.right_child] + + out[i] = (node - self.nodes) # node offset + + return np.asarray(out) + + cdef inline cnp.ndarray _apply_sparse_csr(self, object X): + """Finds the terminal region (=leaf node) for each sample in sparse X. + """ + # Check input + if not isspmatrix_csr(X): + raise ValueError("X should be in csr_matrix format, got %s" + % type(X)) + + if X.dtype != DTYPE: + raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + + # Extract input + cdef const DTYPE_t[:] X_data = X.data + cdef const INT32_t[:] X_indices = X.indices + cdef const INT32_t[:] X_indptr = X.indptr + + cdef SIZE_t n_samples = X.shape[0] + cdef SIZE_t n_features = X.shape[1] + + # Initialize output + cdef SIZE_t[:] out = np.zeros(n_samples, dtype=np.intp) + + # Initialize auxiliary data-structure + cdef DTYPE_t feature_value = 0. + cdef Node* node = NULL + cdef DTYPE_t* X_sample = NULL + cdef SIZE_t i = 0 + cdef INT32_t k = 0 + + # feature_to_sample as a data structure records the last seen sample + # for each feature; functionally, it is an efficient way to identify + # which features are nonzero in the present sample. + cdef SIZE_t* feature_to_sample = NULL + + safe_realloc(&X_sample, n_features) + safe_realloc(&feature_to_sample, n_features) + + with nogil: + memset(feature_to_sample, -1, n_features * sizeof(SIZE_t)) + + for i in range(n_samples): + node = self.nodes + + for k in range(X_indptr[i], X_indptr[i + 1]): + feature_to_sample[X_indices[k]] = i + X_sample[X_indices[k]] = X_data[k] + + # While node not a leaf + while node.left_child != _TREE_LEAF: + # ... and node.right_child != _TREE_LEAF: + if feature_to_sample[node.feature] == i: + feature_value = X_sample[node.feature] + + else: + feature_value = 0. + + if feature_value <= node.threshold: + node = &self.nodes[node.left_child] + else: + node = &self.nodes[node.right_child] + + out[i] = (node - self.nodes) # node offset + + # Free auxiliary arrays + free(X_sample) + free(feature_to_sample) + + return np.asarray(out) + + cpdef object decision_path(self, object X): + """Finds the decision path (=node) for each sample in X.""" + if issparse(X): + return self._decision_path_sparse_csr(X) + else: + return self._decision_path_dense(X) + + cdef inline object _decision_path_dense(self, object X): + """Finds the decision path (=node) for each sample in X.""" + + # Check input + if not isinstance(X, np.ndarray): + raise ValueError("X should be in np.ndarray format, got %s" + % type(X)) + + if X.dtype != DTYPE: + raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + + # Extract input + cdef const DTYPE_t[:, :] X_ndarray = X + cdef SIZE_t n_samples = X.shape[0] + + # Initialize output + cdef SIZE_t[:] indptr = np.zeros(n_samples + 1, dtype=np.intp) + cdef SIZE_t[:] indices = np.zeros( + n_samples * (1 + self.max_depth), dtype=np.intp + ) + + # Initialize auxiliary data-structure + cdef Node* node = NULL + cdef SIZE_t i = 0 + + with nogil: + for i in range(n_samples): + node = self.nodes + indptr[i + 1] = indptr[i] + + # Add all external nodes + while node.left_child != _TREE_LEAF: + # ... and node.right_child != _TREE_LEAF: + indices[indptr[i + 1]] = (node - self.nodes) + indptr[i + 1] += 1 + + if X_ndarray[i, node.feature] <= node.threshold: + node = &self.nodes[node.left_child] + else: + node = &self.nodes[node.right_child] + + # Add the leave node + indices[indptr[i + 1]] = (node - self.nodes) + indptr[i + 1] += 1 + + indices = indices[:indptr[n_samples]] + cdef SIZE_t[:] data = np.ones(shape=len(indices), dtype=np.intp) + out = csr_matrix((data, indices, indptr), + shape=(n_samples, self.node_count)) + + return out + + cdef inline object _decision_path_sparse_csr(self, object X): + """Finds the decision path (=node) for each sample in X.""" + + # Check input + if not isspmatrix_csr(X): + raise ValueError("X should be in csr_matrix format, got %s" + % type(X)) + + if X.dtype != DTYPE: + raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + + # Extract input + cdef const DTYPE_t[:] X_data = X.data + cdef const INT32_t[:] X_indices = X.indices + cdef const INT32_t[:] X_indptr = X.indptr + + cdef SIZE_t n_samples = X.shape[0] + cdef SIZE_t n_features = X.shape[1] + + # Initialize output + cdef SIZE_t[:] indptr = np.zeros(n_samples + 1, dtype=np.intp) + cdef SIZE_t[:] indices = np.zeros( + n_samples * (1 + self.max_depth), dtype=np.intp + ) + + # Initialize auxiliary data-structure + cdef DTYPE_t feature_value = 0. + cdef Node* node = NULL + cdef DTYPE_t* X_sample = NULL + cdef SIZE_t i = 0 + cdef INT32_t k = 0 + + # feature_to_sample as a data structure records the last seen sample + # for each feature; functionally, it is an efficient way to identify + # which features are nonzero in the present sample. + cdef SIZE_t* feature_to_sample = NULL + + safe_realloc(&X_sample, n_features) + safe_realloc(&feature_to_sample, n_features) + + with nogil: + memset(feature_to_sample, -1, n_features * sizeof(SIZE_t)) + + for i in range(n_samples): + node = self.nodes + indptr[i + 1] = indptr[i] + + for k in range(X_indptr[i], X_indptr[i + 1]): + feature_to_sample[X_indices[k]] = i + X_sample[X_indices[k]] = X_data[k] + + # While node not a leaf + while node.left_child != _TREE_LEAF: + # ... and node.right_child != _TREE_LEAF: + + indices[indptr[i + 1]] = (node - self.nodes) + indptr[i + 1] += 1 + + if feature_to_sample[node.feature] == i: + feature_value = X_sample[node.feature] + + else: + feature_value = 0. + + if feature_value <= node.threshold: + node = &self.nodes[node.left_child] + else: + node = &self.nodes[node.right_child] + + # Add the leave node + indices[indptr[i + 1]] = (node - self.nodes) + indptr[i + 1] += 1 + + # Free auxiliary arrays + free(X_sample) + free(feature_to_sample) + + indices = indices[:indptr[n_samples]] + cdef SIZE_t[:] data = np.ones(shape=len(indices), dtype=np.intp) + out = csr_matrix((data, indices, indptr), + shape=(n_samples, self.node_count)) + + return out + + cpdef compute_node_depths(self): + """Compute the depth of each node in a tree. + + .. versionadded:: 1.3 + + Returns + ------- + depths : ndarray of shape (self.node_count,), dtype=np.int64 + The depth of each node in the tree. + """ + cdef: + cnp.int64_t[::1] depths = np.empty(self.node_count, dtype=np.int64) + cnp.npy_intp[:] children_left = self.children_left + cnp.npy_intp[:] children_right = self.children_right + cnp.npy_intp node_id + cnp.npy_intp node_count = self.node_count + cnp.int64_t depth + + depths[0] = 1 # init root node + for node_id in range(node_count): + if children_left[node_id] != _TREE_LEAF: + depth = depths[node_id] + 1 + depths[children_left[node_id]] = depth + depths[children_right[node_id]] = depth + + return depths.base + + cpdef compute_feature_importances(self, normalize=True): + """Computes the importance of each feature (aka variable).""" + cdef Node* left + cdef Node* right + cdef Node* nodes = self.nodes + cdef Node* node = nodes + cdef Node* end_node = node + self.node_count + + cdef double normalizer = 0. + + cdef cnp.float64_t[:] importances = np.zeros(self.n_features) + + with nogil: + while node != end_node: + if node.left_child != _TREE_LEAF: + # ... and node.right_child != _TREE_LEAF: + left = &nodes[node.left_child] + right = &nodes[node.right_child] + + importances[node.feature] += ( + node.weighted_n_node_samples * node.impurity - + left.weighted_n_node_samples * left.impurity - + right.weighted_n_node_samples * right.impurity) + node += 1 + + for i in range(self.n_features): + importances[i] /= nodes[0].weighted_n_node_samples + + if normalize: + normalizer = np.sum(importances) + + if normalizer > 0.0: + # Avoid dividing by zero (e.g., when root is pure) + for i in range(self.n_features): + importances[i] /= normalizer + + return np.asarray(importances) + + cdef cnp.ndarray _get_value_ndarray(self): + """Wraps value as a 3-d NumPy array. + + The array keeps a reference to this Tree, which manages the underlying + memory. + """ + cdef cnp.npy_intp shape[3] + shape[0] = self.node_count + shape[1] = self.n_outputs + shape[2] = self.max_n_classes + cdef cnp.ndarray arr + arr = cnp.PyArray_SimpleNewFromData(3, shape, cnp.NPY_DOUBLE, self.value) + Py_INCREF(self) + if PyArray_SetBaseObject(arr, self) < 0: + raise ValueError("Can't initialize array.") + return arr + + cdef cnp.ndarray _get_node_ndarray(self): + """Wraps nodes as a NumPy struct array. + + The array keeps a reference to this Tree, which manages the underlying + memory. Individual fields are publicly accessible as properties of the + Tree. + """ + cdef cnp.npy_intp shape[1] + shape[0] = self.node_count + cdef cnp.npy_intp strides[1] + strides[0] = sizeof(Node) + cdef cnp.ndarray arr + Py_INCREF(NODE_DTYPE) + arr = PyArray_NewFromDescr( cnp.ndarray, + NODE_DTYPE, 1, shape, + strides, self.nodes, + cnp.NPY_ARRAY_DEFAULT, None) + Py_INCREF(self) + if PyArray_SetBaseObject(arr, self) < 0: + raise ValueError("Can't initialize array.") + return arr + + def compute_partial_dependence(self, DTYPE_t[:, ::1] X, + int[::1] target_features, + double[::1] out): + """Partial dependence of the response on the ``target_feature`` set. + + For each sample in ``X`` a tree traversal is performed. + Each traversal starts from the root with weight 1.0. + + At each non-leaf node that splits on a target feature, either + the left child or the right child is visited based on the feature + value of the current sample, and the weight is not modified. + At each non-leaf node that splits on a complementary feature, + both children are visited and the weight is multiplied by the fraction + of training samples which went to each child. + + At each leaf, the value of the node is multiplied by the current + weight (weights sum to 1 for all visited terminal nodes). + + Parameters + ---------- + X : view on 2d ndarray, shape (n_samples, n_target_features) + The grid points on which the partial dependence should be + evaluated. + target_features : view on 1d ndarray, shape (n_target_features) + The set of target features for which the partial dependence + should be evaluated. + out : view on 1d ndarray, shape (n_samples) + The value of the partial dependence function on each grid + point. + """ + cdef: + double[::1] weight_stack = np.zeros(self.node_count, + dtype=np.float64) + SIZE_t[::1] node_idx_stack = np.zeros(self.node_count, + dtype=np.intp) + SIZE_t sample_idx + SIZE_t feature_idx + int stack_size + double left_sample_frac + double current_weight + double total_weight # used for sanity check only + Node *current_node # use a pointer to avoid copying attributes + SIZE_t current_node_idx + bint is_target_feature + SIZE_t _TREE_LEAF = TREE_LEAF # to avoid python interactions + + for sample_idx in range(X.shape[0]): + # init stacks for current sample + stack_size = 1 + node_idx_stack[0] = 0 # root node + weight_stack[0] = 1 # all the samples are in the root node + total_weight = 0 + + while stack_size > 0: + # pop the stack + stack_size -= 1 + current_node_idx = node_idx_stack[stack_size] + current_node = &self.nodes[current_node_idx] + + if current_node.left_child == _TREE_LEAF: + # leaf node + out[sample_idx] += (weight_stack[stack_size] * + self.value[current_node_idx]) + total_weight += weight_stack[stack_size] + else: + # non-leaf node + + # determine if the split feature is a target feature + is_target_feature = False + for feature_idx in range(target_features.shape[0]): + if target_features[feature_idx] == current_node.feature: + is_target_feature = True + break + + if is_target_feature: + # In this case, we push left or right child on stack + if X[sample_idx, feature_idx] <= current_node.threshold: + node_idx_stack[stack_size] = current_node.left_child + else: + node_idx_stack[stack_size] = current_node.right_child + stack_size += 1 + else: + # In this case, we push both children onto the stack, + # and give a weight proportional to the number of + # samples going through each branch. + + # push left child + node_idx_stack[stack_size] = current_node.left_child + left_sample_frac = ( + self.nodes[current_node.left_child].weighted_n_node_samples / + current_node.weighted_n_node_samples) + current_weight = weight_stack[stack_size] + weight_stack[stack_size] = current_weight * left_sample_frac + stack_size += 1 + + # push right child + node_idx_stack[stack_size] = current_node.right_child + weight_stack[stack_size] = ( + current_weight * (1 - left_sample_frac)) + stack_size += 1 + + # Sanity check. Should never happen. + if not (0.999 < total_weight < 1.001): + raise ValueError("Total weight should be 1.0 but was %.9f" % + total_weight) + + +def _check_n_classes(n_classes, expected_dtype): + if n_classes.ndim != 1: + raise ValueError( + f"Wrong dimensions for n_classes from the pickle: " + f"expected 1, got {n_classes.ndim}" + ) + + if n_classes.dtype == expected_dtype: + return n_classes + + # Handles both different endianness and different bitness + if n_classes.dtype.kind == "i" and n_classes.dtype.itemsize in [4, 8]: + return n_classes.astype(expected_dtype, casting="same_kind") + + raise ValueError( + "n_classes from the pickle has an incompatible dtype:\n" + f"- expected: {expected_dtype}\n" + f"- got: {n_classes.dtype}" + ) + + +def _check_value_ndarray(value_ndarray, expected_dtype, expected_shape): + if value_ndarray.shape != expected_shape: + raise ValueError( + "Wrong shape for value array from the pickle: " + f"expected {expected_shape}, got {value_ndarray.shape}" + ) + + if not value_ndarray.flags.c_contiguous: + raise ValueError( + "value array from the pickle should be a C-contiguous array" + ) + + if value_ndarray.dtype == expected_dtype: + return value_ndarray + + # Handles different endianness + if value_ndarray.dtype.str.endswith('f8'): + return value_ndarray.astype(expected_dtype, casting='equiv') + + raise ValueError( + "value array from the pickle has an incompatible dtype:\n" + f"- expected: {expected_dtype}\n" + f"- got: {value_ndarray.dtype}" + ) + + +def _dtype_to_dict(dtype): + return {name: dt.str for name, (dt, *rest) in dtype.fields.items()} + + +def _dtype_dict_with_modified_bitness(dtype_dict): + # field names in Node struct with SIZE_t types (see sklearn/tree/_tree.pxd) + indexing_field_names = ["left_child", "right_child", "feature", "n_node_samples"] + + expected_dtype_size = str(struct.calcsize("P")) + allowed_dtype_size = "8" if expected_dtype_size == "4" else "4" + + allowed_dtype_dict = dtype_dict.copy() + for name in indexing_field_names: + allowed_dtype_dict[name] = allowed_dtype_dict[name].replace( + expected_dtype_size, allowed_dtype_size + ) + + return allowed_dtype_dict + + +def _all_compatible_dtype_dicts(dtype): + # The Cython code for decision trees uses platform-specific SIZE_t + # typed indexing fields that correspond to either i4 or i8 dtypes for + # the matching fields in the numpy array depending on the bitness of + # the platform (32 bit or 64 bit respectively). + # + # We need to cast the indexing fields of the NODE_DTYPE-dtyped array at + # pickle load time to enable cross-bitness deployment scenarios. We + # typically want to make it possible to run the expensive fit method of + # a tree estimator on a 64 bit server platform, pickle the estimator + # for deployment and run the predict method of a low power 32 bit edge + # platform. + # + # A similar thing happens for endianness, the machine where the pickle was + # saved can have a different endianness than the machine where the pickle + # is loaded + + dtype_dict = _dtype_to_dict(dtype) + dtype_dict_with_modified_bitness = _dtype_dict_with_modified_bitness(dtype_dict) + dtype_dict_with_modified_endianness = _dtype_to_dict(dtype.newbyteorder()) + dtype_dict_with_modified_bitness_and_endianness = _dtype_dict_with_modified_bitness( + dtype_dict_with_modified_endianness + ) + + return [ + dtype_dict, + dtype_dict_with_modified_bitness, + dtype_dict_with_modified_endianness, + dtype_dict_with_modified_bitness_and_endianness, + ] + + +def _check_node_ndarray(node_ndarray, expected_dtype): + if node_ndarray.ndim != 1: + raise ValueError( + "Wrong dimensions for node array from the pickle: " + f"expected 1, got {node_ndarray.ndim}" + ) + + if not node_ndarray.flags.c_contiguous: + raise ValueError( + "node array from the pickle should be a C-contiguous array" + ) + + node_ndarray_dtype = node_ndarray.dtype + if node_ndarray_dtype == expected_dtype: + return node_ndarray + + node_ndarray_dtype_dict = _dtype_to_dict(node_ndarray_dtype) + all_compatible_dtype_dicts = _all_compatible_dtype_dicts(expected_dtype) + + if node_ndarray_dtype_dict not in all_compatible_dtype_dicts: + raise ValueError( + "node array from the pickle has an incompatible dtype:\n" + f"- expected: {expected_dtype}\n" + f"- got : {node_ndarray_dtype}" + ) + + return node_ndarray.astype(expected_dtype, casting="same_kind") + + +# ============================================================================= +# Build Pruned Tree +# ============================================================================= + + +cdef class _CCPPruneController: + """Base class used by build_pruned_tree_ccp and ccp_pruning_path + to control pruning. + """ + cdef bint stop_pruning(self, DOUBLE_t effective_alpha) noexcept nogil: + """Return 1 to stop pruning and 0 to continue pruning""" + return 0 + + cdef void save_metrics(self, DOUBLE_t effective_alpha, + DOUBLE_t subtree_impurities) noexcept nogil: + """Save metrics when pruning""" + pass + + cdef void after_pruning(self, unsigned char[:] in_subtree) noexcept nogil: + """Called after pruning""" + pass + + +cdef class _AlphaPruner(_CCPPruneController): + """Use alpha to control when to stop pruning.""" + cdef DOUBLE_t ccp_alpha + cdef SIZE_t capacity + + def __cinit__(self, DOUBLE_t ccp_alpha): + self.ccp_alpha = ccp_alpha + self.capacity = 0 + + cdef bint stop_pruning(self, DOUBLE_t effective_alpha) noexcept nogil: + # The subtree on the previous iteration has the greatest ccp_alpha + # less than or equal to self.ccp_alpha + return self.ccp_alpha < effective_alpha + + cdef void after_pruning(self, unsigned char[:] in_subtree) noexcept nogil: + """Updates the number of leaves in subtree""" + for i in range(in_subtree.shape[0]): + if in_subtree[i]: + self.capacity += 1 + + +cdef class _PathFinder(_CCPPruneController): + """Record metrics used to return the cost complexity path.""" + cdef DOUBLE_t[:] ccp_alphas + cdef DOUBLE_t[:] impurities + cdef UINT32_t count + + def __cinit__(self, int node_count): + self.ccp_alphas = np.zeros(shape=(node_count), dtype=np.float64) + self.impurities = np.zeros(shape=(node_count), dtype=np.float64) + self.count = 0 + + cdef void save_metrics(self, + DOUBLE_t effective_alpha, + DOUBLE_t subtree_impurities) noexcept nogil: + self.ccp_alphas[self.count] = effective_alpha + self.impurities[self.count] = subtree_impurities + self.count += 1 + + +cdef struct CostComplexityPruningRecord: + SIZE_t node_idx + SIZE_t parent + +cdef _cost_complexity_prune(unsigned char[:] leaves_in_subtree, # OUT + Tree orig_tree, + _CCPPruneController controller): + """Perform cost complexity pruning. + + This function takes an already grown tree, `orig_tree` and outputs a + boolean mask `leaves_in_subtree` which are the leaves in the pruned tree. + During the pruning process, the controller is passed the effective alpha and + the subtree impurities. Furthermore, the controller signals when to stop + pruning. + + Parameters + ---------- + leaves_in_subtree : unsigned char[:] + Output for leaves of subtree + orig_tree : Tree + Original tree + ccp_controller : _CCPPruneController + Cost complexity controller + """ + + cdef: + SIZE_t i + SIZE_t n_nodes = orig_tree.node_count + # prior probability using weighted samples + DOUBLE_t[:] weighted_n_node_samples = orig_tree.weighted_n_node_samples + DOUBLE_t total_sum_weights = weighted_n_node_samples[0] + DOUBLE_t[:] impurity = orig_tree.impurity + # weighted impurity of each node + DOUBLE_t[:] r_node = np.empty(shape=n_nodes, dtype=np.float64) + + SIZE_t[:] child_l = orig_tree.children_left + SIZE_t[:] child_r = orig_tree.children_right + SIZE_t[:] parent = np.zeros(shape=n_nodes, dtype=np.intp) + + stack[CostComplexityPruningRecord] ccp_stack + CostComplexityPruningRecord stack_record + SIZE_t node_idx + stack[SIZE_t] node_indices_stack + + SIZE_t[:] n_leaves = np.zeros(shape=n_nodes, dtype=np.intp) + DOUBLE_t[:] r_branch = np.zeros(shape=n_nodes, dtype=np.float64) + DOUBLE_t current_r + SIZE_t leaf_idx + SIZE_t parent_idx + + # candidate nodes that can be pruned + unsigned char[:] candidate_nodes = np.zeros(shape=n_nodes, + dtype=np.uint8) + # nodes in subtree + unsigned char[:] in_subtree = np.ones(shape=n_nodes, dtype=np.uint8) + SIZE_t pruned_branch_node_idx + DOUBLE_t subtree_alpha + DOUBLE_t effective_alpha + SIZE_t n_pruned_leaves + DOUBLE_t r_diff + DOUBLE_t max_float64 = np.finfo(np.float64).max + + # find parent node ids and leaves + with nogil: + + for i in range(r_node.shape[0]): + r_node[i] = ( + weighted_n_node_samples[i] * impurity[i] / total_sum_weights) + + # Push the root node + ccp_stack.push({"node_idx": 0, "parent": _TREE_UNDEFINED}) + + while not ccp_stack.empty(): + stack_record = ccp_stack.top() + ccp_stack.pop() + + node_idx = stack_record.node_idx + parent[node_idx] = stack_record.parent + + if child_l[node_idx] == _TREE_LEAF: + # ... and child_r[node_idx] == _TREE_LEAF: + leaves_in_subtree[node_idx] = 1 + else: + ccp_stack.push({"node_idx": child_l[node_idx], "parent": node_idx}) + ccp_stack.push({"node_idx": child_r[node_idx], "parent": node_idx}) + + # computes number of leaves in all branches and the overall impurity of + # the branch. The overall impurity is the sum of r_node in its leaves. + for leaf_idx in range(leaves_in_subtree.shape[0]): + if not leaves_in_subtree[leaf_idx]: + continue + r_branch[leaf_idx] = r_node[leaf_idx] + + # bubble up values to ancestor nodes + current_r = r_node[leaf_idx] + while leaf_idx != 0: + parent_idx = parent[leaf_idx] + r_branch[parent_idx] += current_r + n_leaves[parent_idx] += 1 + leaf_idx = parent_idx + + for i in range(leaves_in_subtree.shape[0]): + candidate_nodes[i] = not leaves_in_subtree[i] + + # save metrics before pruning + controller.save_metrics(0.0, r_branch[0]) + + # while root node is not a leaf + while candidate_nodes[0]: + + # computes ccp_alpha for subtrees and finds the minimal alpha + effective_alpha = max_float64 + for i in range(n_nodes): + if not candidate_nodes[i]: + continue + subtree_alpha = (r_node[i] - r_branch[i]) / (n_leaves[i] - 1) + if subtree_alpha < effective_alpha: + effective_alpha = subtree_alpha + pruned_branch_node_idx = i + + if controller.stop_pruning(effective_alpha): + break + + node_indices_stack.push(pruned_branch_node_idx) + + # descendants of branch are not in subtree + while not node_indices_stack.empty(): + node_idx = node_indices_stack.top() + node_indices_stack.pop() + + if not in_subtree[node_idx]: + continue # branch has already been marked for pruning + candidate_nodes[node_idx] = 0 + leaves_in_subtree[node_idx] = 0 + in_subtree[node_idx] = 0 + + if child_l[node_idx] != _TREE_LEAF: + # ... and child_r[node_idx] != _TREE_LEAF: + node_indices_stack.push(child_l[node_idx]) + node_indices_stack.push(child_r[node_idx]) + leaves_in_subtree[pruned_branch_node_idx] = 1 + in_subtree[pruned_branch_node_idx] = 1 + + # updates number of leaves + n_pruned_leaves = n_leaves[pruned_branch_node_idx] - 1 + n_leaves[pruned_branch_node_idx] = 0 + + # computes the increase in r_branch to bubble up + r_diff = r_node[pruned_branch_node_idx] - r_branch[pruned_branch_node_idx] + r_branch[pruned_branch_node_idx] = r_node[pruned_branch_node_idx] + + # bubble up values to ancestors + node_idx = parent[pruned_branch_node_idx] + while node_idx != _TREE_UNDEFINED: + n_leaves[node_idx] -= n_pruned_leaves + r_branch[node_idx] += r_diff + node_idx = parent[node_idx] + + controller.save_metrics(effective_alpha, r_branch[0]) + + controller.after_pruning(in_subtree) + + +def _build_pruned_tree_ccp( + Tree tree, # OUT + Tree orig_tree, + DOUBLE_t ccp_alpha +): + """Build a pruned tree from the original tree using cost complexity + pruning. + + The values and nodes from the original tree are copied into the pruned + tree. + + Parameters + ---------- + tree : Tree + Location to place the pruned tree + orig_tree : Tree + Original tree + ccp_alpha : positive double + Complexity parameter. The subtree with the largest cost complexity + that is smaller than ``ccp_alpha`` will be chosen. By default, + no pruning is performed. + """ + + cdef: + SIZE_t n_nodes = orig_tree.node_count + unsigned char[:] leaves_in_subtree = np.zeros( + shape=n_nodes, dtype=np.uint8) + + pruning_controller = _AlphaPruner(ccp_alpha=ccp_alpha) + + _cost_complexity_prune(leaves_in_subtree, orig_tree, pruning_controller) + + _build_pruned_tree(tree, orig_tree, leaves_in_subtree, + pruning_controller.capacity) + + +def ccp_pruning_path(Tree orig_tree): + """Computes the cost complexity pruning path. + + Parameters + ---------- + tree : Tree + Original tree. + + Returns + ------- + path_info : dict + Information about pruning path with attributes: + + ccp_alphas : ndarray + Effective alphas of subtree during pruning. + + impurities : ndarray + Sum of the impurities of the subtree leaves for the + corresponding alpha value in ``ccp_alphas``. + """ + cdef: + unsigned char[:] leaves_in_subtree = np.zeros( + shape=orig_tree.node_count, dtype=np.uint8) + + path_finder = _PathFinder(orig_tree.node_count) + + _cost_complexity_prune(leaves_in_subtree, orig_tree, path_finder) + + cdef: + UINT32_t total_items = path_finder.count + DOUBLE_t[:] ccp_alphas = np.empty(shape=total_items, dtype=np.float64) + DOUBLE_t[:] impurities = np.empty(shape=total_items, dtype=np.float64) + UINT32_t count = 0 + + while count < total_items: + ccp_alphas[count] = path_finder.ccp_alphas[count] + impurities[count] = path_finder.impurities[count] + count += 1 + + return { + 'ccp_alphas': np.asarray(ccp_alphas), + 'impurities': np.asarray(impurities), + } + + +cdef struct BuildPrunedRecord: + SIZE_t start + SIZE_t depth + SIZE_t parent + bint is_left + +cdef _build_pruned_tree( + Tree tree, # OUT + Tree orig_tree, + const unsigned char[:] leaves_in_subtree, + SIZE_t capacity +): + """Build a pruned tree. + + Build a pruned tree from the original tree by transforming the nodes in + ``leaves_in_subtree`` into leaves. + + Parameters + ---------- + tree : Tree + Location to place the pruned tree + orig_tree : Tree + Original tree + leaves_in_subtree : unsigned char memoryview, shape=(node_count, ) + Boolean mask for leaves to include in subtree + capacity : SIZE_t + Number of nodes to initially allocate in pruned tree + """ + tree._resize(capacity) + + cdef: + SIZE_t orig_node_id + SIZE_t new_node_id + SIZE_t depth + SIZE_t parent + bint is_left + bint is_leaf + + # value_stride for original tree and new tree are the same + SIZE_t value_stride = orig_tree.value_stride + SIZE_t max_depth_seen = -1 + int rc = 0 + Node* node + double* orig_value_ptr + double* new_value_ptr + + stack[BuildPrunedRecord] prune_stack + BuildPrunedRecord stack_record + + with nogil: + # push root node onto stack + prune_stack.push({"start": 0, "depth": 0, "parent": _TREE_UNDEFINED, "is_left": 0}) + + while not prune_stack.empty(): + stack_record = prune_stack.top() + prune_stack.pop() + + orig_node_id = stack_record.start + depth = stack_record.depth + parent = stack_record.parent + is_left = stack_record.is_left + + is_leaf = leaves_in_subtree[orig_node_id] + node = &orig_tree.nodes[orig_node_id] + + new_node_id = tree._add_node( + parent, is_left, is_leaf, node.feature, node.threshold, + node.impurity, node.n_node_samples, + node.weighted_n_node_samples, node.missing_go_to_left) + + if new_node_id == INTPTR_MAX: + rc = -1 + break + + # copy value from original tree to new tree + orig_value_ptr = orig_tree.value + value_stride * orig_node_id + new_value_ptr = tree.value + value_stride * new_node_id + memcpy(new_value_ptr, orig_value_ptr, sizeof(double) * value_stride) + + if not is_leaf: + # Push right child on stack + prune_stack.push({"start": node.right_child, "depth": depth + 1, + "parent": new_node_id, "is_left": 0}) + # push left child on stack + prune_stack.push({"start": node.left_child, "depth": depth + 1, + "parent": new_node_id, "is_left": 1}) + + if depth > max_depth_seen: + max_depth_seen = depth + + if rc >= 0: + tree.max_depth = max_depth_seen + if rc == -1: + raise MemoryError("pruning tree") \ No newline at end of file diff --git a/ivy/functional/frontends/sklearn/tree/tree ivy.py b/ivy/functional/frontends/sklearn/tree/tree ivy.py new file mode 100644 index 0000000000000..fa547997eb761 --- /dev/null +++ b/ivy/functional/frontends/sklearn/tree/tree ivy.py @@ -0,0 +1,840 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +import numpy as np + + + +from scipy.sparse import issparse +from scipy.sparse import csr_matrix +from scipy.sparse import isspmatrix_csr + + + + + + + + + + + + + + + + + + + + +# ============================================================================= +# Types and constants +# ============================================================================= + +from numpy import float32 as DTYPE +from numpy import float64 as DOUBLE + + +# Define constants +INFINITY = np.inf +EPSILON = np.finfo(np.double).eps + +# Some handy constants (BestFirstTreeBuilder) +IS_FIRST = 1 +IS_NOT_FIRST = 0 +IS_LEFT = 1 +IS_NOT_LEFT = 0 + +TREE_LEAF = -1 +TREE_UNDEFINED = -2 +_TREE_LEAF = TREE_LEAF +_TREE_UNDEFINED = TREE_UNDEFINED + +# Since you're dealing with Cython-specific types and features, +# it's important to provide a dummy definition for Node. +class Node: + def __init__(self): + self.left_child = None + self.right_child = None + self.feature = None + self.threshold = None + self.impurity = None + self.n_node_samples = None + self.weighted_n_node_samples = None + self.missing_go_to_left = None + +dummy = Node() +# Create a numpy dtype for Node using the dummy object +NODE_DTYPE = np.asarray([dummy], dtype=object).dtype +# ============================================================================= +# TreeBuilder +# ============================================================================= + +class TreeBuilder: + """Interface for different tree building strategies.""" + def __init__(self): + self.splitter = None + self.min_samples_split = None + self.min_samples_leaf = None + self.min_weight_leaf = None + self.max_depth = None + self.min_impurity_decrease = None + + def build( + self, + tree, + X, + y, + sample_weight=None, + missing_values_in_feature_mask=None, + ): + """Build a decision tree from the training set (X, y).""" + pass + + def _check_input( + self, + X, + y, + sample_weight, + ): + """Check input dtype, layout, and format""" + if issparse(X): + X = X.tocsc() #tocsc() is a method provided by the scipy.sparse module in the SciPy library. It's used to convert a sparse matrix to the Compressed Sparse Column (CSC) format. + X.sort_indices() #This is done to ensure that the indices of non-zero elements within the matrix are sorted in ascending order. + + if X.data.dtype != DTYPE: + X.data = np.ascontiguousarray(X.data, dtype=DTYPE) + + if X.indices.dtype != np.int32 or X.indptr.dtype != np.int32: + raise ValueError("No support for np.int64 index-based sparse matrices") + + elif X.dtype != DTYPE: + # since we have to copy, we will make it Fortran for efficiency + X = np.asfortranarray(X, dtype=DTYPE) + + if y.base.dtype != DTYPE or not y.base.flags.contiguous: + y = np.ascontiguousarray(y, dtype=DTYPE) + + if ( + sample_weight is not None and + ( + sample_weight.base.dtype != DOUBLE or + not sample_weight.base.flags.contiguous + ) + ): + sample_weight = np.asarray(sample_weight, dtype=DOUBLE, order="C") + + return X, y, sample_weight + + + + +# Depth first builder --------------------------------------------------------- +# A record on the stack for depth-first tree growing +class StackRecord: + def __init__(self, start, end, depth, parent, is_left, impurity, n_constant_features): + self.start = start + self.end = end + self.depth = depth + self.parent = parent + self.is_left = is_left + self.impurity = impurity + self.n_constant_features = n_constant_features + + +class SplitRecord: + def __init__(self, feature, pos, threshold, improvement, + impurity_left, impurity_right, missing_go_to_left, + n_missing): + self.feature = feature + self.pos = pos + self.threshold = threshold + self.improvement = improvement + self.impurity_left = impurity_left + self.impurity_right = impurity_right + self.missing_go_to_left = missing_go_to_left + self.n_missing = n_missing + + + + + +class DepthFirstTreeBuilder(TreeBuilder): + """Build a decision tree in depth-first fashion.""" + + def __init__( + self, splitter, min_samples_split, + min_samples_leaf, min_weight_leaf, + max_depth, min_impurity_decrease + ): + self.splitter = splitter + self.min_samples_split = min_samples_split + self.min_samples_leaf = min_samples_leaf + self.min_weight_leaf = min_weight_leaf + self.max_depth = max_depth + self.min_impurity_decrease = min_impurity_decrease + + def build( + self, + tree, + X, + y, + sample_weight=None, + missing_values_in_feature_mask=None + ): + """Build a decision tree from the training set (X, y).""" + + # Check input + X, y, sample_weight = self._check_input(X, y, sample_weight) + + # Initial capacity + init_capacity = (2 ** (tree.max_depth + 1)) - 1 if tree.max_depth <= 10 else 2047 + + tree._resize(init_capacity) + + # Parameters + splitter = self.splitter + max_depth = self.max_depth + min_samples_leaf = self.min_samples_leaf + min_weight_leaf = self.min_weight_leaf + min_samples_split = self.min_samples_split + min_impurity_decrease = self.min_impurity_decrease + + # Recursive partition (without actual recursion) + splitter.init(X, y, sample_weight, missing_values_in_feature_mask) + + stack = [] + + # Push root node onto stack + stack.append( + StackRecord( + start=0, + end=splitter.n_samples, + depth=0, + parent=_TREE_UNDEFINED, + is_left=False, + impurity=INFINITY, + n_constant_features=0 + ) + ) + weighted_n_node_samples = np.zeros(1, dtype=np.double) + while stack: + stack_record = stack.pop() + + start = stack_record.start + end = stack_record.end + depth = stack_record.depth + parent = stack_record.parent + is_left = stack_record.is_left + impurity = stack_record.impurity + n_constant_features = stack_record.n_constant_features + + n_node_samples = end - start + splitter.node_reset(start, end, weighted_n_node_samples) + + is_leaf = ( + depth >= max_depth + or n_node_samples < min_samples_split + or n_node_samples < 2 * min_samples_leaf + or np.sum(sample_weight[start:end]) < 2 * min_weight_leaf + ) + + if is_left: + impurity = splitter.node_impurity() + + is_leaf = is_leaf or impurity <= EPSILON + + if not is_leaf: + split = SplitRecord() # No idea what is SplitRecord in original code. Maybe this never gets called, not sure + splitter.node_split(impurity, split, n_constant_features) + is_leaf = ( + is_leaf + or split.pos >= end + or (split.improvement + EPSILON < min_impurity_decrease) + ) + + node_id = tree._add_node( + parent, + is_left, + is_leaf, + split.feature if not is_leaf else 0, + split.threshold if not is_leaf else 0, + impurity, + n_node_samples, + np.sum(sample_weight[start:end]), + split.missing_go_to_left, + ) + + if node_id == np.iinfo(np.intp).max: + raise MemoryError() + + splitter.node_value(tree.value + node_id * tree.value_stride) + + if not is_leaf: + # Push right child on stack + stack.append( + StackRecord( + start=split.pos, + end=end, + depth=depth + 1, + parent=node_id, + is_left=False, + impurity=split.impurity_right, + n_constant_features=n_constant_features, + ) + ) + # Push left child on stack + stack.append( + StackRecord( + start=start, + end=split.pos, + depth=depth + 1, + parent=node_id, + is_left=True, + impurity=split.impurity_left, + n_constant_features=n_constant_features, + ) + ) + + +class Tree: + def __init__(self, n_features, n_classes, n_outputs): + """Constructor.""" + self.n_features = None + self.n_outputs = None + self.n_classes = None + self.max_n_classes = None + self.max_depth = None + self.node_count = None + self.capacity = None + self.nodes = None + self.value = None + self.value_stride = None + + dummy = 0 + size_t_dtype = np.array(dummy).dtype + + n_classes = _check_n_classes(n_classes, size_t_dtype) + + # Input/Output layout + self.n_features = n_features + self.n_outputs = n_outputs + self.n_classes = np.zeros(n_outputs, dtype=size_t_dtype) + + self.max_n_classes = np.max(n_classes) + self.value_stride = n_outputs * self.max_n_classes + + for k in range(n_outputs): + self.n_classes[k] = n_classes[k] + + # Inner structures + self.max_depth = 0 + self.node_count = 0 + self.capacity = 0 + self.value = None + self.nodes = None + + def __del__(self): + """Destructor.""" + # Free all inner structures + self.n_classes = None + self.value = None + self.nodes = None + + #NOT CONSIDERING PICKINLING FOR NOW + def __reduce__(self): + """Reduce re-implementation, for pickling.""" + raise NotImplementedError + #NOT CONSIDERING PICKINLING FOR NOW + def __getstate__(self): + """Getstate re-implementation, for pickling.""" + d = {} + # capacity is inferred during the __setstate__ using nodes + d["max_depth"] = self.max_depth + d["node_count"] = self.node_count + d["nodes"] = self._get_node_ndarray() + d["values"] = self._get_value_ndarray() + return d + #NOT CONSIDERING PICKINLING FOR NOW + def __setstate__(self, d): + """Setstate re-implementation, for unpickling.""" + raise NotImplementedError + + def _resize(self, capacity): + """ + Resize all inner arrays to `capacity`. If `capacity` is -1, then double the size of the inner arrays. + Returns -1 in case of failure to allocate memory (and raise MemoryError), or 0 otherwise. + """ + if self._resize_c(capacity) != 0: + # Raise MemoryError if resizing fails + raise MemoryError() + + def _resize_c(self, capacity=float('inf')): + """ + Guts of _resize + Returns -1 in case of failure to allocate memory (and raise MemoryError), + or 0 otherwise. + """ + if capacity == self.capacity and self.nodes is not None: + return 0 + + if capacity == float('inf'): + if self.capacity == 0: + capacity = 3 # default initial value + else: + capacity = 2 * self.capacity + + # This section is relevant if the code is dealing with C arrays. + # In Python, resizing arrays is handled automatically by lists or numpy arrays. + # You won't need to explicitly reallocate memory or initialize values like this. + + # replaced safe_realloc(&self.nodes, capacity) with the following + new_nodes = np.empty(capacity, dtype=object) + for i in range(len(self.nodes)): + new_nodes[i] = self.nodes[i] + self.nodes = new_nodes + + # replaced safe_realloc(&self.value, capacity * self.value_stride) with the following + new_value = np.empty(capacity * self.value_stride, dtype=object) + for i in range(len(self.value)): + new_value[i] = self.value[i] + self.value = new_value + + # if capacity smaller than node_count, adjust the counter + if capacity < self.node_count: + self.node_count = capacity + + self.capacity = capacity + return 0 + + + def _add_node(self, parent, is_left, is_leaf, feature, threshold, impurity, + n_node_samples, weighted_n_node_samples, missing_go_to_left): + """ + Add a node to the tree. + + The new node registers itself as the child of its parent. + + Returns -1 on error. + """ + node_id = self.node_count + + if node_id >= self.capacity: + if self._resize_c() != 0: + return -1 + + node = self.nodes[node_id] + node.impurity = impurity + node.n_node_samples = n_node_samples + node.weighted_n_node_samples = weighted_n_node_samples + + if parent != _TREE_UNDEFINED: + if is_left: + self.nodes[parent].left_child = node_id + else: + self.nodes[parent].right_child = node_id + + if is_leaf: + node.left_child = _TREE_LEAF + node.right_child = _TREE_LEAF + node.feature = _TREE_UNDEFINED + node.threshold = _TREE_UNDEFINED + + else: + # left_child and right_child will be set later + node.feature = feature + node.threshold = threshold + node.missing_go_to_left = missing_go_to_left + + self.node_count += 1 + + return node_id + + def predict(self, X): + """Predict target for X.""" + out = self._get_value_ndarray()[self.apply(X), :, :] + + if self.n_outputs == 1: + out = out.reshape(X.shape[0], self.max_n_classes) + + return out + + def apply(self, X): + """Finds the terminal region (=leaf node) for each sample in X.""" + if issparse(X): + return self._apply_sparse_csr(X) + else: + return self._apply_dense(X) + + def _apply_dense(self, X): + """Finds the terminal region (=leaf node) for each sample in X.""" + + # Check input + if not isinstance(X, np.ndarray): + raise ValueError("X should be in np.ndarray format, got %s" % type(X)) + + if X.dtype != np.float32: + raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + + # Extract input + n_samples, n_features = X.shape + + # Initialize output + out = np.zeros(n_samples, dtype=np.intp) + + with np.nditer(X, flags=['c_index', 'multi_index'], op_flags=['readonly'], order='C') as it: + for x_value, index in it: + node = self.nodes + # While node not a leaf + while node.left_child != _TREE_LEAF: + X_i_node_feature = x_value + # ... and node.right_child != _TREE_LEAF: + if np.isnan(X_i_node_feature): + if node.missing_go_to_left: + node = self.nodes[node.left_child] + else: + node = self.nodes[node.right_child] + elif X_i_node_feature <= node.threshold: + node = self.nodes[node.left_child] + else: + node = self.nodes[node.right_child] + + out[index[0]] = node - self.nodes # node offset + + return out + + def _apply_sparse_csr(self, X): + """Finds the terminal region (=leaf node) for each sample in sparse X.""" + if not isinstance(X, csr_matrix): + raise ValueError("X should be in csr_matrix format, got %s" % type(X)) + + if X.dtype != np.float32: + raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + + n_samples, n_features = X.shape + + # Initialize output + out = np.zeros(n_samples, dtype=np.intp) + + # Initialize auxiliary data structures + feature_to_sample = np.full(n_features, -1, dtype=np.intp) + X_sample = np.zeros(n_features, dtype=np.float32) + + for i in range(n_samples): + node = self.nodes + + for k in range(X.indptr[i], X.indptr[i + 1]): + feature_to_sample[X.indices[k]] = i + X_sample[X.indices[k]] = X.data[k] + + while node.left_child != _TREE_LEAF: + if feature_to_sample[node.feature] == i: + feature_value = X_sample[node.feature] + else: + feature_value = 0.0 + + if feature_value <= node.threshold: + node = self.nodes[node.left_child] + else: + node = self.nodes[node.right_child] + + out[i] = node - self.nodes # node offset + + return out + + def decision_path(self, X): + """Finds the decision path (=node) for each sample in X.""" + if issparse(X): + return self._decision_path_sparse_csr(X) + else: + return self._decision_path_dense(X) + + def _decision_path_dense(self, X): + """Finds the decision path (=node) for each sample in X.""" + + # Check input + if not isinstance(X, np.ndarray): + raise ValueError("X should be in np.ndarray format, got %s" % type(X)) + + if X.dtype != DTYPE: + raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + + # Extract input + X_ndarray = X + n_samples = X.shape[0] + + # Initialize output + indptr = np.zeros(n_samples + 1, dtype=np.intp) + indices = np.zeros(n_samples * (1 + self.max_depth), dtype=np.intp) + + # Initialize auxiliary data-structure + node = None + i = 0 + + for i in range(n_samples): + node = self.nodes + indptr[i + 1] = indptr[i] + + # Add all external nodes + while node.left_child != _TREE_LEAF: + # ... and node.right_child != _TREE_LEAF: + indices[indptr[i + 1]] = node - self.nodes + indptr[i + 1] += 1 + + if X_ndarray[i, node.feature] <= node.threshold: + node = self.nodes[node.left_child] + else: + node = self.nodes[node.right_child] + + # Add the leaf node + indices[indptr[i + 1]] = node - self.nodes + indptr[i + 1] += 1 + + indices = indices[:indptr[n_samples]] + data = np.ones(shape=len(indices), dtype=np.intp) + out = csr_matrix((data, indices, indptr), shape=(n_samples, self.node_count)) + + return out + + def _decision_path_sparse_csr(self, X): + """Finds the decision path (=node) for each sample in X.""" + + # Check input + if not isspmatrix_csr(X): + raise ValueError("X should be in csr_matrix format, got %s" % type(X)) + + if X.dtype != DTYPE: + raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + + # Extract input + X_data = X.data + X_indices = X.indices + X_indptr = X.indptr + + n_samples = X.shape[0] + n_features = X.shape[1] + + # Initialize output + indptr = np.zeros(n_samples + 1, dtype=np.intp) + indices = np.zeros(n_samples * (1 + self.max_depth), dtype=np.intp) + + # Initialize auxiliary data-structure + feature_value = 0.0 + node = None + X_sample = np.zeros(n_features, dtype=DTYPE) + feature_to_sample = np.full(n_features, -1, dtype=np.intp) + + for i in range(n_samples): + node = self.nodes + indptr[i + 1] = indptr[i] + + for k in range(X_indptr[i], X_indptr[i + 1]): + feature_to_sample[X_indices[k]] = i + X_sample[X_indices[k]] = X_data[k] + + # While node not a leaf + while node.left_child != _TREE_LEAF: + indices[indptr[i + 1]] = node - self.nodes + indptr[i + 1] += 1 + + if feature_to_sample[node.feature] == i: + feature_value = X_sample[node.feature] + else: + feature_value = 0.0 + + if feature_value <= node.threshold: + node = self.nodes[node.left_child] + else: + node = self.nodes[node.right_child] + + # Add the leaf node + indices[indptr[i + 1]] = node - self.nodes + indptr[i + 1] += 1 + + indices = indices[:indptr[n_samples]] + data = np.ones(shape=len(indices), dtype=np.intp) + out = csr_matrix((data, indices, indptr), shape=(n_samples, self.node_count)) + + return out + + def compute_node_depths(self): + """Compute the depth of each node in a tree. + + .. versionadded:: 1.3 + + Returns + ------- + depths : ndarray of shape (self.node_count,), dtype=np.int64 + The depth of each node in the tree. + """ + depths = np.empty(self.node_count, dtype=np.int64) + children_left = self.children_left + children_right = self.children_right + node_count = self.node_count + + depths[0] = 1 # init root node + for node_id in range(node_count): + if children_left[node_id] != _TREE_LEAF: + depth = depths[node_id] + 1 + depths[children_left[node_id]] = depth + depths[children_right[node_id]] = depth + + return depths.base + + def compute_feature_importances(self, normalize=True): + """Computes the importance of each feature (aka variable).""" + nodes = self.nodes + node = nodes + end_node = node + self.node_count + + importances = np.zeros(self.n_features, dtype=np.float64) + + while node != end_node: + if node.left_child != _TREE_LEAF: + left = nodes[node.left_child] + right = nodes[node.right_child] + + importances[node.feature] += ( + node.weighted_n_node_samples * node.impurity - + left.weighted_n_node_samples * left.impurity - + right.weighted_n_node_samples * right.impurity) + node += 1 + + for i in range(self.n_features): + importances[i] /= nodes[0].weighted_n_node_samples + + if normalize: + normalizer = np.sum(importances) + + if normalizer > 0.0: + # Avoid dividing by zero (e.g., when root is pure) + importances /= normalizer + + return importances + + def _get_value_ndarray(self): + """Wraps value as a 3-d NumPy array. + + The array keeps a reference to this Tree, which manages the underlying + memory. + """ + shape = (self.node_count, self.n_outputs, self.max_n_classes) + arr = np.ndarray(shape, dtype=np.float64, buffer=self.value) + arr.base = self + return arr + + def _get_node_ndarray(self): + """Wraps nodes as a NumPy struct array. + + The array keeps a reference to this Tree, which manages the underlying + memory. Individual fields are publicly accessible as properties of the + Tree. + """ + shape = (self.node_count,) + dtype = np.dtype([ + ('left_child', np.intp), + ('right_child', np.intp), + ('feature', np.intp), + ('threshold', np.float64), + ('impurity', np.float64), + ('n_node_samples', np.intp), + ('weighted_n_node_samples', np.float64), + ('missing_go_to_left', np.uint8) + ]) + arr = np.ndarray(shape, dtype=dtype, buffer=self.nodes) + arr.base = self + return arr + + def compute_partial_dependence(self, X, target_features, out): + out.fill(0.0) # Initialize the output array + + _TREE_LEAF = self._TREE_LEAF # The value for leaf nodes + + for sample_idx in range(X.shape[0]): + stack_size = 1 + node_idx_stack = [0] # root node + weight_stack = [1.0] # all samples are in the root node + total_weight = 0.0 + + while stack_size > 0: + stack_size -= 1 + current_node_idx = node_idx_stack[stack_size] + current_node = self.nodes[current_node_idx] + + if current_node.left_child == _TREE_LEAF: + # Leaf node + out[sample_idx] += weight_stack[stack_size] * self.value[current_node_idx] + total_weight += weight_stack[stack_size] + else: + is_target_feature = any(target_feature == current_node.feature for target_feature in target_features) + if is_target_feature: + if X[sample_idx, current_node.feature] <= current_node.threshold: + node_idx_stack.append(current_node.left_child) + weight_stack.append(weight_stack[stack_size]) + stack_size += 1 + else: + node_idx_stack.append(current_node.right_child) + weight_stack.append(weight_stack[stack_size]) + stack_size += 1 + else: + left_sample_frac = self.nodes[current_node.left_child].weighted_n_node_samples / current_node.weighted_n_node_samples + current_weight = weight_stack[stack_size] + node_idx_stack.extend([current_node.left_child, current_node.right_child]) + weight_stack.extend([current_weight * left_sample_frac, current_weight * (1 - left_sample_frac)]) + stack_size += 2 + + if not (0.999 < total_weight < 1.001): + raise ValueError(f"Total weight should be 1.0 but was {total_weight:.9f}") + + +def _check_n_classes(n_classes, expected_dtype): + if n_classes.ndim != 1: + raise ValueError( + f"Wrong dimensions for n_classes from the pickle: " + f"expected 1, got {n_classes.ndim}" + ) + + if n_classes.dtype == expected_dtype: + return n_classes + + # Handles both different endianness and different bitness + if n_classes.dtype.kind == "i" and n_classes.dtype.itemsize in [4, 8]: + return n_classes.astype(expected_dtype, casting="same_kind") + + raise ValueError( + "n_classes from the pickle has an incompatible dtype:\n" + f"- expected: {expected_dtype}\n" + f"- got: {n_classes.dtype}" + ) + + + + + + diff --git a/ivy/functional/frontends/sklearn/tree/tree.py b/ivy/functional/frontends/sklearn/tree/tree.py new file mode 100644 index 0000000000000..55ab869bd01f7 --- /dev/null +++ b/ivy/functional/frontends/sklearn/tree/tree.py @@ -0,0 +1,822 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +import numpy as np + + + +from scipy.sparse import issparse +from scipy.sparse import csr_matrix +from scipy.sparse import isspmatrix_csr + + + + + + + + + + + + + + + + + + + + +# ============================================================================= +# Types and constants +# ============================================================================= + +from numpy import float32 as DTYPE +from numpy import float64 as DOUBLE + + +# Define constants +INFINITY = np.inf +EPSILON = np.finfo(np.double).eps + +# Some handy constants (BestFirstTreeBuilder) +IS_FIRST = 1 +IS_NOT_FIRST = 0 +IS_LEFT = 1 +IS_NOT_LEFT = 0 + +TREE_LEAF = -1 +TREE_UNDEFINED = -2 +_TREE_LEAF = TREE_LEAF +_TREE_UNDEFINED = TREE_UNDEFINED + +# Since you're dealing with Cython-specific types and features, +# it's important to provide a dummy definition for Node. +class Node: + def __init__(self): + self.left_child = None + self.right_child = None + self.feature = None + self.threshold = None + self.impurity = None + self.n_node_samples = None + self.weighted_n_node_samples = None + self.missing_go_to_left = None + +dummy = Node() +# Create a numpy dtype for Node using the dummy object +NODE_DTYPE = np.asarray([dummy], dtype=object).dtype +# ============================================================================= +# TreeBuilder +# ============================================================================= + +class TreeBuilder: + """Interface for different tree building strategies.""" + def __init__(self): + self.splitter = None + self.min_samples_split = None + self.min_samples_leaf = None + self.min_weight_leaf = None + self.max_depth = None + self.min_impurity_decrease = None + + def build( + self, + tree, + X, + y, + sample_weight=None, + missing_values_in_feature_mask=None, + ): + """Build a decision tree from the training set (X, y).""" + pass + + def _check_input( + self, + X, + y, + sample_weight, + ): + """Check input dtype, layout, and format""" + if issparse(X): + X = X.tocsc() #tocsc() is a method provided by the scipy.sparse module in the SciPy library. It's used to convert a sparse matrix to the Compressed Sparse Column (CSC) format. + X.sort_indices() #This is done to ensure that the indices of non-zero elements within the matrix are sorted in ascending order. + + if X.data.dtype != DTYPE: + X.data = np.ascontiguousarray(X.data, dtype=DTYPE) + + if X.indices.dtype != np.int32 or X.indptr.dtype != np.int32: + raise ValueError("No support for np.int64 index-based sparse matrices") + + elif X.dtype != DTYPE: + # since we have to copy, we will make it Fortran for efficiency + X = np.asfortranarray(X, dtype=DTYPE) + + if y.base.dtype != DTYPE or not y.base.flags.contiguous: + y = np.ascontiguousarray(y, dtype=DTYPE) + + if ( + sample_weight is not None and + ( + sample_weight.base.dtype != DOUBLE or + not sample_weight.base.flags.contiguous + ) + ): + sample_weight = np.asarray(sample_weight, dtype=DOUBLE, order="C") + + return X, y, sample_weight + + + + +# Depth first builder --------------------------------------------------------- +# A record on the stack for depth-first tree growing +class StackRecord: + def __init__(self, start, end, depth, parent, is_left, impurity, n_constant_features): + self.start = start + self.end = end + self.depth = depth + self.parent = parent + self.is_left = is_left + self.impurity = impurity + self.n_constant_features = n_constant_features + + + + + + + + +class DepthFirstTreeBuilder(TreeBuilder): + """Build a decision tree in depth-first fashion.""" + + def __init__( + self, splitter, min_samples_split, + min_samples_leaf, min_weight_leaf, + max_depth, min_impurity_decrease + ): + self.splitter = splitter + self.min_samples_split = min_samples_split + self.min_samples_leaf = min_samples_leaf + self.min_weight_leaf = min_weight_leaf + self.max_depth = max_depth + self.min_impurity_decrease = min_impurity_decrease + + def build( + self, + tree, + X, + y, + sample_weight=None, + missing_values_in_feature_mask=None + ): + """Build a decision tree from the training set (X, y).""" + + # Check input + X, y, sample_weight = self._check_input(X, y, sample_weight) + + # Initial capacity + init_capacity = (2 ** (tree.max_depth + 1)) - 1 if tree.max_depth <= 10 else 2047 + + tree._resize(init_capacity) + + # Parameters + splitter = self.splitter + max_depth = self.max_depth + min_samples_leaf = self.min_samples_leaf + min_weight_leaf = self.min_weight_leaf + min_samples_split = self.min_samples_split + min_impurity_decrease = self.min_impurity_decrease + + # Recursive partition (without actual recursion) + splitter.init(X, y, sample_weight, missing_values_in_feature_mask) + + stack = [] + + # Push root node onto stack + stack.append( + StackRecord( + start=0, + end=splitter.n_samples, + depth=0, + parent=_TREE_UNDEFINED, + is_left=False, + impurity=INFINITY, + n_constant_features=0 + ) + ) + weighted_n_node_samples = np.zeros(1, dtype=np.double) + while stack: + stack_record = stack.pop() + + start = stack_record.start + end = stack_record.end + depth = stack_record.depth + parent = stack_record.parent + is_left = stack_record.is_left + impurity = stack_record.impurity + n_constant_features = stack_record.n_constant_features + + n_node_samples = end - start + splitter.node_reset(start, end, weighted_n_node_samples) + + is_leaf = ( + depth >= max_depth + or n_node_samples < min_samples_split + or n_node_samples < 2 * min_samples_leaf + or np.sum(sample_weight[start:end]) < 2 * min_weight_leaf + ) + + if is_left: + impurity = splitter.node_impurity() + + is_leaf = is_leaf or impurity <= EPSILON + + if not is_leaf: + split = SplitRecord() # No idea what is SplitRecord in original code. Maybe this never gets called, not sure + splitter.node_split(impurity, split, n_constant_features) + is_leaf = ( + is_leaf + or split.pos >= end + or (split.improvement + EPSILON < min_impurity_decrease) + ) + + node_id = tree._add_node( + parent, + is_left, + is_leaf, + split.feature if not is_leaf else 0, + split.threshold if not is_leaf else 0, + impurity, + n_node_samples, + np.sum(sample_weight[start:end]), + split.missing_go_to_left, + ) + + if node_id == np.iinfo(np.intp).max: + raise MemoryError() + + splitter.node_value(tree.value + node_id * tree.value_stride) + + if not is_leaf: + # Push right child on stack + stack.append( + StackRecord( + start=split.pos, + end=end, + depth=depth + 1, + parent=node_id, + is_left=False, + impurity=split.impurity_right, + n_constant_features=n_constant_features, + ) + ) + # Push left child on stack + stack.append( + StackRecord( + start=start, + end=split.pos, + depth=depth + 1, + parent=node_id, + is_left=True, + impurity=split.impurity_left, + n_constant_features=n_constant_features, + ) + ) + + +class Tree: + def __init__(self, n_features, n_classes, n_outputs): + """Constructor.""" + self.n_features = None + self.n_classes = None + self.n_outputs = None + self.max_n_classes = None + self.max_depth = None + self.node_count = None + self.capacity = None + self.nodes = [] #replaced it with array since this array will contain nodes + self.value = None + self.value_stride = None + + dummy = 0 + size_t_dtype = np.array(dummy).dtype + + n_classes = _check_n_classes(n_classes, size_t_dtype) + + # Input/Output layout + self.n_features = n_features + self.n_outputs = n_outputs + self.n_classes = np.zeros(n_outputs, dtype=size_t_dtype) + + self.max_n_classes = np.max(n_classes) + self.value_stride = n_outputs * self.max_n_classes + + for k in range(n_outputs): + self.n_classes[k] = n_classes[k] + + # Inner structures + self.max_depth = 0 + self.node_count = 0 + self.capacity = 0 + self.value = None + self.nodes = None + + def __del__(self): + """Destructor.""" + # Free all inner structures + self.n_classes = None + self.value = None + self.nodes = None + + def __reduce__(self): + """Reduce re-implementation, for pickling.""" + raise NotImplementedError + + def __getstate__(self): + """Getstate re-implementation, for pickling.""" + d = {} + # capacity is inferred during the __setstate__ using nodes + d["max_depth"] = self.max_depth + d["node_count"] = self.node_count + d["nodes"] = self._get_node_ndarray() + d["values"] = self._get_value_ndarray() + return d + + def __setstate__(self, d): + """Setstate re-implementation, for unpickling.""" + raise NotImplementedError + + def _resize(self, capacity): + """ + Resize all inner arrays to `capacity`. If `capacity` is -1, then double the size of the inner arrays. + Returns -1 in case of failure to allocate memory (and raise MemoryError), or 0 otherwise. + """ + if self._resize_c(capacity) != 0: + # Raise MemoryError if resizing fails + raise MemoryError() + + def _resize_c(self, capacity=float('inf')): + # """ + # Guts of _resize + # Returns -1 in case of failure to allocate memory (and raise MemoryError), + # or 0 otherwise. + # """ + # if capacity == self.capacity and self.nodes is not None: + # return 0 + + # if capacity == float('inf'): + # if self.capacity == 0: + # capacity = 3 # default initial value + # else: + # capacity = 2 * self.capacity + + # # This section is relevant if the code is dealing with C arrays. + # # In Python, resizing arrays is handled automatically by lists or numpy arrays. + # # You won't need to explicitly reallocate memory or initialize values like this. + # self.nodes = [None] * capacity #doubtfull either the Node classes get reallocated or something else happends + # self.value = [0.0] * (capacity * self.value_stride) #doubtfull either the value classes get reallocated or something else happends + + # # value memory is initialized to 0 to enable classifier argmax + # if capacity > self.capacity: + # self.value += [0.0] * ((capacity - self.capacity) * self.value_stride) + + # # if capacity smaller than node_count, adjust the counter + # if capacity < self.node_count: + # self.node_count = capacity + + # self.capacity = capacity + # return 0 + raise NotImplementedError + + + def _add_node(self, parent, is_left, is_leaf, feature, threshold, impurity, + n_node_samples, weighted_n_node_samples, missing_go_to_left): + """ + Add a node to the tree. + + The new node registers itself as the child of its parent. + + Returns -1 on error. + """ + node_id = self.node_count + + #no need to resize since python reallocates lists dynamically + # if node_id >= self.capacity: + # if self._resize_c() != 0: + # return -1 #throw error if resize not possible + + node = Node() #self.nodes contains a list of nodes, it returns the node at node_id location + self.nodes.append(node) + node.impurity = impurity + node.n_node_samples = n_node_samples + node.weighted_n_node_samples = weighted_n_node_samples + + if parent != _TREE_UNDEFINED: + if is_left: + self.nodes[parent].left_child = node_id + else: + self.nodes[parent].right_child = node_id + + if is_leaf: + node.left_child = _TREE_LEAF + node.right_child = _TREE_LEAF + node.feature = _TREE_UNDEFINED + node.threshold = _TREE_UNDEFINED + + else: + # left_child and right_child will be set later + node.feature = feature + node.threshold = threshold + node.missing_go_to_left = missing_go_to_left + + self.node_count += 1 + + return node_id + + def predict(self, X): + # Apply the model to the input data X + predictions = self.apply(X) + # Get the internal data as a NumPy array + internal_data = self._get_value_ndarray() + # Use the predictions to index the internal data + out = internal_data[predictions] #not sure if this accurately translates to .take(self.apply(X), axis=0, mode='clip') + # Reshape the output if the model is single-output + if self.n_outputs == 1: + out = out.reshape(X.shape[0], self.max_n_classes) + return out + + def apply(self, X): + """Finds the terminal region (=leaf node) for each sample in X.""" + if issparse(X): + return self._apply_sparse_csr(X) + else: + return self._apply_dense(X) + + def _apply_dense(self, X): + if not isinstance(X, torch.Tensor): + raise ValueError("X should be a torch.Tensor, got %s" % type(X)) + + if X.dtype != torch.float32: + raise ValueError("X.dtype should be torch.float32, got %s" % X.dtype) + + X_tensor = X + n_samples = X.shape[0] + out = torch.zeros(n_samples, dtype=torch.int64) + + for i in range(n_samples): + node = self.nodes + + while node.left_child != _TREE_LEAF: + X_i_node_feature = X_tensor[i, node.feature] + + if torch.isnan(X_i_node_feature): + if node.missing_go_to_left: + node = self.nodes[node.left_child] + else: + node = self.nodes[node.right_child] + elif X_i_node_feature <= node.threshold: + node = self.nodes[node.left_child] + else: + node = self.nodes[node.right_child] + + out[i] = node - self.nodes + + return out + + def _apply_sparse_csr(self, X): + """Finds the terminal region (=leaf node) for each sample in sparse X.""" + if not isinstance(X, csr_matrix): + raise ValueError("X should be in csr_matrix format, got %s" % type(X)) + + if X.dtype != np.float32: + raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + + n_samples, n_features = X.shape + + # Initialize output + out = np.zeros(n_samples, dtype=np.intp) + + # Initialize auxiliary data structures + feature_to_sample = np.full(n_features, -1, dtype=np.intp) + X_sample = np.zeros(n_features, dtype=np.float32) + + for i in range(n_samples): + node = self.nodes + + for k in range(X.indptr[i], X.indptr[i + 1]): + feature_to_sample[X.indices[k]] = i + X_sample[X.indices[k]] = X.data[k] + + while node.left_child != _TREE_LEAF: + if feature_to_sample[node.feature] == i: + feature_value = X_sample[node.feature] + else: + feature_value = 0.0 + + if feature_value <= node.threshold: + node = self.nodes[node.left_child] + else: + node = self.nodes[node.right_child] + + out[i] = node - self.nodes # node offset + + return out + + def decision_path(self, X): + """Finds the decision path (=node) for each sample in X.""" + if issparse(X): + return self._decision_path_sparse_csr(X) + else: + return self._decision_path_dense(X) + + def _decision_path_dense(self, X): + """Finds the decision path (=node) for each sample in X.""" + + # Check input + if not isinstance(X, np.ndarray): + raise ValueError("X should be in np.ndarray format, got %s" % type(X)) + + if X.dtype != DTYPE: + raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + + # Extract input + X_ndarray = X + n_samples = X.shape[0] + + # Initialize output + indptr = np.zeros(n_samples + 1, dtype=np.intp) + indices = np.zeros(n_samples * (1 + self.max_depth), dtype=np.intp) + + # Initialize auxiliary data-structure + node = None + i = 0 + + for i in range(n_samples): + node = self.nodes + indptr[i + 1] = indptr[i] + + # Add all external nodes + while node.left_child != _TREE_LEAF: + # ... and node.right_child != _TREE_LEAF: + indices[indptr[i + 1]] = node - self.nodes + indptr[i + 1] += 1 + + if X_ndarray[i, node.feature] <= node.threshold: + node = self.nodes[node.left_child] + else: + node = self.nodes[node.right_child] + + # Add the leaf node + indices[indptr[i + 1]] = node - self.nodes + indptr[i + 1] += 1 + + indices = indices[:indptr[n_samples]] + data = np.ones(shape=len(indices), dtype=np.intp) + out = csr_matrix((data, indices, indptr), shape=(n_samples, self.node_count)) + + return out + + def _decision_path_sparse_csr(self, X): + """Finds the decision path (=node) for each sample in X.""" + + # Check input + if not isspmatrix_csr(X): + raise ValueError("X should be in csr_matrix format, got %s" % type(X)) + + if X.dtype != DTYPE: + raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + + # Extract input + X_data = X.data + X_indices = X.indices + X_indptr = X.indptr + + n_samples = X.shape[0] + n_features = X.shape[1] + + # Initialize output + indptr = np.zeros(n_samples + 1, dtype=np.intp) + indices = np.zeros(n_samples * (1 + self.max_depth), dtype=np.intp) + + # Initialize auxiliary data-structure + feature_value = 0.0 + node = None + X_sample = np.zeros(n_features, dtype=DTYPE) + feature_to_sample = np.full(n_features, -1, dtype=np.intp) + + for i in range(n_samples): + node = self.nodes + indptr[i + 1] = indptr[i] + + for k in range(X_indptr[i], X_indptr[i + 1]): + feature_to_sample[X_indices[k]] = i + X_sample[X_indices[k]] = X_data[k] + + # While node not a leaf + while node.left_child != _TREE_LEAF: + indices[indptr[i + 1]] = node - self.nodes + indptr[i + 1] += 1 + + if feature_to_sample[node.feature] == i: + feature_value = X_sample[node.feature] + else: + feature_value = 0.0 + + if feature_value <= node.threshold: + node = self.nodes[node.left_child] + else: + node = self.nodes[node.right_child] + + # Add the leaf node + indices[indptr[i + 1]] = node - self.nodes + indptr[i + 1] += 1 + + indices = indices[:indptr[n_samples]] + data = np.ones(shape=len(indices), dtype=np.intp) + out = csr_matrix((data, indices, indptr), shape=(n_samples, self.node_count)) + + return out + + def compute_node_depths(self): + """Compute the depth of each node in a tree. + + .. versionadded:: 1.3 + + Returns + ------- + depths : ndarray of shape (self.node_count,), dtype=np.int64 + The depth of each node in the tree. + """ + depths = np.empty(self.node_count, dtype=np.int64) + children_left = self.children_left + children_right = self.children_right + node_count = self.node_count + + depths[0] = 1 # init root node + for node_id in range(node_count): + if children_left[node_id] != _TREE_LEAF: + depth = depths[node_id] + 1 + depths[children_left[node_id]] = depth + depths[children_right[node_id]] = depth + + return depths.base + + def compute_feature_importances(self, normalize=True): + """Computes the importance of each feature (aka variable).""" + nodes = self.nodes + node = nodes + end_node = node + self.node_count + + importances = np.zeros(self.n_features, dtype=np.float64) + + while node != end_node: + if node.left_child != _TREE_LEAF: + left = nodes[node.left_child] + right = nodes[node.right_child] + + importances[node.feature] += ( + node.weighted_n_node_samples * node.impurity - + left.weighted_n_node_samples * left.impurity - + right.weighted_n_node_samples * right.impurity) + node += 1 + + for i in range(self.n_features): + importances[i] /= nodes[0].weighted_n_node_samples + + if normalize: + normalizer = np.sum(importances) + + if normalizer > 0.0: + # Avoid dividing by zero (e.g., when root is pure) + importances /= normalizer + + return importances + + def _get_value_ndarray(self): + """Wraps value as a 3-d NumPy array. + + The array keeps a reference to this Tree, which manages the underlying + memory. + """ + shape = (self.node_count, self.n_outputs, self.max_n_classes) + arr = np.ndarray(shape, dtype=np.float64, buffer=self.value) + arr.base = self + return arr + + def _get_node_ndarray(self): + """Wraps nodes as a NumPy struct array. + + The array keeps a reference to this Tree, which manages the underlying + memory. Individual fields are publicly accessible as properties of the + Tree. + """ + shape = (self.node_count,) + dtype = np.dtype([ + ('left_child', np.intp), + ('right_child', np.intp), + ('feature', np.intp), + ('threshold', np.float64), + ('impurity', np.float64), + ('n_node_samples', np.intp), + ('weighted_n_node_samples', np.float64), + ('missing_go_to_left', np.uint8) + ]) + arr = np.ndarray(shape, dtype=dtype, buffer=self.nodes) + arr.base = self + return arr + + def compute_partial_dependence(self, X, target_features, out): + out.fill(0.0) # Initialize the output array + + _TREE_LEAF = self._TREE_LEAF # The value for leaf nodes + + for sample_idx in range(X.shape[0]): + stack_size = 1 + node_idx_stack = [0] # root node + weight_stack = [1.0] # all samples are in the root node + total_weight = 0.0 + + while stack_size > 0: + stack_size -= 1 + current_node_idx = node_idx_stack[stack_size] + current_node = self.nodes[current_node_idx] + + if current_node.left_child == _TREE_LEAF: + # Leaf node + out[sample_idx] += weight_stack[stack_size] * self.value[current_node_idx] + total_weight += weight_stack[stack_size] + else: + is_target_feature = any(target_feature == current_node.feature for target_feature in target_features) + if is_target_feature: + if X[sample_idx, current_node.feature] <= current_node.threshold: + node_idx_stack.append(current_node.left_child) + weight_stack.append(weight_stack[stack_size]) + stack_size += 1 + else: + node_idx_stack.append(current_node.right_child) + weight_stack.append(weight_stack[stack_size]) + stack_size += 1 + else: + left_sample_frac = self.nodes[current_node.left_child].weighted_n_node_samples / current_node.weighted_n_node_samples + current_weight = weight_stack[stack_size] + node_idx_stack.extend([current_node.left_child, current_node.right_child]) + weight_stack.extend([current_weight * left_sample_frac, current_weight * (1 - left_sample_frac)]) + stack_size += 2 + + if not (0.999 < total_weight < 1.001): + raise ValueError(f"Total weight should be 1.0 but was {total_weight:.9f}") + + +def _check_n_classes(n_classes, expected_dtype): + if n_classes.ndim != 1: + raise ValueError( + f"Wrong dimensions for n_classes from the pickle: " + f"expected 1, got {n_classes.ndim}" + ) + + if n_classes.dtype == expected_dtype: + return n_classes + + # Handles both different endianness and different bitness + if n_classes.dtype.kind == "i" and n_classes.dtype.itemsize in [4, 8]: + return n_classes.astype(expected_dtype, casting="same_kind") + + raise ValueError( + "n_classes from the pickle has an incompatible dtype:\n" + f"- expected: {expected_dtype}\n" + f"- got: {n_classes.dtype}" + ) + + + + + + diff --git a/ivy/functional/frontends/sklearn/tree/try.py b/ivy/functional/frontends/sklearn/tree/try.py new file mode 100644 index 0000000000000..a953aa4ef01d2 --- /dev/null +++ b/ivy/functional/frontends/sklearn/tree/try.py @@ -0,0 +1,5 @@ +import ivy + +a = ivy.array([1,2,3]) + +print(isinstance(a, ivy.data_classes.array.array.Array)) \ No newline at end of file From a8a4ef7624b0da3e92bcee41b61bd428a55a589b Mon Sep 17 00:00:00 2001 From: umairjavaid Date: Sun, 3 Sep 2023 03:58:05 +0000 Subject: [PATCH 004/117] removed the files that are not being worked on rn --- .../frontends/sklearn/_criterion copy.pyx | 1548 -------------- .../frontends/sklearn/_criterion.pyx | 1548 -------------- .../frontends/sklearn/_splitter copy.pyx | 1531 -------------- .../frontends/sklearn/_splitter.pyx | 1531 -------------- .../frontends/sklearn/_tree copy.pyx | 1833 ----------------- ivy/functional/frontends/sklearn/_tree.pyx | 1833 ----------------- .../frontends/sklearn/_tree_ivy copy.pyx | 1833 ----------------- .../frontends/sklearn/_tree_ivy.pyx | 1833 ----------------- ivy/functional/frontends/sklearn/tree copy.py | 822 -------- .../frontends/sklearn/tree ivy copy.py | 840 -------- ivy/functional/frontends/sklearn/tree ivy.py | 840 -------- ivy/functional/frontends/sklearn/tree.py | 822 -------- 12 files changed, 16814 deletions(-) delete mode 100644 ivy/functional/frontends/sklearn/_criterion copy.pyx delete mode 100644 ivy/functional/frontends/sklearn/_criterion.pyx delete mode 100644 ivy/functional/frontends/sklearn/_splitter copy.pyx delete mode 100644 ivy/functional/frontends/sklearn/_splitter.pyx delete mode 100644 ivy/functional/frontends/sklearn/_tree copy.pyx delete mode 100644 ivy/functional/frontends/sklearn/_tree.pyx delete mode 100644 ivy/functional/frontends/sklearn/_tree_ivy copy.pyx delete mode 100644 ivy/functional/frontends/sklearn/_tree_ivy.pyx delete mode 100644 ivy/functional/frontends/sklearn/tree copy.py delete mode 100644 ivy/functional/frontends/sklearn/tree ivy copy.py delete mode 100644 ivy/functional/frontends/sklearn/tree ivy.py delete mode 100644 ivy/functional/frontends/sklearn/tree.py diff --git a/ivy/functional/frontends/sklearn/_criterion copy.pyx b/ivy/functional/frontends/sklearn/_criterion copy.pyx deleted file mode 100644 index df2da34a5303d..0000000000000 --- a/ivy/functional/frontends/sklearn/_criterion copy.pyx +++ /dev/null @@ -1,1548 +0,0 @@ -# Authors: Gilles Louppe -# Peter Prettenhofer -# Brian Holt -# Noel Dawe -# Satrajit Gosh -# Lars Buitinck -# Arnaud Joly -# Joel Nothman -# Fares Hedayati -# Jacob Schreiber -# Nelson Liu -# -# License: BSD 3 clause - -from libc.string cimport memcpy -from libc.string cimport memset -from libc.math cimport fabs, INFINITY - -import numpy as np -cimport numpy as cnp -cnp.import_array() - -from scipy.special.cython_special cimport xlogy - -from ._utils cimport log -from ._utils cimport WeightedMedianCalculator - -# EPSILON is used in the Poisson criterion -cdef double EPSILON = 10 * np.finfo('double').eps - -cdef class Criterion: - """Interface for impurity criteria. - - This object stores methods on how to calculate how good a split is using - different metrics. - """ - def __getstate__(self): - return {} - - def __setstate__(self, d): - pass - - cdef int init( - self, - const DOUBLE_t[:, ::1] y, - const DOUBLE_t[:] sample_weight, - double weighted_n_samples, - const SIZE_t[:] sample_indices, - SIZE_t start, - SIZE_t end, - ) except -1 nogil: - """Placeholder for a method which will initialize the criterion. - - Returns -1 in case of failure to allocate memory (and raise MemoryError) - or 0 otherwise. - - Parameters - ---------- - y : ndarray, dtype=DOUBLE_t - y is a buffer that can store values for n_outputs target variables - stored as a Cython memoryview. - sample_weight : ndarray, dtype=DOUBLE_t - The weight of each sample stored as a Cython memoryview. - weighted_n_samples : double - The total weight of the samples being considered - sample_indices : ndarray, dtype=SIZE_t - A mask on the samples. Indices of the samples in X and y we want to use, - where sample_indices[start:end] correspond to the samples in this node. - start : SIZE_t - The first sample to be used on this node - end : SIZE_t - The last sample used on this node - - """ - pass - - cdef void init_missing(self, SIZE_t n_missing) noexcept nogil: - """Initialize sum_missing if there are missing values. - - This method assumes that caller placed the missing samples in - self.sample_indices[-n_missing:] - - Parameters - ---------- - n_missing: SIZE_t - Number of missing values for specific feature. - """ - pass - - cdef int reset(self) except -1 nogil: - """Reset the criterion at pos=start. - - This method must be implemented by the subclass. - """ - pass - - cdef int reverse_reset(self) except -1 nogil: - """Reset the criterion at pos=end. - - This method must be implemented by the subclass. - """ - pass - - cdef int update(self, SIZE_t new_pos) except -1 nogil: - """Updated statistics by moving sample_indices[pos:new_pos] to the left child. - - This updates the collected statistics by moving sample_indices[pos:new_pos] - from the right child to the left child. It must be implemented by - the subclass. - - Parameters - ---------- - new_pos : SIZE_t - New starting index position of the sample_indices in the right child - """ - pass - - cdef double node_impurity(self) noexcept nogil: - """Placeholder for calculating the impurity of the node. - - Placeholder for a method which will evaluate the impurity of - the current node, i.e. the impurity of sample_indices[start:end]. This is the - primary function of the criterion class. The smaller the impurity the - better. - """ - pass - - cdef void children_impurity(self, double* impurity_left, - double* impurity_right) noexcept nogil: - """Placeholder for calculating the impurity of children. - - Placeholder for a method which evaluates the impurity in - children nodes, i.e. the impurity of sample_indices[start:pos] + the impurity - of sample_indices[pos:end]. - - Parameters - ---------- - impurity_left : double pointer - The memory address where the impurity of the left child should be - stored. - impurity_right : double pointer - The memory address where the impurity of the right child should be - stored - """ - pass - - cdef void node_value(self, double* dest) noexcept nogil: - """Placeholder for storing the node value. - - Placeholder for a method which will compute the node value - of sample_indices[start:end] and save the value into dest. - - Parameters - ---------- - dest : double pointer - The memory address where the node value should be stored. - """ - pass - - cdef double proxy_impurity_improvement(self) noexcept nogil: - """Compute a proxy of the impurity reduction. - - This method is used to speed up the search for the best split. - It is a proxy quantity such that the split that maximizes this value - also maximizes the impurity improvement. It neglects all constant terms - of the impurity decrease for a given split. - - The absolute impurity improvement is only computed by the - impurity_improvement method once the best split has been found. - """ - cdef double impurity_left - cdef double impurity_right - self.children_impurity(&impurity_left, &impurity_right) - - return (- self.weighted_n_right * impurity_right - - self.weighted_n_left * impurity_left) - - cdef double impurity_improvement(self, double impurity_parent, - double impurity_left, - double impurity_right) noexcept nogil: - """Compute the improvement in impurity. - - This method computes the improvement in impurity when a split occurs. - The weighted impurity improvement equation is the following: - - N_t / N * (impurity - N_t_R / N_t * right_impurity - - N_t_L / N_t * left_impurity) - - where N is the total number of samples, N_t is the number of samples - at the current node, N_t_L is the number of samples in the left child, - and N_t_R is the number of samples in the right child, - - Parameters - ---------- - impurity_parent : double - The initial impurity of the parent node before the split - - impurity_left : double - The impurity of the left child - - impurity_right : double - The impurity of the right child - - Return - ------ - double : improvement in impurity after the split occurs - """ - return ((self.weighted_n_node_samples / self.weighted_n_samples) * - (impurity_parent - (self.weighted_n_right / - self.weighted_n_node_samples * impurity_right) - - (self.weighted_n_left / - self.weighted_n_node_samples * impurity_left))) - - cdef void init_sum_missing(self): - """Init sum_missing to hold sums for missing values.""" - -cdef inline void _move_sums_classification( - ClassificationCriterion criterion, - double[:, ::1] sum_1, - double[:, ::1] sum_2, - double* weighted_n_1, - double* weighted_n_2, - bint put_missing_in_1, -) noexcept nogil: - """Distribute sum_total and sum_missing into sum_1 and sum_2. - - If there are missing values and: - - put_missing_in_1 is True, then missing values to go sum_1. Specifically: - sum_1 = sum_missing - sum_2 = sum_total - sum_missing - - - put_missing_in_1 is False, then missing values go to sum_2. Specifically: - sum_1 = 0 - sum_2 = sum_total - """ - cdef SIZE_t k, c, n_bytes - if criterion.n_missing != 0 and put_missing_in_1: - for k in range(criterion.n_outputs): - n_bytes = criterion.n_classes[k] * sizeof(double) - memcpy(&sum_1[k, 0], &criterion.sum_missing[k, 0], n_bytes) - - for k in range(criterion.n_outputs): - for c in range(criterion.n_classes[k]): - sum_2[k, c] = criterion.sum_total[k, c] - criterion.sum_missing[k, c] - - weighted_n_1[0] = criterion.weighted_n_missing - weighted_n_2[0] = criterion.weighted_n_node_samples - criterion.weighted_n_missing - else: - # Assigning sum_2 = sum_total for all outputs. - for k in range(criterion.n_outputs): - n_bytes = criterion.n_classes[k] * sizeof(double) - memset(&sum_1[k, 0], 0, n_bytes) - memcpy(&sum_2[k, 0], &criterion.sum_total[k, 0], n_bytes) - - weighted_n_1[0] = 0.0 - weighted_n_2[0] = criterion.weighted_n_node_samples - - -cdef class ClassificationCriterion(Criterion): - """Abstract criterion for classification.""" - - def __cinit__(self, SIZE_t n_outputs, - cnp.ndarray[SIZE_t, ndim=1] n_classes): - """Initialize attributes for this criterion. - - Parameters - ---------- - n_outputs : SIZE_t - The number of targets, the dimensionality of the prediction - n_classes : numpy.ndarray, dtype=SIZE_t - The number of unique classes in each target - """ - self.start = 0 - self.pos = 0 - self.end = 0 - self.missing_go_to_left = 0 - - self.n_outputs = n_outputs - self.n_samples = 0 - self.n_node_samples = 0 - self.weighted_n_node_samples = 0.0 - self.weighted_n_left = 0.0 - self.weighted_n_right = 0.0 - self.weighted_n_missing = 0.0 - - self.n_classes = np.empty(n_outputs, dtype=np.intp) - - cdef SIZE_t k = 0 - cdef SIZE_t max_n_classes = 0 - - # For each target, set the number of unique classes in that target, - # and also compute the maximal stride of all targets - for k in range(n_outputs): - self.n_classes[k] = n_classes[k] - - if n_classes[k] > max_n_classes: - max_n_classes = n_classes[k] - - self.max_n_classes = max_n_classes - - # Count labels for each output - self.sum_total = np.zeros((n_outputs, max_n_classes), dtype=np.float64) - self.sum_left = np.zeros((n_outputs, max_n_classes), dtype=np.float64) - self.sum_right = np.zeros((n_outputs, max_n_classes), dtype=np.float64) - - def __reduce__(self): - return (type(self), - (self.n_outputs, np.asarray(self.n_classes)), self.__getstate__()) - - cdef int init( - self, - const DOUBLE_t[:, ::1] y, - const DOUBLE_t[:] sample_weight, - double weighted_n_samples, - const SIZE_t[:] sample_indices, - SIZE_t start, - SIZE_t end - ) except -1 nogil: - """Initialize the criterion. - - This initializes the criterion at node sample_indices[start:end] and children - sample_indices[start:start] and sample_indices[start:end]. - - Returns -1 in case of failure to allocate memory (and raise MemoryError) - or 0 otherwise. - - Parameters - ---------- - y : ndarray, dtype=DOUBLE_t - The target stored as a buffer for memory efficiency. - sample_weight : ndarray, dtype=DOUBLE_t - The weight of each sample stored as a Cython memoryview. - weighted_n_samples : double - The total weight of all samples - sample_indices : ndarray, dtype=SIZE_t - A mask on the samples. Indices of the samples in X and y we want to use, - where sample_indices[start:end] correspond to the samples in this node. - start : SIZE_t - The first sample to use in the mask - end : SIZE_t - The last sample to use in the mask - """ - self.y = y - self.sample_weight = sample_weight - self.sample_indices = sample_indices - self.start = start - self.end = end - self.n_node_samples = end - start - self.weighted_n_samples = weighted_n_samples - self.weighted_n_node_samples = 0.0 - - cdef SIZE_t i - cdef SIZE_t p - cdef SIZE_t k - cdef SIZE_t c - cdef DOUBLE_t w = 1.0 - - for k in range(self.n_outputs): - memset(&self.sum_total[k, 0], 0, self.n_classes[k] * sizeof(double)) - - for p in range(start, end): - i = sample_indices[p] - - # w is originally set to be 1.0, meaning that if no sample weights - # are given, the default weight of each sample is 1.0. - if sample_weight is not None: - w = sample_weight[i] - - # Count weighted class frequency for each target - for k in range(self.n_outputs): - c = self.y[i, k] - self.sum_total[k, c] += w - - self.weighted_n_node_samples += w - - # Reset to pos=start - self.reset() - return 0 - - cdef void init_sum_missing(self): - """Init sum_missing to hold sums for missing values.""" - self.sum_missing = np.zeros((self.n_outputs, self.max_n_classes), dtype=np.float64) - - cdef void init_missing(self, SIZE_t n_missing) noexcept nogil: - """Initialize sum_missing if there are missing values. - - This method assumes that caller placed the missing samples in - self.sample_indices[-n_missing:] - """ - cdef SIZE_t i, p, k, c - cdef DOUBLE_t w = 1.0 - - self.n_missing = n_missing - if n_missing == 0: - return - - memset(&self.sum_missing[0, 0], 0, self.max_n_classes * self.n_outputs * sizeof(double)) - - self.weighted_n_missing = 0.0 - - # The missing samples are assumed to be in self.sample_indices[-n_missing:] - for p in range(self.end - n_missing, self.end): - i = self.sample_indices[p] - if self.sample_weight is not None: - w = self.sample_weight[i] - - for k in range(self.n_outputs): - c = self.y[i, k] - self.sum_missing[k, c] += w - - self.weighted_n_missing += w - - cdef int reset(self) except -1 nogil: - """Reset the criterion at pos=start. - - Returns -1 in case of failure to allocate memory (and raise MemoryError) - or 0 otherwise. - """ - self.pos = self.start - _move_sums_classification( - self, - self.sum_left, - self.sum_right, - &self.weighted_n_left, - &self.weighted_n_right, - self.missing_go_to_left, - ) - return 0 - - cdef int reverse_reset(self) except -1 nogil: - """Reset the criterion at pos=end. - - Returns -1 in case of failure to allocate memory (and raise MemoryError) - or 0 otherwise. - """ - self.pos = self.end - _move_sums_classification( - self, - self.sum_right, - self.sum_left, - &self.weighted_n_right, - &self.weighted_n_left, - not self.missing_go_to_left - ) - return 0 - - cdef int update(self, SIZE_t new_pos) except -1 nogil: - """Updated statistics by moving sample_indices[pos:new_pos] to the left child. - - Returns -1 in case of failure to allocate memory (and raise MemoryError) - or 0 otherwise. - - Parameters - ---------- - new_pos : SIZE_t - The new ending position for which to move sample_indices from the right - child to the left child. - """ - cdef SIZE_t pos = self.pos - # The missing samples are assumed to be in - # self.sample_indices[-self.n_missing:] that is - # self.sample_indices[end_non_missing:self.end]. - cdef SIZE_t end_non_missing = self.end - self.n_missing - - cdef const SIZE_t[:] sample_indices = self.sample_indices - cdef const DOUBLE_t[:] sample_weight = self.sample_weight - - cdef SIZE_t i - cdef SIZE_t p - cdef SIZE_t k - cdef SIZE_t c - cdef DOUBLE_t w = 1.0 - - # Update statistics up to new_pos - # - # Given that - # sum_left[x] + sum_right[x] = sum_total[x] - # and that sum_total is known, we are going to update - # sum_left from the direction that require the least amount - # of computations, i.e. from pos to new_pos or from end to new_po. - if (new_pos - pos) <= (end_non_missing - new_pos): - for p in range(pos, new_pos): - i = sample_indices[p] - - if sample_weight is not None: - w = sample_weight[i] - - for k in range(self.n_outputs): - self.sum_left[k, self.y[i, k]] += w - - self.weighted_n_left += w - - else: - self.reverse_reset() - - for p in range(end_non_missing - 1, new_pos - 1, -1): - i = sample_indices[p] - - if sample_weight is not None: - w = sample_weight[i] - - for k in range(self.n_outputs): - self.sum_left[k, self.y[i, k]] -= w - - self.weighted_n_left -= w - - # Update right part statistics - self.weighted_n_right = self.weighted_n_node_samples - self.weighted_n_left - for k in range(self.n_outputs): - for c in range(self.n_classes[k]): - self.sum_right[k, c] = self.sum_total[k, c] - self.sum_left[k, c] - - self.pos = new_pos - return 0 - - cdef double node_impurity(self) noexcept nogil: - pass - - cdef void children_impurity(self, double* impurity_left, - double* impurity_right) noexcept nogil: - pass - - cdef void node_value(self, double* dest) noexcept nogil: - """Compute the node value of sample_indices[start:end] and save it into dest. - - Parameters - ---------- - dest : double pointer - The memory address which we will save the node value into. - """ - cdef SIZE_t k - - for k in range(self.n_outputs): - memcpy(dest, &self.sum_total[k, 0], self.n_classes[k] * sizeof(double)) - dest += self.max_n_classes - - -cdef class Entropy(ClassificationCriterion): - r"""Cross Entropy impurity criterion. - - This handles cases where the target is a classification taking values - 0, 1, ... K-2, K-1. If node m represents a region Rm with Nm observations, - then let - - count_k = 1 / Nm \sum_{x_i in Rm} I(yi = k) - - be the proportion of class k observations in node m. - - The cross-entropy is then defined as - - cross-entropy = -\sum_{k=0}^{K-1} count_k log(count_k) - """ - - cdef double node_impurity(self) noexcept nogil: - """Evaluate the impurity of the current node. - - Evaluate the cross-entropy criterion as impurity of the current node, - i.e. the impurity of sample_indices[start:end]. The smaller the impurity the - better. - """ - cdef double entropy = 0.0 - cdef double count_k - cdef SIZE_t k - cdef SIZE_t c - - for k in range(self.n_outputs): - for c in range(self.n_classes[k]): - count_k = self.sum_total[k, c] - if count_k > 0.0: - count_k /= self.weighted_n_node_samples - entropy -= count_k * log(count_k) - - return entropy / self.n_outputs - - cdef void children_impurity(self, double* impurity_left, - double* impurity_right) noexcept nogil: - """Evaluate the impurity in children nodes. - - i.e. the impurity of the left child (sample_indices[start:pos]) and the - impurity the right child (sample_indices[pos:end]). - - Parameters - ---------- - impurity_left : double pointer - The memory address to save the impurity of the left node - impurity_right : double pointer - The memory address to save the impurity of the right node - """ - cdef double entropy_left = 0.0 - cdef double entropy_right = 0.0 - cdef double count_k - cdef SIZE_t k - cdef SIZE_t c - - for k in range(self.n_outputs): - for c in range(self.n_classes[k]): - count_k = self.sum_left[k, c] - if count_k > 0.0: - count_k /= self.weighted_n_left - entropy_left -= count_k * log(count_k) - - count_k = self.sum_right[k, c] - if count_k > 0.0: - count_k /= self.weighted_n_right - entropy_right -= count_k * log(count_k) - - impurity_left[0] = entropy_left / self.n_outputs - impurity_right[0] = entropy_right / self.n_outputs - - -cdef class Gini(ClassificationCriterion): - r"""Gini Index impurity criterion. - - This handles cases where the target is a classification taking values - 0, 1, ... K-2, K-1. If node m represents a region Rm with Nm observations, - then let - - count_k = 1/ Nm \sum_{x_i in Rm} I(yi = k) - - be the proportion of class k observations in node m. - - The Gini Index is then defined as: - - index = \sum_{k=0}^{K-1} count_k (1 - count_k) - = 1 - \sum_{k=0}^{K-1} count_k ** 2 - """ - - cdef double node_impurity(self) noexcept nogil: - """Evaluate the impurity of the current node. - - Evaluate the Gini criterion as impurity of the current node, - i.e. the impurity of sample_indices[start:end]. The smaller the impurity the - better. - """ - cdef double gini = 0.0 - cdef double sq_count - cdef double count_k - cdef SIZE_t k - cdef SIZE_t c - - for k in range(self.n_outputs): - sq_count = 0.0 - - for c in range(self.n_classes[k]): - count_k = self.sum_total[k, c] - sq_count += count_k * count_k - - gini += 1.0 - sq_count / (self.weighted_n_node_samples * - self.weighted_n_node_samples) - - return gini / self.n_outputs - - cdef void children_impurity(self, double* impurity_left, - double* impurity_right) noexcept nogil: - """Evaluate the impurity in children nodes. - - i.e. the impurity of the left child (sample_indices[start:pos]) and the - impurity the right child (sample_indices[pos:end]) using the Gini index. - - Parameters - ---------- - impurity_left : double pointer - The memory address to save the impurity of the left node to - impurity_right : double pointer - The memory address to save the impurity of the right node to - """ - cdef double gini_left = 0.0 - cdef double gini_right = 0.0 - cdef double sq_count_left - cdef double sq_count_right - cdef double count_k - cdef SIZE_t k - cdef SIZE_t c - - for k in range(self.n_outputs): - sq_count_left = 0.0 - sq_count_right = 0.0 - - for c in range(self.n_classes[k]): - count_k = self.sum_left[k, c] - sq_count_left += count_k * count_k - - count_k = self.sum_right[k, c] - sq_count_right += count_k * count_k - - gini_left += 1.0 - sq_count_left / (self.weighted_n_left * - self.weighted_n_left) - - gini_right += 1.0 - sq_count_right / (self.weighted_n_right * - self.weighted_n_right) - - impurity_left[0] = gini_left / self.n_outputs - impurity_right[0] = gini_right / self.n_outputs - - -cdef inline void _move_sums_regression( - RegressionCriterion criterion, - double[::1] sum_1, - double[::1] sum_2, - double* weighted_n_1, - double* weighted_n_2, - bint put_missing_in_1, -) noexcept nogil: - """Distribute sum_total and sum_missing into sum_1 and sum_2. - - If there are missing values and: - - put_missing_in_1 is True, then missing values to go sum_1. Specifically: - sum_1 = sum_missing - sum_2 = sum_total - sum_missing - - - put_missing_in_1 is False, then missing values go to sum_2. Specifically: - sum_1 = 0 - sum_2 = sum_total - """ - cdef: - SIZE_t i - SIZE_t n_bytes = criterion.n_outputs * sizeof(double) - bint has_missing = criterion.n_missing != 0 - - if has_missing and put_missing_in_1: - memcpy(&sum_1[0], &criterion.sum_missing[0], n_bytes) - for i in range(criterion.n_outputs): - sum_2[i] = criterion.sum_total[i] - criterion.sum_missing[i] - weighted_n_1[0] = criterion.weighted_n_missing - weighted_n_2[0] = criterion.weighted_n_node_samples - criterion.weighted_n_missing - else: - memset(&sum_1[0], 0, n_bytes) - # Assigning sum_2 = sum_total for all outputs. - memcpy(&sum_2[0], &criterion.sum_total[0], n_bytes) - weighted_n_1[0] = 0.0 - weighted_n_2[0] = criterion.weighted_n_node_samples - - -cdef class RegressionCriterion(Criterion): - r"""Abstract regression criterion. - - This handles cases where the target is a continuous value, and is - evaluated by computing the variance of the target values left and right - of the split point. The computation takes linear time with `n_samples` - by using :: - - var = \sum_i^n (y_i - y_bar) ** 2 - = (\sum_i^n y_i ** 2) - n_samples * y_bar ** 2 - """ - - def __cinit__(self, SIZE_t n_outputs, SIZE_t n_samples): - """Initialize parameters for this criterion. - - Parameters - ---------- - n_outputs : SIZE_t - The number of targets to be predicted - - n_samples : SIZE_t - The total number of samples to fit on - """ - # Default values - self.start = 0 - self.pos = 0 - self.end = 0 - - self.n_outputs = n_outputs - self.n_samples = n_samples - self.n_node_samples = 0 - self.weighted_n_node_samples = 0.0 - self.weighted_n_left = 0.0 - self.weighted_n_right = 0.0 - self.weighted_n_missing = 0.0 - - self.sq_sum_total = 0.0 - - self.sum_total = np.zeros(n_outputs, dtype=np.float64) - self.sum_left = np.zeros(n_outputs, dtype=np.float64) - self.sum_right = np.zeros(n_outputs, dtype=np.float64) - - def __reduce__(self): - return (type(self), (self.n_outputs, self.n_samples), self.__getstate__()) - - cdef int init( - self, - const DOUBLE_t[:, ::1] y, - const DOUBLE_t[:] sample_weight, - double weighted_n_samples, - const SIZE_t[:] sample_indices, - SIZE_t start, - SIZE_t end, - ) except -1 nogil: - """Initialize the criterion. - - This initializes the criterion at node sample_indices[start:end] and children - sample_indices[start:start] and sample_indices[start:end]. - """ - # Initialize fields - self.y = y - self.sample_weight = sample_weight - self.sample_indices = sample_indices - self.start = start - self.end = end - self.n_node_samples = end - start - self.weighted_n_samples = weighted_n_samples - self.weighted_n_node_samples = 0. - - cdef SIZE_t i - cdef SIZE_t p - cdef SIZE_t k - cdef DOUBLE_t y_ik - cdef DOUBLE_t w_y_ik - cdef DOUBLE_t w = 1.0 - self.sq_sum_total = 0.0 - memset(&self.sum_total[0], 0, self.n_outputs * sizeof(double)) - - for p in range(start, end): - i = sample_indices[p] - - if sample_weight is not None: - w = sample_weight[i] - - for k in range(self.n_outputs): - y_ik = self.y[i, k] - w_y_ik = w * y_ik - self.sum_total[k] += w_y_ik - self.sq_sum_total += w_y_ik * y_ik - - self.weighted_n_node_samples += w - - # Reset to pos=start - self.reset() - return 0 - - cdef void init_sum_missing(self): - """Init sum_missing to hold sums for missing values.""" - self.sum_missing = np.zeros(self.n_outputs, dtype=np.float64) - - cdef void init_missing(self, SIZE_t n_missing) noexcept nogil: - """Initialize sum_missing if there are missing values. - - This method assumes that caller placed the missing samples in - self.sample_indices[-n_missing:] - """ - cdef SIZE_t i, p, k - cdef DOUBLE_t y_ik - cdef DOUBLE_t w_y_ik - cdef DOUBLE_t w = 1.0 - - self.n_missing = n_missing - if n_missing == 0: - return - - memset(&self.sum_missing[0], 0, self.n_outputs * sizeof(double)) - - self.weighted_n_missing = 0.0 - - # The missing samples are assumed to be in self.sample_indices[-n_missing:] - for p in range(self.end - n_missing, self.end): - i = self.sample_indices[p] - if self.sample_weight is not None: - w = self.sample_weight[i] - - for k in range(self.n_outputs): - y_ik = self.y[i, k] - w_y_ik = w * y_ik - self.sum_missing[k] += w_y_ik - - self.weighted_n_missing += w - - cdef int reset(self) except -1 nogil: - """Reset the criterion at pos=start.""" - self.pos = self.start - _move_sums_regression( - self, - self.sum_left, - self.sum_right, - &self.weighted_n_left, - &self.weighted_n_right, - self.missing_go_to_left - ) - return 0 - - cdef int reverse_reset(self) except -1 nogil: - """Reset the criterion at pos=end.""" - self.pos = self.end - _move_sums_regression( - self, - self.sum_right, - self.sum_left, - &self.weighted_n_right, - &self.weighted_n_left, - not self.missing_go_to_left - ) - return 0 - - cdef int update(self, SIZE_t new_pos) except -1 nogil: - """Updated statistics by moving sample_indices[pos:new_pos] to the left.""" - cdef const DOUBLE_t[:] sample_weight = self.sample_weight - cdef const SIZE_t[:] sample_indices = self.sample_indices - - cdef SIZE_t pos = self.pos - - # The missing samples are assumed to be in - # self.sample_indices[-self.n_missing:] that is - # self.sample_indices[end_non_missing:self.end]. - cdef SIZE_t end_non_missing = self.end - self.n_missing - cdef SIZE_t i - cdef SIZE_t p - cdef SIZE_t k - cdef DOUBLE_t w = 1.0 - - # Update statistics up to new_pos - # - # Given that - # sum_left[x] + sum_right[x] = sum_total[x] - # and that sum_total is known, we are going to update - # sum_left from the direction that require the least amount - # of computations, i.e. from pos to new_pos or from end to new_pos. - if (new_pos - pos) <= (end_non_missing - new_pos): - for p in range(pos, new_pos): - i = sample_indices[p] - - if sample_weight is not None: - w = sample_weight[i] - - for k in range(self.n_outputs): - self.sum_left[k] += w * self.y[i, k] - - self.weighted_n_left += w - else: - self.reverse_reset() - - for p in range(end_non_missing - 1, new_pos - 1, -1): - i = sample_indices[p] - - if sample_weight is not None: - w = sample_weight[i] - - for k in range(self.n_outputs): - self.sum_left[k] -= w * self.y[i, k] - - self.weighted_n_left -= w - - self.weighted_n_right = (self.weighted_n_node_samples - - self.weighted_n_left) - for k in range(self.n_outputs): - self.sum_right[k] = self.sum_total[k] - self.sum_left[k] - - self.pos = new_pos - return 0 - - cdef double node_impurity(self) noexcept nogil: - pass - - cdef void children_impurity(self, double* impurity_left, - double* impurity_right) noexcept nogil: - pass - - cdef void node_value(self, double* dest) noexcept nogil: - """Compute the node value of sample_indices[start:end] into dest.""" - cdef SIZE_t k - - for k in range(self.n_outputs): - dest[k] = self.sum_total[k] / self.weighted_n_node_samples - - -cdef class MSE(RegressionCriterion): - """Mean squared error impurity criterion. - - MSE = var_left + var_right - """ - - cdef double node_impurity(self) noexcept nogil: - """Evaluate the impurity of the current node. - - Evaluate the MSE criterion as impurity of the current node, - i.e. the impurity of sample_indices[start:end]. The smaller the impurity the - better. - """ - cdef double impurity - cdef SIZE_t k - - impurity = self.sq_sum_total / self.weighted_n_node_samples - for k in range(self.n_outputs): - impurity -= (self.sum_total[k] / self.weighted_n_node_samples)**2.0 - - return impurity / self.n_outputs - - cdef double proxy_impurity_improvement(self) noexcept nogil: - """Compute a proxy of the impurity reduction. - - This method is used to speed up the search for the best split. - It is a proxy quantity such that the split that maximizes this value - also maximizes the impurity improvement. It neglects all constant terms - of the impurity decrease for a given split. - - The absolute impurity improvement is only computed by the - impurity_improvement method once the best split has been found. - - The MSE proxy is derived from - - sum_{i left}(y_i - y_pred_L)^2 + sum_{i right}(y_i - y_pred_R)^2 - = sum(y_i^2) - n_L * mean_{i left}(y_i)^2 - n_R * mean_{i right}(y_i)^2 - - Neglecting constant terms, this gives: - - - 1/n_L * sum_{i left}(y_i)^2 - 1/n_R * sum_{i right}(y_i)^2 - """ - cdef SIZE_t k - cdef double proxy_impurity_left = 0.0 - cdef double proxy_impurity_right = 0.0 - - for k in range(self.n_outputs): - proxy_impurity_left += self.sum_left[k] * self.sum_left[k] - proxy_impurity_right += self.sum_right[k] * self.sum_right[k] - - return (proxy_impurity_left / self.weighted_n_left + - proxy_impurity_right / self.weighted_n_right) - - cdef void children_impurity(self, double* impurity_left, - double* impurity_right) noexcept nogil: - """Evaluate the impurity in children nodes. - - i.e. the impurity of the left child (sample_indices[start:pos]) and the - impurity the right child (sample_indices[pos:end]). - """ - cdef const DOUBLE_t[:] sample_weight = self.sample_weight - cdef const SIZE_t[:] sample_indices = self.sample_indices - cdef SIZE_t pos = self.pos - cdef SIZE_t start = self.start - - cdef DOUBLE_t y_ik - - cdef double sq_sum_left = 0.0 - cdef double sq_sum_right - - cdef SIZE_t i - cdef SIZE_t p - cdef SIZE_t k - cdef DOUBLE_t w = 1.0 - - for p in range(start, pos): - i = sample_indices[p] - - if sample_weight is not None: - w = sample_weight[i] - - for k in range(self.n_outputs): - y_ik = self.y[i, k] - sq_sum_left += w * y_ik * y_ik - - sq_sum_right = self.sq_sum_total - sq_sum_left - - impurity_left[0] = sq_sum_left / self.weighted_n_left - impurity_right[0] = sq_sum_right / self.weighted_n_right - - for k in range(self.n_outputs): - impurity_left[0] -= (self.sum_left[k] / self.weighted_n_left) ** 2.0 - impurity_right[0] -= (self.sum_right[k] / self.weighted_n_right) ** 2.0 - - impurity_left[0] /= self.n_outputs - impurity_right[0] /= self.n_outputs - - -cdef class MAE(RegressionCriterion): - r"""Mean absolute error impurity criterion. - - MAE = (1 / n)*(\sum_i |y_i - f_i|), where y_i is the true - value and f_i is the predicted value.""" - - cdef cnp.ndarray left_child - cdef cnp.ndarray right_child - cdef void** left_child_ptr - cdef void** right_child_ptr - cdef DOUBLE_t[::1] node_medians - - def __cinit__(self, SIZE_t n_outputs, SIZE_t n_samples): - """Initialize parameters for this criterion. - - Parameters - ---------- - n_outputs : SIZE_t - The number of targets to be predicted - - n_samples : SIZE_t - The total number of samples to fit on - """ - # Default values - self.start = 0 - self.pos = 0 - self.end = 0 - - self.n_outputs = n_outputs - self.n_samples = n_samples - self.n_node_samples = 0 - self.weighted_n_node_samples = 0.0 - self.weighted_n_left = 0.0 - self.weighted_n_right = 0.0 - - self.node_medians = np.zeros(n_outputs, dtype=np.float64) - - self.left_child = np.empty(n_outputs, dtype='object') - self.right_child = np.empty(n_outputs, dtype='object') - # initialize WeightedMedianCalculators - for k in range(n_outputs): - self.left_child[k] = WeightedMedianCalculator(n_samples) - self.right_child[k] = WeightedMedianCalculator(n_samples) - - self.left_child_ptr = cnp.PyArray_DATA(self.left_child) - self.right_child_ptr = cnp.PyArray_DATA(self.right_child) - - cdef int init( - self, - const DOUBLE_t[:, ::1] y, - const DOUBLE_t[:] sample_weight, - double weighted_n_samples, - const SIZE_t[:] sample_indices, - SIZE_t start, - SIZE_t end, - ) except -1 nogil: - """Initialize the criterion. - - This initializes the criterion at node sample_indices[start:end] and children - sample_indices[start:start] and sample_indices[start:end]. - """ - cdef SIZE_t i, p, k - cdef DOUBLE_t w = 1.0 - - # Initialize fields - self.y = y - self.sample_weight = sample_weight - self.sample_indices = sample_indices - self.start = start - self.end = end - self.n_node_samples = end - start - self.weighted_n_samples = weighted_n_samples - self.weighted_n_node_samples = 0. - - cdef void** left_child = self.left_child_ptr - cdef void** right_child = self.right_child_ptr - - for k in range(self.n_outputs): - ( left_child[k]).reset() - ( right_child[k]).reset() - - for p in range(start, end): - i = sample_indices[p] - - if sample_weight is not None: - w = sample_weight[i] - - for k in range(self.n_outputs): - # push method ends up calling safe_realloc, hence `except -1` - # push all values to the right side, - # since pos = start initially anyway - ( right_child[k]).push(self.y[i, k], w) - - self.weighted_n_node_samples += w - # calculate the node medians - for k in range(self.n_outputs): - self.node_medians[k] = ( right_child[k]).get_median() - - # Reset to pos=start - self.reset() - return 0 - - cdef void init_missing(self, SIZE_t n_missing) noexcept nogil: - """Raise error if n_missing != 0.""" - if n_missing == 0: - return - with gil: - raise ValueError("missing values is not supported for MAE.") - - cdef int reset(self) except -1 nogil: - """Reset the criterion at pos=start. - - Returns -1 in case of failure to allocate memory (and raise MemoryError) - or 0 otherwise. - """ - cdef SIZE_t i, k - cdef DOUBLE_t value - cdef DOUBLE_t weight - - cdef void** left_child = self.left_child_ptr - cdef void** right_child = self.right_child_ptr - - self.weighted_n_left = 0.0 - self.weighted_n_right = self.weighted_n_node_samples - self.pos = self.start - - # reset the WeightedMedianCalculators, left should have no - # elements and right should have all elements. - - for k in range(self.n_outputs): - # if left has no elements, it's already reset - for i in range(( left_child[k]).size()): - # remove everything from left and put it into right - ( left_child[k]).pop(&value, - &weight) - # push method ends up calling safe_realloc, hence `except -1` - ( right_child[k]).push(value, - weight) - return 0 - - cdef int reverse_reset(self) except -1 nogil: - """Reset the criterion at pos=end. - - Returns -1 in case of failure to allocate memory (and raise MemoryError) - or 0 otherwise. - """ - self.weighted_n_right = 0.0 - self.weighted_n_left = self.weighted_n_node_samples - self.pos = self.end - - cdef DOUBLE_t value - cdef DOUBLE_t weight - cdef void** left_child = self.left_child_ptr - cdef void** right_child = self.right_child_ptr - - # reverse reset the WeightedMedianCalculators, right should have no - # elements and left should have all elements. - for k in range(self.n_outputs): - # if right has no elements, it's already reset - for i in range(( right_child[k]).size()): - # remove everything from right and put it into left - ( right_child[k]).pop(&value, - &weight) - # push method ends up calling safe_realloc, hence `except -1` - ( left_child[k]).push(value, - weight) - return 0 - - cdef int update(self, SIZE_t new_pos) except -1 nogil: - """Updated statistics by moving sample_indices[pos:new_pos] to the left. - - Returns -1 in case of failure to allocate memory (and raise MemoryError) - or 0 otherwise. - """ - cdef const DOUBLE_t[:] sample_weight = self.sample_weight - cdef const SIZE_t[:] sample_indices = self.sample_indices - - cdef void** left_child = self.left_child_ptr - cdef void** right_child = self.right_child_ptr - - cdef SIZE_t pos = self.pos - cdef SIZE_t end = self.end - cdef SIZE_t i, p, k - cdef DOUBLE_t w = 1.0 - - # Update statistics up to new_pos - # - # We are going to update right_child and left_child - # from the direction that require the least amount of - # computations, i.e. from pos to new_pos or from end to new_pos. - if (new_pos - pos) <= (end - new_pos): - for p in range(pos, new_pos): - i = sample_indices[p] - - if sample_weight is not None: - w = sample_weight[i] - - for k in range(self.n_outputs): - # remove y_ik and its weight w from right and add to left - ( right_child[k]).remove(self.y[i, k], w) - # push method ends up calling safe_realloc, hence except -1 - ( left_child[k]).push(self.y[i, k], w) - - self.weighted_n_left += w - else: - self.reverse_reset() - - for p in range(end - 1, new_pos - 1, -1): - i = sample_indices[p] - - if sample_weight is not None: - w = sample_weight[i] - - for k in range(self.n_outputs): - # remove y_ik and its weight w from left and add to right - ( left_child[k]).remove(self.y[i, k], w) - ( right_child[k]).push(self.y[i, k], w) - - self.weighted_n_left -= w - - self.weighted_n_right = (self.weighted_n_node_samples - - self.weighted_n_left) - self.pos = new_pos - return 0 - - cdef void node_value(self, double* dest) noexcept nogil: - """Computes the node value of sample_indices[start:end] into dest.""" - cdef SIZE_t k - for k in range(self.n_outputs): - dest[k] = self.node_medians[k] - - cdef double node_impurity(self) noexcept nogil: - """Evaluate the impurity of the current node. - - Evaluate the MAE criterion as impurity of the current node, - i.e. the impurity of sample_indices[start:end]. The smaller the impurity the - better. - """ - cdef const DOUBLE_t[:] sample_weight = self.sample_weight - cdef const SIZE_t[:] sample_indices = self.sample_indices - cdef SIZE_t i, p, k - cdef DOUBLE_t w = 1.0 - cdef DOUBLE_t impurity = 0.0 - - for k in range(self.n_outputs): - for p in range(self.start, self.end): - i = sample_indices[p] - - if sample_weight is not None: - w = sample_weight[i] - - impurity += fabs(self.y[i, k] - self.node_medians[k]) * w - - return impurity / (self.weighted_n_node_samples * self.n_outputs) - - cdef void children_impurity(self, double* p_impurity_left, - double* p_impurity_right) noexcept nogil: - """Evaluate the impurity in children nodes. - - i.e. the impurity of the left child (sample_indices[start:pos]) and the - impurity the right child (sample_indices[pos:end]). - """ - cdef const DOUBLE_t[:] sample_weight = self.sample_weight - cdef const SIZE_t[:] sample_indices = self.sample_indices - - cdef SIZE_t start = self.start - cdef SIZE_t pos = self.pos - cdef SIZE_t end = self.end - - cdef SIZE_t i, p, k - cdef DOUBLE_t median - cdef DOUBLE_t w = 1.0 - cdef DOUBLE_t impurity_left = 0.0 - cdef DOUBLE_t impurity_right = 0.0 - - cdef void** left_child = self.left_child_ptr - cdef void** right_child = self.right_child_ptr - - for k in range(self.n_outputs): - median = ( left_child[k]).get_median() - for p in range(start, pos): - i = sample_indices[p] - - if sample_weight is not None: - w = sample_weight[i] - - impurity_left += fabs(self.y[i, k] - median) * w - p_impurity_left[0] = impurity_left / (self.weighted_n_left * - self.n_outputs) - - for k in range(self.n_outputs): - median = ( right_child[k]).get_median() - for p in range(pos, end): - i = sample_indices[p] - - if sample_weight is not None: - w = sample_weight[i] - - impurity_right += fabs(self.y[i, k] - median) * w - p_impurity_right[0] = impurity_right / (self.weighted_n_right * - self.n_outputs) - - -cdef class FriedmanMSE(MSE): - """Mean squared error impurity criterion with improvement score by Friedman. - - Uses the formula (35) in Friedman's original Gradient Boosting paper: - - diff = mean_left - mean_right - improvement = n_left * n_right * diff^2 / (n_left + n_right) - """ - - cdef double proxy_impurity_improvement(self) noexcept nogil: - """Compute a proxy of the impurity reduction. - - This method is used to speed up the search for the best split. - It is a proxy quantity such that the split that maximizes this value - also maximizes the impurity improvement. It neglects all constant terms - of the impurity decrease for a given split. - - The absolute impurity improvement is only computed by the - impurity_improvement method once the best split has been found. - """ - cdef double total_sum_left = 0.0 - cdef double total_sum_right = 0.0 - - cdef SIZE_t k - cdef double diff = 0.0 - - for k in range(self.n_outputs): - total_sum_left += self.sum_left[k] - total_sum_right += self.sum_right[k] - - diff = (self.weighted_n_right * total_sum_left - - self.weighted_n_left * total_sum_right) - - return diff * diff / (self.weighted_n_left * self.weighted_n_right) - - cdef double impurity_improvement(self, double impurity_parent, double - impurity_left, double impurity_right) noexcept nogil: - # Note: none of the arguments are used here - cdef double total_sum_left = 0.0 - cdef double total_sum_right = 0.0 - - cdef SIZE_t k - cdef double diff = 0.0 - - for k in range(self.n_outputs): - total_sum_left += self.sum_left[k] - total_sum_right += self.sum_right[k] - - diff = (self.weighted_n_right * total_sum_left - - self.weighted_n_left * total_sum_right) / self.n_outputs - - return (diff * diff / (self.weighted_n_left * self.weighted_n_right * - self.weighted_n_node_samples)) - - -cdef class Poisson(RegressionCriterion): - """Half Poisson deviance as impurity criterion. - - Poisson deviance = 2/n * sum(y_true * log(y_true/y_pred) + y_pred - y_true) - - Note that the deviance is >= 0, and since we have `y_pred = mean(y_true)` - at the leaves, one always has `sum(y_pred - y_true) = 0`. It remains the - implemented impurity (factor 2 is skipped): - 1/n * sum(y_true * log(y_true/y_pred) - """ - # FIXME in 1.0: - # min_impurity_split with default = 0 forces us to use a non-negative - # impurity like the Poisson deviance. Without this restriction, one could - # throw away the 'constant' term sum(y_true * log(y_true)) and just use - # Poisson loss = - 1/n * sum(y_true * log(y_pred)) - # = - 1/n * sum(y_true * log(mean(y_true)) - # = - mean(y_true) * log(mean(y_true)) - # With this trick (used in proxy_impurity_improvement()), as for MSE, - # children_impurity would only need to go over left xor right split, not - # both. This could be faster. - - cdef double node_impurity(self) noexcept nogil: - """Evaluate the impurity of the current node. - - Evaluate the Poisson criterion as impurity of the current node, - i.e. the impurity of sample_indices[start:end]. The smaller the impurity the - better. - """ - return self.poisson_loss(self.start, self.end, self.sum_total, - self.weighted_n_node_samples) - - cdef double proxy_impurity_improvement(self) noexcept nogil: - """Compute a proxy of the impurity reduction. - - This method is used to speed up the search for the best split. - It is a proxy quantity such that the split that maximizes this value - also maximizes the impurity improvement. It neglects all constant terms - of the impurity decrease for a given split. - - The absolute impurity improvement is only computed by the - impurity_improvement method once the best split has been found. - - The Poisson proxy is derived from: - - sum_{i left }(y_i * log(y_i / y_pred_L)) - + sum_{i right}(y_i * log(y_i / y_pred_R)) - = sum(y_i * log(y_i) - n_L * mean_{i left}(y_i) * log(mean_{i left}(y_i)) - - n_R * mean_{i right}(y_i) * log(mean_{i right}(y_i)) - - Neglecting constant terms, this gives - - - sum{i left }(y_i) * log(mean{i left}(y_i)) - - sum{i right}(y_i) * log(mean{i right}(y_i)) - """ - cdef SIZE_t k - cdef double proxy_impurity_left = 0.0 - cdef double proxy_impurity_right = 0.0 - cdef double y_mean_left = 0. - cdef double y_mean_right = 0. - - for k in range(self.n_outputs): - if (self.sum_left[k] <= EPSILON) or (self.sum_right[k] <= EPSILON): - # Poisson loss does not allow non-positive predictions. We - # therefore forbid splits that have child nodes with - # sum(y_i) <= 0. - # Since sum_right = sum_total - sum_left, it can lead to - # floating point rounding error and will not give zero. Thus, - # we relax the above comparison to sum(y_i) <= EPSILON. - return -INFINITY - else: - y_mean_left = self.sum_left[k] / self.weighted_n_left - y_mean_right = self.sum_right[k] / self.weighted_n_right - proxy_impurity_left -= self.sum_left[k] * log(y_mean_left) - proxy_impurity_right -= self.sum_right[k] * log(y_mean_right) - - return - proxy_impurity_left - proxy_impurity_right - - cdef void children_impurity(self, double* impurity_left, - double* impurity_right) noexcept nogil: - """Evaluate the impurity in children nodes. - - i.e. the impurity of the left child (sample_indices[start:pos]) and the - impurity of the right child (sample_indices[pos:end]) for Poisson. - """ - cdef SIZE_t start = self.start - cdef SIZE_t pos = self.pos - cdef SIZE_t end = self.end - - impurity_left[0] = self.poisson_loss(start, pos, self.sum_left, - self.weighted_n_left) - - impurity_right[0] = self.poisson_loss(pos, end, self.sum_right, - self.weighted_n_right) - - cdef inline DOUBLE_t poisson_loss(self, - SIZE_t start, - SIZE_t end, - const double[::1] y_sum, - DOUBLE_t weight_sum) noexcept nogil: - """Helper function to compute Poisson loss (~deviance) of a given node. - """ - cdef const DOUBLE_t[:, ::1] y = self.y - cdef const DOUBLE_t[:] sample_weight = self.sample_weight - cdef const SIZE_t[:] sample_indices = self.sample_indices - - cdef DOUBLE_t y_mean = 0. - cdef DOUBLE_t poisson_loss = 0. - cdef DOUBLE_t w = 1.0 - cdef SIZE_t i, k, p - cdef SIZE_t n_outputs = self.n_outputs - - for k in range(n_outputs): - if y_sum[k] <= EPSILON: - # y_sum could be computed from the subtraction - # sum_right = sum_total - sum_left leading to a potential - # floating point rounding error. - # Thus, we relax the comparison y_sum <= 0 to - # y_sum <= EPSILON. - return INFINITY - - y_mean = y_sum[k] / weight_sum - - for p in range(start, end): - i = sample_indices[p] - - if sample_weight is not None: - w = sample_weight[i] - - poisson_loss += w * xlogy(y[i, k], y[i, k] / y_mean) - return poisson_loss / (weight_sum * n_outputs) \ No newline at end of file diff --git a/ivy/functional/frontends/sklearn/_criterion.pyx b/ivy/functional/frontends/sklearn/_criterion.pyx deleted file mode 100644 index df2da34a5303d..0000000000000 --- a/ivy/functional/frontends/sklearn/_criterion.pyx +++ /dev/null @@ -1,1548 +0,0 @@ -# Authors: Gilles Louppe -# Peter Prettenhofer -# Brian Holt -# Noel Dawe -# Satrajit Gosh -# Lars Buitinck -# Arnaud Joly -# Joel Nothman -# Fares Hedayati -# Jacob Schreiber -# Nelson Liu -# -# License: BSD 3 clause - -from libc.string cimport memcpy -from libc.string cimport memset -from libc.math cimport fabs, INFINITY - -import numpy as np -cimport numpy as cnp -cnp.import_array() - -from scipy.special.cython_special cimport xlogy - -from ._utils cimport log -from ._utils cimport WeightedMedianCalculator - -# EPSILON is used in the Poisson criterion -cdef double EPSILON = 10 * np.finfo('double').eps - -cdef class Criterion: - """Interface for impurity criteria. - - This object stores methods on how to calculate how good a split is using - different metrics. - """ - def __getstate__(self): - return {} - - def __setstate__(self, d): - pass - - cdef int init( - self, - const DOUBLE_t[:, ::1] y, - const DOUBLE_t[:] sample_weight, - double weighted_n_samples, - const SIZE_t[:] sample_indices, - SIZE_t start, - SIZE_t end, - ) except -1 nogil: - """Placeholder for a method which will initialize the criterion. - - Returns -1 in case of failure to allocate memory (and raise MemoryError) - or 0 otherwise. - - Parameters - ---------- - y : ndarray, dtype=DOUBLE_t - y is a buffer that can store values for n_outputs target variables - stored as a Cython memoryview. - sample_weight : ndarray, dtype=DOUBLE_t - The weight of each sample stored as a Cython memoryview. - weighted_n_samples : double - The total weight of the samples being considered - sample_indices : ndarray, dtype=SIZE_t - A mask on the samples. Indices of the samples in X and y we want to use, - where sample_indices[start:end] correspond to the samples in this node. - start : SIZE_t - The first sample to be used on this node - end : SIZE_t - The last sample used on this node - - """ - pass - - cdef void init_missing(self, SIZE_t n_missing) noexcept nogil: - """Initialize sum_missing if there are missing values. - - This method assumes that caller placed the missing samples in - self.sample_indices[-n_missing:] - - Parameters - ---------- - n_missing: SIZE_t - Number of missing values for specific feature. - """ - pass - - cdef int reset(self) except -1 nogil: - """Reset the criterion at pos=start. - - This method must be implemented by the subclass. - """ - pass - - cdef int reverse_reset(self) except -1 nogil: - """Reset the criterion at pos=end. - - This method must be implemented by the subclass. - """ - pass - - cdef int update(self, SIZE_t new_pos) except -1 nogil: - """Updated statistics by moving sample_indices[pos:new_pos] to the left child. - - This updates the collected statistics by moving sample_indices[pos:new_pos] - from the right child to the left child. It must be implemented by - the subclass. - - Parameters - ---------- - new_pos : SIZE_t - New starting index position of the sample_indices in the right child - """ - pass - - cdef double node_impurity(self) noexcept nogil: - """Placeholder for calculating the impurity of the node. - - Placeholder for a method which will evaluate the impurity of - the current node, i.e. the impurity of sample_indices[start:end]. This is the - primary function of the criterion class. The smaller the impurity the - better. - """ - pass - - cdef void children_impurity(self, double* impurity_left, - double* impurity_right) noexcept nogil: - """Placeholder for calculating the impurity of children. - - Placeholder for a method which evaluates the impurity in - children nodes, i.e. the impurity of sample_indices[start:pos] + the impurity - of sample_indices[pos:end]. - - Parameters - ---------- - impurity_left : double pointer - The memory address where the impurity of the left child should be - stored. - impurity_right : double pointer - The memory address where the impurity of the right child should be - stored - """ - pass - - cdef void node_value(self, double* dest) noexcept nogil: - """Placeholder for storing the node value. - - Placeholder for a method which will compute the node value - of sample_indices[start:end] and save the value into dest. - - Parameters - ---------- - dest : double pointer - The memory address where the node value should be stored. - """ - pass - - cdef double proxy_impurity_improvement(self) noexcept nogil: - """Compute a proxy of the impurity reduction. - - This method is used to speed up the search for the best split. - It is a proxy quantity such that the split that maximizes this value - also maximizes the impurity improvement. It neglects all constant terms - of the impurity decrease for a given split. - - The absolute impurity improvement is only computed by the - impurity_improvement method once the best split has been found. - """ - cdef double impurity_left - cdef double impurity_right - self.children_impurity(&impurity_left, &impurity_right) - - return (- self.weighted_n_right * impurity_right - - self.weighted_n_left * impurity_left) - - cdef double impurity_improvement(self, double impurity_parent, - double impurity_left, - double impurity_right) noexcept nogil: - """Compute the improvement in impurity. - - This method computes the improvement in impurity when a split occurs. - The weighted impurity improvement equation is the following: - - N_t / N * (impurity - N_t_R / N_t * right_impurity - - N_t_L / N_t * left_impurity) - - where N is the total number of samples, N_t is the number of samples - at the current node, N_t_L is the number of samples in the left child, - and N_t_R is the number of samples in the right child, - - Parameters - ---------- - impurity_parent : double - The initial impurity of the parent node before the split - - impurity_left : double - The impurity of the left child - - impurity_right : double - The impurity of the right child - - Return - ------ - double : improvement in impurity after the split occurs - """ - return ((self.weighted_n_node_samples / self.weighted_n_samples) * - (impurity_parent - (self.weighted_n_right / - self.weighted_n_node_samples * impurity_right) - - (self.weighted_n_left / - self.weighted_n_node_samples * impurity_left))) - - cdef void init_sum_missing(self): - """Init sum_missing to hold sums for missing values.""" - -cdef inline void _move_sums_classification( - ClassificationCriterion criterion, - double[:, ::1] sum_1, - double[:, ::1] sum_2, - double* weighted_n_1, - double* weighted_n_2, - bint put_missing_in_1, -) noexcept nogil: - """Distribute sum_total and sum_missing into sum_1 and sum_2. - - If there are missing values and: - - put_missing_in_1 is True, then missing values to go sum_1. Specifically: - sum_1 = sum_missing - sum_2 = sum_total - sum_missing - - - put_missing_in_1 is False, then missing values go to sum_2. Specifically: - sum_1 = 0 - sum_2 = sum_total - """ - cdef SIZE_t k, c, n_bytes - if criterion.n_missing != 0 and put_missing_in_1: - for k in range(criterion.n_outputs): - n_bytes = criterion.n_classes[k] * sizeof(double) - memcpy(&sum_1[k, 0], &criterion.sum_missing[k, 0], n_bytes) - - for k in range(criterion.n_outputs): - for c in range(criterion.n_classes[k]): - sum_2[k, c] = criterion.sum_total[k, c] - criterion.sum_missing[k, c] - - weighted_n_1[0] = criterion.weighted_n_missing - weighted_n_2[0] = criterion.weighted_n_node_samples - criterion.weighted_n_missing - else: - # Assigning sum_2 = sum_total for all outputs. - for k in range(criterion.n_outputs): - n_bytes = criterion.n_classes[k] * sizeof(double) - memset(&sum_1[k, 0], 0, n_bytes) - memcpy(&sum_2[k, 0], &criterion.sum_total[k, 0], n_bytes) - - weighted_n_1[0] = 0.0 - weighted_n_2[0] = criterion.weighted_n_node_samples - - -cdef class ClassificationCriterion(Criterion): - """Abstract criterion for classification.""" - - def __cinit__(self, SIZE_t n_outputs, - cnp.ndarray[SIZE_t, ndim=1] n_classes): - """Initialize attributes for this criterion. - - Parameters - ---------- - n_outputs : SIZE_t - The number of targets, the dimensionality of the prediction - n_classes : numpy.ndarray, dtype=SIZE_t - The number of unique classes in each target - """ - self.start = 0 - self.pos = 0 - self.end = 0 - self.missing_go_to_left = 0 - - self.n_outputs = n_outputs - self.n_samples = 0 - self.n_node_samples = 0 - self.weighted_n_node_samples = 0.0 - self.weighted_n_left = 0.0 - self.weighted_n_right = 0.0 - self.weighted_n_missing = 0.0 - - self.n_classes = np.empty(n_outputs, dtype=np.intp) - - cdef SIZE_t k = 0 - cdef SIZE_t max_n_classes = 0 - - # For each target, set the number of unique classes in that target, - # and also compute the maximal stride of all targets - for k in range(n_outputs): - self.n_classes[k] = n_classes[k] - - if n_classes[k] > max_n_classes: - max_n_classes = n_classes[k] - - self.max_n_classes = max_n_classes - - # Count labels for each output - self.sum_total = np.zeros((n_outputs, max_n_classes), dtype=np.float64) - self.sum_left = np.zeros((n_outputs, max_n_classes), dtype=np.float64) - self.sum_right = np.zeros((n_outputs, max_n_classes), dtype=np.float64) - - def __reduce__(self): - return (type(self), - (self.n_outputs, np.asarray(self.n_classes)), self.__getstate__()) - - cdef int init( - self, - const DOUBLE_t[:, ::1] y, - const DOUBLE_t[:] sample_weight, - double weighted_n_samples, - const SIZE_t[:] sample_indices, - SIZE_t start, - SIZE_t end - ) except -1 nogil: - """Initialize the criterion. - - This initializes the criterion at node sample_indices[start:end] and children - sample_indices[start:start] and sample_indices[start:end]. - - Returns -1 in case of failure to allocate memory (and raise MemoryError) - or 0 otherwise. - - Parameters - ---------- - y : ndarray, dtype=DOUBLE_t - The target stored as a buffer for memory efficiency. - sample_weight : ndarray, dtype=DOUBLE_t - The weight of each sample stored as a Cython memoryview. - weighted_n_samples : double - The total weight of all samples - sample_indices : ndarray, dtype=SIZE_t - A mask on the samples. Indices of the samples in X and y we want to use, - where sample_indices[start:end] correspond to the samples in this node. - start : SIZE_t - The first sample to use in the mask - end : SIZE_t - The last sample to use in the mask - """ - self.y = y - self.sample_weight = sample_weight - self.sample_indices = sample_indices - self.start = start - self.end = end - self.n_node_samples = end - start - self.weighted_n_samples = weighted_n_samples - self.weighted_n_node_samples = 0.0 - - cdef SIZE_t i - cdef SIZE_t p - cdef SIZE_t k - cdef SIZE_t c - cdef DOUBLE_t w = 1.0 - - for k in range(self.n_outputs): - memset(&self.sum_total[k, 0], 0, self.n_classes[k] * sizeof(double)) - - for p in range(start, end): - i = sample_indices[p] - - # w is originally set to be 1.0, meaning that if no sample weights - # are given, the default weight of each sample is 1.0. - if sample_weight is not None: - w = sample_weight[i] - - # Count weighted class frequency for each target - for k in range(self.n_outputs): - c = self.y[i, k] - self.sum_total[k, c] += w - - self.weighted_n_node_samples += w - - # Reset to pos=start - self.reset() - return 0 - - cdef void init_sum_missing(self): - """Init sum_missing to hold sums for missing values.""" - self.sum_missing = np.zeros((self.n_outputs, self.max_n_classes), dtype=np.float64) - - cdef void init_missing(self, SIZE_t n_missing) noexcept nogil: - """Initialize sum_missing if there are missing values. - - This method assumes that caller placed the missing samples in - self.sample_indices[-n_missing:] - """ - cdef SIZE_t i, p, k, c - cdef DOUBLE_t w = 1.0 - - self.n_missing = n_missing - if n_missing == 0: - return - - memset(&self.sum_missing[0, 0], 0, self.max_n_classes * self.n_outputs * sizeof(double)) - - self.weighted_n_missing = 0.0 - - # The missing samples are assumed to be in self.sample_indices[-n_missing:] - for p in range(self.end - n_missing, self.end): - i = self.sample_indices[p] - if self.sample_weight is not None: - w = self.sample_weight[i] - - for k in range(self.n_outputs): - c = self.y[i, k] - self.sum_missing[k, c] += w - - self.weighted_n_missing += w - - cdef int reset(self) except -1 nogil: - """Reset the criterion at pos=start. - - Returns -1 in case of failure to allocate memory (and raise MemoryError) - or 0 otherwise. - """ - self.pos = self.start - _move_sums_classification( - self, - self.sum_left, - self.sum_right, - &self.weighted_n_left, - &self.weighted_n_right, - self.missing_go_to_left, - ) - return 0 - - cdef int reverse_reset(self) except -1 nogil: - """Reset the criterion at pos=end. - - Returns -1 in case of failure to allocate memory (and raise MemoryError) - or 0 otherwise. - """ - self.pos = self.end - _move_sums_classification( - self, - self.sum_right, - self.sum_left, - &self.weighted_n_right, - &self.weighted_n_left, - not self.missing_go_to_left - ) - return 0 - - cdef int update(self, SIZE_t new_pos) except -1 nogil: - """Updated statistics by moving sample_indices[pos:new_pos] to the left child. - - Returns -1 in case of failure to allocate memory (and raise MemoryError) - or 0 otherwise. - - Parameters - ---------- - new_pos : SIZE_t - The new ending position for which to move sample_indices from the right - child to the left child. - """ - cdef SIZE_t pos = self.pos - # The missing samples are assumed to be in - # self.sample_indices[-self.n_missing:] that is - # self.sample_indices[end_non_missing:self.end]. - cdef SIZE_t end_non_missing = self.end - self.n_missing - - cdef const SIZE_t[:] sample_indices = self.sample_indices - cdef const DOUBLE_t[:] sample_weight = self.sample_weight - - cdef SIZE_t i - cdef SIZE_t p - cdef SIZE_t k - cdef SIZE_t c - cdef DOUBLE_t w = 1.0 - - # Update statistics up to new_pos - # - # Given that - # sum_left[x] + sum_right[x] = sum_total[x] - # and that sum_total is known, we are going to update - # sum_left from the direction that require the least amount - # of computations, i.e. from pos to new_pos or from end to new_po. - if (new_pos - pos) <= (end_non_missing - new_pos): - for p in range(pos, new_pos): - i = sample_indices[p] - - if sample_weight is not None: - w = sample_weight[i] - - for k in range(self.n_outputs): - self.sum_left[k, self.y[i, k]] += w - - self.weighted_n_left += w - - else: - self.reverse_reset() - - for p in range(end_non_missing - 1, new_pos - 1, -1): - i = sample_indices[p] - - if sample_weight is not None: - w = sample_weight[i] - - for k in range(self.n_outputs): - self.sum_left[k, self.y[i, k]] -= w - - self.weighted_n_left -= w - - # Update right part statistics - self.weighted_n_right = self.weighted_n_node_samples - self.weighted_n_left - for k in range(self.n_outputs): - for c in range(self.n_classes[k]): - self.sum_right[k, c] = self.sum_total[k, c] - self.sum_left[k, c] - - self.pos = new_pos - return 0 - - cdef double node_impurity(self) noexcept nogil: - pass - - cdef void children_impurity(self, double* impurity_left, - double* impurity_right) noexcept nogil: - pass - - cdef void node_value(self, double* dest) noexcept nogil: - """Compute the node value of sample_indices[start:end] and save it into dest. - - Parameters - ---------- - dest : double pointer - The memory address which we will save the node value into. - """ - cdef SIZE_t k - - for k in range(self.n_outputs): - memcpy(dest, &self.sum_total[k, 0], self.n_classes[k] * sizeof(double)) - dest += self.max_n_classes - - -cdef class Entropy(ClassificationCriterion): - r"""Cross Entropy impurity criterion. - - This handles cases where the target is a classification taking values - 0, 1, ... K-2, K-1. If node m represents a region Rm with Nm observations, - then let - - count_k = 1 / Nm \sum_{x_i in Rm} I(yi = k) - - be the proportion of class k observations in node m. - - The cross-entropy is then defined as - - cross-entropy = -\sum_{k=0}^{K-1} count_k log(count_k) - """ - - cdef double node_impurity(self) noexcept nogil: - """Evaluate the impurity of the current node. - - Evaluate the cross-entropy criterion as impurity of the current node, - i.e. the impurity of sample_indices[start:end]. The smaller the impurity the - better. - """ - cdef double entropy = 0.0 - cdef double count_k - cdef SIZE_t k - cdef SIZE_t c - - for k in range(self.n_outputs): - for c in range(self.n_classes[k]): - count_k = self.sum_total[k, c] - if count_k > 0.0: - count_k /= self.weighted_n_node_samples - entropy -= count_k * log(count_k) - - return entropy / self.n_outputs - - cdef void children_impurity(self, double* impurity_left, - double* impurity_right) noexcept nogil: - """Evaluate the impurity in children nodes. - - i.e. the impurity of the left child (sample_indices[start:pos]) and the - impurity the right child (sample_indices[pos:end]). - - Parameters - ---------- - impurity_left : double pointer - The memory address to save the impurity of the left node - impurity_right : double pointer - The memory address to save the impurity of the right node - """ - cdef double entropy_left = 0.0 - cdef double entropy_right = 0.0 - cdef double count_k - cdef SIZE_t k - cdef SIZE_t c - - for k in range(self.n_outputs): - for c in range(self.n_classes[k]): - count_k = self.sum_left[k, c] - if count_k > 0.0: - count_k /= self.weighted_n_left - entropy_left -= count_k * log(count_k) - - count_k = self.sum_right[k, c] - if count_k > 0.0: - count_k /= self.weighted_n_right - entropy_right -= count_k * log(count_k) - - impurity_left[0] = entropy_left / self.n_outputs - impurity_right[0] = entropy_right / self.n_outputs - - -cdef class Gini(ClassificationCriterion): - r"""Gini Index impurity criterion. - - This handles cases where the target is a classification taking values - 0, 1, ... K-2, K-1. If node m represents a region Rm with Nm observations, - then let - - count_k = 1/ Nm \sum_{x_i in Rm} I(yi = k) - - be the proportion of class k observations in node m. - - The Gini Index is then defined as: - - index = \sum_{k=0}^{K-1} count_k (1 - count_k) - = 1 - \sum_{k=0}^{K-1} count_k ** 2 - """ - - cdef double node_impurity(self) noexcept nogil: - """Evaluate the impurity of the current node. - - Evaluate the Gini criterion as impurity of the current node, - i.e. the impurity of sample_indices[start:end]. The smaller the impurity the - better. - """ - cdef double gini = 0.0 - cdef double sq_count - cdef double count_k - cdef SIZE_t k - cdef SIZE_t c - - for k in range(self.n_outputs): - sq_count = 0.0 - - for c in range(self.n_classes[k]): - count_k = self.sum_total[k, c] - sq_count += count_k * count_k - - gini += 1.0 - sq_count / (self.weighted_n_node_samples * - self.weighted_n_node_samples) - - return gini / self.n_outputs - - cdef void children_impurity(self, double* impurity_left, - double* impurity_right) noexcept nogil: - """Evaluate the impurity in children nodes. - - i.e. the impurity of the left child (sample_indices[start:pos]) and the - impurity the right child (sample_indices[pos:end]) using the Gini index. - - Parameters - ---------- - impurity_left : double pointer - The memory address to save the impurity of the left node to - impurity_right : double pointer - The memory address to save the impurity of the right node to - """ - cdef double gini_left = 0.0 - cdef double gini_right = 0.0 - cdef double sq_count_left - cdef double sq_count_right - cdef double count_k - cdef SIZE_t k - cdef SIZE_t c - - for k in range(self.n_outputs): - sq_count_left = 0.0 - sq_count_right = 0.0 - - for c in range(self.n_classes[k]): - count_k = self.sum_left[k, c] - sq_count_left += count_k * count_k - - count_k = self.sum_right[k, c] - sq_count_right += count_k * count_k - - gini_left += 1.0 - sq_count_left / (self.weighted_n_left * - self.weighted_n_left) - - gini_right += 1.0 - sq_count_right / (self.weighted_n_right * - self.weighted_n_right) - - impurity_left[0] = gini_left / self.n_outputs - impurity_right[0] = gini_right / self.n_outputs - - -cdef inline void _move_sums_regression( - RegressionCriterion criterion, - double[::1] sum_1, - double[::1] sum_2, - double* weighted_n_1, - double* weighted_n_2, - bint put_missing_in_1, -) noexcept nogil: - """Distribute sum_total and sum_missing into sum_1 and sum_2. - - If there are missing values and: - - put_missing_in_1 is True, then missing values to go sum_1. Specifically: - sum_1 = sum_missing - sum_2 = sum_total - sum_missing - - - put_missing_in_1 is False, then missing values go to sum_2. Specifically: - sum_1 = 0 - sum_2 = sum_total - """ - cdef: - SIZE_t i - SIZE_t n_bytes = criterion.n_outputs * sizeof(double) - bint has_missing = criterion.n_missing != 0 - - if has_missing and put_missing_in_1: - memcpy(&sum_1[0], &criterion.sum_missing[0], n_bytes) - for i in range(criterion.n_outputs): - sum_2[i] = criterion.sum_total[i] - criterion.sum_missing[i] - weighted_n_1[0] = criterion.weighted_n_missing - weighted_n_2[0] = criterion.weighted_n_node_samples - criterion.weighted_n_missing - else: - memset(&sum_1[0], 0, n_bytes) - # Assigning sum_2 = sum_total for all outputs. - memcpy(&sum_2[0], &criterion.sum_total[0], n_bytes) - weighted_n_1[0] = 0.0 - weighted_n_2[0] = criterion.weighted_n_node_samples - - -cdef class RegressionCriterion(Criterion): - r"""Abstract regression criterion. - - This handles cases where the target is a continuous value, and is - evaluated by computing the variance of the target values left and right - of the split point. The computation takes linear time with `n_samples` - by using :: - - var = \sum_i^n (y_i - y_bar) ** 2 - = (\sum_i^n y_i ** 2) - n_samples * y_bar ** 2 - """ - - def __cinit__(self, SIZE_t n_outputs, SIZE_t n_samples): - """Initialize parameters for this criterion. - - Parameters - ---------- - n_outputs : SIZE_t - The number of targets to be predicted - - n_samples : SIZE_t - The total number of samples to fit on - """ - # Default values - self.start = 0 - self.pos = 0 - self.end = 0 - - self.n_outputs = n_outputs - self.n_samples = n_samples - self.n_node_samples = 0 - self.weighted_n_node_samples = 0.0 - self.weighted_n_left = 0.0 - self.weighted_n_right = 0.0 - self.weighted_n_missing = 0.0 - - self.sq_sum_total = 0.0 - - self.sum_total = np.zeros(n_outputs, dtype=np.float64) - self.sum_left = np.zeros(n_outputs, dtype=np.float64) - self.sum_right = np.zeros(n_outputs, dtype=np.float64) - - def __reduce__(self): - return (type(self), (self.n_outputs, self.n_samples), self.__getstate__()) - - cdef int init( - self, - const DOUBLE_t[:, ::1] y, - const DOUBLE_t[:] sample_weight, - double weighted_n_samples, - const SIZE_t[:] sample_indices, - SIZE_t start, - SIZE_t end, - ) except -1 nogil: - """Initialize the criterion. - - This initializes the criterion at node sample_indices[start:end] and children - sample_indices[start:start] and sample_indices[start:end]. - """ - # Initialize fields - self.y = y - self.sample_weight = sample_weight - self.sample_indices = sample_indices - self.start = start - self.end = end - self.n_node_samples = end - start - self.weighted_n_samples = weighted_n_samples - self.weighted_n_node_samples = 0. - - cdef SIZE_t i - cdef SIZE_t p - cdef SIZE_t k - cdef DOUBLE_t y_ik - cdef DOUBLE_t w_y_ik - cdef DOUBLE_t w = 1.0 - self.sq_sum_total = 0.0 - memset(&self.sum_total[0], 0, self.n_outputs * sizeof(double)) - - for p in range(start, end): - i = sample_indices[p] - - if sample_weight is not None: - w = sample_weight[i] - - for k in range(self.n_outputs): - y_ik = self.y[i, k] - w_y_ik = w * y_ik - self.sum_total[k] += w_y_ik - self.sq_sum_total += w_y_ik * y_ik - - self.weighted_n_node_samples += w - - # Reset to pos=start - self.reset() - return 0 - - cdef void init_sum_missing(self): - """Init sum_missing to hold sums for missing values.""" - self.sum_missing = np.zeros(self.n_outputs, dtype=np.float64) - - cdef void init_missing(self, SIZE_t n_missing) noexcept nogil: - """Initialize sum_missing if there are missing values. - - This method assumes that caller placed the missing samples in - self.sample_indices[-n_missing:] - """ - cdef SIZE_t i, p, k - cdef DOUBLE_t y_ik - cdef DOUBLE_t w_y_ik - cdef DOUBLE_t w = 1.0 - - self.n_missing = n_missing - if n_missing == 0: - return - - memset(&self.sum_missing[0], 0, self.n_outputs * sizeof(double)) - - self.weighted_n_missing = 0.0 - - # The missing samples are assumed to be in self.sample_indices[-n_missing:] - for p in range(self.end - n_missing, self.end): - i = self.sample_indices[p] - if self.sample_weight is not None: - w = self.sample_weight[i] - - for k in range(self.n_outputs): - y_ik = self.y[i, k] - w_y_ik = w * y_ik - self.sum_missing[k] += w_y_ik - - self.weighted_n_missing += w - - cdef int reset(self) except -1 nogil: - """Reset the criterion at pos=start.""" - self.pos = self.start - _move_sums_regression( - self, - self.sum_left, - self.sum_right, - &self.weighted_n_left, - &self.weighted_n_right, - self.missing_go_to_left - ) - return 0 - - cdef int reverse_reset(self) except -1 nogil: - """Reset the criterion at pos=end.""" - self.pos = self.end - _move_sums_regression( - self, - self.sum_right, - self.sum_left, - &self.weighted_n_right, - &self.weighted_n_left, - not self.missing_go_to_left - ) - return 0 - - cdef int update(self, SIZE_t new_pos) except -1 nogil: - """Updated statistics by moving sample_indices[pos:new_pos] to the left.""" - cdef const DOUBLE_t[:] sample_weight = self.sample_weight - cdef const SIZE_t[:] sample_indices = self.sample_indices - - cdef SIZE_t pos = self.pos - - # The missing samples are assumed to be in - # self.sample_indices[-self.n_missing:] that is - # self.sample_indices[end_non_missing:self.end]. - cdef SIZE_t end_non_missing = self.end - self.n_missing - cdef SIZE_t i - cdef SIZE_t p - cdef SIZE_t k - cdef DOUBLE_t w = 1.0 - - # Update statistics up to new_pos - # - # Given that - # sum_left[x] + sum_right[x] = sum_total[x] - # and that sum_total is known, we are going to update - # sum_left from the direction that require the least amount - # of computations, i.e. from pos to new_pos or from end to new_pos. - if (new_pos - pos) <= (end_non_missing - new_pos): - for p in range(pos, new_pos): - i = sample_indices[p] - - if sample_weight is not None: - w = sample_weight[i] - - for k in range(self.n_outputs): - self.sum_left[k] += w * self.y[i, k] - - self.weighted_n_left += w - else: - self.reverse_reset() - - for p in range(end_non_missing - 1, new_pos - 1, -1): - i = sample_indices[p] - - if sample_weight is not None: - w = sample_weight[i] - - for k in range(self.n_outputs): - self.sum_left[k] -= w * self.y[i, k] - - self.weighted_n_left -= w - - self.weighted_n_right = (self.weighted_n_node_samples - - self.weighted_n_left) - for k in range(self.n_outputs): - self.sum_right[k] = self.sum_total[k] - self.sum_left[k] - - self.pos = new_pos - return 0 - - cdef double node_impurity(self) noexcept nogil: - pass - - cdef void children_impurity(self, double* impurity_left, - double* impurity_right) noexcept nogil: - pass - - cdef void node_value(self, double* dest) noexcept nogil: - """Compute the node value of sample_indices[start:end] into dest.""" - cdef SIZE_t k - - for k in range(self.n_outputs): - dest[k] = self.sum_total[k] / self.weighted_n_node_samples - - -cdef class MSE(RegressionCriterion): - """Mean squared error impurity criterion. - - MSE = var_left + var_right - """ - - cdef double node_impurity(self) noexcept nogil: - """Evaluate the impurity of the current node. - - Evaluate the MSE criterion as impurity of the current node, - i.e. the impurity of sample_indices[start:end]. The smaller the impurity the - better. - """ - cdef double impurity - cdef SIZE_t k - - impurity = self.sq_sum_total / self.weighted_n_node_samples - for k in range(self.n_outputs): - impurity -= (self.sum_total[k] / self.weighted_n_node_samples)**2.0 - - return impurity / self.n_outputs - - cdef double proxy_impurity_improvement(self) noexcept nogil: - """Compute a proxy of the impurity reduction. - - This method is used to speed up the search for the best split. - It is a proxy quantity such that the split that maximizes this value - also maximizes the impurity improvement. It neglects all constant terms - of the impurity decrease for a given split. - - The absolute impurity improvement is only computed by the - impurity_improvement method once the best split has been found. - - The MSE proxy is derived from - - sum_{i left}(y_i - y_pred_L)^2 + sum_{i right}(y_i - y_pred_R)^2 - = sum(y_i^2) - n_L * mean_{i left}(y_i)^2 - n_R * mean_{i right}(y_i)^2 - - Neglecting constant terms, this gives: - - - 1/n_L * sum_{i left}(y_i)^2 - 1/n_R * sum_{i right}(y_i)^2 - """ - cdef SIZE_t k - cdef double proxy_impurity_left = 0.0 - cdef double proxy_impurity_right = 0.0 - - for k in range(self.n_outputs): - proxy_impurity_left += self.sum_left[k] * self.sum_left[k] - proxy_impurity_right += self.sum_right[k] * self.sum_right[k] - - return (proxy_impurity_left / self.weighted_n_left + - proxy_impurity_right / self.weighted_n_right) - - cdef void children_impurity(self, double* impurity_left, - double* impurity_right) noexcept nogil: - """Evaluate the impurity in children nodes. - - i.e. the impurity of the left child (sample_indices[start:pos]) and the - impurity the right child (sample_indices[pos:end]). - """ - cdef const DOUBLE_t[:] sample_weight = self.sample_weight - cdef const SIZE_t[:] sample_indices = self.sample_indices - cdef SIZE_t pos = self.pos - cdef SIZE_t start = self.start - - cdef DOUBLE_t y_ik - - cdef double sq_sum_left = 0.0 - cdef double sq_sum_right - - cdef SIZE_t i - cdef SIZE_t p - cdef SIZE_t k - cdef DOUBLE_t w = 1.0 - - for p in range(start, pos): - i = sample_indices[p] - - if sample_weight is not None: - w = sample_weight[i] - - for k in range(self.n_outputs): - y_ik = self.y[i, k] - sq_sum_left += w * y_ik * y_ik - - sq_sum_right = self.sq_sum_total - sq_sum_left - - impurity_left[0] = sq_sum_left / self.weighted_n_left - impurity_right[0] = sq_sum_right / self.weighted_n_right - - for k in range(self.n_outputs): - impurity_left[0] -= (self.sum_left[k] / self.weighted_n_left) ** 2.0 - impurity_right[0] -= (self.sum_right[k] / self.weighted_n_right) ** 2.0 - - impurity_left[0] /= self.n_outputs - impurity_right[0] /= self.n_outputs - - -cdef class MAE(RegressionCriterion): - r"""Mean absolute error impurity criterion. - - MAE = (1 / n)*(\sum_i |y_i - f_i|), where y_i is the true - value and f_i is the predicted value.""" - - cdef cnp.ndarray left_child - cdef cnp.ndarray right_child - cdef void** left_child_ptr - cdef void** right_child_ptr - cdef DOUBLE_t[::1] node_medians - - def __cinit__(self, SIZE_t n_outputs, SIZE_t n_samples): - """Initialize parameters for this criterion. - - Parameters - ---------- - n_outputs : SIZE_t - The number of targets to be predicted - - n_samples : SIZE_t - The total number of samples to fit on - """ - # Default values - self.start = 0 - self.pos = 0 - self.end = 0 - - self.n_outputs = n_outputs - self.n_samples = n_samples - self.n_node_samples = 0 - self.weighted_n_node_samples = 0.0 - self.weighted_n_left = 0.0 - self.weighted_n_right = 0.0 - - self.node_medians = np.zeros(n_outputs, dtype=np.float64) - - self.left_child = np.empty(n_outputs, dtype='object') - self.right_child = np.empty(n_outputs, dtype='object') - # initialize WeightedMedianCalculators - for k in range(n_outputs): - self.left_child[k] = WeightedMedianCalculator(n_samples) - self.right_child[k] = WeightedMedianCalculator(n_samples) - - self.left_child_ptr = cnp.PyArray_DATA(self.left_child) - self.right_child_ptr = cnp.PyArray_DATA(self.right_child) - - cdef int init( - self, - const DOUBLE_t[:, ::1] y, - const DOUBLE_t[:] sample_weight, - double weighted_n_samples, - const SIZE_t[:] sample_indices, - SIZE_t start, - SIZE_t end, - ) except -1 nogil: - """Initialize the criterion. - - This initializes the criterion at node sample_indices[start:end] and children - sample_indices[start:start] and sample_indices[start:end]. - """ - cdef SIZE_t i, p, k - cdef DOUBLE_t w = 1.0 - - # Initialize fields - self.y = y - self.sample_weight = sample_weight - self.sample_indices = sample_indices - self.start = start - self.end = end - self.n_node_samples = end - start - self.weighted_n_samples = weighted_n_samples - self.weighted_n_node_samples = 0. - - cdef void** left_child = self.left_child_ptr - cdef void** right_child = self.right_child_ptr - - for k in range(self.n_outputs): - ( left_child[k]).reset() - ( right_child[k]).reset() - - for p in range(start, end): - i = sample_indices[p] - - if sample_weight is not None: - w = sample_weight[i] - - for k in range(self.n_outputs): - # push method ends up calling safe_realloc, hence `except -1` - # push all values to the right side, - # since pos = start initially anyway - ( right_child[k]).push(self.y[i, k], w) - - self.weighted_n_node_samples += w - # calculate the node medians - for k in range(self.n_outputs): - self.node_medians[k] = ( right_child[k]).get_median() - - # Reset to pos=start - self.reset() - return 0 - - cdef void init_missing(self, SIZE_t n_missing) noexcept nogil: - """Raise error if n_missing != 0.""" - if n_missing == 0: - return - with gil: - raise ValueError("missing values is not supported for MAE.") - - cdef int reset(self) except -1 nogil: - """Reset the criterion at pos=start. - - Returns -1 in case of failure to allocate memory (and raise MemoryError) - or 0 otherwise. - """ - cdef SIZE_t i, k - cdef DOUBLE_t value - cdef DOUBLE_t weight - - cdef void** left_child = self.left_child_ptr - cdef void** right_child = self.right_child_ptr - - self.weighted_n_left = 0.0 - self.weighted_n_right = self.weighted_n_node_samples - self.pos = self.start - - # reset the WeightedMedianCalculators, left should have no - # elements and right should have all elements. - - for k in range(self.n_outputs): - # if left has no elements, it's already reset - for i in range(( left_child[k]).size()): - # remove everything from left and put it into right - ( left_child[k]).pop(&value, - &weight) - # push method ends up calling safe_realloc, hence `except -1` - ( right_child[k]).push(value, - weight) - return 0 - - cdef int reverse_reset(self) except -1 nogil: - """Reset the criterion at pos=end. - - Returns -1 in case of failure to allocate memory (and raise MemoryError) - or 0 otherwise. - """ - self.weighted_n_right = 0.0 - self.weighted_n_left = self.weighted_n_node_samples - self.pos = self.end - - cdef DOUBLE_t value - cdef DOUBLE_t weight - cdef void** left_child = self.left_child_ptr - cdef void** right_child = self.right_child_ptr - - # reverse reset the WeightedMedianCalculators, right should have no - # elements and left should have all elements. - for k in range(self.n_outputs): - # if right has no elements, it's already reset - for i in range(( right_child[k]).size()): - # remove everything from right and put it into left - ( right_child[k]).pop(&value, - &weight) - # push method ends up calling safe_realloc, hence `except -1` - ( left_child[k]).push(value, - weight) - return 0 - - cdef int update(self, SIZE_t new_pos) except -1 nogil: - """Updated statistics by moving sample_indices[pos:new_pos] to the left. - - Returns -1 in case of failure to allocate memory (and raise MemoryError) - or 0 otherwise. - """ - cdef const DOUBLE_t[:] sample_weight = self.sample_weight - cdef const SIZE_t[:] sample_indices = self.sample_indices - - cdef void** left_child = self.left_child_ptr - cdef void** right_child = self.right_child_ptr - - cdef SIZE_t pos = self.pos - cdef SIZE_t end = self.end - cdef SIZE_t i, p, k - cdef DOUBLE_t w = 1.0 - - # Update statistics up to new_pos - # - # We are going to update right_child and left_child - # from the direction that require the least amount of - # computations, i.e. from pos to new_pos or from end to new_pos. - if (new_pos - pos) <= (end - new_pos): - for p in range(pos, new_pos): - i = sample_indices[p] - - if sample_weight is not None: - w = sample_weight[i] - - for k in range(self.n_outputs): - # remove y_ik and its weight w from right and add to left - ( right_child[k]).remove(self.y[i, k], w) - # push method ends up calling safe_realloc, hence except -1 - ( left_child[k]).push(self.y[i, k], w) - - self.weighted_n_left += w - else: - self.reverse_reset() - - for p in range(end - 1, new_pos - 1, -1): - i = sample_indices[p] - - if sample_weight is not None: - w = sample_weight[i] - - for k in range(self.n_outputs): - # remove y_ik and its weight w from left and add to right - ( left_child[k]).remove(self.y[i, k], w) - ( right_child[k]).push(self.y[i, k], w) - - self.weighted_n_left -= w - - self.weighted_n_right = (self.weighted_n_node_samples - - self.weighted_n_left) - self.pos = new_pos - return 0 - - cdef void node_value(self, double* dest) noexcept nogil: - """Computes the node value of sample_indices[start:end] into dest.""" - cdef SIZE_t k - for k in range(self.n_outputs): - dest[k] = self.node_medians[k] - - cdef double node_impurity(self) noexcept nogil: - """Evaluate the impurity of the current node. - - Evaluate the MAE criterion as impurity of the current node, - i.e. the impurity of sample_indices[start:end]. The smaller the impurity the - better. - """ - cdef const DOUBLE_t[:] sample_weight = self.sample_weight - cdef const SIZE_t[:] sample_indices = self.sample_indices - cdef SIZE_t i, p, k - cdef DOUBLE_t w = 1.0 - cdef DOUBLE_t impurity = 0.0 - - for k in range(self.n_outputs): - for p in range(self.start, self.end): - i = sample_indices[p] - - if sample_weight is not None: - w = sample_weight[i] - - impurity += fabs(self.y[i, k] - self.node_medians[k]) * w - - return impurity / (self.weighted_n_node_samples * self.n_outputs) - - cdef void children_impurity(self, double* p_impurity_left, - double* p_impurity_right) noexcept nogil: - """Evaluate the impurity in children nodes. - - i.e. the impurity of the left child (sample_indices[start:pos]) and the - impurity the right child (sample_indices[pos:end]). - """ - cdef const DOUBLE_t[:] sample_weight = self.sample_weight - cdef const SIZE_t[:] sample_indices = self.sample_indices - - cdef SIZE_t start = self.start - cdef SIZE_t pos = self.pos - cdef SIZE_t end = self.end - - cdef SIZE_t i, p, k - cdef DOUBLE_t median - cdef DOUBLE_t w = 1.0 - cdef DOUBLE_t impurity_left = 0.0 - cdef DOUBLE_t impurity_right = 0.0 - - cdef void** left_child = self.left_child_ptr - cdef void** right_child = self.right_child_ptr - - for k in range(self.n_outputs): - median = ( left_child[k]).get_median() - for p in range(start, pos): - i = sample_indices[p] - - if sample_weight is not None: - w = sample_weight[i] - - impurity_left += fabs(self.y[i, k] - median) * w - p_impurity_left[0] = impurity_left / (self.weighted_n_left * - self.n_outputs) - - for k in range(self.n_outputs): - median = ( right_child[k]).get_median() - for p in range(pos, end): - i = sample_indices[p] - - if sample_weight is not None: - w = sample_weight[i] - - impurity_right += fabs(self.y[i, k] - median) * w - p_impurity_right[0] = impurity_right / (self.weighted_n_right * - self.n_outputs) - - -cdef class FriedmanMSE(MSE): - """Mean squared error impurity criterion with improvement score by Friedman. - - Uses the formula (35) in Friedman's original Gradient Boosting paper: - - diff = mean_left - mean_right - improvement = n_left * n_right * diff^2 / (n_left + n_right) - """ - - cdef double proxy_impurity_improvement(self) noexcept nogil: - """Compute a proxy of the impurity reduction. - - This method is used to speed up the search for the best split. - It is a proxy quantity such that the split that maximizes this value - also maximizes the impurity improvement. It neglects all constant terms - of the impurity decrease for a given split. - - The absolute impurity improvement is only computed by the - impurity_improvement method once the best split has been found. - """ - cdef double total_sum_left = 0.0 - cdef double total_sum_right = 0.0 - - cdef SIZE_t k - cdef double diff = 0.0 - - for k in range(self.n_outputs): - total_sum_left += self.sum_left[k] - total_sum_right += self.sum_right[k] - - diff = (self.weighted_n_right * total_sum_left - - self.weighted_n_left * total_sum_right) - - return diff * diff / (self.weighted_n_left * self.weighted_n_right) - - cdef double impurity_improvement(self, double impurity_parent, double - impurity_left, double impurity_right) noexcept nogil: - # Note: none of the arguments are used here - cdef double total_sum_left = 0.0 - cdef double total_sum_right = 0.0 - - cdef SIZE_t k - cdef double diff = 0.0 - - for k in range(self.n_outputs): - total_sum_left += self.sum_left[k] - total_sum_right += self.sum_right[k] - - diff = (self.weighted_n_right * total_sum_left - - self.weighted_n_left * total_sum_right) / self.n_outputs - - return (diff * diff / (self.weighted_n_left * self.weighted_n_right * - self.weighted_n_node_samples)) - - -cdef class Poisson(RegressionCriterion): - """Half Poisson deviance as impurity criterion. - - Poisson deviance = 2/n * sum(y_true * log(y_true/y_pred) + y_pred - y_true) - - Note that the deviance is >= 0, and since we have `y_pred = mean(y_true)` - at the leaves, one always has `sum(y_pred - y_true) = 0`. It remains the - implemented impurity (factor 2 is skipped): - 1/n * sum(y_true * log(y_true/y_pred) - """ - # FIXME in 1.0: - # min_impurity_split with default = 0 forces us to use a non-negative - # impurity like the Poisson deviance. Without this restriction, one could - # throw away the 'constant' term sum(y_true * log(y_true)) and just use - # Poisson loss = - 1/n * sum(y_true * log(y_pred)) - # = - 1/n * sum(y_true * log(mean(y_true)) - # = - mean(y_true) * log(mean(y_true)) - # With this trick (used in proxy_impurity_improvement()), as for MSE, - # children_impurity would only need to go over left xor right split, not - # both. This could be faster. - - cdef double node_impurity(self) noexcept nogil: - """Evaluate the impurity of the current node. - - Evaluate the Poisson criterion as impurity of the current node, - i.e. the impurity of sample_indices[start:end]. The smaller the impurity the - better. - """ - return self.poisson_loss(self.start, self.end, self.sum_total, - self.weighted_n_node_samples) - - cdef double proxy_impurity_improvement(self) noexcept nogil: - """Compute a proxy of the impurity reduction. - - This method is used to speed up the search for the best split. - It is a proxy quantity such that the split that maximizes this value - also maximizes the impurity improvement. It neglects all constant terms - of the impurity decrease for a given split. - - The absolute impurity improvement is only computed by the - impurity_improvement method once the best split has been found. - - The Poisson proxy is derived from: - - sum_{i left }(y_i * log(y_i / y_pred_L)) - + sum_{i right}(y_i * log(y_i / y_pred_R)) - = sum(y_i * log(y_i) - n_L * mean_{i left}(y_i) * log(mean_{i left}(y_i)) - - n_R * mean_{i right}(y_i) * log(mean_{i right}(y_i)) - - Neglecting constant terms, this gives - - - sum{i left }(y_i) * log(mean{i left}(y_i)) - - sum{i right}(y_i) * log(mean{i right}(y_i)) - """ - cdef SIZE_t k - cdef double proxy_impurity_left = 0.0 - cdef double proxy_impurity_right = 0.0 - cdef double y_mean_left = 0. - cdef double y_mean_right = 0. - - for k in range(self.n_outputs): - if (self.sum_left[k] <= EPSILON) or (self.sum_right[k] <= EPSILON): - # Poisson loss does not allow non-positive predictions. We - # therefore forbid splits that have child nodes with - # sum(y_i) <= 0. - # Since sum_right = sum_total - sum_left, it can lead to - # floating point rounding error and will not give zero. Thus, - # we relax the above comparison to sum(y_i) <= EPSILON. - return -INFINITY - else: - y_mean_left = self.sum_left[k] / self.weighted_n_left - y_mean_right = self.sum_right[k] / self.weighted_n_right - proxy_impurity_left -= self.sum_left[k] * log(y_mean_left) - proxy_impurity_right -= self.sum_right[k] * log(y_mean_right) - - return - proxy_impurity_left - proxy_impurity_right - - cdef void children_impurity(self, double* impurity_left, - double* impurity_right) noexcept nogil: - """Evaluate the impurity in children nodes. - - i.e. the impurity of the left child (sample_indices[start:pos]) and the - impurity of the right child (sample_indices[pos:end]) for Poisson. - """ - cdef SIZE_t start = self.start - cdef SIZE_t pos = self.pos - cdef SIZE_t end = self.end - - impurity_left[0] = self.poisson_loss(start, pos, self.sum_left, - self.weighted_n_left) - - impurity_right[0] = self.poisson_loss(pos, end, self.sum_right, - self.weighted_n_right) - - cdef inline DOUBLE_t poisson_loss(self, - SIZE_t start, - SIZE_t end, - const double[::1] y_sum, - DOUBLE_t weight_sum) noexcept nogil: - """Helper function to compute Poisson loss (~deviance) of a given node. - """ - cdef const DOUBLE_t[:, ::1] y = self.y - cdef const DOUBLE_t[:] sample_weight = self.sample_weight - cdef const SIZE_t[:] sample_indices = self.sample_indices - - cdef DOUBLE_t y_mean = 0. - cdef DOUBLE_t poisson_loss = 0. - cdef DOUBLE_t w = 1.0 - cdef SIZE_t i, k, p - cdef SIZE_t n_outputs = self.n_outputs - - for k in range(n_outputs): - if y_sum[k] <= EPSILON: - # y_sum could be computed from the subtraction - # sum_right = sum_total - sum_left leading to a potential - # floating point rounding error. - # Thus, we relax the comparison y_sum <= 0 to - # y_sum <= EPSILON. - return INFINITY - - y_mean = y_sum[k] / weight_sum - - for p in range(start, end): - i = sample_indices[p] - - if sample_weight is not None: - w = sample_weight[i] - - poisson_loss += w * xlogy(y[i, k], y[i, k] / y_mean) - return poisson_loss / (weight_sum * n_outputs) \ No newline at end of file diff --git a/ivy/functional/frontends/sklearn/_splitter copy.pyx b/ivy/functional/frontends/sklearn/_splitter copy.pyx deleted file mode 100644 index bbe67f15c9bb5..0000000000000 --- a/ivy/functional/frontends/sklearn/_splitter copy.pyx +++ /dev/null @@ -1,1531 +0,0 @@ -# Authors: Gilles Louppe -# Peter Prettenhofer -# Brian Holt -# Noel Dawe -# Satrajit Gosh -# Lars Buitinck -# Arnaud Joly -# Joel Nothman -# Fares Hedayati -# Jacob Schreiber -# -# License: BSD 3 clause - -from ._criterion cimport Criterion - -from libc.stdlib cimport qsort -from libc.string cimport memcpy -from libc.math cimport isnan -from cython cimport final - -import numpy as np - -from scipy.sparse import isspmatrix_csc - -from ._utils cimport log -from ._utils cimport rand_int -from ._utils cimport rand_uniform -from ._utils cimport RAND_R_MAX - -cdef double INFINITY = np.inf - -# Mitigate precision differences between 32 bit and 64 bit -cdef DTYPE_t FEATURE_THRESHOLD = 1e-7 - -# Constant to switch between algorithm non zero value extract algorithm -# in SparsePartitioner -cdef DTYPE_t EXTRACT_NNZ_SWITCH = 0.1 - -cdef inline void _init_split(SplitRecord* self, SIZE_t start_pos) noexcept nogil: - self.impurity_left = INFINITY - self.impurity_right = INFINITY - self.pos = start_pos - self.feature = 0 - self.threshold = 0. - self.improvement = -INFINITY - self.missing_go_to_left = False - self.n_missing = 0 - -cdef class Splitter: - """Abstract splitter class. - - Splitters are called by tree builders to find the best splits on both - sparse and dense data, one split at a time. - """ - - def __cinit__(self, Criterion criterion, SIZE_t max_features, - SIZE_t min_samples_leaf, double min_weight_leaf, - object random_state): - """ - Parameters - ---------- - criterion : Criterion - The criterion to measure the quality of a split. - - max_features : SIZE_t - The maximal number of randomly selected features which can be - considered for a split. - - min_samples_leaf : SIZE_t - The minimal number of samples each leaf can have, where splits - which would result in having less samples in a leaf are not - considered. - - min_weight_leaf : double - The minimal weight each leaf can have, where the weight is the sum - of the weights of each sample in it. - - random_state : object - The user inputted random state to be used for pseudo-randomness - """ - - self.criterion = criterion - - self.n_samples = 0 - self.n_features = 0 - - self.max_features = max_features - self.min_samples_leaf = min_samples_leaf - self.min_weight_leaf = min_weight_leaf - self.random_state = random_state - - def __getstate__(self): - return {} - - def __setstate__(self, d): - pass - - def __reduce__(self): - return (type(self), (self.criterion, - self.max_features, - self.min_samples_leaf, - self.min_weight_leaf, - self.random_state), self.__getstate__()) - - cdef int init( - self, - object X, - const DOUBLE_t[:, ::1] y, - const DOUBLE_t[:] sample_weight, - const unsigned char[::1] missing_values_in_feature_mask, - ) except -1: - """Initialize the splitter. - - Take in the input data X, the target Y, and optional sample weights. - - Returns -1 in case of failure to allocate memory (and raise MemoryError) - or 0 otherwise. - - Parameters - ---------- - X : object - This contains the inputs. Usually it is a 2d numpy array. - - y : ndarray, dtype=DOUBLE_t - This is the vector of targets, or true labels, for the samples represented - as a Cython memoryview. - - sample_weight : ndarray, dtype=DOUBLE_t - The weights of the samples, where higher weighted samples are fit - closer than lower weight samples. If not provided, all samples - are assumed to have uniform weight. This is represented - as a Cython memoryview. - - has_missing : bool - At least one missing values is in X. - """ - - self.rand_r_state = self.random_state.randint(0, RAND_R_MAX) - cdef SIZE_t n_samples = X.shape[0] - - # Create a new array which will be used to store nonzero - # samples from the feature of interest - self.samples = np.empty(n_samples, dtype=np.intp) - cdef SIZE_t[::1] samples = self.samples - - cdef SIZE_t i, j - cdef double weighted_n_samples = 0.0 - j = 0 - - for i in range(n_samples): - # Only work with positively weighted samples - if sample_weight is None or sample_weight[i] != 0.0: - samples[j] = i - j += 1 - - if sample_weight is not None: - weighted_n_samples += sample_weight[i] - else: - weighted_n_samples += 1.0 - - # Number of samples is number of positively weighted samples - self.n_samples = j - self.weighted_n_samples = weighted_n_samples - - cdef SIZE_t n_features = X.shape[1] - self.features = np.arange(n_features, dtype=np.intp) - self.n_features = n_features - - self.feature_values = np.empty(n_samples, dtype=np.float32) - self.constant_features = np.empty(n_features, dtype=np.intp) - - self.y = y - - self.sample_weight = sample_weight - if missing_values_in_feature_mask is not None: - self.criterion.init_sum_missing() - return 0 - - cdef int node_reset(self, SIZE_t start, SIZE_t end, - double* weighted_n_node_samples) except -1 nogil: - """Reset splitter on node samples[start:end]. - - Returns -1 in case of failure to allocate memory (and raise MemoryError) - or 0 otherwise. - - Parameters - ---------- - start : SIZE_t - The index of the first sample to consider - end : SIZE_t - The index of the last sample to consider - weighted_n_node_samples : ndarray, dtype=double pointer - The total weight of those samples - """ - - self.start = start - self.end = end - - self.criterion.init( - self.y, - self.sample_weight, - self.weighted_n_samples, - self.samples, - start, - end - ) - - weighted_n_node_samples[0] = self.criterion.weighted_n_node_samples - return 0 - - cdef int node_split(self, double impurity, SplitRecord* split, - SIZE_t* n_constant_features) except -1 nogil: - """Find the best split on node samples[start:end]. - - This is a placeholder method. The majority of computation will be done - here. - - It should return -1 upon errors. - """ - - pass - - cdef void node_value(self, double* dest) noexcept nogil: - """Copy the value of node samples[start:end] into dest.""" - - self.criterion.node_value(dest) - - cdef double node_impurity(self) noexcept nogil: - """Return the impurity of the current node.""" - - return self.criterion.node_impurity() - -cdef inline void shift_missing_values_to_left_if_required( - SplitRecord* best, - SIZE_t[::1] samples, - SIZE_t end, -) nogil: - cdef SIZE_t i, p, current_end - # The partitioner partitions the data such that the missing values are in - # samples[-n_missing:] for the criterion to consume. If the missing values - # are going to the right node, then the missing values are already in the - # correct position. If the missing values go left, then we move the missing - # values to samples[best.pos:best.pos+n_missing] and update `best.pos`. - if best.n_missing > 0 and best.missing_go_to_left: - for p in range(best.n_missing): - i = best.pos + p - current_end = end - 1 - p - samples[i], samples[current_end] = samples[current_end], samples[i] - best.pos += best.n_missing - -# Introduce a fused-class to make it possible to share the split implementation -# between the dense and sparse cases in the node_split_best and node_split_random -# functions. The alternative would have been to use inheritance-based polymorphism -# but it would have resulted in a ~10% overall tree fitting performance -# degradation caused by the overhead frequent virtual method lookups. -ctypedef fused Partitioner: - DensePartitioner - SparsePartitioner - -cdef inline int node_split_best( - Splitter splitter, - Partitioner partitioner, - Criterion criterion, - double impurity, - SplitRecord* split, - SIZE_t* n_constant_features, -) except -1 nogil: - """Find the best split on node samples[start:end] - - Returns -1 in case of failure to allocate memory (and raise MemoryError) - or 0 otherwise. - """ - # Find the best split - cdef SIZE_t start = splitter.start - cdef SIZE_t end = splitter.end - cdef SIZE_t end_non_missing - cdef SIZE_t n_missing = 0 - cdef bint has_missing = 0 - cdef SIZE_t n_searches - cdef SIZE_t n_left, n_right - cdef bint missing_go_to_left - - cdef SIZE_t[::1] samples = splitter.samples - cdef SIZE_t[::1] features = splitter.features - cdef SIZE_t[::1] constant_features = splitter.constant_features - cdef SIZE_t n_features = splitter.n_features - - cdef DTYPE_t[::1] feature_values = splitter.feature_values - cdef SIZE_t max_features = splitter.max_features - cdef SIZE_t min_samples_leaf = splitter.min_samples_leaf - cdef double min_weight_leaf = splitter.min_weight_leaf - cdef UINT32_t* random_state = &splitter.rand_r_state - - cdef SplitRecord best_split, current_split - cdef double current_proxy_improvement = -INFINITY - cdef double best_proxy_improvement = -INFINITY - - cdef SIZE_t f_i = n_features - cdef SIZE_t f_j - cdef SIZE_t p - cdef SIZE_t p_prev - - cdef SIZE_t n_visited_features = 0 - # Number of features discovered to be constant during the split search - cdef SIZE_t n_found_constants = 0 - # Number of features known to be constant and drawn without replacement - cdef SIZE_t n_drawn_constants = 0 - cdef SIZE_t n_known_constants = n_constant_features[0] - # n_total_constants = n_known_constants + n_found_constants - cdef SIZE_t n_total_constants = n_known_constants - - _init_split(&best_split, end) - - partitioner.init_node_split(start, end) - - # Sample up to max_features without replacement using a - # Fisher-Yates-based algorithm (using the local variables `f_i` and - # `f_j` to compute a permutation of the `features` array). - # - # Skip the CPU intensive evaluation of the impurity criterion for - # features that were already detected as constant (hence not suitable - # for good splitting) by ancestor nodes and save the information on - # newly discovered constant features to spare computation on descendant - # nodes. - while (f_i > n_total_constants and # Stop early if remaining features - # are constant - (n_visited_features < max_features or - # At least one drawn features must be non constant - n_visited_features <= n_found_constants + n_drawn_constants)): - - n_visited_features += 1 - - # Loop invariant: elements of features in - # - [:n_drawn_constant[ holds drawn and known constant features; - # - [n_drawn_constant:n_known_constant[ holds known constant - # features that haven't been drawn yet; - # - [n_known_constant:n_total_constant[ holds newly found constant - # features; - # - [n_total_constant:f_i[ holds features that haven't been drawn - # yet and aren't constant apriori. - # - [f_i:n_features[ holds features that have been drawn - # and aren't constant. - - # Draw a feature at random - f_j = rand_int(n_drawn_constants, f_i - n_found_constants, - random_state) - - if f_j < n_known_constants: - # f_j in the interval [n_drawn_constants, n_known_constants[ - features[n_drawn_constants], features[f_j] = features[f_j], features[n_drawn_constants] - - n_drawn_constants += 1 - continue - - # f_j in the interval [n_known_constants, f_i - n_found_constants[ - f_j += n_found_constants - # f_j in the interval [n_total_constants, f_i[ - current_split.feature = features[f_j] - partitioner.sort_samples_and_feature_values(current_split.feature) - n_missing = partitioner.n_missing - end_non_missing = end - n_missing - - if ( - # All values for this feature are missing, or - end_non_missing == start or - # This feature is considered constant (max - min <= FEATURE_THRESHOLD) - feature_values[end_non_missing - 1] <= feature_values[start] + FEATURE_THRESHOLD - ): - # We consider this feature constant in this case. - # Since finding a split among constant feature is not valuable, - # we do not consider this feature for splitting. - features[f_j], features[n_total_constants] = features[n_total_constants], features[f_j] - - n_found_constants += 1 - n_total_constants += 1 - continue - - f_i -= 1 - features[f_i], features[f_j] = features[f_j], features[f_i] - has_missing = n_missing != 0 - if has_missing: - criterion.init_missing(n_missing) - # Evaluate all splits - - # If there are missing values, then we search twice for the most optimal split. - # The first search will have all the missing values going to the right node. - # The second search will have all the missing values going to the left node. - # If there are no missing values, then we search only once for the most - # optimal split. - n_searches = 2 if has_missing else 1 - - for i in range(n_searches): - missing_go_to_left = i == 1 - criterion.missing_go_to_left = missing_go_to_left - criterion.reset() - - p = start - - while p < end_non_missing: - partitioner.next_p(&p_prev, &p) - - if p >= end_non_missing: - continue - - if missing_go_to_left: - n_left = p - start + n_missing - n_right = end_non_missing - p - else: - n_left = p - start - n_right = end_non_missing - p + n_missing - - # Reject if min_samples_leaf is not guaranteed - if n_left < min_samples_leaf or n_right < min_samples_leaf: - continue - - current_split.pos = p - criterion.update(current_split.pos) - - # Reject if min_weight_leaf is not satisfied - if ((criterion.weighted_n_left < min_weight_leaf) or - (criterion.weighted_n_right < min_weight_leaf)): - continue - - current_proxy_improvement = criterion.proxy_impurity_improvement() - - if current_proxy_improvement > best_proxy_improvement: - best_proxy_improvement = current_proxy_improvement - # sum of halves is used to avoid infinite value - current_split.threshold = ( - feature_values[p_prev] / 2.0 + feature_values[p] / 2.0 - ) - - if ( - current_split.threshold == feature_values[p] or - current_split.threshold == INFINITY or - current_split.threshold == -INFINITY - ): - current_split.threshold = feature_values[p_prev] - - current_split.n_missing = n_missing - if n_missing == 0: - current_split.missing_go_to_left = n_left > n_right - else: - current_split.missing_go_to_left = missing_go_to_left - - best_split = current_split # copy - - # Evaluate when there are missing values and all missing values goes - # to the right node and non-missing values goes to the left node. - if has_missing: - n_left, n_right = end - start - n_missing, n_missing - p = end - n_missing - missing_go_to_left = 0 - - if not (n_left < min_samples_leaf or n_right < min_samples_leaf): - criterion.missing_go_to_left = missing_go_to_left - criterion.update(p) - - if not ((criterion.weighted_n_left < min_weight_leaf) or - (criterion.weighted_n_right < min_weight_leaf)): - current_proxy_improvement = criterion.proxy_impurity_improvement() - - if current_proxy_improvement > best_proxy_improvement: - best_proxy_improvement = current_proxy_improvement - current_split.threshold = INFINITY - current_split.missing_go_to_left = missing_go_to_left - current_split.n_missing = n_missing - current_split.pos = p - best_split = current_split - - # Reorganize into samples[start:best_split.pos] + samples[best_split.pos:end] - if best_split.pos < end: - partitioner.partition_samples_final( - best_split.pos, - best_split.threshold, - best_split.feature, - best_split.n_missing - ) - if best_split.n_missing != 0: - criterion.init_missing(best_split.n_missing) - criterion.missing_go_to_left = best_split.missing_go_to_left - - criterion.reset() - criterion.update(best_split.pos) - criterion.children_impurity( - &best_split.impurity_left, &best_split.impurity_right - ) - best_split.improvement = criterion.impurity_improvement( - impurity, - best_split.impurity_left, - best_split.impurity_right - ) - - shift_missing_values_to_left_if_required(&best_split, samples, end) - - # Respect invariant for constant features: the original order of - # element in features[:n_known_constants] must be preserved for sibling - # and child nodes - memcpy(&features[0], &constant_features[0], sizeof(SIZE_t) * n_known_constants) - - # Copy newly found constant features - memcpy(&constant_features[n_known_constants], - &features[n_known_constants], - sizeof(SIZE_t) * n_found_constants) - - # Return values - split[0] = best_split - n_constant_features[0] = n_total_constants - return 0 - - -# Sort n-element arrays pointed to by feature_values and samples, simultaneously, -# by the values in feature_values. Algorithm: Introsort (Musser, SP&E, 1997). -cdef inline void sort(DTYPE_t* feature_values, SIZE_t* samples, SIZE_t n) noexcept nogil: - if n == 0: - return - cdef int maxd = 2 * log(n) - introsort(feature_values, samples, n, maxd) - - -cdef inline void swap(DTYPE_t* feature_values, SIZE_t* samples, - SIZE_t i, SIZE_t j) noexcept nogil: - # Helper for sort - feature_values[i], feature_values[j] = feature_values[j], feature_values[i] - samples[i], samples[j] = samples[j], samples[i] - - -cdef inline DTYPE_t median3(DTYPE_t* feature_values, SIZE_t n) noexcept nogil: - # Median of three pivot selection, after Bentley and McIlroy (1993). - # Engineering a sort function. SP&E. Requires 8/3 comparisons on average. - cdef DTYPE_t a = feature_values[0], b = feature_values[n / 2], c = feature_values[n - 1] - if a < b: - if b < c: - return b - elif a < c: - return c - else: - return a - elif b < c: - if a < c: - return a - else: - return c - else: - return b - - -# Introsort with median of 3 pivot selection and 3-way partition function -# (robust to repeated elements, e.g. lots of zero features). -cdef void introsort(DTYPE_t* feature_values, SIZE_t *samples, - SIZE_t n, int maxd) noexcept nogil: - cdef DTYPE_t pivot - cdef SIZE_t i, l, r - - while n > 1: - if maxd <= 0: # max depth limit exceeded ("gone quadratic") - heapsort(feature_values, samples, n) - return - maxd -= 1 - - pivot = median3(feature_values, n) - - # Three-way partition. - i = l = 0 - r = n - while i < r: - if feature_values[i] < pivot: - swap(feature_values, samples, i, l) - i += 1 - l += 1 - elif feature_values[i] > pivot: - r -= 1 - swap(feature_values, samples, i, r) - else: - i += 1 - - introsort(feature_values, samples, l, maxd) - feature_values += r - samples += r - n -= r - - -cdef inline void sift_down(DTYPE_t* feature_values, SIZE_t* samples, - SIZE_t start, SIZE_t end) noexcept nogil: - # Restore heap order in feature_values[start:end] by moving the max element to start. - cdef SIZE_t child, maxind, root - - root = start - while True: - child = root * 2 + 1 - - # find max of root, left child, right child - maxind = root - if child < end and feature_values[maxind] < feature_values[child]: - maxind = child - if child + 1 < end and feature_values[maxind] < feature_values[child + 1]: - maxind = child + 1 - - if maxind == root: - break - else: - swap(feature_values, samples, root, maxind) - root = maxind - - -cdef void heapsort(DTYPE_t* feature_values, SIZE_t* samples, SIZE_t n) noexcept nogil: - cdef SIZE_t start, end - - # heapify - start = (n - 2) / 2 - end = n - while True: - sift_down(feature_values, samples, start, end) - if start == 0: - break - start -= 1 - - # sort by shrinking the heap, putting the max element immediately after it - end = n - 1 - while end > 0: - swap(feature_values, samples, 0, end) - sift_down(feature_values, samples, 0, end) - end = end - 1 - -cdef inline int node_split_random( - Splitter splitter, - Partitioner partitioner, - Criterion criterion, - double impurity, - SplitRecord* split, - SIZE_t* n_constant_features -) except -1 nogil: - """Find the best random split on node samples[start:end] - - Returns -1 in case of failure to allocate memory (and raise MemoryError) - or 0 otherwise. - """ - # Draw random splits and pick the best - cdef SIZE_t start = splitter.start - cdef SIZE_t end = splitter.end - - cdef SIZE_t[::1] features = splitter.features - cdef SIZE_t[::1] constant_features = splitter.constant_features - cdef SIZE_t n_features = splitter.n_features - - cdef SIZE_t max_features = splitter.max_features - cdef SIZE_t min_samples_leaf = splitter.min_samples_leaf - cdef double min_weight_leaf = splitter.min_weight_leaf - cdef UINT32_t* random_state = &splitter.rand_r_state - - cdef SplitRecord best_split, current_split - cdef double current_proxy_improvement = - INFINITY - cdef double best_proxy_improvement = - INFINITY - - cdef SIZE_t f_i = n_features - cdef SIZE_t f_j - # Number of features discovered to be constant during the split search - cdef SIZE_t n_found_constants = 0 - # Number of features known to be constant and drawn without replacement - cdef SIZE_t n_drawn_constants = 0 - cdef SIZE_t n_known_constants = n_constant_features[0] - # n_total_constants = n_known_constants + n_found_constants - cdef SIZE_t n_total_constants = n_known_constants - cdef SIZE_t n_visited_features = 0 - cdef DTYPE_t min_feature_value - cdef DTYPE_t max_feature_value - - _init_split(&best_split, end) - - partitioner.init_node_split(start, end) - - # Sample up to max_features without replacement using a - # Fisher-Yates-based algorithm (using the local variables `f_i` and - # `f_j` to compute a permutation of the `features` array). - # - # Skip the CPU intensive evaluation of the impurity criterion for - # features that were already detected as constant (hence not suitable - # for good splitting) by ancestor nodes and save the information on - # newly discovered constant features to spare computation on descendant - # nodes. - while (f_i > n_total_constants and # Stop early if remaining features - # are constant - (n_visited_features < max_features or - # At least one drawn features must be non constant - n_visited_features <= n_found_constants + n_drawn_constants)): - n_visited_features += 1 - - # Loop invariant: elements of features in - # - [:n_drawn_constant[ holds drawn and known constant features; - # - [n_drawn_constant:n_known_constant[ holds known constant - # features that haven't been drawn yet; - # - [n_known_constant:n_total_constant[ holds newly found constant - # features; - # - [n_total_constant:f_i[ holds features that haven't been drawn - # yet and aren't constant apriori. - # - [f_i:n_features[ holds features that have been drawn - # and aren't constant. - - # Draw a feature at random - f_j = rand_int(n_drawn_constants, f_i - n_found_constants, - random_state) - - if f_j < n_known_constants: - # f_j in the interval [n_drawn_constants, n_known_constants[ - features[n_drawn_constants], features[f_j] = features[f_j], features[n_drawn_constants] - n_drawn_constants += 1 - continue - - # f_j in the interval [n_known_constants, f_i - n_found_constants[ - f_j += n_found_constants - # f_j in the interval [n_total_constants, f_i[ - - current_split.feature = features[f_j] - - # Find min, max - partitioner.find_min_max( - current_split.feature, &min_feature_value, &max_feature_value - ) - - if max_feature_value <= min_feature_value + FEATURE_THRESHOLD: - features[f_j], features[n_total_constants] = features[n_total_constants], current_split.feature - - n_found_constants += 1 - n_total_constants += 1 - continue - - f_i -= 1 - features[f_i], features[f_j] = features[f_j], features[f_i] - - # Draw a random threshold - current_split.threshold = rand_uniform( - min_feature_value, - max_feature_value, - random_state, - ) - - if current_split.threshold == max_feature_value: - current_split.threshold = min_feature_value - - # Partition - current_split.pos = partitioner.partition_samples(current_split.threshold) - - # Reject if min_samples_leaf is not guaranteed - if (((current_split.pos - start) < min_samples_leaf) or - ((end - current_split.pos) < min_samples_leaf)): - continue - - # Evaluate split - # At this point, the criterion has a view into the samples that was partitioned - # by the partitioner. The criterion will use the partition to evaluating the split. - criterion.reset() - criterion.update(current_split.pos) - - # Reject if min_weight_leaf is not satisfied - if ((criterion.weighted_n_left < min_weight_leaf) or - (criterion.weighted_n_right < min_weight_leaf)): - continue - - current_proxy_improvement = criterion.proxy_impurity_improvement() - - if current_proxy_improvement > best_proxy_improvement: - best_proxy_improvement = current_proxy_improvement - best_split = current_split # copy - - # Reorganize into samples[start:best.pos] + samples[best.pos:end] - if best_split.pos < end: - if current_split.feature != best_split.feature: - # TODO: Pass in best.n_missing when random splitter supports missing values. - partitioner.partition_samples_final( - best_split.pos, best_split.threshold, best_split.feature, 0 - ) - - criterion.reset() - criterion.update(best_split.pos) - criterion.children_impurity( - &best_split.impurity_left, &best_split.impurity_right - ) - best_split.improvement = criterion.impurity_improvement( - impurity, best_split.impurity_left, best_split.impurity_right - ) - - # Respect invariant for constant features: the original order of - # element in features[:n_known_constants] must be preserved for sibling - # and child nodes - memcpy(&features[0], &constant_features[0], sizeof(SIZE_t) * n_known_constants) - - # Copy newly found constant features - memcpy(&constant_features[n_known_constants], - &features[n_known_constants], - sizeof(SIZE_t) * n_found_constants) - - # Return values - split[0] = best_split - n_constant_features[0] = n_total_constants - return 0 - - -@final -cdef class DensePartitioner: - """Partitioner specialized for dense data. - - Note that this partitioner is agnostic to the splitting strategy (best vs. random). - """ - cdef: - const DTYPE_t[:, :] X - cdef SIZE_t[::1] samples - cdef DTYPE_t[::1] feature_values - cdef SIZE_t start - cdef SIZE_t end - cdef SIZE_t n_missing - cdef const unsigned char[::1] missing_values_in_feature_mask - - def __init__( - self, - const DTYPE_t[:, :] X, - SIZE_t[::1] samples, - DTYPE_t[::1] feature_values, - const unsigned char[::1] missing_values_in_feature_mask, - ): - self.X = X - self.samples = samples - self.feature_values = feature_values - self.missing_values_in_feature_mask = missing_values_in_feature_mask - - cdef inline void init_node_split(self, SIZE_t start, SIZE_t end) noexcept nogil: - """Initialize splitter at the beginning of node_split.""" - self.start = start - self.end = end - self.n_missing = 0 - - cdef inline void sort_samples_and_feature_values( - self, SIZE_t current_feature - ) noexcept nogil: - """Simultaneously sort based on the feature_values. - - Missing values are stored at the end of feature_values. - The number of missing values observed in feature_values is stored - in self.n_missing. - """ - cdef: - SIZE_t i, current_end - DTYPE_t[::1] feature_values = self.feature_values - const DTYPE_t[:, :] X = self.X - SIZE_t[::1] samples = self.samples - SIZE_t n_missing = 0 - const unsigned char[::1] missing_values_in_feature_mask = self.missing_values_in_feature_mask - - # Sort samples along that feature; by - # copying the values into an array and - # sorting the array in a manner which utilizes the cache more - # effectively. - if missing_values_in_feature_mask is not None and missing_values_in_feature_mask[current_feature]: - i, current_end = self.start, self.end - 1 - # Missing values are placed at the end and do not participate in the sorting. - while i <= current_end: - # Finds the right-most value that is not missing so that - # it can be swapped with missing values at its left. - if isnan(X[samples[current_end], current_feature]): - n_missing += 1 - current_end -= 1 - continue - - # X[samples[current_end], current_feature] is a non-missing value - if isnan(X[samples[i], current_feature]): - samples[i], samples[current_end] = samples[current_end], samples[i] - n_missing += 1 - current_end -= 1 - - feature_values[i] = X[samples[i], current_feature] - i += 1 - else: - # When there are no missing values, we only need to copy the data into - # feature_values - for i in range(self.start, self.end): - feature_values[i] = X[samples[i], current_feature] - - sort(&feature_values[self.start], &samples[self.start], self.end - self.start - n_missing) - self.n_missing = n_missing - - cdef inline void find_min_max( - self, - SIZE_t current_feature, - DTYPE_t* min_feature_value_out, - DTYPE_t* max_feature_value_out, - ) noexcept nogil: - """Find the minimum and maximum value for current_feature.""" - cdef: - SIZE_t p - DTYPE_t current_feature_value - const DTYPE_t[:, :] X = self.X - SIZE_t[::1] samples = self.samples - DTYPE_t min_feature_value = X[samples[self.start], current_feature] - DTYPE_t max_feature_value = min_feature_value - DTYPE_t[::1] feature_values = self.feature_values - - feature_values[self.start] = min_feature_value - - for p in range(self.start + 1, self.end): - current_feature_value = X[samples[p], current_feature] - feature_values[p] = current_feature_value - - if current_feature_value < min_feature_value: - min_feature_value = current_feature_value - elif current_feature_value > max_feature_value: - max_feature_value = current_feature_value - - min_feature_value_out[0] = min_feature_value - max_feature_value_out[0] = max_feature_value - - cdef inline void next_p(self, SIZE_t* p_prev, SIZE_t* p) noexcept nogil: - """Compute the next p_prev and p for iteratiing over feature values. - - The missing values are not included when iterating through the feature values. - """ - cdef: - DTYPE_t[::1] feature_values = self.feature_values - SIZE_t end_non_missing = self.end - self.n_missing - - while ( - p[0] + 1 < end_non_missing and - feature_values[p[0] + 1] <= feature_values[p[0]] + FEATURE_THRESHOLD - ): - p[0] += 1 - - p_prev[0] = p[0] - - # By adding 1, we have - # (feature_values[p] >= end) or (feature_values[p] > feature_values[p - 1]) - p[0] += 1 - - cdef inline SIZE_t partition_samples(self, double current_threshold) noexcept nogil: - """Partition samples for feature_values at the current_threshold.""" - cdef: - SIZE_t p = self.start - SIZE_t partition_end = self.end - SIZE_t[::1] samples = self.samples - DTYPE_t[::1] feature_values = self.feature_values - - while p < partition_end: - if feature_values[p] <= current_threshold: - p += 1 - else: - partition_end -= 1 - - feature_values[p], feature_values[partition_end] = ( - feature_values[partition_end], feature_values[p] - ) - samples[p], samples[partition_end] = samples[partition_end], samples[p] - - return partition_end - - cdef inline void partition_samples_final( - self, - SIZE_t best_pos, - double best_threshold, - SIZE_t best_feature, - SIZE_t best_n_missing, - ) noexcept nogil: - """Partition samples for X at the best_threshold and best_feature. - - If missing values are present, this method partitions `samples` - so that the `best_n_missing` missing values' indices are in the - right-most end of `samples`, that is `samples[end_non_missing:end]`. - """ - cdef: - # Local invariance: start <= p <= partition_end <= end - SIZE_t start = self.start - SIZE_t p = start - SIZE_t end = self.end - 1 - SIZE_t partition_end = end - best_n_missing - SIZE_t[::1] samples = self.samples - const DTYPE_t[:, :] X = self.X - DTYPE_t current_value - - if best_n_missing != 0: - # Move samples with missing values to the end while partitioning the - # non-missing samples - while p < partition_end: - # Keep samples with missing values at the end - if isnan(X[samples[end], best_feature]): - end -= 1 - continue - - # Swap sample with missing values with the sample at the end - current_value = X[samples[p], best_feature] - if isnan(current_value): - samples[p], samples[end] = samples[end], samples[p] - end -= 1 - - # The swapped sample at the end is always a non-missing value, so - # we can continue the algorithm without checking for missingness. - current_value = X[samples[p], best_feature] - - # Partition the non-missing samples - if current_value <= best_threshold: - p += 1 - else: - samples[p], samples[partition_end] = samples[partition_end], samples[p] - partition_end -= 1 - else: - # Partitioning routine when there are no missing values - while p < partition_end: - if X[samples[p], best_feature] <= best_threshold: - p += 1 - else: - samples[p], samples[partition_end] = samples[partition_end], samples[p] - partition_end -= 1 - - -@final -cdef class SparsePartitioner: - """Partitioner specialized for sparse CSC data. - - Note that this partitioner is agnostic to the splitting strategy (best vs. random). - """ - cdef SIZE_t[::1] samples - cdef DTYPE_t[::1] feature_values - cdef SIZE_t start - cdef SIZE_t end - cdef SIZE_t n_missing - cdef const unsigned char[::1] missing_values_in_feature_mask - - cdef const DTYPE_t[::1] X_data - cdef const INT32_t[::1] X_indices - cdef const INT32_t[::1] X_indptr - - cdef SIZE_t n_total_samples - - cdef SIZE_t[::1] index_to_samples - cdef SIZE_t[::1] sorted_samples - - cdef SIZE_t start_positive - cdef SIZE_t end_negative - cdef bint is_samples_sorted - - def __init__( - self, - object X, - SIZE_t[::1] samples, - SIZE_t n_samples, - DTYPE_t[::1] feature_values, - const unsigned char[::1] missing_values_in_feature_mask, - ): - if not isspmatrix_csc(X): - raise ValueError("X should be in csc format") - - self.samples = samples - self.feature_values = feature_values - - # Initialize X - cdef SIZE_t n_total_samples = X.shape[0] - - self.X_data = X.data - self.X_indices = X.indices - self.X_indptr = X.indptr - self.n_total_samples = n_total_samples - - # Initialize auxiliary array used to perform split - self.index_to_samples = np.full(n_total_samples, fill_value=-1, dtype=np.intp) - self.sorted_samples = np.empty(n_samples, dtype=np.intp) - - cdef SIZE_t p - for p in range(n_samples): - self.index_to_samples[samples[p]] = p - - self.missing_values_in_feature_mask = missing_values_in_feature_mask - - cdef inline void init_node_split(self, SIZE_t start, SIZE_t end) noexcept nogil: - """Initialize splitter at the beginning of node_split.""" - self.start = start - self.end = end - self.is_samples_sorted = 0 - self.n_missing = 0 - - cdef inline void sort_samples_and_feature_values( - self, SIZE_t current_feature - ) noexcept nogil: - """Simultaneously sort based on the feature_values.""" - cdef: - DTYPE_t[::1] feature_values = self.feature_values - SIZE_t[::1] index_to_samples = self.index_to_samples - SIZE_t[::1] samples = self.samples - - self.extract_nnz(current_feature) - # Sort the positive and negative parts of `feature_values` - sort(&feature_values[self.start], &samples[self.start], self.end_negative - self.start) - if self.start_positive < self.end: - sort( - &feature_values[self.start_positive], - &samples[self.start_positive], - self.end - self.start_positive - ) - - # Update index_to_samples to take into account the sort - for p in range(self.start, self.end_negative): - index_to_samples[samples[p]] = p - for p in range(self.start_positive, self.end): - index_to_samples[samples[p]] = p - - # Add one or two zeros in feature_values, if there is any - if self.end_negative < self.start_positive: - self.start_positive -= 1 - feature_values[self.start_positive] = 0. - - if self.end_negative != self.start_positive: - feature_values[self.end_negative] = 0. - self.end_negative += 1 - - # XXX: When sparse supports missing values, this should be set to the - # number of missing values for current_feature - self.n_missing = 0 - - cdef inline void find_min_max( - self, - SIZE_t current_feature, - DTYPE_t* min_feature_value_out, - DTYPE_t* max_feature_value_out, - ) noexcept nogil: - """Find the minimum and maximum value for current_feature.""" - cdef: - SIZE_t p - DTYPE_t current_feature_value, min_feature_value, max_feature_value - DTYPE_t[::1] feature_values = self.feature_values - - self.extract_nnz(current_feature) - - if self.end_negative != self.start_positive: - # There is a zero - min_feature_value = 0 - max_feature_value = 0 - else: - min_feature_value = feature_values[self.start] - max_feature_value = min_feature_value - - # Find min, max in feature_values[start:end_negative] - for p in range(self.start, self.end_negative): - current_feature_value = feature_values[p] - - if current_feature_value < min_feature_value: - min_feature_value = current_feature_value - elif current_feature_value > max_feature_value: - max_feature_value = current_feature_value - - # Update min, max given feature_values[start_positive:end] - for p in range(self.start_positive, self.end): - current_feature_value = feature_values[p] - - if current_feature_value < min_feature_value: - min_feature_value = current_feature_value - elif current_feature_value > max_feature_value: - max_feature_value = current_feature_value - - min_feature_value_out[0] = min_feature_value - max_feature_value_out[0] = max_feature_value - - cdef inline void next_p(self, SIZE_t* p_prev, SIZE_t* p) noexcept nogil: - """Compute the next p_prev and p for iteratiing over feature values.""" - cdef: - SIZE_t p_next - DTYPE_t[::1] feature_values = self.feature_values - - if p[0] + 1 != self.end_negative: - p_next = p[0] + 1 - else: - p_next = self.start_positive - - while (p_next < self.end and - feature_values[p_next] <= feature_values[p[0]] + FEATURE_THRESHOLD): - p[0] = p_next - if p[0] + 1 != self.end_negative: - p_next = p[0] + 1 - else: - p_next = self.start_positive - - p_prev[0] = p[0] - p[0] = p_next - - cdef inline SIZE_t partition_samples(self, double current_threshold) noexcept nogil: - """Partition samples for feature_values at the current_threshold.""" - return self._partition(current_threshold, self.start_positive) - - cdef inline void partition_samples_final( - self, - SIZE_t best_pos, - double best_threshold, - SIZE_t best_feature, - SIZE_t n_missing, - ) noexcept nogil: - """Partition samples for X at the best_threshold and best_feature.""" - self.extract_nnz(best_feature) - self._partition(best_threshold, best_pos) - - cdef inline SIZE_t _partition(self, double threshold, SIZE_t zero_pos) noexcept nogil: - """Partition samples[start:end] based on threshold.""" - cdef: - SIZE_t p, partition_end - SIZE_t[::1] index_to_samples = self.index_to_samples - DTYPE_t[::1] feature_values = self.feature_values - SIZE_t[::1] samples = self.samples - - if threshold < 0.: - p = self.start - partition_end = self.end_negative - elif threshold > 0.: - p = self.start_positive - partition_end = self.end - else: - # Data are already split - return zero_pos - - while p < partition_end: - if feature_values[p] <= threshold: - p += 1 - - else: - partition_end -= 1 - - feature_values[p], feature_values[partition_end] = ( - feature_values[partition_end], feature_values[p] - ) - sparse_swap(index_to_samples, samples, p, partition_end) - - return partition_end - - cdef inline void extract_nnz(self, SIZE_t feature) noexcept nogil: - """Extract and partition values for a given feature. - - The extracted values are partitioned between negative values - feature_values[start:end_negative[0]] and positive values - feature_values[start_positive[0]:end]. - The samples and index_to_samples are modified according to this - partition. - - The extraction corresponds to the intersection between the arrays - X_indices[indptr_start:indptr_end] and samples[start:end]. - This is done efficiently using either an index_to_samples based approach - or binary search based approach. - - Parameters - ---------- - feature : SIZE_t, - Index of the feature we want to extract non zero value. - """ - cdef SIZE_t[::1] samples = self.samples - cdef DTYPE_t[::1] feature_values = self.feature_values - cdef SIZE_t indptr_start = self.X_indptr[feature], - cdef SIZE_t indptr_end = self.X_indptr[feature + 1] - cdef SIZE_t n_indices = (indptr_end - indptr_start) - cdef SIZE_t n_samples = self.end - self.start - cdef SIZE_t[::1] index_to_samples = self.index_to_samples - cdef SIZE_t[::1] sorted_samples = self.sorted_samples - cdef const INT32_t[::1] X_indices = self.X_indices - cdef const DTYPE_t[::1] X_data = self.X_data - - # Use binary search if n_samples * log(n_indices) < - # n_indices and index_to_samples approach otherwise. - # O(n_samples * log(n_indices)) is the running time of binary - # search and O(n_indices) is the running time of index_to_samples - # approach. - if ((1 - self.is_samples_sorted) * n_samples * log(n_samples) + - n_samples * log(n_indices) < EXTRACT_NNZ_SWITCH * n_indices): - extract_nnz_binary_search(X_indices, X_data, - indptr_start, indptr_end, - samples, self.start, self.end, - index_to_samples, - feature_values, - &self.end_negative, &self.start_positive, - sorted_samples, &self.is_samples_sorted) - - # Using an index to samples technique to extract non zero values - # index_to_samples is a mapping from X_indices to samples - else: - extract_nnz_index_to_samples(X_indices, X_data, - indptr_start, indptr_end, - samples, self.start, self.end, - index_to_samples, - feature_values, - &self.end_negative, &self.start_positive) - - -cdef int compare_SIZE_t(const void* a, const void* b) noexcept nogil: - """Comparison function for sort.""" - return ((a)[0] - (b)[0]) - - -cdef inline void binary_search(const INT32_t[::1] sorted_array, - INT32_t start, INT32_t end, - SIZE_t value, SIZE_t* index, - INT32_t* new_start) noexcept nogil: - """Return the index of value in the sorted array. - - If not found, return -1. new_start is the last pivot + 1 - """ - cdef INT32_t pivot - index[0] = -1 - while start < end: - pivot = start + (end - start) / 2 - - if sorted_array[pivot] == value: - index[0] = pivot - start = pivot + 1 - break - - if sorted_array[pivot] < value: - start = pivot + 1 - else: - end = pivot - new_start[0] = start - - -cdef inline void extract_nnz_index_to_samples(const INT32_t[::1] X_indices, - const DTYPE_t[::1] X_data, - INT32_t indptr_start, - INT32_t indptr_end, - SIZE_t[::1] samples, - SIZE_t start, - SIZE_t end, - SIZE_t[::1] index_to_samples, - DTYPE_t[::1] feature_values, - SIZE_t* end_negative, - SIZE_t* start_positive) noexcept nogil: - """Extract and partition values for a feature using index_to_samples. - - Complexity is O(indptr_end - indptr_start). - """ - cdef INT32_t k - cdef SIZE_t index - cdef SIZE_t end_negative_ = start - cdef SIZE_t start_positive_ = end - - for k in range(indptr_start, indptr_end): - if start <= index_to_samples[X_indices[k]] < end: - if X_data[k] > 0: - start_positive_ -= 1 - feature_values[start_positive_] = X_data[k] - index = index_to_samples[X_indices[k]] - sparse_swap(index_to_samples, samples, index, start_positive_) - - elif X_data[k] < 0: - feature_values[end_negative_] = X_data[k] - index = index_to_samples[X_indices[k]] - sparse_swap(index_to_samples, samples, index, end_negative_) - end_negative_ += 1 - - # Returned values - end_negative[0] = end_negative_ - start_positive[0] = start_positive_ - - -cdef inline void extract_nnz_binary_search(const INT32_t[::1] X_indices, - const DTYPE_t[::1] X_data, - INT32_t indptr_start, - INT32_t indptr_end, - SIZE_t[::1] samples, - SIZE_t start, - SIZE_t end, - SIZE_t[::1] index_to_samples, - DTYPE_t[::1] feature_values, - SIZE_t* end_negative, - SIZE_t* start_positive, - SIZE_t[::1] sorted_samples, - bint* is_samples_sorted) noexcept nogil: - """Extract and partition values for a given feature using binary search. - - If n_samples = end - start and n_indices = indptr_end - indptr_start, - the complexity is - - O((1 - is_samples_sorted[0]) * n_samples * log(n_samples) + - n_samples * log(n_indices)). - """ - cdef SIZE_t n_samples - - if not is_samples_sorted[0]: - n_samples = end - start - memcpy(&sorted_samples[start], &samples[start], - n_samples * sizeof(SIZE_t)) - qsort(&sorted_samples[start], n_samples, sizeof(SIZE_t), - compare_SIZE_t) - is_samples_sorted[0] = 1 - - while (indptr_start < indptr_end and - sorted_samples[start] > X_indices[indptr_start]): - indptr_start += 1 - - while (indptr_start < indptr_end and - sorted_samples[end - 1] < X_indices[indptr_end - 1]): - indptr_end -= 1 - - cdef SIZE_t p = start - cdef SIZE_t index - cdef SIZE_t k - cdef SIZE_t end_negative_ = start - cdef SIZE_t start_positive_ = end - - while (p < end and indptr_start < indptr_end): - # Find index of sorted_samples[p] in X_indices - binary_search(X_indices, indptr_start, indptr_end, - sorted_samples[p], &k, &indptr_start) - - if k != -1: - # If k != -1, we have found a non zero value - - if X_data[k] > 0: - start_positive_ -= 1 - feature_values[start_positive_] = X_data[k] - index = index_to_samples[X_indices[k]] - sparse_swap(index_to_samples, samples, index, start_positive_) - - elif X_data[k] < 0: - feature_values[end_negative_] = X_data[k] - index = index_to_samples[X_indices[k]] - sparse_swap(index_to_samples, samples, index, end_negative_) - end_negative_ += 1 - p += 1 - - # Returned values - end_negative[0] = end_negative_ - start_positive[0] = start_positive_ - - -cdef inline void sparse_swap(SIZE_t[::1] index_to_samples, SIZE_t[::1] samples, - SIZE_t pos_1, SIZE_t pos_2) noexcept nogil: - """Swap sample pos_1 and pos_2 preserving sparse invariant.""" - samples[pos_1], samples[pos_2] = samples[pos_2], samples[pos_1] - index_to_samples[samples[pos_1]] = pos_1 - index_to_samples[samples[pos_2]] = pos_2 - - -cdef class BestSplitter(Splitter): - """Splitter for finding the best split on dense data.""" - cdef DensePartitioner partitioner - cdef int init( - self, - object X, - const DOUBLE_t[:, ::1] y, - const DOUBLE_t[:] sample_weight, - const unsigned char[::1] missing_values_in_feature_mask, - ) except -1: - Splitter.init(self, X, y, sample_weight, missing_values_in_feature_mask) - self.partitioner = DensePartitioner( - X, self.samples, self.feature_values, missing_values_in_feature_mask - ) - - cdef int node_split(self, double impurity, SplitRecord* split, - SIZE_t* n_constant_features) except -1 nogil: - return node_split_best( - self, - self.partitioner, - self.criterion, - impurity, - split, - n_constant_features, - ) - -cdef class BestSparseSplitter(Splitter): - """Splitter for finding the best split, using the sparse data.""" - cdef SparsePartitioner partitioner - cdef int init( - self, - object X, - const DOUBLE_t[:, ::1] y, - const DOUBLE_t[:] sample_weight, - const unsigned char[::1] missing_values_in_feature_mask, - ) except -1: - Splitter.init(self, X, y, sample_weight, missing_values_in_feature_mask) - self.partitioner = SparsePartitioner( - X, self.samples, self.n_samples, self.feature_values, missing_values_in_feature_mask - ) - - cdef int node_split(self, double impurity, SplitRecord* split, - SIZE_t* n_constant_features) except -1 nogil: - return node_split_best( - self, - self.partitioner, - self.criterion, - impurity, - split, - n_constant_features, - ) - -cdef class RandomSplitter(Splitter): - """Splitter for finding the best random split on dense data.""" - cdef DensePartitioner partitioner - cdef int init( - self, - object X, - const DOUBLE_t[:, ::1] y, - const DOUBLE_t[:] sample_weight, - const unsigned char[::1] missing_values_in_feature_mask, - ) except -1: - Splitter.init(self, X, y, sample_weight, missing_values_in_feature_mask) - self.partitioner = DensePartitioner( - X, self.samples, self.feature_values, missing_values_in_feature_mask - ) - - cdef int node_split(self, double impurity, SplitRecord* split, - SIZE_t* n_constant_features) except -1 nogil: - return node_split_random( - self, - self.partitioner, - self.criterion, - impurity, - split, - n_constant_features, - ) - -cdef class RandomSparseSplitter(Splitter): - """Splitter for finding the best random split, using the sparse data.""" - cdef SparsePartitioner partitioner - cdef int init( - self, - object X, - const DOUBLE_t[:, ::1] y, - const DOUBLE_t[:] sample_weight, - const unsigned char[::1] missing_values_in_feature_mask, - ) except -1: - Splitter.init(self, X, y, sample_weight, missing_values_in_feature_mask) - self.partitioner = SparsePartitioner( - X, self.samples, self.n_samples, self.feature_values, missing_values_in_feature_mask - ) - - cdef int node_split(self, double impurity, SplitRecord* split, - SIZE_t* n_constant_features) except -1 nogil: - return node_split_random( - self, - self.partitioner, - self.criterion, - impurity, - split, - n_constant_features, - ) \ No newline at end of file diff --git a/ivy/functional/frontends/sklearn/_splitter.pyx b/ivy/functional/frontends/sklearn/_splitter.pyx deleted file mode 100644 index bbe67f15c9bb5..0000000000000 --- a/ivy/functional/frontends/sklearn/_splitter.pyx +++ /dev/null @@ -1,1531 +0,0 @@ -# Authors: Gilles Louppe -# Peter Prettenhofer -# Brian Holt -# Noel Dawe -# Satrajit Gosh -# Lars Buitinck -# Arnaud Joly -# Joel Nothman -# Fares Hedayati -# Jacob Schreiber -# -# License: BSD 3 clause - -from ._criterion cimport Criterion - -from libc.stdlib cimport qsort -from libc.string cimport memcpy -from libc.math cimport isnan -from cython cimport final - -import numpy as np - -from scipy.sparse import isspmatrix_csc - -from ._utils cimport log -from ._utils cimport rand_int -from ._utils cimport rand_uniform -from ._utils cimport RAND_R_MAX - -cdef double INFINITY = np.inf - -# Mitigate precision differences between 32 bit and 64 bit -cdef DTYPE_t FEATURE_THRESHOLD = 1e-7 - -# Constant to switch between algorithm non zero value extract algorithm -# in SparsePartitioner -cdef DTYPE_t EXTRACT_NNZ_SWITCH = 0.1 - -cdef inline void _init_split(SplitRecord* self, SIZE_t start_pos) noexcept nogil: - self.impurity_left = INFINITY - self.impurity_right = INFINITY - self.pos = start_pos - self.feature = 0 - self.threshold = 0. - self.improvement = -INFINITY - self.missing_go_to_left = False - self.n_missing = 0 - -cdef class Splitter: - """Abstract splitter class. - - Splitters are called by tree builders to find the best splits on both - sparse and dense data, one split at a time. - """ - - def __cinit__(self, Criterion criterion, SIZE_t max_features, - SIZE_t min_samples_leaf, double min_weight_leaf, - object random_state): - """ - Parameters - ---------- - criterion : Criterion - The criterion to measure the quality of a split. - - max_features : SIZE_t - The maximal number of randomly selected features which can be - considered for a split. - - min_samples_leaf : SIZE_t - The minimal number of samples each leaf can have, where splits - which would result in having less samples in a leaf are not - considered. - - min_weight_leaf : double - The minimal weight each leaf can have, where the weight is the sum - of the weights of each sample in it. - - random_state : object - The user inputted random state to be used for pseudo-randomness - """ - - self.criterion = criterion - - self.n_samples = 0 - self.n_features = 0 - - self.max_features = max_features - self.min_samples_leaf = min_samples_leaf - self.min_weight_leaf = min_weight_leaf - self.random_state = random_state - - def __getstate__(self): - return {} - - def __setstate__(self, d): - pass - - def __reduce__(self): - return (type(self), (self.criterion, - self.max_features, - self.min_samples_leaf, - self.min_weight_leaf, - self.random_state), self.__getstate__()) - - cdef int init( - self, - object X, - const DOUBLE_t[:, ::1] y, - const DOUBLE_t[:] sample_weight, - const unsigned char[::1] missing_values_in_feature_mask, - ) except -1: - """Initialize the splitter. - - Take in the input data X, the target Y, and optional sample weights. - - Returns -1 in case of failure to allocate memory (and raise MemoryError) - or 0 otherwise. - - Parameters - ---------- - X : object - This contains the inputs. Usually it is a 2d numpy array. - - y : ndarray, dtype=DOUBLE_t - This is the vector of targets, or true labels, for the samples represented - as a Cython memoryview. - - sample_weight : ndarray, dtype=DOUBLE_t - The weights of the samples, where higher weighted samples are fit - closer than lower weight samples. If not provided, all samples - are assumed to have uniform weight. This is represented - as a Cython memoryview. - - has_missing : bool - At least one missing values is in X. - """ - - self.rand_r_state = self.random_state.randint(0, RAND_R_MAX) - cdef SIZE_t n_samples = X.shape[0] - - # Create a new array which will be used to store nonzero - # samples from the feature of interest - self.samples = np.empty(n_samples, dtype=np.intp) - cdef SIZE_t[::1] samples = self.samples - - cdef SIZE_t i, j - cdef double weighted_n_samples = 0.0 - j = 0 - - for i in range(n_samples): - # Only work with positively weighted samples - if sample_weight is None or sample_weight[i] != 0.0: - samples[j] = i - j += 1 - - if sample_weight is not None: - weighted_n_samples += sample_weight[i] - else: - weighted_n_samples += 1.0 - - # Number of samples is number of positively weighted samples - self.n_samples = j - self.weighted_n_samples = weighted_n_samples - - cdef SIZE_t n_features = X.shape[1] - self.features = np.arange(n_features, dtype=np.intp) - self.n_features = n_features - - self.feature_values = np.empty(n_samples, dtype=np.float32) - self.constant_features = np.empty(n_features, dtype=np.intp) - - self.y = y - - self.sample_weight = sample_weight - if missing_values_in_feature_mask is not None: - self.criterion.init_sum_missing() - return 0 - - cdef int node_reset(self, SIZE_t start, SIZE_t end, - double* weighted_n_node_samples) except -1 nogil: - """Reset splitter on node samples[start:end]. - - Returns -1 in case of failure to allocate memory (and raise MemoryError) - or 0 otherwise. - - Parameters - ---------- - start : SIZE_t - The index of the first sample to consider - end : SIZE_t - The index of the last sample to consider - weighted_n_node_samples : ndarray, dtype=double pointer - The total weight of those samples - """ - - self.start = start - self.end = end - - self.criterion.init( - self.y, - self.sample_weight, - self.weighted_n_samples, - self.samples, - start, - end - ) - - weighted_n_node_samples[0] = self.criterion.weighted_n_node_samples - return 0 - - cdef int node_split(self, double impurity, SplitRecord* split, - SIZE_t* n_constant_features) except -1 nogil: - """Find the best split on node samples[start:end]. - - This is a placeholder method. The majority of computation will be done - here. - - It should return -1 upon errors. - """ - - pass - - cdef void node_value(self, double* dest) noexcept nogil: - """Copy the value of node samples[start:end] into dest.""" - - self.criterion.node_value(dest) - - cdef double node_impurity(self) noexcept nogil: - """Return the impurity of the current node.""" - - return self.criterion.node_impurity() - -cdef inline void shift_missing_values_to_left_if_required( - SplitRecord* best, - SIZE_t[::1] samples, - SIZE_t end, -) nogil: - cdef SIZE_t i, p, current_end - # The partitioner partitions the data such that the missing values are in - # samples[-n_missing:] for the criterion to consume. If the missing values - # are going to the right node, then the missing values are already in the - # correct position. If the missing values go left, then we move the missing - # values to samples[best.pos:best.pos+n_missing] and update `best.pos`. - if best.n_missing > 0 and best.missing_go_to_left: - for p in range(best.n_missing): - i = best.pos + p - current_end = end - 1 - p - samples[i], samples[current_end] = samples[current_end], samples[i] - best.pos += best.n_missing - -# Introduce a fused-class to make it possible to share the split implementation -# between the dense and sparse cases in the node_split_best and node_split_random -# functions. The alternative would have been to use inheritance-based polymorphism -# but it would have resulted in a ~10% overall tree fitting performance -# degradation caused by the overhead frequent virtual method lookups. -ctypedef fused Partitioner: - DensePartitioner - SparsePartitioner - -cdef inline int node_split_best( - Splitter splitter, - Partitioner partitioner, - Criterion criterion, - double impurity, - SplitRecord* split, - SIZE_t* n_constant_features, -) except -1 nogil: - """Find the best split on node samples[start:end] - - Returns -1 in case of failure to allocate memory (and raise MemoryError) - or 0 otherwise. - """ - # Find the best split - cdef SIZE_t start = splitter.start - cdef SIZE_t end = splitter.end - cdef SIZE_t end_non_missing - cdef SIZE_t n_missing = 0 - cdef bint has_missing = 0 - cdef SIZE_t n_searches - cdef SIZE_t n_left, n_right - cdef bint missing_go_to_left - - cdef SIZE_t[::1] samples = splitter.samples - cdef SIZE_t[::1] features = splitter.features - cdef SIZE_t[::1] constant_features = splitter.constant_features - cdef SIZE_t n_features = splitter.n_features - - cdef DTYPE_t[::1] feature_values = splitter.feature_values - cdef SIZE_t max_features = splitter.max_features - cdef SIZE_t min_samples_leaf = splitter.min_samples_leaf - cdef double min_weight_leaf = splitter.min_weight_leaf - cdef UINT32_t* random_state = &splitter.rand_r_state - - cdef SplitRecord best_split, current_split - cdef double current_proxy_improvement = -INFINITY - cdef double best_proxy_improvement = -INFINITY - - cdef SIZE_t f_i = n_features - cdef SIZE_t f_j - cdef SIZE_t p - cdef SIZE_t p_prev - - cdef SIZE_t n_visited_features = 0 - # Number of features discovered to be constant during the split search - cdef SIZE_t n_found_constants = 0 - # Number of features known to be constant and drawn without replacement - cdef SIZE_t n_drawn_constants = 0 - cdef SIZE_t n_known_constants = n_constant_features[0] - # n_total_constants = n_known_constants + n_found_constants - cdef SIZE_t n_total_constants = n_known_constants - - _init_split(&best_split, end) - - partitioner.init_node_split(start, end) - - # Sample up to max_features without replacement using a - # Fisher-Yates-based algorithm (using the local variables `f_i` and - # `f_j` to compute a permutation of the `features` array). - # - # Skip the CPU intensive evaluation of the impurity criterion for - # features that were already detected as constant (hence not suitable - # for good splitting) by ancestor nodes and save the information on - # newly discovered constant features to spare computation on descendant - # nodes. - while (f_i > n_total_constants and # Stop early if remaining features - # are constant - (n_visited_features < max_features or - # At least one drawn features must be non constant - n_visited_features <= n_found_constants + n_drawn_constants)): - - n_visited_features += 1 - - # Loop invariant: elements of features in - # - [:n_drawn_constant[ holds drawn and known constant features; - # - [n_drawn_constant:n_known_constant[ holds known constant - # features that haven't been drawn yet; - # - [n_known_constant:n_total_constant[ holds newly found constant - # features; - # - [n_total_constant:f_i[ holds features that haven't been drawn - # yet and aren't constant apriori. - # - [f_i:n_features[ holds features that have been drawn - # and aren't constant. - - # Draw a feature at random - f_j = rand_int(n_drawn_constants, f_i - n_found_constants, - random_state) - - if f_j < n_known_constants: - # f_j in the interval [n_drawn_constants, n_known_constants[ - features[n_drawn_constants], features[f_j] = features[f_j], features[n_drawn_constants] - - n_drawn_constants += 1 - continue - - # f_j in the interval [n_known_constants, f_i - n_found_constants[ - f_j += n_found_constants - # f_j in the interval [n_total_constants, f_i[ - current_split.feature = features[f_j] - partitioner.sort_samples_and_feature_values(current_split.feature) - n_missing = partitioner.n_missing - end_non_missing = end - n_missing - - if ( - # All values for this feature are missing, or - end_non_missing == start or - # This feature is considered constant (max - min <= FEATURE_THRESHOLD) - feature_values[end_non_missing - 1] <= feature_values[start] + FEATURE_THRESHOLD - ): - # We consider this feature constant in this case. - # Since finding a split among constant feature is not valuable, - # we do not consider this feature for splitting. - features[f_j], features[n_total_constants] = features[n_total_constants], features[f_j] - - n_found_constants += 1 - n_total_constants += 1 - continue - - f_i -= 1 - features[f_i], features[f_j] = features[f_j], features[f_i] - has_missing = n_missing != 0 - if has_missing: - criterion.init_missing(n_missing) - # Evaluate all splits - - # If there are missing values, then we search twice for the most optimal split. - # The first search will have all the missing values going to the right node. - # The second search will have all the missing values going to the left node. - # If there are no missing values, then we search only once for the most - # optimal split. - n_searches = 2 if has_missing else 1 - - for i in range(n_searches): - missing_go_to_left = i == 1 - criterion.missing_go_to_left = missing_go_to_left - criterion.reset() - - p = start - - while p < end_non_missing: - partitioner.next_p(&p_prev, &p) - - if p >= end_non_missing: - continue - - if missing_go_to_left: - n_left = p - start + n_missing - n_right = end_non_missing - p - else: - n_left = p - start - n_right = end_non_missing - p + n_missing - - # Reject if min_samples_leaf is not guaranteed - if n_left < min_samples_leaf or n_right < min_samples_leaf: - continue - - current_split.pos = p - criterion.update(current_split.pos) - - # Reject if min_weight_leaf is not satisfied - if ((criterion.weighted_n_left < min_weight_leaf) or - (criterion.weighted_n_right < min_weight_leaf)): - continue - - current_proxy_improvement = criterion.proxy_impurity_improvement() - - if current_proxy_improvement > best_proxy_improvement: - best_proxy_improvement = current_proxy_improvement - # sum of halves is used to avoid infinite value - current_split.threshold = ( - feature_values[p_prev] / 2.0 + feature_values[p] / 2.0 - ) - - if ( - current_split.threshold == feature_values[p] or - current_split.threshold == INFINITY or - current_split.threshold == -INFINITY - ): - current_split.threshold = feature_values[p_prev] - - current_split.n_missing = n_missing - if n_missing == 0: - current_split.missing_go_to_left = n_left > n_right - else: - current_split.missing_go_to_left = missing_go_to_left - - best_split = current_split # copy - - # Evaluate when there are missing values and all missing values goes - # to the right node and non-missing values goes to the left node. - if has_missing: - n_left, n_right = end - start - n_missing, n_missing - p = end - n_missing - missing_go_to_left = 0 - - if not (n_left < min_samples_leaf or n_right < min_samples_leaf): - criterion.missing_go_to_left = missing_go_to_left - criterion.update(p) - - if not ((criterion.weighted_n_left < min_weight_leaf) or - (criterion.weighted_n_right < min_weight_leaf)): - current_proxy_improvement = criterion.proxy_impurity_improvement() - - if current_proxy_improvement > best_proxy_improvement: - best_proxy_improvement = current_proxy_improvement - current_split.threshold = INFINITY - current_split.missing_go_to_left = missing_go_to_left - current_split.n_missing = n_missing - current_split.pos = p - best_split = current_split - - # Reorganize into samples[start:best_split.pos] + samples[best_split.pos:end] - if best_split.pos < end: - partitioner.partition_samples_final( - best_split.pos, - best_split.threshold, - best_split.feature, - best_split.n_missing - ) - if best_split.n_missing != 0: - criterion.init_missing(best_split.n_missing) - criterion.missing_go_to_left = best_split.missing_go_to_left - - criterion.reset() - criterion.update(best_split.pos) - criterion.children_impurity( - &best_split.impurity_left, &best_split.impurity_right - ) - best_split.improvement = criterion.impurity_improvement( - impurity, - best_split.impurity_left, - best_split.impurity_right - ) - - shift_missing_values_to_left_if_required(&best_split, samples, end) - - # Respect invariant for constant features: the original order of - # element in features[:n_known_constants] must be preserved for sibling - # and child nodes - memcpy(&features[0], &constant_features[0], sizeof(SIZE_t) * n_known_constants) - - # Copy newly found constant features - memcpy(&constant_features[n_known_constants], - &features[n_known_constants], - sizeof(SIZE_t) * n_found_constants) - - # Return values - split[0] = best_split - n_constant_features[0] = n_total_constants - return 0 - - -# Sort n-element arrays pointed to by feature_values and samples, simultaneously, -# by the values in feature_values. Algorithm: Introsort (Musser, SP&E, 1997). -cdef inline void sort(DTYPE_t* feature_values, SIZE_t* samples, SIZE_t n) noexcept nogil: - if n == 0: - return - cdef int maxd = 2 * log(n) - introsort(feature_values, samples, n, maxd) - - -cdef inline void swap(DTYPE_t* feature_values, SIZE_t* samples, - SIZE_t i, SIZE_t j) noexcept nogil: - # Helper for sort - feature_values[i], feature_values[j] = feature_values[j], feature_values[i] - samples[i], samples[j] = samples[j], samples[i] - - -cdef inline DTYPE_t median3(DTYPE_t* feature_values, SIZE_t n) noexcept nogil: - # Median of three pivot selection, after Bentley and McIlroy (1993). - # Engineering a sort function. SP&E. Requires 8/3 comparisons on average. - cdef DTYPE_t a = feature_values[0], b = feature_values[n / 2], c = feature_values[n - 1] - if a < b: - if b < c: - return b - elif a < c: - return c - else: - return a - elif b < c: - if a < c: - return a - else: - return c - else: - return b - - -# Introsort with median of 3 pivot selection and 3-way partition function -# (robust to repeated elements, e.g. lots of zero features). -cdef void introsort(DTYPE_t* feature_values, SIZE_t *samples, - SIZE_t n, int maxd) noexcept nogil: - cdef DTYPE_t pivot - cdef SIZE_t i, l, r - - while n > 1: - if maxd <= 0: # max depth limit exceeded ("gone quadratic") - heapsort(feature_values, samples, n) - return - maxd -= 1 - - pivot = median3(feature_values, n) - - # Three-way partition. - i = l = 0 - r = n - while i < r: - if feature_values[i] < pivot: - swap(feature_values, samples, i, l) - i += 1 - l += 1 - elif feature_values[i] > pivot: - r -= 1 - swap(feature_values, samples, i, r) - else: - i += 1 - - introsort(feature_values, samples, l, maxd) - feature_values += r - samples += r - n -= r - - -cdef inline void sift_down(DTYPE_t* feature_values, SIZE_t* samples, - SIZE_t start, SIZE_t end) noexcept nogil: - # Restore heap order in feature_values[start:end] by moving the max element to start. - cdef SIZE_t child, maxind, root - - root = start - while True: - child = root * 2 + 1 - - # find max of root, left child, right child - maxind = root - if child < end and feature_values[maxind] < feature_values[child]: - maxind = child - if child + 1 < end and feature_values[maxind] < feature_values[child + 1]: - maxind = child + 1 - - if maxind == root: - break - else: - swap(feature_values, samples, root, maxind) - root = maxind - - -cdef void heapsort(DTYPE_t* feature_values, SIZE_t* samples, SIZE_t n) noexcept nogil: - cdef SIZE_t start, end - - # heapify - start = (n - 2) / 2 - end = n - while True: - sift_down(feature_values, samples, start, end) - if start == 0: - break - start -= 1 - - # sort by shrinking the heap, putting the max element immediately after it - end = n - 1 - while end > 0: - swap(feature_values, samples, 0, end) - sift_down(feature_values, samples, 0, end) - end = end - 1 - -cdef inline int node_split_random( - Splitter splitter, - Partitioner partitioner, - Criterion criterion, - double impurity, - SplitRecord* split, - SIZE_t* n_constant_features -) except -1 nogil: - """Find the best random split on node samples[start:end] - - Returns -1 in case of failure to allocate memory (and raise MemoryError) - or 0 otherwise. - """ - # Draw random splits and pick the best - cdef SIZE_t start = splitter.start - cdef SIZE_t end = splitter.end - - cdef SIZE_t[::1] features = splitter.features - cdef SIZE_t[::1] constant_features = splitter.constant_features - cdef SIZE_t n_features = splitter.n_features - - cdef SIZE_t max_features = splitter.max_features - cdef SIZE_t min_samples_leaf = splitter.min_samples_leaf - cdef double min_weight_leaf = splitter.min_weight_leaf - cdef UINT32_t* random_state = &splitter.rand_r_state - - cdef SplitRecord best_split, current_split - cdef double current_proxy_improvement = - INFINITY - cdef double best_proxy_improvement = - INFINITY - - cdef SIZE_t f_i = n_features - cdef SIZE_t f_j - # Number of features discovered to be constant during the split search - cdef SIZE_t n_found_constants = 0 - # Number of features known to be constant and drawn without replacement - cdef SIZE_t n_drawn_constants = 0 - cdef SIZE_t n_known_constants = n_constant_features[0] - # n_total_constants = n_known_constants + n_found_constants - cdef SIZE_t n_total_constants = n_known_constants - cdef SIZE_t n_visited_features = 0 - cdef DTYPE_t min_feature_value - cdef DTYPE_t max_feature_value - - _init_split(&best_split, end) - - partitioner.init_node_split(start, end) - - # Sample up to max_features without replacement using a - # Fisher-Yates-based algorithm (using the local variables `f_i` and - # `f_j` to compute a permutation of the `features` array). - # - # Skip the CPU intensive evaluation of the impurity criterion for - # features that were already detected as constant (hence not suitable - # for good splitting) by ancestor nodes and save the information on - # newly discovered constant features to spare computation on descendant - # nodes. - while (f_i > n_total_constants and # Stop early if remaining features - # are constant - (n_visited_features < max_features or - # At least one drawn features must be non constant - n_visited_features <= n_found_constants + n_drawn_constants)): - n_visited_features += 1 - - # Loop invariant: elements of features in - # - [:n_drawn_constant[ holds drawn and known constant features; - # - [n_drawn_constant:n_known_constant[ holds known constant - # features that haven't been drawn yet; - # - [n_known_constant:n_total_constant[ holds newly found constant - # features; - # - [n_total_constant:f_i[ holds features that haven't been drawn - # yet and aren't constant apriori. - # - [f_i:n_features[ holds features that have been drawn - # and aren't constant. - - # Draw a feature at random - f_j = rand_int(n_drawn_constants, f_i - n_found_constants, - random_state) - - if f_j < n_known_constants: - # f_j in the interval [n_drawn_constants, n_known_constants[ - features[n_drawn_constants], features[f_j] = features[f_j], features[n_drawn_constants] - n_drawn_constants += 1 - continue - - # f_j in the interval [n_known_constants, f_i - n_found_constants[ - f_j += n_found_constants - # f_j in the interval [n_total_constants, f_i[ - - current_split.feature = features[f_j] - - # Find min, max - partitioner.find_min_max( - current_split.feature, &min_feature_value, &max_feature_value - ) - - if max_feature_value <= min_feature_value + FEATURE_THRESHOLD: - features[f_j], features[n_total_constants] = features[n_total_constants], current_split.feature - - n_found_constants += 1 - n_total_constants += 1 - continue - - f_i -= 1 - features[f_i], features[f_j] = features[f_j], features[f_i] - - # Draw a random threshold - current_split.threshold = rand_uniform( - min_feature_value, - max_feature_value, - random_state, - ) - - if current_split.threshold == max_feature_value: - current_split.threshold = min_feature_value - - # Partition - current_split.pos = partitioner.partition_samples(current_split.threshold) - - # Reject if min_samples_leaf is not guaranteed - if (((current_split.pos - start) < min_samples_leaf) or - ((end - current_split.pos) < min_samples_leaf)): - continue - - # Evaluate split - # At this point, the criterion has a view into the samples that was partitioned - # by the partitioner. The criterion will use the partition to evaluating the split. - criterion.reset() - criterion.update(current_split.pos) - - # Reject if min_weight_leaf is not satisfied - if ((criterion.weighted_n_left < min_weight_leaf) or - (criterion.weighted_n_right < min_weight_leaf)): - continue - - current_proxy_improvement = criterion.proxy_impurity_improvement() - - if current_proxy_improvement > best_proxy_improvement: - best_proxy_improvement = current_proxy_improvement - best_split = current_split # copy - - # Reorganize into samples[start:best.pos] + samples[best.pos:end] - if best_split.pos < end: - if current_split.feature != best_split.feature: - # TODO: Pass in best.n_missing when random splitter supports missing values. - partitioner.partition_samples_final( - best_split.pos, best_split.threshold, best_split.feature, 0 - ) - - criterion.reset() - criterion.update(best_split.pos) - criterion.children_impurity( - &best_split.impurity_left, &best_split.impurity_right - ) - best_split.improvement = criterion.impurity_improvement( - impurity, best_split.impurity_left, best_split.impurity_right - ) - - # Respect invariant for constant features: the original order of - # element in features[:n_known_constants] must be preserved for sibling - # and child nodes - memcpy(&features[0], &constant_features[0], sizeof(SIZE_t) * n_known_constants) - - # Copy newly found constant features - memcpy(&constant_features[n_known_constants], - &features[n_known_constants], - sizeof(SIZE_t) * n_found_constants) - - # Return values - split[0] = best_split - n_constant_features[0] = n_total_constants - return 0 - - -@final -cdef class DensePartitioner: - """Partitioner specialized for dense data. - - Note that this partitioner is agnostic to the splitting strategy (best vs. random). - """ - cdef: - const DTYPE_t[:, :] X - cdef SIZE_t[::1] samples - cdef DTYPE_t[::1] feature_values - cdef SIZE_t start - cdef SIZE_t end - cdef SIZE_t n_missing - cdef const unsigned char[::1] missing_values_in_feature_mask - - def __init__( - self, - const DTYPE_t[:, :] X, - SIZE_t[::1] samples, - DTYPE_t[::1] feature_values, - const unsigned char[::1] missing_values_in_feature_mask, - ): - self.X = X - self.samples = samples - self.feature_values = feature_values - self.missing_values_in_feature_mask = missing_values_in_feature_mask - - cdef inline void init_node_split(self, SIZE_t start, SIZE_t end) noexcept nogil: - """Initialize splitter at the beginning of node_split.""" - self.start = start - self.end = end - self.n_missing = 0 - - cdef inline void sort_samples_and_feature_values( - self, SIZE_t current_feature - ) noexcept nogil: - """Simultaneously sort based on the feature_values. - - Missing values are stored at the end of feature_values. - The number of missing values observed in feature_values is stored - in self.n_missing. - """ - cdef: - SIZE_t i, current_end - DTYPE_t[::1] feature_values = self.feature_values - const DTYPE_t[:, :] X = self.X - SIZE_t[::1] samples = self.samples - SIZE_t n_missing = 0 - const unsigned char[::1] missing_values_in_feature_mask = self.missing_values_in_feature_mask - - # Sort samples along that feature; by - # copying the values into an array and - # sorting the array in a manner which utilizes the cache more - # effectively. - if missing_values_in_feature_mask is not None and missing_values_in_feature_mask[current_feature]: - i, current_end = self.start, self.end - 1 - # Missing values are placed at the end and do not participate in the sorting. - while i <= current_end: - # Finds the right-most value that is not missing so that - # it can be swapped with missing values at its left. - if isnan(X[samples[current_end], current_feature]): - n_missing += 1 - current_end -= 1 - continue - - # X[samples[current_end], current_feature] is a non-missing value - if isnan(X[samples[i], current_feature]): - samples[i], samples[current_end] = samples[current_end], samples[i] - n_missing += 1 - current_end -= 1 - - feature_values[i] = X[samples[i], current_feature] - i += 1 - else: - # When there are no missing values, we only need to copy the data into - # feature_values - for i in range(self.start, self.end): - feature_values[i] = X[samples[i], current_feature] - - sort(&feature_values[self.start], &samples[self.start], self.end - self.start - n_missing) - self.n_missing = n_missing - - cdef inline void find_min_max( - self, - SIZE_t current_feature, - DTYPE_t* min_feature_value_out, - DTYPE_t* max_feature_value_out, - ) noexcept nogil: - """Find the minimum and maximum value for current_feature.""" - cdef: - SIZE_t p - DTYPE_t current_feature_value - const DTYPE_t[:, :] X = self.X - SIZE_t[::1] samples = self.samples - DTYPE_t min_feature_value = X[samples[self.start], current_feature] - DTYPE_t max_feature_value = min_feature_value - DTYPE_t[::1] feature_values = self.feature_values - - feature_values[self.start] = min_feature_value - - for p in range(self.start + 1, self.end): - current_feature_value = X[samples[p], current_feature] - feature_values[p] = current_feature_value - - if current_feature_value < min_feature_value: - min_feature_value = current_feature_value - elif current_feature_value > max_feature_value: - max_feature_value = current_feature_value - - min_feature_value_out[0] = min_feature_value - max_feature_value_out[0] = max_feature_value - - cdef inline void next_p(self, SIZE_t* p_prev, SIZE_t* p) noexcept nogil: - """Compute the next p_prev and p for iteratiing over feature values. - - The missing values are not included when iterating through the feature values. - """ - cdef: - DTYPE_t[::1] feature_values = self.feature_values - SIZE_t end_non_missing = self.end - self.n_missing - - while ( - p[0] + 1 < end_non_missing and - feature_values[p[0] + 1] <= feature_values[p[0]] + FEATURE_THRESHOLD - ): - p[0] += 1 - - p_prev[0] = p[0] - - # By adding 1, we have - # (feature_values[p] >= end) or (feature_values[p] > feature_values[p - 1]) - p[0] += 1 - - cdef inline SIZE_t partition_samples(self, double current_threshold) noexcept nogil: - """Partition samples for feature_values at the current_threshold.""" - cdef: - SIZE_t p = self.start - SIZE_t partition_end = self.end - SIZE_t[::1] samples = self.samples - DTYPE_t[::1] feature_values = self.feature_values - - while p < partition_end: - if feature_values[p] <= current_threshold: - p += 1 - else: - partition_end -= 1 - - feature_values[p], feature_values[partition_end] = ( - feature_values[partition_end], feature_values[p] - ) - samples[p], samples[partition_end] = samples[partition_end], samples[p] - - return partition_end - - cdef inline void partition_samples_final( - self, - SIZE_t best_pos, - double best_threshold, - SIZE_t best_feature, - SIZE_t best_n_missing, - ) noexcept nogil: - """Partition samples for X at the best_threshold and best_feature. - - If missing values are present, this method partitions `samples` - so that the `best_n_missing` missing values' indices are in the - right-most end of `samples`, that is `samples[end_non_missing:end]`. - """ - cdef: - # Local invariance: start <= p <= partition_end <= end - SIZE_t start = self.start - SIZE_t p = start - SIZE_t end = self.end - 1 - SIZE_t partition_end = end - best_n_missing - SIZE_t[::1] samples = self.samples - const DTYPE_t[:, :] X = self.X - DTYPE_t current_value - - if best_n_missing != 0: - # Move samples with missing values to the end while partitioning the - # non-missing samples - while p < partition_end: - # Keep samples with missing values at the end - if isnan(X[samples[end], best_feature]): - end -= 1 - continue - - # Swap sample with missing values with the sample at the end - current_value = X[samples[p], best_feature] - if isnan(current_value): - samples[p], samples[end] = samples[end], samples[p] - end -= 1 - - # The swapped sample at the end is always a non-missing value, so - # we can continue the algorithm without checking for missingness. - current_value = X[samples[p], best_feature] - - # Partition the non-missing samples - if current_value <= best_threshold: - p += 1 - else: - samples[p], samples[partition_end] = samples[partition_end], samples[p] - partition_end -= 1 - else: - # Partitioning routine when there are no missing values - while p < partition_end: - if X[samples[p], best_feature] <= best_threshold: - p += 1 - else: - samples[p], samples[partition_end] = samples[partition_end], samples[p] - partition_end -= 1 - - -@final -cdef class SparsePartitioner: - """Partitioner specialized for sparse CSC data. - - Note that this partitioner is agnostic to the splitting strategy (best vs. random). - """ - cdef SIZE_t[::1] samples - cdef DTYPE_t[::1] feature_values - cdef SIZE_t start - cdef SIZE_t end - cdef SIZE_t n_missing - cdef const unsigned char[::1] missing_values_in_feature_mask - - cdef const DTYPE_t[::1] X_data - cdef const INT32_t[::1] X_indices - cdef const INT32_t[::1] X_indptr - - cdef SIZE_t n_total_samples - - cdef SIZE_t[::1] index_to_samples - cdef SIZE_t[::1] sorted_samples - - cdef SIZE_t start_positive - cdef SIZE_t end_negative - cdef bint is_samples_sorted - - def __init__( - self, - object X, - SIZE_t[::1] samples, - SIZE_t n_samples, - DTYPE_t[::1] feature_values, - const unsigned char[::1] missing_values_in_feature_mask, - ): - if not isspmatrix_csc(X): - raise ValueError("X should be in csc format") - - self.samples = samples - self.feature_values = feature_values - - # Initialize X - cdef SIZE_t n_total_samples = X.shape[0] - - self.X_data = X.data - self.X_indices = X.indices - self.X_indptr = X.indptr - self.n_total_samples = n_total_samples - - # Initialize auxiliary array used to perform split - self.index_to_samples = np.full(n_total_samples, fill_value=-1, dtype=np.intp) - self.sorted_samples = np.empty(n_samples, dtype=np.intp) - - cdef SIZE_t p - for p in range(n_samples): - self.index_to_samples[samples[p]] = p - - self.missing_values_in_feature_mask = missing_values_in_feature_mask - - cdef inline void init_node_split(self, SIZE_t start, SIZE_t end) noexcept nogil: - """Initialize splitter at the beginning of node_split.""" - self.start = start - self.end = end - self.is_samples_sorted = 0 - self.n_missing = 0 - - cdef inline void sort_samples_and_feature_values( - self, SIZE_t current_feature - ) noexcept nogil: - """Simultaneously sort based on the feature_values.""" - cdef: - DTYPE_t[::1] feature_values = self.feature_values - SIZE_t[::1] index_to_samples = self.index_to_samples - SIZE_t[::1] samples = self.samples - - self.extract_nnz(current_feature) - # Sort the positive and negative parts of `feature_values` - sort(&feature_values[self.start], &samples[self.start], self.end_negative - self.start) - if self.start_positive < self.end: - sort( - &feature_values[self.start_positive], - &samples[self.start_positive], - self.end - self.start_positive - ) - - # Update index_to_samples to take into account the sort - for p in range(self.start, self.end_negative): - index_to_samples[samples[p]] = p - for p in range(self.start_positive, self.end): - index_to_samples[samples[p]] = p - - # Add one or two zeros in feature_values, if there is any - if self.end_negative < self.start_positive: - self.start_positive -= 1 - feature_values[self.start_positive] = 0. - - if self.end_negative != self.start_positive: - feature_values[self.end_negative] = 0. - self.end_negative += 1 - - # XXX: When sparse supports missing values, this should be set to the - # number of missing values for current_feature - self.n_missing = 0 - - cdef inline void find_min_max( - self, - SIZE_t current_feature, - DTYPE_t* min_feature_value_out, - DTYPE_t* max_feature_value_out, - ) noexcept nogil: - """Find the minimum and maximum value for current_feature.""" - cdef: - SIZE_t p - DTYPE_t current_feature_value, min_feature_value, max_feature_value - DTYPE_t[::1] feature_values = self.feature_values - - self.extract_nnz(current_feature) - - if self.end_negative != self.start_positive: - # There is a zero - min_feature_value = 0 - max_feature_value = 0 - else: - min_feature_value = feature_values[self.start] - max_feature_value = min_feature_value - - # Find min, max in feature_values[start:end_negative] - for p in range(self.start, self.end_negative): - current_feature_value = feature_values[p] - - if current_feature_value < min_feature_value: - min_feature_value = current_feature_value - elif current_feature_value > max_feature_value: - max_feature_value = current_feature_value - - # Update min, max given feature_values[start_positive:end] - for p in range(self.start_positive, self.end): - current_feature_value = feature_values[p] - - if current_feature_value < min_feature_value: - min_feature_value = current_feature_value - elif current_feature_value > max_feature_value: - max_feature_value = current_feature_value - - min_feature_value_out[0] = min_feature_value - max_feature_value_out[0] = max_feature_value - - cdef inline void next_p(self, SIZE_t* p_prev, SIZE_t* p) noexcept nogil: - """Compute the next p_prev and p for iteratiing over feature values.""" - cdef: - SIZE_t p_next - DTYPE_t[::1] feature_values = self.feature_values - - if p[0] + 1 != self.end_negative: - p_next = p[0] + 1 - else: - p_next = self.start_positive - - while (p_next < self.end and - feature_values[p_next] <= feature_values[p[0]] + FEATURE_THRESHOLD): - p[0] = p_next - if p[0] + 1 != self.end_negative: - p_next = p[0] + 1 - else: - p_next = self.start_positive - - p_prev[0] = p[0] - p[0] = p_next - - cdef inline SIZE_t partition_samples(self, double current_threshold) noexcept nogil: - """Partition samples for feature_values at the current_threshold.""" - return self._partition(current_threshold, self.start_positive) - - cdef inline void partition_samples_final( - self, - SIZE_t best_pos, - double best_threshold, - SIZE_t best_feature, - SIZE_t n_missing, - ) noexcept nogil: - """Partition samples for X at the best_threshold and best_feature.""" - self.extract_nnz(best_feature) - self._partition(best_threshold, best_pos) - - cdef inline SIZE_t _partition(self, double threshold, SIZE_t zero_pos) noexcept nogil: - """Partition samples[start:end] based on threshold.""" - cdef: - SIZE_t p, partition_end - SIZE_t[::1] index_to_samples = self.index_to_samples - DTYPE_t[::1] feature_values = self.feature_values - SIZE_t[::1] samples = self.samples - - if threshold < 0.: - p = self.start - partition_end = self.end_negative - elif threshold > 0.: - p = self.start_positive - partition_end = self.end - else: - # Data are already split - return zero_pos - - while p < partition_end: - if feature_values[p] <= threshold: - p += 1 - - else: - partition_end -= 1 - - feature_values[p], feature_values[partition_end] = ( - feature_values[partition_end], feature_values[p] - ) - sparse_swap(index_to_samples, samples, p, partition_end) - - return partition_end - - cdef inline void extract_nnz(self, SIZE_t feature) noexcept nogil: - """Extract and partition values for a given feature. - - The extracted values are partitioned between negative values - feature_values[start:end_negative[0]] and positive values - feature_values[start_positive[0]:end]. - The samples and index_to_samples are modified according to this - partition. - - The extraction corresponds to the intersection between the arrays - X_indices[indptr_start:indptr_end] and samples[start:end]. - This is done efficiently using either an index_to_samples based approach - or binary search based approach. - - Parameters - ---------- - feature : SIZE_t, - Index of the feature we want to extract non zero value. - """ - cdef SIZE_t[::1] samples = self.samples - cdef DTYPE_t[::1] feature_values = self.feature_values - cdef SIZE_t indptr_start = self.X_indptr[feature], - cdef SIZE_t indptr_end = self.X_indptr[feature + 1] - cdef SIZE_t n_indices = (indptr_end - indptr_start) - cdef SIZE_t n_samples = self.end - self.start - cdef SIZE_t[::1] index_to_samples = self.index_to_samples - cdef SIZE_t[::1] sorted_samples = self.sorted_samples - cdef const INT32_t[::1] X_indices = self.X_indices - cdef const DTYPE_t[::1] X_data = self.X_data - - # Use binary search if n_samples * log(n_indices) < - # n_indices and index_to_samples approach otherwise. - # O(n_samples * log(n_indices)) is the running time of binary - # search and O(n_indices) is the running time of index_to_samples - # approach. - if ((1 - self.is_samples_sorted) * n_samples * log(n_samples) + - n_samples * log(n_indices) < EXTRACT_NNZ_SWITCH * n_indices): - extract_nnz_binary_search(X_indices, X_data, - indptr_start, indptr_end, - samples, self.start, self.end, - index_to_samples, - feature_values, - &self.end_negative, &self.start_positive, - sorted_samples, &self.is_samples_sorted) - - # Using an index to samples technique to extract non zero values - # index_to_samples is a mapping from X_indices to samples - else: - extract_nnz_index_to_samples(X_indices, X_data, - indptr_start, indptr_end, - samples, self.start, self.end, - index_to_samples, - feature_values, - &self.end_negative, &self.start_positive) - - -cdef int compare_SIZE_t(const void* a, const void* b) noexcept nogil: - """Comparison function for sort.""" - return ((a)[0] - (b)[0]) - - -cdef inline void binary_search(const INT32_t[::1] sorted_array, - INT32_t start, INT32_t end, - SIZE_t value, SIZE_t* index, - INT32_t* new_start) noexcept nogil: - """Return the index of value in the sorted array. - - If not found, return -1. new_start is the last pivot + 1 - """ - cdef INT32_t pivot - index[0] = -1 - while start < end: - pivot = start + (end - start) / 2 - - if sorted_array[pivot] == value: - index[0] = pivot - start = pivot + 1 - break - - if sorted_array[pivot] < value: - start = pivot + 1 - else: - end = pivot - new_start[0] = start - - -cdef inline void extract_nnz_index_to_samples(const INT32_t[::1] X_indices, - const DTYPE_t[::1] X_data, - INT32_t indptr_start, - INT32_t indptr_end, - SIZE_t[::1] samples, - SIZE_t start, - SIZE_t end, - SIZE_t[::1] index_to_samples, - DTYPE_t[::1] feature_values, - SIZE_t* end_negative, - SIZE_t* start_positive) noexcept nogil: - """Extract and partition values for a feature using index_to_samples. - - Complexity is O(indptr_end - indptr_start). - """ - cdef INT32_t k - cdef SIZE_t index - cdef SIZE_t end_negative_ = start - cdef SIZE_t start_positive_ = end - - for k in range(indptr_start, indptr_end): - if start <= index_to_samples[X_indices[k]] < end: - if X_data[k] > 0: - start_positive_ -= 1 - feature_values[start_positive_] = X_data[k] - index = index_to_samples[X_indices[k]] - sparse_swap(index_to_samples, samples, index, start_positive_) - - elif X_data[k] < 0: - feature_values[end_negative_] = X_data[k] - index = index_to_samples[X_indices[k]] - sparse_swap(index_to_samples, samples, index, end_negative_) - end_negative_ += 1 - - # Returned values - end_negative[0] = end_negative_ - start_positive[0] = start_positive_ - - -cdef inline void extract_nnz_binary_search(const INT32_t[::1] X_indices, - const DTYPE_t[::1] X_data, - INT32_t indptr_start, - INT32_t indptr_end, - SIZE_t[::1] samples, - SIZE_t start, - SIZE_t end, - SIZE_t[::1] index_to_samples, - DTYPE_t[::1] feature_values, - SIZE_t* end_negative, - SIZE_t* start_positive, - SIZE_t[::1] sorted_samples, - bint* is_samples_sorted) noexcept nogil: - """Extract and partition values for a given feature using binary search. - - If n_samples = end - start and n_indices = indptr_end - indptr_start, - the complexity is - - O((1 - is_samples_sorted[0]) * n_samples * log(n_samples) + - n_samples * log(n_indices)). - """ - cdef SIZE_t n_samples - - if not is_samples_sorted[0]: - n_samples = end - start - memcpy(&sorted_samples[start], &samples[start], - n_samples * sizeof(SIZE_t)) - qsort(&sorted_samples[start], n_samples, sizeof(SIZE_t), - compare_SIZE_t) - is_samples_sorted[0] = 1 - - while (indptr_start < indptr_end and - sorted_samples[start] > X_indices[indptr_start]): - indptr_start += 1 - - while (indptr_start < indptr_end and - sorted_samples[end - 1] < X_indices[indptr_end - 1]): - indptr_end -= 1 - - cdef SIZE_t p = start - cdef SIZE_t index - cdef SIZE_t k - cdef SIZE_t end_negative_ = start - cdef SIZE_t start_positive_ = end - - while (p < end and indptr_start < indptr_end): - # Find index of sorted_samples[p] in X_indices - binary_search(X_indices, indptr_start, indptr_end, - sorted_samples[p], &k, &indptr_start) - - if k != -1: - # If k != -1, we have found a non zero value - - if X_data[k] > 0: - start_positive_ -= 1 - feature_values[start_positive_] = X_data[k] - index = index_to_samples[X_indices[k]] - sparse_swap(index_to_samples, samples, index, start_positive_) - - elif X_data[k] < 0: - feature_values[end_negative_] = X_data[k] - index = index_to_samples[X_indices[k]] - sparse_swap(index_to_samples, samples, index, end_negative_) - end_negative_ += 1 - p += 1 - - # Returned values - end_negative[0] = end_negative_ - start_positive[0] = start_positive_ - - -cdef inline void sparse_swap(SIZE_t[::1] index_to_samples, SIZE_t[::1] samples, - SIZE_t pos_1, SIZE_t pos_2) noexcept nogil: - """Swap sample pos_1 and pos_2 preserving sparse invariant.""" - samples[pos_1], samples[pos_2] = samples[pos_2], samples[pos_1] - index_to_samples[samples[pos_1]] = pos_1 - index_to_samples[samples[pos_2]] = pos_2 - - -cdef class BestSplitter(Splitter): - """Splitter for finding the best split on dense data.""" - cdef DensePartitioner partitioner - cdef int init( - self, - object X, - const DOUBLE_t[:, ::1] y, - const DOUBLE_t[:] sample_weight, - const unsigned char[::1] missing_values_in_feature_mask, - ) except -1: - Splitter.init(self, X, y, sample_weight, missing_values_in_feature_mask) - self.partitioner = DensePartitioner( - X, self.samples, self.feature_values, missing_values_in_feature_mask - ) - - cdef int node_split(self, double impurity, SplitRecord* split, - SIZE_t* n_constant_features) except -1 nogil: - return node_split_best( - self, - self.partitioner, - self.criterion, - impurity, - split, - n_constant_features, - ) - -cdef class BestSparseSplitter(Splitter): - """Splitter for finding the best split, using the sparse data.""" - cdef SparsePartitioner partitioner - cdef int init( - self, - object X, - const DOUBLE_t[:, ::1] y, - const DOUBLE_t[:] sample_weight, - const unsigned char[::1] missing_values_in_feature_mask, - ) except -1: - Splitter.init(self, X, y, sample_weight, missing_values_in_feature_mask) - self.partitioner = SparsePartitioner( - X, self.samples, self.n_samples, self.feature_values, missing_values_in_feature_mask - ) - - cdef int node_split(self, double impurity, SplitRecord* split, - SIZE_t* n_constant_features) except -1 nogil: - return node_split_best( - self, - self.partitioner, - self.criterion, - impurity, - split, - n_constant_features, - ) - -cdef class RandomSplitter(Splitter): - """Splitter for finding the best random split on dense data.""" - cdef DensePartitioner partitioner - cdef int init( - self, - object X, - const DOUBLE_t[:, ::1] y, - const DOUBLE_t[:] sample_weight, - const unsigned char[::1] missing_values_in_feature_mask, - ) except -1: - Splitter.init(self, X, y, sample_weight, missing_values_in_feature_mask) - self.partitioner = DensePartitioner( - X, self.samples, self.feature_values, missing_values_in_feature_mask - ) - - cdef int node_split(self, double impurity, SplitRecord* split, - SIZE_t* n_constant_features) except -1 nogil: - return node_split_random( - self, - self.partitioner, - self.criterion, - impurity, - split, - n_constant_features, - ) - -cdef class RandomSparseSplitter(Splitter): - """Splitter for finding the best random split, using the sparse data.""" - cdef SparsePartitioner partitioner - cdef int init( - self, - object X, - const DOUBLE_t[:, ::1] y, - const DOUBLE_t[:] sample_weight, - const unsigned char[::1] missing_values_in_feature_mask, - ) except -1: - Splitter.init(self, X, y, sample_weight, missing_values_in_feature_mask) - self.partitioner = SparsePartitioner( - X, self.samples, self.n_samples, self.feature_values, missing_values_in_feature_mask - ) - - cdef int node_split(self, double impurity, SplitRecord* split, - SIZE_t* n_constant_features) except -1 nogil: - return node_split_random( - self, - self.partitioner, - self.criterion, - impurity, - split, - n_constant_features, - ) \ No newline at end of file diff --git a/ivy/functional/frontends/sklearn/_tree copy.pyx b/ivy/functional/frontends/sklearn/_tree copy.pyx deleted file mode 100644 index f436c919f8bc2..0000000000000 --- a/ivy/functional/frontends/sklearn/_tree copy.pyx +++ /dev/null @@ -1,1833 +0,0 @@ -# Authors: Gilles Louppe -# Peter Prettenhofer -# Brian Holt -# Noel Dawe -# Satrajit Gosh -# Lars Buitinck -# Arnaud Joly -# Joel Nothman -# Fares Hedayati -# Jacob Schreiber -# Nelson Liu -# -# License: BSD 3 clause - -from cpython cimport Py_INCREF, PyObject, PyTypeObject - -from libc.stdlib cimport free -from libc.string cimport memcpy -from libc.string cimport memset -from libc.stdint cimport INTPTR_MAX -from libc.math cimport isnan -from libcpp.vector cimport vector -from libcpp.algorithm cimport pop_heap -from libcpp.algorithm cimport push_heap -from libcpp cimport bool - -import struct - -import numpy as np -cimport numpy as cnp -cnp.import_array() - -from scipy.sparse import issparse -from scipy.sparse import csr_matrix -from scipy.sparse import isspmatrix_csr - -from ._utils cimport safe_realloc -from ._utils cimport sizet_ptr_to_ndarray - -cdef extern from "numpy/arrayobject.h": - object PyArray_NewFromDescr(PyTypeObject* subtype, cnp.dtype descr, - int nd, cnp.npy_intp* dims, - cnp.npy_intp* strides, - void* data, int flags, object obj) - int PyArray_SetBaseObject(cnp.ndarray arr, PyObject* obj) - -cdef extern from "" namespace "std" nogil: - cdef cppclass stack[T]: - ctypedef T value_type - stack() except + - bint empty() - void pop() - void push(T&) except + # Raise c++ exception for bad_alloc -> MemoryError - T& top() - -# ============================================================================= -# Types and constants -# ============================================================================= - -from numpy import float32 as DTYPE -from numpy import float64 as DOUBLE - -cdef double INFINITY = np.inf -cdef double EPSILON = np.finfo('double').eps - -# Some handy constants (BestFirstTreeBuilder) -cdef int IS_FIRST = 1 -cdef int IS_NOT_FIRST = 0 -cdef int IS_LEFT = 1 -cdef int IS_NOT_LEFT = 0 - -TREE_LEAF = -1 -TREE_UNDEFINED = -2 -cdef SIZE_t _TREE_LEAF = TREE_LEAF -cdef SIZE_t _TREE_UNDEFINED = TREE_UNDEFINED - -# Build the corresponding numpy dtype for Node. -# This works by casting `dummy` to an array of Node of length 1, which numpy -# can construct a `dtype`-object for. See https://stackoverflow.com/q/62448946 -# for a more detailed explanation. -cdef Node dummy -NODE_DTYPE = np.asarray((&dummy)).dtype - -# ============================================================================= -# TreeBuilder -# ============================================================================= - -cdef class TreeBuilder: - """Interface for different tree building strategies.""" - - cpdef build( - self, - Tree tree, - object X, - const DOUBLE_t[:, ::1] y, - const DOUBLE_t[:] sample_weight=None, - const unsigned char[::1] missing_values_in_feature_mask=None, - ): - """Build a decision tree from the training set (X, y).""" - pass - - cdef inline _check_input( - self, - object X, - const DOUBLE_t[:, ::1] y, - const DOUBLE_t[:] sample_weight, - ): - """Check input dtype, layout and format""" - if issparse(X): - X = X.tocsc() - X.sort_indices() - - if X.data.dtype != DTYPE: - X.data = np.ascontiguousarray(X.data, dtype=DTYPE) - - if X.indices.dtype != np.int32 or X.indptr.dtype != np.int32: - raise ValueError("No support for np.int64 index based " - "sparse matrices") - - elif X.dtype != DTYPE: - # since we have to copy we will make it fortran for efficiency - X = np.asfortranarray(X, dtype=DTYPE) - - # TODO: This check for y seems to be redundant, as it is also - # present in the BaseDecisionTree's fit method, and therefore - # can be removed. - if y.base.dtype != DOUBLE or not y.base.flags.contiguous: - y = np.ascontiguousarray(y, dtype=DOUBLE) - - if ( - sample_weight is not None and - ( - sample_weight.base.dtype != DOUBLE or - not sample_weight.base.flags.contiguous - ) - ): - sample_weight = np.asarray(sample_weight, dtype=DOUBLE, order="C") - - return X, y, sample_weight - -# Depth first builder --------------------------------------------------------- -# A record on the stack for depth-first tree growing -cdef struct StackRecord: - SIZE_t start - SIZE_t end - SIZE_t depth - SIZE_t parent - bint is_left - double impurity - SIZE_t n_constant_features - - - - - - - -cdef class DepthFirstTreeBuilder(TreeBuilder): - """Build a decision tree in depth-first fashion.""" - - def __cinit__(self, Splitter splitter, SIZE_t min_samples_split, - SIZE_t min_samples_leaf, double min_weight_leaf, - SIZE_t max_depth, double min_impurity_decrease): - self.splitter = splitter - self.min_samples_split = min_samples_split - self.min_samples_leaf = min_samples_leaf - self.min_weight_leaf = min_weight_leaf - self.max_depth = max_depth - self.min_impurity_decrease = min_impurity_decrease - - cpdef build( - self, - Tree tree, - object X, - const DOUBLE_t[:, ::1] y, - const DOUBLE_t[:] sample_weight=None, - const unsigned char[::1] missing_values_in_feature_mask=None, - ): - """Build a decision tree from the training set (X, y).""" - - # check input - X, y, sample_weight = self._check_input(X, y, sample_weight) - - # Initial capacity - cdef int init_capacity - - if tree.max_depth <= 10: - init_capacity = (2 ** (tree.max_depth + 1)) - 1 - else: - init_capacity = 2047 - - tree._resize(init_capacity) - - # Parameters - cdef Splitter splitter = self.splitter - cdef SIZE_t max_depth = self.max_depth - cdef SIZE_t min_samples_leaf = self.min_samples_leaf - cdef double min_weight_leaf = self.min_weight_leaf - cdef SIZE_t min_samples_split = self.min_samples_split - cdef double min_impurity_decrease = self.min_impurity_decrease - - # Recursive partition (without actual recursion) - splitter.init(X, y, sample_weight, missing_values_in_feature_mask) - - cdef SIZE_t start - cdef SIZE_t end - cdef SIZE_t depth - cdef SIZE_t parent - cdef bint is_left - cdef SIZE_t n_node_samples = splitter.n_samples - cdef double weighted_n_node_samples - cdef SplitRecord split - cdef SIZE_t node_id - - cdef double impurity = INFINITY - cdef SIZE_t n_constant_features - cdef bint is_leaf - cdef bint first = 1 - cdef SIZE_t max_depth_seen = -1 - cdef int rc = 0 - - cdef stack[StackRecord] builder_stack - cdef StackRecord stack_record - - with nogil: - # push root node onto stack - builder_stack.push({ - "start": 0, - "end": n_node_samples, - "depth": 0, - "parent": _TREE_UNDEFINED, - "is_left": 0, - "impurity": INFINITY, - "n_constant_features": 0}) - - while not builder_stack.empty(): - stack_record = builder_stack.top() - builder_stack.pop() - - start = stack_record.start - end = stack_record.end - depth = stack_record.depth - parent = stack_record.parent - is_left = stack_record.is_left - impurity = stack_record.impurity - n_constant_features = stack_record.n_constant_features - - n_node_samples = end - start - splitter.node_reset(start, end, &weighted_n_node_samples) - - is_leaf = (depth >= max_depth or - n_node_samples < min_samples_split or - n_node_samples < 2 * min_samples_leaf or - weighted_n_node_samples < 2 * min_weight_leaf) - - if first: - impurity = splitter.node_impurity() - first = 0 - - # impurity == 0 with tolerance due to rounding errors - is_leaf = is_leaf or impurity <= EPSILON - - if not is_leaf: - splitter.node_split(impurity, &split, &n_constant_features) - # If EPSILON=0 in the below comparison, float precision - # issues stop splitting, producing trees that are - # dissimilar to v0.18 - is_leaf = (is_leaf or split.pos >= end or - (split.improvement + EPSILON < - min_impurity_decrease)) - - node_id = tree._add_node(parent, is_left, is_leaf, split.feature, - split.threshold, impurity, n_node_samples, - weighted_n_node_samples, - split.missing_go_to_left) - - if node_id == INTPTR_MAX: - rc = -1 - break - - # Store value for all nodes, to facilitate tree/model - # inspection and interpretation - splitter.node_value(tree.value + node_id * tree.value_stride) - - if not is_leaf: - # Push right child on stack - builder_stack.push({ - "start": split.pos, - "end": end, - "depth": depth + 1, - "parent": node_id, - "is_left": 0, - "impurity": split.impurity_right, - "n_constant_features": n_constant_features}) - - # Push left child on stack - builder_stack.push({ - "start": start, - "end": split.pos, - "depth": depth + 1, - "parent": node_id, - "is_left": 1, - "impurity": split.impurity_left, - "n_constant_features": n_constant_features}) - - if depth > max_depth_seen: - max_depth_seen = depth - - if rc >= 0: - rc = tree._resize_c(tree.node_count) - - if rc >= 0: - tree.max_depth = max_depth_seen - if rc == -1: - raise MemoryError() - - -# Best first builder ---------------------------------------------------------- -cdef struct FrontierRecord: - # Record of information of a Node, the frontier for a split. Those records are - # maintained in a heap to access the Node with the best improvement in impurity, - # allowing growing trees greedily on this improvement. - SIZE_t node_id - SIZE_t start - SIZE_t end - SIZE_t pos - SIZE_t depth - bint is_leaf - double impurity - double impurity_left - double impurity_right - double improvement - -cdef inline bool _compare_records( - const FrontierRecord& left, - const FrontierRecord& right, -): - return left.improvement < right.improvement - -cdef inline void _add_to_frontier( - FrontierRecord rec, - vector[FrontierRecord]& frontier, -) noexcept nogil: - """Adds record `rec` to the priority queue `frontier`.""" - frontier.push_back(rec) - push_heap(frontier.begin(), frontier.end(), &_compare_records) - - -cdef class BestFirstTreeBuilder(TreeBuilder): - """Build a decision tree in best-first fashion. - - The best node to expand is given by the node at the frontier that has the - highest impurity improvement. - """ - cdef SIZE_t max_leaf_nodes - - def __cinit__(self, Splitter splitter, SIZE_t min_samples_split, - SIZE_t min_samples_leaf, min_weight_leaf, - SIZE_t max_depth, SIZE_t max_leaf_nodes, - double min_impurity_decrease): - self.splitter = splitter - self.min_samples_split = min_samples_split - self.min_samples_leaf = min_samples_leaf - self.min_weight_leaf = min_weight_leaf - self.max_depth = max_depth - self.max_leaf_nodes = max_leaf_nodes - self.min_impurity_decrease = min_impurity_decrease - - cpdef build( - self, - Tree tree, - object X, - const DOUBLE_t[:, ::1] y, - const DOUBLE_t[:] sample_weight=None, - const unsigned char[::1] missing_values_in_feature_mask=None, - ): - """Build a decision tree from the training set (X, y).""" - - # check input - X, y, sample_weight = self._check_input(X, y, sample_weight) - - # Parameters - cdef Splitter splitter = self.splitter - cdef SIZE_t max_leaf_nodes = self.max_leaf_nodes - - # Recursive partition (without actual recursion) - splitter.init(X, y, sample_weight, missing_values_in_feature_mask) - - cdef vector[FrontierRecord] frontier - cdef FrontierRecord record - cdef FrontierRecord split_node_left - cdef FrontierRecord split_node_right - - cdef SIZE_t n_node_samples = splitter.n_samples - cdef SIZE_t max_split_nodes = max_leaf_nodes - 1 - cdef bint is_leaf - cdef SIZE_t max_depth_seen = -1 - cdef int rc = 0 - cdef Node* node - - # Initial capacity - cdef SIZE_t init_capacity = max_split_nodes + max_leaf_nodes - tree._resize(init_capacity) - - with nogil: - # add root to frontier - rc = self._add_split_node(splitter, tree, 0, n_node_samples, - INFINITY, IS_FIRST, IS_LEFT, NULL, 0, - &split_node_left) - if rc >= 0: - _add_to_frontier(split_node_left, frontier) - - while not frontier.empty(): - pop_heap(frontier.begin(), frontier.end(), &_compare_records) - record = frontier.back() - frontier.pop_back() - - node = &tree.nodes[record.node_id] - is_leaf = (record.is_leaf or max_split_nodes <= 0) - - if is_leaf: - # Node is not expandable; set node as leaf - node.left_child = _TREE_LEAF - node.right_child = _TREE_LEAF - node.feature = _TREE_UNDEFINED - node.threshold = _TREE_UNDEFINED - - else: - # Node is expandable - - # Decrement number of split nodes available - max_split_nodes -= 1 - - # Compute left split node - rc = self._add_split_node(splitter, tree, - record.start, record.pos, - record.impurity_left, - IS_NOT_FIRST, IS_LEFT, node, - record.depth + 1, - &split_node_left) - if rc == -1: - break - - # tree.nodes may have changed - node = &tree.nodes[record.node_id] - - # Compute right split node - rc = self._add_split_node(splitter, tree, record.pos, - record.end, - record.impurity_right, - IS_NOT_FIRST, IS_NOT_LEFT, node, - record.depth + 1, - &split_node_right) - if rc == -1: - break - - # Add nodes to queue - _add_to_frontier(split_node_left, frontier) - _add_to_frontier(split_node_right, frontier) - - if record.depth > max_depth_seen: - max_depth_seen = record.depth - - if rc >= 0: - rc = tree._resize_c(tree.node_count) - - if rc >= 0: - tree.max_depth = max_depth_seen - - if rc == -1: - raise MemoryError() - - cdef inline int _add_split_node(self, Splitter splitter, Tree tree, - SIZE_t start, SIZE_t end, double impurity, - bint is_first, bint is_left, Node* parent, - SIZE_t depth, - FrontierRecord* res) except -1 nogil: - """Adds node w/ partition ``[start, end)`` to the frontier. """ - cdef SplitRecord split - cdef SIZE_t node_id - cdef SIZE_t n_node_samples - cdef SIZE_t n_constant_features = 0 - cdef double min_impurity_decrease = self.min_impurity_decrease - cdef double weighted_n_node_samples - cdef bint is_leaf - - splitter.node_reset(start, end, &weighted_n_node_samples) - - if is_first: - impurity = splitter.node_impurity() - - n_node_samples = end - start - is_leaf = (depth >= self.max_depth or - n_node_samples < self.min_samples_split or - n_node_samples < 2 * self.min_samples_leaf or - weighted_n_node_samples < 2 * self.min_weight_leaf or - impurity <= EPSILON # impurity == 0 with tolerance - ) - - if not is_leaf: - splitter.node_split(impurity, &split, &n_constant_features) - # If EPSILON=0 in the below comparison, float precision issues stop - # splitting early, producing trees that are dissimilar to v0.18 - is_leaf = (is_leaf or split.pos >= end or - split.improvement + EPSILON < min_impurity_decrease) - - node_id = tree._add_node(parent - tree.nodes - if parent != NULL - else _TREE_UNDEFINED, - is_left, is_leaf, - split.feature, split.threshold, impurity, n_node_samples, - weighted_n_node_samples, - split.missing_go_to_left) - if node_id == INTPTR_MAX: - return -1 - - # compute values also for split nodes (might become leafs later). - splitter.node_value(tree.value + node_id * tree.value_stride) - - res.node_id = node_id - res.start = start - res.end = end - res.depth = depth - res.impurity = impurity - - if not is_leaf: - # is split node - res.pos = split.pos - res.is_leaf = 0 - res.improvement = split.improvement - res.impurity_left = split.impurity_left - res.impurity_right = split.impurity_right - - else: - # is leaf => 0 improvement - res.pos = end - res.is_leaf = 1 - res.improvement = 0.0 - res.impurity_left = impurity - res.impurity_right = impurity - - return 0 - - -# ============================================================================= -# Tree -# ============================================================================= - -cdef class Tree: - """Array-based representation of a binary decision tree. - - The binary tree is represented as a number of parallel arrays. The i-th - element of each array holds information about the node `i`. Node 0 is the - tree's root. You can find a detailed description of all arrays in - `_tree.pxd`. NOTE: Some of the arrays only apply to either leaves or split - nodes, resp. In this case the values of nodes of the other type are - arbitrary! - - Attributes - ---------- - node_count : int - The number of nodes (internal nodes + leaves) in the tree. - - capacity : int - The current capacity (i.e., size) of the arrays, which is at least as - great as `node_count`. - - max_depth : int - The depth of the tree, i.e. the maximum depth of its leaves. - - children_left : array of int, shape [node_count] - children_left[i] holds the node id of the left child of node i. - For leaves, children_left[i] == TREE_LEAF. Otherwise, - children_left[i] > i. This child handles the case where - X[:, feature[i]] <= threshold[i]. - - children_right : array of int, shape [node_count] - children_right[i] holds the node id of the right child of node i. - For leaves, children_right[i] == TREE_LEAF. Otherwise, - children_right[i] > i. This child handles the case where - X[:, feature[i]] > threshold[i]. - - feature : array of int, shape [node_count] - feature[i] holds the feature to split on, for the internal node i. - - threshold : array of double, shape [node_count] - threshold[i] holds the threshold for the internal node i. - - value : array of double, shape [node_count, n_outputs, max_n_classes] - Contains the constant prediction value of each node. - - impurity : array of double, shape [node_count] - impurity[i] holds the impurity (i.e., the value of the splitting - criterion) at node i. - - n_node_samples : array of int, shape [node_count] - n_node_samples[i] holds the number of training samples reaching node i. - - weighted_n_node_samples : array of double, shape [node_count] - weighted_n_node_samples[i] holds the weighted number of training samples - reaching node i. - """ - # Wrap for outside world. - # WARNING: these reference the current `nodes` and `value` buffers, which - # must not be freed by a subsequent memory allocation. - # (i.e. through `_resize` or `__setstate__`) - @property - def n_classes(self): - return sizet_ptr_to_ndarray(self.n_classes, self.n_outputs) - - @property - def children_left(self): - return self._get_node_ndarray()['left_child'][:self.node_count] - - @property - def children_right(self): - return self._get_node_ndarray()['right_child'][:self.node_count] - - @property - def n_leaves(self): - return np.sum(np.logical_and( - self.children_left == -1, - self.children_right == -1)) - - @property - def feature(self): - return self._get_node_ndarray()['feature'][:self.node_count] - - @property - def threshold(self): - return self._get_node_ndarray()['threshold'][:self.node_count] - - @property - def impurity(self): - return self._get_node_ndarray()['impurity'][:self.node_count] - - @property - def n_node_samples(self): - return self._get_node_ndarray()['n_node_samples'][:self.node_count] - - @property - def weighted_n_node_samples(self): - return self._get_node_ndarray()['weighted_n_node_samples'][:self.node_count] - - @property - def missing_go_to_left(self): - return self._get_node_ndarray()['missing_go_to_left'][:self.node_count] - - @property - def value(self): - return self._get_value_ndarray()[:self.node_count] - - # TODO: Convert n_classes to cython.integral memory view once - # https://github.com/cython/cython/issues/5243 is fixed - def __cinit__(self, int n_features, cnp.ndarray n_classes, int n_outputs): - """Constructor.""" - cdef SIZE_t dummy = 0 - size_t_dtype = np.array(dummy).dtype - - n_classes = _check_n_classes(n_classes, size_t_dtype) - - # Input/Output layout - self.n_features = n_features - self.n_outputs = n_outputs - self.n_classes = NULL - safe_realloc(&self.n_classes, n_outputs) - - self.max_n_classes = np.max(n_classes) - self.value_stride = n_outputs * self.max_n_classes - - cdef SIZE_t k - for k in range(n_outputs): - self.n_classes[k] = n_classes[k] - - # Inner structures - self.max_depth = 0 - self.node_count = 0 - self.capacity = 0 - self.value = NULL - self.nodes = NULL - - def __dealloc__(self): - """Destructor.""" - # Free all inner structures - free(self.n_classes) - free(self.value) - free(self.nodes) - - def __reduce__(self): - """Reduce re-implementation, for pickling.""" - return (Tree, (self.n_features, - sizet_ptr_to_ndarray(self.n_classes, self.n_outputs), - self.n_outputs), self.__getstate__()) - - def __getstate__(self): - """Getstate re-implementation, for pickling.""" - d = {} - # capacity is inferred during the __setstate__ using nodes - d["max_depth"] = self.max_depth - d["node_count"] = self.node_count - d["nodes"] = self._get_node_ndarray() - d["values"] = self._get_value_ndarray() - return d - - def __setstate__(self, d): - """Setstate re-implementation, for unpickling.""" - self.max_depth = d["max_depth"] - self.node_count = d["node_count"] - - if 'nodes' not in d: - raise ValueError('You have loaded Tree version which ' - 'cannot be imported') - - node_ndarray = d['nodes'] - value_ndarray = d['values'] - - value_shape = (node_ndarray.shape[0], self.n_outputs, - self.max_n_classes) - - node_ndarray = _check_node_ndarray(node_ndarray, expected_dtype=NODE_DTYPE) - value_ndarray = _check_value_ndarray( - value_ndarray, - expected_dtype=np.dtype(np.float64), - expected_shape=value_shape - ) - - self.capacity = node_ndarray.shape[0] - if self._resize_c(self.capacity) != 0: - raise MemoryError("resizing tree to %d" % self.capacity) - - memcpy(self.nodes, cnp.PyArray_DATA(node_ndarray), - self.capacity * sizeof(Node)) - memcpy(self.value, cnp.PyArray_DATA(value_ndarray), - self.capacity * self.value_stride * sizeof(double)) - - cdef int _resize(self, SIZE_t capacity) except -1 nogil: - """Resize all inner arrays to `capacity`, if `capacity` == -1, then - double the size of the inner arrays. - - Returns -1 in case of failure to allocate memory (and raise MemoryError) - or 0 otherwise. - """ - if self._resize_c(capacity) != 0: - # Acquire gil only if we need to raise - with gil: - raise MemoryError() - - cdef int _resize_c(self, SIZE_t capacity=INTPTR_MAX) except -1 nogil: - """Guts of _resize - - Returns -1 in case of failure to allocate memory (and raise MemoryError) - or 0 otherwise. - """ - if capacity == self.capacity and self.nodes != NULL: - return 0 - - if capacity == INTPTR_MAX: - if self.capacity == 0: - capacity = 3 # default initial value - else: - capacity = 2 * self.capacity - - safe_realloc(&self.nodes, capacity) - safe_realloc(&self.value, capacity * self.value_stride) - - # value memory is initialised to 0 to enable classifier argmax - if capacity > self.capacity: - memset((self.value + self.capacity * self.value_stride), 0, - (capacity - self.capacity) * self.value_stride * - sizeof(double)) - - # if capacity smaller than node_count, adjust the counter - if capacity < self.node_count: - self.node_count = capacity - - self.capacity = capacity - return 0 - - cdef SIZE_t _add_node(self, SIZE_t parent, bint is_left, bint is_leaf, - SIZE_t feature, double threshold, double impurity, - SIZE_t n_node_samples, - double weighted_n_node_samples, - unsigned char missing_go_to_left) except -1 nogil: - """Add a node to the tree. - - The new node registers itself as the child of its parent. - - Returns (size_t)(-1) on error. - """ - cdef SIZE_t node_id = self.node_count - - if node_id >= self.capacity: - if self._resize_c() != 0: - return INTPTR_MAX - - cdef Node* node = &self.nodes[node_id] - node.impurity = impurity - node.n_node_samples = n_node_samples - node.weighted_n_node_samples = weighted_n_node_samples - - if parent != _TREE_UNDEFINED: - if is_left: - self.nodes[parent].left_child = node_id - else: - self.nodes[parent].right_child = node_id - - if is_leaf: - node.left_child = _TREE_LEAF - node.right_child = _TREE_LEAF - node.feature = _TREE_UNDEFINED - node.threshold = _TREE_UNDEFINED - - else: - # left_child and right_child will be set later - node.feature = feature - node.threshold = threshold - node.missing_go_to_left = missing_go_to_left - - self.node_count += 1 - - return node_id - - cpdef cnp.ndarray predict(self, object X): - """Predict target for X.""" - out = self._get_value_ndarray().take(self.apply(X), axis=0, - mode='clip') - if self.n_outputs == 1: - out = out.reshape(X.shape[0], self.max_n_classes) - return out - - cpdef cnp.ndarray apply(self, object X): - """Finds the terminal region (=leaf node) for each sample in X.""" - if issparse(X): - return self._apply_sparse_csr(X) - else: - return self._apply_dense(X) - - cdef inline cnp.ndarray _apply_dense(self, object X): - """Finds the terminal region (=leaf node) for each sample in X.""" - - # Check input - if not isinstance(X, np.ndarray): - raise ValueError("X should be in np.ndarray format, got %s" - % type(X)) - - if X.dtype != DTYPE: - raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) - - # Extract input - cdef const DTYPE_t[:, :] X_ndarray = X - cdef SIZE_t n_samples = X.shape[0] - cdef DTYPE_t X_i_node_feature - - # Initialize output - cdef SIZE_t[:] out = np.zeros(n_samples, dtype=np.intp) - - # Initialize auxiliary data-structure - cdef Node* node = NULL - cdef SIZE_t i = 0 - - with nogil: - for i in range(n_samples): - node = self.nodes - # While node not a leaf - while node.left_child != _TREE_LEAF: - X_i_node_feature = X_ndarray[i, node.feature] - # ... and node.right_child != _TREE_LEAF: - if isnan(X_i_node_feature): - if node.missing_go_to_left: - node = &self.nodes[node.left_child] - else: - node = &self.nodes[node.right_child] - elif X_i_node_feature <= node.threshold: - node = &self.nodes[node.left_child] - else: - node = &self.nodes[node.right_child] - - out[i] = (node - self.nodes) # node offset - - return np.asarray(out) - - cdef inline cnp.ndarray _apply_sparse_csr(self, object X): - """Finds the terminal region (=leaf node) for each sample in sparse X. - """ - # Check input - if not isspmatrix_csr(X): - raise ValueError("X should be in csr_matrix format, got %s" - % type(X)) - - if X.dtype != DTYPE: - raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) - - # Extract input - cdef const DTYPE_t[:] X_data = X.data - cdef const INT32_t[:] X_indices = X.indices - cdef const INT32_t[:] X_indptr = X.indptr - - cdef SIZE_t n_samples = X.shape[0] - cdef SIZE_t n_features = X.shape[1] - - # Initialize output - cdef SIZE_t[:] out = np.zeros(n_samples, dtype=np.intp) - - # Initialize auxiliary data-structure - cdef DTYPE_t feature_value = 0. - cdef Node* node = NULL - cdef DTYPE_t* X_sample = NULL - cdef SIZE_t i = 0 - cdef INT32_t k = 0 - - # feature_to_sample as a data structure records the last seen sample - # for each feature; functionally, it is an efficient way to identify - # which features are nonzero in the present sample. - cdef SIZE_t* feature_to_sample = NULL - - safe_realloc(&X_sample, n_features) - safe_realloc(&feature_to_sample, n_features) - - with nogil: - memset(feature_to_sample, -1, n_features * sizeof(SIZE_t)) - - for i in range(n_samples): - node = self.nodes - - for k in range(X_indptr[i], X_indptr[i + 1]): - feature_to_sample[X_indices[k]] = i - X_sample[X_indices[k]] = X_data[k] - - # While node not a leaf - while node.left_child != _TREE_LEAF: - # ... and node.right_child != _TREE_LEAF: - if feature_to_sample[node.feature] == i: - feature_value = X_sample[node.feature] - - else: - feature_value = 0. - - if feature_value <= node.threshold: - node = &self.nodes[node.left_child] - else: - node = &self.nodes[node.right_child] - - out[i] = (node - self.nodes) # node offset - - # Free auxiliary arrays - free(X_sample) - free(feature_to_sample) - - return np.asarray(out) - - cpdef object decision_path(self, object X): - """Finds the decision path (=node) for each sample in X.""" - if issparse(X): - return self._decision_path_sparse_csr(X) - else: - return self._decision_path_dense(X) - - cdef inline object _decision_path_dense(self, object X): - """Finds the decision path (=node) for each sample in X.""" - - # Check input - if not isinstance(X, np.ndarray): - raise ValueError("X should be in np.ndarray format, got %s" - % type(X)) - - if X.dtype != DTYPE: - raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) - - # Extract input - cdef const DTYPE_t[:, :] X_ndarray = X - cdef SIZE_t n_samples = X.shape[0] - - # Initialize output - cdef SIZE_t[:] indptr = np.zeros(n_samples + 1, dtype=np.intp) - cdef SIZE_t[:] indices = np.zeros( - n_samples * (1 + self.max_depth), dtype=np.intp - ) - - # Initialize auxiliary data-structure - cdef Node* node = NULL - cdef SIZE_t i = 0 - - with nogil: - for i in range(n_samples): - node = self.nodes - indptr[i + 1] = indptr[i] - - # Add all external nodes - while node.left_child != _TREE_LEAF: - # ... and node.right_child != _TREE_LEAF: - indices[indptr[i + 1]] = (node - self.nodes) - indptr[i + 1] += 1 - - if X_ndarray[i, node.feature] <= node.threshold: - node = &self.nodes[node.left_child] - else: - node = &self.nodes[node.right_child] - - # Add the leave node - indices[indptr[i + 1]] = (node - self.nodes) - indptr[i + 1] += 1 - - indices = indices[:indptr[n_samples]] - cdef SIZE_t[:] data = np.ones(shape=len(indices), dtype=np.intp) - out = csr_matrix((data, indices, indptr), - shape=(n_samples, self.node_count)) - - return out - - cdef inline object _decision_path_sparse_csr(self, object X): - """Finds the decision path (=node) for each sample in X.""" - - # Check input - if not isspmatrix_csr(X): - raise ValueError("X should be in csr_matrix format, got %s" - % type(X)) - - if X.dtype != DTYPE: - raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) - - # Extract input - cdef const DTYPE_t[:] X_data = X.data - cdef const INT32_t[:] X_indices = X.indices - cdef const INT32_t[:] X_indptr = X.indptr - - cdef SIZE_t n_samples = X.shape[0] - cdef SIZE_t n_features = X.shape[1] - - # Initialize output - cdef SIZE_t[:] indptr = np.zeros(n_samples + 1, dtype=np.intp) - cdef SIZE_t[:] indices = np.zeros( - n_samples * (1 + self.max_depth), dtype=np.intp - ) - - # Initialize auxiliary data-structure - cdef DTYPE_t feature_value = 0. - cdef Node* node = NULL - cdef DTYPE_t* X_sample = NULL - cdef SIZE_t i = 0 - cdef INT32_t k = 0 - - # feature_to_sample as a data structure records the last seen sample - # for each feature; functionally, it is an efficient way to identify - # which features are nonzero in the present sample. - cdef SIZE_t* feature_to_sample = NULL - - safe_realloc(&X_sample, n_features) - safe_realloc(&feature_to_sample, n_features) - - with nogil: - memset(feature_to_sample, -1, n_features * sizeof(SIZE_t)) - - for i in range(n_samples): - node = self.nodes - indptr[i + 1] = indptr[i] - - for k in range(X_indptr[i], X_indptr[i + 1]): - feature_to_sample[X_indices[k]] = i - X_sample[X_indices[k]] = X_data[k] - - # While node not a leaf - while node.left_child != _TREE_LEAF: - # ... and node.right_child != _TREE_LEAF: - - indices[indptr[i + 1]] = (node - self.nodes) - indptr[i + 1] += 1 - - if feature_to_sample[node.feature] == i: - feature_value = X_sample[node.feature] - - else: - feature_value = 0. - - if feature_value <= node.threshold: - node = &self.nodes[node.left_child] - else: - node = &self.nodes[node.right_child] - - # Add the leave node - indices[indptr[i + 1]] = (node - self.nodes) - indptr[i + 1] += 1 - - # Free auxiliary arrays - free(X_sample) - free(feature_to_sample) - - indices = indices[:indptr[n_samples]] - cdef SIZE_t[:] data = np.ones(shape=len(indices), dtype=np.intp) - out = csr_matrix((data, indices, indptr), - shape=(n_samples, self.node_count)) - - return out - - cpdef compute_node_depths(self): - """Compute the depth of each node in a tree. - - .. versionadded:: 1.3 - - Returns - ------- - depths : ndarray of shape (self.node_count,), dtype=np.int64 - The depth of each node in the tree. - """ - cdef: - cnp.int64_t[::1] depths = np.empty(self.node_count, dtype=np.int64) - cnp.npy_intp[:] children_left = self.children_left - cnp.npy_intp[:] children_right = self.children_right - cnp.npy_intp node_id - cnp.npy_intp node_count = self.node_count - cnp.int64_t depth - - depths[0] = 1 # init root node - for node_id in range(node_count): - if children_left[node_id] != _TREE_LEAF: - depth = depths[node_id] + 1 - depths[children_left[node_id]] = depth - depths[children_right[node_id]] = depth - - return depths.base - - cpdef compute_feature_importances(self, normalize=True): - """Computes the importance of each feature (aka variable).""" - cdef Node* left - cdef Node* right - cdef Node* nodes = self.nodes - cdef Node* node = nodes - cdef Node* end_node = node + self.node_count - - cdef double normalizer = 0. - - cdef cnp.float64_t[:] importances = np.zeros(self.n_features) - - with nogil: - while node != end_node: - if node.left_child != _TREE_LEAF: - # ... and node.right_child != _TREE_LEAF: - left = &nodes[node.left_child] - right = &nodes[node.right_child] - - importances[node.feature] += ( - node.weighted_n_node_samples * node.impurity - - left.weighted_n_node_samples * left.impurity - - right.weighted_n_node_samples * right.impurity) - node += 1 - - for i in range(self.n_features): - importances[i] /= nodes[0].weighted_n_node_samples - - if normalize: - normalizer = np.sum(importances) - - if normalizer > 0.0: - # Avoid dividing by zero (e.g., when root is pure) - for i in range(self.n_features): - importances[i] /= normalizer - - return np.asarray(importances) - - cdef cnp.ndarray _get_value_ndarray(self): - """Wraps value as a 3-d NumPy array. - - The array keeps a reference to this Tree, which manages the underlying - memory. - """ - cdef cnp.npy_intp shape[3] - shape[0] = self.node_count - shape[1] = self.n_outputs - shape[2] = self.max_n_classes - cdef cnp.ndarray arr - arr = cnp.PyArray_SimpleNewFromData(3, shape, cnp.NPY_DOUBLE, self.value) - Py_INCREF(self) - if PyArray_SetBaseObject(arr, self) < 0: - raise ValueError("Can't initialize array.") - return arr - - cdef cnp.ndarray _get_node_ndarray(self): - """Wraps nodes as a NumPy struct array. - - The array keeps a reference to this Tree, which manages the underlying - memory. Individual fields are publicly accessible as properties of the - Tree. - """ - cdef cnp.npy_intp shape[1] - shape[0] = self.node_count - cdef cnp.npy_intp strides[1] - strides[0] = sizeof(Node) - cdef cnp.ndarray arr - Py_INCREF(NODE_DTYPE) - arr = PyArray_NewFromDescr( cnp.ndarray, - NODE_DTYPE, 1, shape, - strides, self.nodes, - cnp.NPY_ARRAY_DEFAULT, None) - Py_INCREF(self) - if PyArray_SetBaseObject(arr, self) < 0: - raise ValueError("Can't initialize array.") - return arr - - def compute_partial_dependence(self, DTYPE_t[:, ::1] X, - int[::1] target_features, - double[::1] out): - """Partial dependence of the response on the ``target_feature`` set. - - For each sample in ``X`` a tree traversal is performed. - Each traversal starts from the root with weight 1.0. - - At each non-leaf node that splits on a target feature, either - the left child or the right child is visited based on the feature - value of the current sample, and the weight is not modified. - At each non-leaf node that splits on a complementary feature, - both children are visited and the weight is multiplied by the fraction - of training samples which went to each child. - - At each leaf, the value of the node is multiplied by the current - weight (weights sum to 1 for all visited terminal nodes). - - Parameters - ---------- - X : view on 2d ndarray, shape (n_samples, n_target_features) - The grid points on which the partial dependence should be - evaluated. - target_features : view on 1d ndarray, shape (n_target_features) - The set of target features for which the partial dependence - should be evaluated. - out : view on 1d ndarray, shape (n_samples) - The value of the partial dependence function on each grid - point. - """ - cdef: - double[::1] weight_stack = np.zeros(self.node_count, - dtype=np.float64) - SIZE_t[::1] node_idx_stack = np.zeros(self.node_count, - dtype=np.intp) - SIZE_t sample_idx - SIZE_t feature_idx - int stack_size - double left_sample_frac - double current_weight - double total_weight # used for sanity check only - Node *current_node # use a pointer to avoid copying attributes - SIZE_t current_node_idx - bint is_target_feature - SIZE_t _TREE_LEAF = TREE_LEAF # to avoid python interactions - - for sample_idx in range(X.shape[0]): - # init stacks for current sample - stack_size = 1 - node_idx_stack[0] = 0 # root node - weight_stack[0] = 1 # all the samples are in the root node - total_weight = 0 - - while stack_size > 0: - # pop the stack - stack_size -= 1 - current_node_idx = node_idx_stack[stack_size] - current_node = &self.nodes[current_node_idx] - - if current_node.left_child == _TREE_LEAF: - # leaf node - out[sample_idx] += (weight_stack[stack_size] * - self.value[current_node_idx]) - total_weight += weight_stack[stack_size] - else: - # non-leaf node - - # determine if the split feature is a target feature - is_target_feature = False - for feature_idx in range(target_features.shape[0]): - if target_features[feature_idx] == current_node.feature: - is_target_feature = True - break - - if is_target_feature: - # In this case, we push left or right child on stack - if X[sample_idx, feature_idx] <= current_node.threshold: - node_idx_stack[stack_size] = current_node.left_child - else: - node_idx_stack[stack_size] = current_node.right_child - stack_size += 1 - else: - # In this case, we push both children onto the stack, - # and give a weight proportional to the number of - # samples going through each branch. - - # push left child - node_idx_stack[stack_size] = current_node.left_child - left_sample_frac = ( - self.nodes[current_node.left_child].weighted_n_node_samples / - current_node.weighted_n_node_samples) - current_weight = weight_stack[stack_size] - weight_stack[stack_size] = current_weight * left_sample_frac - stack_size += 1 - - # push right child - node_idx_stack[stack_size] = current_node.right_child - weight_stack[stack_size] = ( - current_weight * (1 - left_sample_frac)) - stack_size += 1 - - # Sanity check. Should never happen. - if not (0.999 < total_weight < 1.001): - raise ValueError("Total weight should be 1.0 but was %.9f" % - total_weight) - - -def _check_n_classes(n_classes, expected_dtype): - if n_classes.ndim != 1: - raise ValueError( - f"Wrong dimensions for n_classes from the pickle: " - f"expected 1, got {n_classes.ndim}" - ) - - if n_classes.dtype == expected_dtype: - return n_classes - - # Handles both different endianness and different bitness - if n_classes.dtype.kind == "i" and n_classes.dtype.itemsize in [4, 8]: - return n_classes.astype(expected_dtype, casting="same_kind") - - raise ValueError( - "n_classes from the pickle has an incompatible dtype:\n" - f"- expected: {expected_dtype}\n" - f"- got: {n_classes.dtype}" - ) - - -def _check_value_ndarray(value_ndarray, expected_dtype, expected_shape): - if value_ndarray.shape != expected_shape: - raise ValueError( - "Wrong shape for value array from the pickle: " - f"expected {expected_shape}, got {value_ndarray.shape}" - ) - - if not value_ndarray.flags.c_contiguous: - raise ValueError( - "value array from the pickle should be a C-contiguous array" - ) - - if value_ndarray.dtype == expected_dtype: - return value_ndarray - - # Handles different endianness - if value_ndarray.dtype.str.endswith('f8'): - return value_ndarray.astype(expected_dtype, casting='equiv') - - raise ValueError( - "value array from the pickle has an incompatible dtype:\n" - f"- expected: {expected_dtype}\n" - f"- got: {value_ndarray.dtype}" - ) - - -def _dtype_to_dict(dtype): - return {name: dt.str for name, (dt, *rest) in dtype.fields.items()} - - -def _dtype_dict_with_modified_bitness(dtype_dict): - # field names in Node struct with SIZE_t types (see sklearn/tree/_tree.pxd) - indexing_field_names = ["left_child", "right_child", "feature", "n_node_samples"] - - expected_dtype_size = str(struct.calcsize("P")) - allowed_dtype_size = "8" if expected_dtype_size == "4" else "4" - - allowed_dtype_dict = dtype_dict.copy() - for name in indexing_field_names: - allowed_dtype_dict[name] = allowed_dtype_dict[name].replace( - expected_dtype_size, allowed_dtype_size - ) - - return allowed_dtype_dict - - -def _all_compatible_dtype_dicts(dtype): - # The Cython code for decision trees uses platform-specific SIZE_t - # typed indexing fields that correspond to either i4 or i8 dtypes for - # the matching fields in the numpy array depending on the bitness of - # the platform (32 bit or 64 bit respectively). - # - # We need to cast the indexing fields of the NODE_DTYPE-dtyped array at - # pickle load time to enable cross-bitness deployment scenarios. We - # typically want to make it possible to run the expensive fit method of - # a tree estimator on a 64 bit server platform, pickle the estimator - # for deployment and run the predict method of a low power 32 bit edge - # platform. - # - # A similar thing happens for endianness, the machine where the pickle was - # saved can have a different endianness than the machine where the pickle - # is loaded - - dtype_dict = _dtype_to_dict(dtype) - dtype_dict_with_modified_bitness = _dtype_dict_with_modified_bitness(dtype_dict) - dtype_dict_with_modified_endianness = _dtype_to_dict(dtype.newbyteorder()) - dtype_dict_with_modified_bitness_and_endianness = _dtype_dict_with_modified_bitness( - dtype_dict_with_modified_endianness - ) - - return [ - dtype_dict, - dtype_dict_with_modified_bitness, - dtype_dict_with_modified_endianness, - dtype_dict_with_modified_bitness_and_endianness, - ] - - -def _check_node_ndarray(node_ndarray, expected_dtype): - if node_ndarray.ndim != 1: - raise ValueError( - "Wrong dimensions for node array from the pickle: " - f"expected 1, got {node_ndarray.ndim}" - ) - - if not node_ndarray.flags.c_contiguous: - raise ValueError( - "node array from the pickle should be a C-contiguous array" - ) - - node_ndarray_dtype = node_ndarray.dtype - if node_ndarray_dtype == expected_dtype: - return node_ndarray - - node_ndarray_dtype_dict = _dtype_to_dict(node_ndarray_dtype) - all_compatible_dtype_dicts = _all_compatible_dtype_dicts(expected_dtype) - - if node_ndarray_dtype_dict not in all_compatible_dtype_dicts: - raise ValueError( - "node array from the pickle has an incompatible dtype:\n" - f"- expected: {expected_dtype}\n" - f"- got : {node_ndarray_dtype}" - ) - - return node_ndarray.astype(expected_dtype, casting="same_kind") - - -# ============================================================================= -# Build Pruned Tree -# ============================================================================= - - -cdef class _CCPPruneController: - """Base class used by build_pruned_tree_ccp and ccp_pruning_path - to control pruning. - """ - cdef bint stop_pruning(self, DOUBLE_t effective_alpha) noexcept nogil: - """Return 1 to stop pruning and 0 to continue pruning""" - return 0 - - cdef void save_metrics(self, DOUBLE_t effective_alpha, - DOUBLE_t subtree_impurities) noexcept nogil: - """Save metrics when pruning""" - pass - - cdef void after_pruning(self, unsigned char[:] in_subtree) noexcept nogil: - """Called after pruning""" - pass - - -cdef class _AlphaPruner(_CCPPruneController): - """Use alpha to control when to stop pruning.""" - cdef DOUBLE_t ccp_alpha - cdef SIZE_t capacity - - def __cinit__(self, DOUBLE_t ccp_alpha): - self.ccp_alpha = ccp_alpha - self.capacity = 0 - - cdef bint stop_pruning(self, DOUBLE_t effective_alpha) noexcept nogil: - # The subtree on the previous iteration has the greatest ccp_alpha - # less than or equal to self.ccp_alpha - return self.ccp_alpha < effective_alpha - - cdef void after_pruning(self, unsigned char[:] in_subtree) noexcept nogil: - """Updates the number of leaves in subtree""" - for i in range(in_subtree.shape[0]): - if in_subtree[i]: - self.capacity += 1 - - -cdef class _PathFinder(_CCPPruneController): - """Record metrics used to return the cost complexity path.""" - cdef DOUBLE_t[:] ccp_alphas - cdef DOUBLE_t[:] impurities - cdef UINT32_t count - - def __cinit__(self, int node_count): - self.ccp_alphas = np.zeros(shape=(node_count), dtype=np.float64) - self.impurities = np.zeros(shape=(node_count), dtype=np.float64) - self.count = 0 - - cdef void save_metrics(self, - DOUBLE_t effective_alpha, - DOUBLE_t subtree_impurities) noexcept nogil: - self.ccp_alphas[self.count] = effective_alpha - self.impurities[self.count] = subtree_impurities - self.count += 1 - - -cdef struct CostComplexityPruningRecord: - SIZE_t node_idx - SIZE_t parent - -cdef _cost_complexity_prune(unsigned char[:] leaves_in_subtree, # OUT - Tree orig_tree, - _CCPPruneController controller): - """Perform cost complexity pruning. - - This function takes an already grown tree, `orig_tree` and outputs a - boolean mask `leaves_in_subtree` which are the leaves in the pruned tree. - During the pruning process, the controller is passed the effective alpha and - the subtree impurities. Furthermore, the controller signals when to stop - pruning. - - Parameters - ---------- - leaves_in_subtree : unsigned char[:] - Output for leaves of subtree - orig_tree : Tree - Original tree - ccp_controller : _CCPPruneController - Cost complexity controller - """ - - cdef: - SIZE_t i - SIZE_t n_nodes = orig_tree.node_count - # prior probability using weighted samples - DOUBLE_t[:] weighted_n_node_samples = orig_tree.weighted_n_node_samples - DOUBLE_t total_sum_weights = weighted_n_node_samples[0] - DOUBLE_t[:] impurity = orig_tree.impurity - # weighted impurity of each node - DOUBLE_t[:] r_node = np.empty(shape=n_nodes, dtype=np.float64) - - SIZE_t[:] child_l = orig_tree.children_left - SIZE_t[:] child_r = orig_tree.children_right - SIZE_t[:] parent = np.zeros(shape=n_nodes, dtype=np.intp) - - stack[CostComplexityPruningRecord] ccp_stack - CostComplexityPruningRecord stack_record - SIZE_t node_idx - stack[SIZE_t] node_indices_stack - - SIZE_t[:] n_leaves = np.zeros(shape=n_nodes, dtype=np.intp) - DOUBLE_t[:] r_branch = np.zeros(shape=n_nodes, dtype=np.float64) - DOUBLE_t current_r - SIZE_t leaf_idx - SIZE_t parent_idx - - # candidate nodes that can be pruned - unsigned char[:] candidate_nodes = np.zeros(shape=n_nodes, - dtype=np.uint8) - # nodes in subtree - unsigned char[:] in_subtree = np.ones(shape=n_nodes, dtype=np.uint8) - SIZE_t pruned_branch_node_idx - DOUBLE_t subtree_alpha - DOUBLE_t effective_alpha - SIZE_t n_pruned_leaves - DOUBLE_t r_diff - DOUBLE_t max_float64 = np.finfo(np.float64).max - - # find parent node ids and leaves - with nogil: - - for i in range(r_node.shape[0]): - r_node[i] = ( - weighted_n_node_samples[i] * impurity[i] / total_sum_weights) - - # Push the root node - ccp_stack.push({"node_idx": 0, "parent": _TREE_UNDEFINED}) - - while not ccp_stack.empty(): - stack_record = ccp_stack.top() - ccp_stack.pop() - - node_idx = stack_record.node_idx - parent[node_idx] = stack_record.parent - - if child_l[node_idx] == _TREE_LEAF: - # ... and child_r[node_idx] == _TREE_LEAF: - leaves_in_subtree[node_idx] = 1 - else: - ccp_stack.push({"node_idx": child_l[node_idx], "parent": node_idx}) - ccp_stack.push({"node_idx": child_r[node_idx], "parent": node_idx}) - - # computes number of leaves in all branches and the overall impurity of - # the branch. The overall impurity is the sum of r_node in its leaves. - for leaf_idx in range(leaves_in_subtree.shape[0]): - if not leaves_in_subtree[leaf_idx]: - continue - r_branch[leaf_idx] = r_node[leaf_idx] - - # bubble up values to ancestor nodes - current_r = r_node[leaf_idx] - while leaf_idx != 0: - parent_idx = parent[leaf_idx] - r_branch[parent_idx] += current_r - n_leaves[parent_idx] += 1 - leaf_idx = parent_idx - - for i in range(leaves_in_subtree.shape[0]): - candidate_nodes[i] = not leaves_in_subtree[i] - - # save metrics before pruning - controller.save_metrics(0.0, r_branch[0]) - - # while root node is not a leaf - while candidate_nodes[0]: - - # computes ccp_alpha for subtrees and finds the minimal alpha - effective_alpha = max_float64 - for i in range(n_nodes): - if not candidate_nodes[i]: - continue - subtree_alpha = (r_node[i] - r_branch[i]) / (n_leaves[i] - 1) - if subtree_alpha < effective_alpha: - effective_alpha = subtree_alpha - pruned_branch_node_idx = i - - if controller.stop_pruning(effective_alpha): - break - - node_indices_stack.push(pruned_branch_node_idx) - - # descendants of branch are not in subtree - while not node_indices_stack.empty(): - node_idx = node_indices_stack.top() - node_indices_stack.pop() - - if not in_subtree[node_idx]: - continue # branch has already been marked for pruning - candidate_nodes[node_idx] = 0 - leaves_in_subtree[node_idx] = 0 - in_subtree[node_idx] = 0 - - if child_l[node_idx] != _TREE_LEAF: - # ... and child_r[node_idx] != _TREE_LEAF: - node_indices_stack.push(child_l[node_idx]) - node_indices_stack.push(child_r[node_idx]) - leaves_in_subtree[pruned_branch_node_idx] = 1 - in_subtree[pruned_branch_node_idx] = 1 - - # updates number of leaves - n_pruned_leaves = n_leaves[pruned_branch_node_idx] - 1 - n_leaves[pruned_branch_node_idx] = 0 - - # computes the increase in r_branch to bubble up - r_diff = r_node[pruned_branch_node_idx] - r_branch[pruned_branch_node_idx] - r_branch[pruned_branch_node_idx] = r_node[pruned_branch_node_idx] - - # bubble up values to ancestors - node_idx = parent[pruned_branch_node_idx] - while node_idx != _TREE_UNDEFINED: - n_leaves[node_idx] -= n_pruned_leaves - r_branch[node_idx] += r_diff - node_idx = parent[node_idx] - - controller.save_metrics(effective_alpha, r_branch[0]) - - controller.after_pruning(in_subtree) - - -def _build_pruned_tree_ccp( - Tree tree, # OUT - Tree orig_tree, - DOUBLE_t ccp_alpha -): - """Build a pruned tree from the original tree using cost complexity - pruning. - - The values and nodes from the original tree are copied into the pruned - tree. - - Parameters - ---------- - tree : Tree - Location to place the pruned tree - orig_tree : Tree - Original tree - ccp_alpha : positive double - Complexity parameter. The subtree with the largest cost complexity - that is smaller than ``ccp_alpha`` will be chosen. By default, - no pruning is performed. - """ - - cdef: - SIZE_t n_nodes = orig_tree.node_count - unsigned char[:] leaves_in_subtree = np.zeros( - shape=n_nodes, dtype=np.uint8) - - pruning_controller = _AlphaPruner(ccp_alpha=ccp_alpha) - - _cost_complexity_prune(leaves_in_subtree, orig_tree, pruning_controller) - - _build_pruned_tree(tree, orig_tree, leaves_in_subtree, - pruning_controller.capacity) - - -def ccp_pruning_path(Tree orig_tree): - """Computes the cost complexity pruning path. - - Parameters - ---------- - tree : Tree - Original tree. - - Returns - ------- - path_info : dict - Information about pruning path with attributes: - - ccp_alphas : ndarray - Effective alphas of subtree during pruning. - - impurities : ndarray - Sum of the impurities of the subtree leaves for the - corresponding alpha value in ``ccp_alphas``. - """ - cdef: - unsigned char[:] leaves_in_subtree = np.zeros( - shape=orig_tree.node_count, dtype=np.uint8) - - path_finder = _PathFinder(orig_tree.node_count) - - _cost_complexity_prune(leaves_in_subtree, orig_tree, path_finder) - - cdef: - UINT32_t total_items = path_finder.count - DOUBLE_t[:] ccp_alphas = np.empty(shape=total_items, dtype=np.float64) - DOUBLE_t[:] impurities = np.empty(shape=total_items, dtype=np.float64) - UINT32_t count = 0 - - while count < total_items: - ccp_alphas[count] = path_finder.ccp_alphas[count] - impurities[count] = path_finder.impurities[count] - count += 1 - - return { - 'ccp_alphas': np.asarray(ccp_alphas), - 'impurities': np.asarray(impurities), - } - - -cdef struct BuildPrunedRecord: - SIZE_t start - SIZE_t depth - SIZE_t parent - bint is_left - -cdef _build_pruned_tree( - Tree tree, # OUT - Tree orig_tree, - const unsigned char[:] leaves_in_subtree, - SIZE_t capacity -): - """Build a pruned tree. - - Build a pruned tree from the original tree by transforming the nodes in - ``leaves_in_subtree`` into leaves. - - Parameters - ---------- - tree : Tree - Location to place the pruned tree - orig_tree : Tree - Original tree - leaves_in_subtree : unsigned char memoryview, shape=(node_count, ) - Boolean mask for leaves to include in subtree - capacity : SIZE_t - Number of nodes to initially allocate in pruned tree - """ - tree._resize(capacity) - - cdef: - SIZE_t orig_node_id - SIZE_t new_node_id - SIZE_t depth - SIZE_t parent - bint is_left - bint is_leaf - - # value_stride for original tree and new tree are the same - SIZE_t value_stride = orig_tree.value_stride - SIZE_t max_depth_seen = -1 - int rc = 0 - Node* node - double* orig_value_ptr - double* new_value_ptr - - stack[BuildPrunedRecord] prune_stack - BuildPrunedRecord stack_record - - with nogil: - # push root node onto stack - prune_stack.push({"start": 0, "depth": 0, "parent": _TREE_UNDEFINED, "is_left": 0}) - - while not prune_stack.empty(): - stack_record = prune_stack.top() - prune_stack.pop() - - orig_node_id = stack_record.start - depth = stack_record.depth - parent = stack_record.parent - is_left = stack_record.is_left - - is_leaf = leaves_in_subtree[orig_node_id] - node = &orig_tree.nodes[orig_node_id] - - new_node_id = tree._add_node( - parent, is_left, is_leaf, node.feature, node.threshold, - node.impurity, node.n_node_samples, - node.weighted_n_node_samples, node.missing_go_to_left) - - if new_node_id == INTPTR_MAX: - rc = -1 - break - - # copy value from original tree to new tree - orig_value_ptr = orig_tree.value + value_stride * orig_node_id - new_value_ptr = tree.value + value_stride * new_node_id - memcpy(new_value_ptr, orig_value_ptr, sizeof(double) * value_stride) - - if not is_leaf: - # Push right child on stack - prune_stack.push({"start": node.right_child, "depth": depth + 1, - "parent": new_node_id, "is_left": 0}) - # push left child on stack - prune_stack.push({"start": node.left_child, "depth": depth + 1, - "parent": new_node_id, "is_left": 1}) - - if depth > max_depth_seen: - max_depth_seen = depth - - if rc >= 0: - tree.max_depth = max_depth_seen - if rc == -1: - raise MemoryError("pruning tree") \ No newline at end of file diff --git a/ivy/functional/frontends/sklearn/_tree.pyx b/ivy/functional/frontends/sklearn/_tree.pyx deleted file mode 100644 index f436c919f8bc2..0000000000000 --- a/ivy/functional/frontends/sklearn/_tree.pyx +++ /dev/null @@ -1,1833 +0,0 @@ -# Authors: Gilles Louppe -# Peter Prettenhofer -# Brian Holt -# Noel Dawe -# Satrajit Gosh -# Lars Buitinck -# Arnaud Joly -# Joel Nothman -# Fares Hedayati -# Jacob Schreiber -# Nelson Liu -# -# License: BSD 3 clause - -from cpython cimport Py_INCREF, PyObject, PyTypeObject - -from libc.stdlib cimport free -from libc.string cimport memcpy -from libc.string cimport memset -from libc.stdint cimport INTPTR_MAX -from libc.math cimport isnan -from libcpp.vector cimport vector -from libcpp.algorithm cimport pop_heap -from libcpp.algorithm cimport push_heap -from libcpp cimport bool - -import struct - -import numpy as np -cimport numpy as cnp -cnp.import_array() - -from scipy.sparse import issparse -from scipy.sparse import csr_matrix -from scipy.sparse import isspmatrix_csr - -from ._utils cimport safe_realloc -from ._utils cimport sizet_ptr_to_ndarray - -cdef extern from "numpy/arrayobject.h": - object PyArray_NewFromDescr(PyTypeObject* subtype, cnp.dtype descr, - int nd, cnp.npy_intp* dims, - cnp.npy_intp* strides, - void* data, int flags, object obj) - int PyArray_SetBaseObject(cnp.ndarray arr, PyObject* obj) - -cdef extern from "" namespace "std" nogil: - cdef cppclass stack[T]: - ctypedef T value_type - stack() except + - bint empty() - void pop() - void push(T&) except + # Raise c++ exception for bad_alloc -> MemoryError - T& top() - -# ============================================================================= -# Types and constants -# ============================================================================= - -from numpy import float32 as DTYPE -from numpy import float64 as DOUBLE - -cdef double INFINITY = np.inf -cdef double EPSILON = np.finfo('double').eps - -# Some handy constants (BestFirstTreeBuilder) -cdef int IS_FIRST = 1 -cdef int IS_NOT_FIRST = 0 -cdef int IS_LEFT = 1 -cdef int IS_NOT_LEFT = 0 - -TREE_LEAF = -1 -TREE_UNDEFINED = -2 -cdef SIZE_t _TREE_LEAF = TREE_LEAF -cdef SIZE_t _TREE_UNDEFINED = TREE_UNDEFINED - -# Build the corresponding numpy dtype for Node. -# This works by casting `dummy` to an array of Node of length 1, which numpy -# can construct a `dtype`-object for. See https://stackoverflow.com/q/62448946 -# for a more detailed explanation. -cdef Node dummy -NODE_DTYPE = np.asarray((&dummy)).dtype - -# ============================================================================= -# TreeBuilder -# ============================================================================= - -cdef class TreeBuilder: - """Interface for different tree building strategies.""" - - cpdef build( - self, - Tree tree, - object X, - const DOUBLE_t[:, ::1] y, - const DOUBLE_t[:] sample_weight=None, - const unsigned char[::1] missing_values_in_feature_mask=None, - ): - """Build a decision tree from the training set (X, y).""" - pass - - cdef inline _check_input( - self, - object X, - const DOUBLE_t[:, ::1] y, - const DOUBLE_t[:] sample_weight, - ): - """Check input dtype, layout and format""" - if issparse(X): - X = X.tocsc() - X.sort_indices() - - if X.data.dtype != DTYPE: - X.data = np.ascontiguousarray(X.data, dtype=DTYPE) - - if X.indices.dtype != np.int32 or X.indptr.dtype != np.int32: - raise ValueError("No support for np.int64 index based " - "sparse matrices") - - elif X.dtype != DTYPE: - # since we have to copy we will make it fortran for efficiency - X = np.asfortranarray(X, dtype=DTYPE) - - # TODO: This check for y seems to be redundant, as it is also - # present in the BaseDecisionTree's fit method, and therefore - # can be removed. - if y.base.dtype != DOUBLE or not y.base.flags.contiguous: - y = np.ascontiguousarray(y, dtype=DOUBLE) - - if ( - sample_weight is not None and - ( - sample_weight.base.dtype != DOUBLE or - not sample_weight.base.flags.contiguous - ) - ): - sample_weight = np.asarray(sample_weight, dtype=DOUBLE, order="C") - - return X, y, sample_weight - -# Depth first builder --------------------------------------------------------- -# A record on the stack for depth-first tree growing -cdef struct StackRecord: - SIZE_t start - SIZE_t end - SIZE_t depth - SIZE_t parent - bint is_left - double impurity - SIZE_t n_constant_features - - - - - - - -cdef class DepthFirstTreeBuilder(TreeBuilder): - """Build a decision tree in depth-first fashion.""" - - def __cinit__(self, Splitter splitter, SIZE_t min_samples_split, - SIZE_t min_samples_leaf, double min_weight_leaf, - SIZE_t max_depth, double min_impurity_decrease): - self.splitter = splitter - self.min_samples_split = min_samples_split - self.min_samples_leaf = min_samples_leaf - self.min_weight_leaf = min_weight_leaf - self.max_depth = max_depth - self.min_impurity_decrease = min_impurity_decrease - - cpdef build( - self, - Tree tree, - object X, - const DOUBLE_t[:, ::1] y, - const DOUBLE_t[:] sample_weight=None, - const unsigned char[::1] missing_values_in_feature_mask=None, - ): - """Build a decision tree from the training set (X, y).""" - - # check input - X, y, sample_weight = self._check_input(X, y, sample_weight) - - # Initial capacity - cdef int init_capacity - - if tree.max_depth <= 10: - init_capacity = (2 ** (tree.max_depth + 1)) - 1 - else: - init_capacity = 2047 - - tree._resize(init_capacity) - - # Parameters - cdef Splitter splitter = self.splitter - cdef SIZE_t max_depth = self.max_depth - cdef SIZE_t min_samples_leaf = self.min_samples_leaf - cdef double min_weight_leaf = self.min_weight_leaf - cdef SIZE_t min_samples_split = self.min_samples_split - cdef double min_impurity_decrease = self.min_impurity_decrease - - # Recursive partition (without actual recursion) - splitter.init(X, y, sample_weight, missing_values_in_feature_mask) - - cdef SIZE_t start - cdef SIZE_t end - cdef SIZE_t depth - cdef SIZE_t parent - cdef bint is_left - cdef SIZE_t n_node_samples = splitter.n_samples - cdef double weighted_n_node_samples - cdef SplitRecord split - cdef SIZE_t node_id - - cdef double impurity = INFINITY - cdef SIZE_t n_constant_features - cdef bint is_leaf - cdef bint first = 1 - cdef SIZE_t max_depth_seen = -1 - cdef int rc = 0 - - cdef stack[StackRecord] builder_stack - cdef StackRecord stack_record - - with nogil: - # push root node onto stack - builder_stack.push({ - "start": 0, - "end": n_node_samples, - "depth": 0, - "parent": _TREE_UNDEFINED, - "is_left": 0, - "impurity": INFINITY, - "n_constant_features": 0}) - - while not builder_stack.empty(): - stack_record = builder_stack.top() - builder_stack.pop() - - start = stack_record.start - end = stack_record.end - depth = stack_record.depth - parent = stack_record.parent - is_left = stack_record.is_left - impurity = stack_record.impurity - n_constant_features = stack_record.n_constant_features - - n_node_samples = end - start - splitter.node_reset(start, end, &weighted_n_node_samples) - - is_leaf = (depth >= max_depth or - n_node_samples < min_samples_split or - n_node_samples < 2 * min_samples_leaf or - weighted_n_node_samples < 2 * min_weight_leaf) - - if first: - impurity = splitter.node_impurity() - first = 0 - - # impurity == 0 with tolerance due to rounding errors - is_leaf = is_leaf or impurity <= EPSILON - - if not is_leaf: - splitter.node_split(impurity, &split, &n_constant_features) - # If EPSILON=0 in the below comparison, float precision - # issues stop splitting, producing trees that are - # dissimilar to v0.18 - is_leaf = (is_leaf or split.pos >= end or - (split.improvement + EPSILON < - min_impurity_decrease)) - - node_id = tree._add_node(parent, is_left, is_leaf, split.feature, - split.threshold, impurity, n_node_samples, - weighted_n_node_samples, - split.missing_go_to_left) - - if node_id == INTPTR_MAX: - rc = -1 - break - - # Store value for all nodes, to facilitate tree/model - # inspection and interpretation - splitter.node_value(tree.value + node_id * tree.value_stride) - - if not is_leaf: - # Push right child on stack - builder_stack.push({ - "start": split.pos, - "end": end, - "depth": depth + 1, - "parent": node_id, - "is_left": 0, - "impurity": split.impurity_right, - "n_constant_features": n_constant_features}) - - # Push left child on stack - builder_stack.push({ - "start": start, - "end": split.pos, - "depth": depth + 1, - "parent": node_id, - "is_left": 1, - "impurity": split.impurity_left, - "n_constant_features": n_constant_features}) - - if depth > max_depth_seen: - max_depth_seen = depth - - if rc >= 0: - rc = tree._resize_c(tree.node_count) - - if rc >= 0: - tree.max_depth = max_depth_seen - if rc == -1: - raise MemoryError() - - -# Best first builder ---------------------------------------------------------- -cdef struct FrontierRecord: - # Record of information of a Node, the frontier for a split. Those records are - # maintained in a heap to access the Node with the best improvement in impurity, - # allowing growing trees greedily on this improvement. - SIZE_t node_id - SIZE_t start - SIZE_t end - SIZE_t pos - SIZE_t depth - bint is_leaf - double impurity - double impurity_left - double impurity_right - double improvement - -cdef inline bool _compare_records( - const FrontierRecord& left, - const FrontierRecord& right, -): - return left.improvement < right.improvement - -cdef inline void _add_to_frontier( - FrontierRecord rec, - vector[FrontierRecord]& frontier, -) noexcept nogil: - """Adds record `rec` to the priority queue `frontier`.""" - frontier.push_back(rec) - push_heap(frontier.begin(), frontier.end(), &_compare_records) - - -cdef class BestFirstTreeBuilder(TreeBuilder): - """Build a decision tree in best-first fashion. - - The best node to expand is given by the node at the frontier that has the - highest impurity improvement. - """ - cdef SIZE_t max_leaf_nodes - - def __cinit__(self, Splitter splitter, SIZE_t min_samples_split, - SIZE_t min_samples_leaf, min_weight_leaf, - SIZE_t max_depth, SIZE_t max_leaf_nodes, - double min_impurity_decrease): - self.splitter = splitter - self.min_samples_split = min_samples_split - self.min_samples_leaf = min_samples_leaf - self.min_weight_leaf = min_weight_leaf - self.max_depth = max_depth - self.max_leaf_nodes = max_leaf_nodes - self.min_impurity_decrease = min_impurity_decrease - - cpdef build( - self, - Tree tree, - object X, - const DOUBLE_t[:, ::1] y, - const DOUBLE_t[:] sample_weight=None, - const unsigned char[::1] missing_values_in_feature_mask=None, - ): - """Build a decision tree from the training set (X, y).""" - - # check input - X, y, sample_weight = self._check_input(X, y, sample_weight) - - # Parameters - cdef Splitter splitter = self.splitter - cdef SIZE_t max_leaf_nodes = self.max_leaf_nodes - - # Recursive partition (without actual recursion) - splitter.init(X, y, sample_weight, missing_values_in_feature_mask) - - cdef vector[FrontierRecord] frontier - cdef FrontierRecord record - cdef FrontierRecord split_node_left - cdef FrontierRecord split_node_right - - cdef SIZE_t n_node_samples = splitter.n_samples - cdef SIZE_t max_split_nodes = max_leaf_nodes - 1 - cdef bint is_leaf - cdef SIZE_t max_depth_seen = -1 - cdef int rc = 0 - cdef Node* node - - # Initial capacity - cdef SIZE_t init_capacity = max_split_nodes + max_leaf_nodes - tree._resize(init_capacity) - - with nogil: - # add root to frontier - rc = self._add_split_node(splitter, tree, 0, n_node_samples, - INFINITY, IS_FIRST, IS_LEFT, NULL, 0, - &split_node_left) - if rc >= 0: - _add_to_frontier(split_node_left, frontier) - - while not frontier.empty(): - pop_heap(frontier.begin(), frontier.end(), &_compare_records) - record = frontier.back() - frontier.pop_back() - - node = &tree.nodes[record.node_id] - is_leaf = (record.is_leaf or max_split_nodes <= 0) - - if is_leaf: - # Node is not expandable; set node as leaf - node.left_child = _TREE_LEAF - node.right_child = _TREE_LEAF - node.feature = _TREE_UNDEFINED - node.threshold = _TREE_UNDEFINED - - else: - # Node is expandable - - # Decrement number of split nodes available - max_split_nodes -= 1 - - # Compute left split node - rc = self._add_split_node(splitter, tree, - record.start, record.pos, - record.impurity_left, - IS_NOT_FIRST, IS_LEFT, node, - record.depth + 1, - &split_node_left) - if rc == -1: - break - - # tree.nodes may have changed - node = &tree.nodes[record.node_id] - - # Compute right split node - rc = self._add_split_node(splitter, tree, record.pos, - record.end, - record.impurity_right, - IS_NOT_FIRST, IS_NOT_LEFT, node, - record.depth + 1, - &split_node_right) - if rc == -1: - break - - # Add nodes to queue - _add_to_frontier(split_node_left, frontier) - _add_to_frontier(split_node_right, frontier) - - if record.depth > max_depth_seen: - max_depth_seen = record.depth - - if rc >= 0: - rc = tree._resize_c(tree.node_count) - - if rc >= 0: - tree.max_depth = max_depth_seen - - if rc == -1: - raise MemoryError() - - cdef inline int _add_split_node(self, Splitter splitter, Tree tree, - SIZE_t start, SIZE_t end, double impurity, - bint is_first, bint is_left, Node* parent, - SIZE_t depth, - FrontierRecord* res) except -1 nogil: - """Adds node w/ partition ``[start, end)`` to the frontier. """ - cdef SplitRecord split - cdef SIZE_t node_id - cdef SIZE_t n_node_samples - cdef SIZE_t n_constant_features = 0 - cdef double min_impurity_decrease = self.min_impurity_decrease - cdef double weighted_n_node_samples - cdef bint is_leaf - - splitter.node_reset(start, end, &weighted_n_node_samples) - - if is_first: - impurity = splitter.node_impurity() - - n_node_samples = end - start - is_leaf = (depth >= self.max_depth or - n_node_samples < self.min_samples_split or - n_node_samples < 2 * self.min_samples_leaf or - weighted_n_node_samples < 2 * self.min_weight_leaf or - impurity <= EPSILON # impurity == 0 with tolerance - ) - - if not is_leaf: - splitter.node_split(impurity, &split, &n_constant_features) - # If EPSILON=0 in the below comparison, float precision issues stop - # splitting early, producing trees that are dissimilar to v0.18 - is_leaf = (is_leaf or split.pos >= end or - split.improvement + EPSILON < min_impurity_decrease) - - node_id = tree._add_node(parent - tree.nodes - if parent != NULL - else _TREE_UNDEFINED, - is_left, is_leaf, - split.feature, split.threshold, impurity, n_node_samples, - weighted_n_node_samples, - split.missing_go_to_left) - if node_id == INTPTR_MAX: - return -1 - - # compute values also for split nodes (might become leafs later). - splitter.node_value(tree.value + node_id * tree.value_stride) - - res.node_id = node_id - res.start = start - res.end = end - res.depth = depth - res.impurity = impurity - - if not is_leaf: - # is split node - res.pos = split.pos - res.is_leaf = 0 - res.improvement = split.improvement - res.impurity_left = split.impurity_left - res.impurity_right = split.impurity_right - - else: - # is leaf => 0 improvement - res.pos = end - res.is_leaf = 1 - res.improvement = 0.0 - res.impurity_left = impurity - res.impurity_right = impurity - - return 0 - - -# ============================================================================= -# Tree -# ============================================================================= - -cdef class Tree: - """Array-based representation of a binary decision tree. - - The binary tree is represented as a number of parallel arrays. The i-th - element of each array holds information about the node `i`. Node 0 is the - tree's root. You can find a detailed description of all arrays in - `_tree.pxd`. NOTE: Some of the arrays only apply to either leaves or split - nodes, resp. In this case the values of nodes of the other type are - arbitrary! - - Attributes - ---------- - node_count : int - The number of nodes (internal nodes + leaves) in the tree. - - capacity : int - The current capacity (i.e., size) of the arrays, which is at least as - great as `node_count`. - - max_depth : int - The depth of the tree, i.e. the maximum depth of its leaves. - - children_left : array of int, shape [node_count] - children_left[i] holds the node id of the left child of node i. - For leaves, children_left[i] == TREE_LEAF. Otherwise, - children_left[i] > i. This child handles the case where - X[:, feature[i]] <= threshold[i]. - - children_right : array of int, shape [node_count] - children_right[i] holds the node id of the right child of node i. - For leaves, children_right[i] == TREE_LEAF. Otherwise, - children_right[i] > i. This child handles the case where - X[:, feature[i]] > threshold[i]. - - feature : array of int, shape [node_count] - feature[i] holds the feature to split on, for the internal node i. - - threshold : array of double, shape [node_count] - threshold[i] holds the threshold for the internal node i. - - value : array of double, shape [node_count, n_outputs, max_n_classes] - Contains the constant prediction value of each node. - - impurity : array of double, shape [node_count] - impurity[i] holds the impurity (i.e., the value of the splitting - criterion) at node i. - - n_node_samples : array of int, shape [node_count] - n_node_samples[i] holds the number of training samples reaching node i. - - weighted_n_node_samples : array of double, shape [node_count] - weighted_n_node_samples[i] holds the weighted number of training samples - reaching node i. - """ - # Wrap for outside world. - # WARNING: these reference the current `nodes` and `value` buffers, which - # must not be freed by a subsequent memory allocation. - # (i.e. through `_resize` or `__setstate__`) - @property - def n_classes(self): - return sizet_ptr_to_ndarray(self.n_classes, self.n_outputs) - - @property - def children_left(self): - return self._get_node_ndarray()['left_child'][:self.node_count] - - @property - def children_right(self): - return self._get_node_ndarray()['right_child'][:self.node_count] - - @property - def n_leaves(self): - return np.sum(np.logical_and( - self.children_left == -1, - self.children_right == -1)) - - @property - def feature(self): - return self._get_node_ndarray()['feature'][:self.node_count] - - @property - def threshold(self): - return self._get_node_ndarray()['threshold'][:self.node_count] - - @property - def impurity(self): - return self._get_node_ndarray()['impurity'][:self.node_count] - - @property - def n_node_samples(self): - return self._get_node_ndarray()['n_node_samples'][:self.node_count] - - @property - def weighted_n_node_samples(self): - return self._get_node_ndarray()['weighted_n_node_samples'][:self.node_count] - - @property - def missing_go_to_left(self): - return self._get_node_ndarray()['missing_go_to_left'][:self.node_count] - - @property - def value(self): - return self._get_value_ndarray()[:self.node_count] - - # TODO: Convert n_classes to cython.integral memory view once - # https://github.com/cython/cython/issues/5243 is fixed - def __cinit__(self, int n_features, cnp.ndarray n_classes, int n_outputs): - """Constructor.""" - cdef SIZE_t dummy = 0 - size_t_dtype = np.array(dummy).dtype - - n_classes = _check_n_classes(n_classes, size_t_dtype) - - # Input/Output layout - self.n_features = n_features - self.n_outputs = n_outputs - self.n_classes = NULL - safe_realloc(&self.n_classes, n_outputs) - - self.max_n_classes = np.max(n_classes) - self.value_stride = n_outputs * self.max_n_classes - - cdef SIZE_t k - for k in range(n_outputs): - self.n_classes[k] = n_classes[k] - - # Inner structures - self.max_depth = 0 - self.node_count = 0 - self.capacity = 0 - self.value = NULL - self.nodes = NULL - - def __dealloc__(self): - """Destructor.""" - # Free all inner structures - free(self.n_classes) - free(self.value) - free(self.nodes) - - def __reduce__(self): - """Reduce re-implementation, for pickling.""" - return (Tree, (self.n_features, - sizet_ptr_to_ndarray(self.n_classes, self.n_outputs), - self.n_outputs), self.__getstate__()) - - def __getstate__(self): - """Getstate re-implementation, for pickling.""" - d = {} - # capacity is inferred during the __setstate__ using nodes - d["max_depth"] = self.max_depth - d["node_count"] = self.node_count - d["nodes"] = self._get_node_ndarray() - d["values"] = self._get_value_ndarray() - return d - - def __setstate__(self, d): - """Setstate re-implementation, for unpickling.""" - self.max_depth = d["max_depth"] - self.node_count = d["node_count"] - - if 'nodes' not in d: - raise ValueError('You have loaded Tree version which ' - 'cannot be imported') - - node_ndarray = d['nodes'] - value_ndarray = d['values'] - - value_shape = (node_ndarray.shape[0], self.n_outputs, - self.max_n_classes) - - node_ndarray = _check_node_ndarray(node_ndarray, expected_dtype=NODE_DTYPE) - value_ndarray = _check_value_ndarray( - value_ndarray, - expected_dtype=np.dtype(np.float64), - expected_shape=value_shape - ) - - self.capacity = node_ndarray.shape[0] - if self._resize_c(self.capacity) != 0: - raise MemoryError("resizing tree to %d" % self.capacity) - - memcpy(self.nodes, cnp.PyArray_DATA(node_ndarray), - self.capacity * sizeof(Node)) - memcpy(self.value, cnp.PyArray_DATA(value_ndarray), - self.capacity * self.value_stride * sizeof(double)) - - cdef int _resize(self, SIZE_t capacity) except -1 nogil: - """Resize all inner arrays to `capacity`, if `capacity` == -1, then - double the size of the inner arrays. - - Returns -1 in case of failure to allocate memory (and raise MemoryError) - or 0 otherwise. - """ - if self._resize_c(capacity) != 0: - # Acquire gil only if we need to raise - with gil: - raise MemoryError() - - cdef int _resize_c(self, SIZE_t capacity=INTPTR_MAX) except -1 nogil: - """Guts of _resize - - Returns -1 in case of failure to allocate memory (and raise MemoryError) - or 0 otherwise. - """ - if capacity == self.capacity and self.nodes != NULL: - return 0 - - if capacity == INTPTR_MAX: - if self.capacity == 0: - capacity = 3 # default initial value - else: - capacity = 2 * self.capacity - - safe_realloc(&self.nodes, capacity) - safe_realloc(&self.value, capacity * self.value_stride) - - # value memory is initialised to 0 to enable classifier argmax - if capacity > self.capacity: - memset((self.value + self.capacity * self.value_stride), 0, - (capacity - self.capacity) * self.value_stride * - sizeof(double)) - - # if capacity smaller than node_count, adjust the counter - if capacity < self.node_count: - self.node_count = capacity - - self.capacity = capacity - return 0 - - cdef SIZE_t _add_node(self, SIZE_t parent, bint is_left, bint is_leaf, - SIZE_t feature, double threshold, double impurity, - SIZE_t n_node_samples, - double weighted_n_node_samples, - unsigned char missing_go_to_left) except -1 nogil: - """Add a node to the tree. - - The new node registers itself as the child of its parent. - - Returns (size_t)(-1) on error. - """ - cdef SIZE_t node_id = self.node_count - - if node_id >= self.capacity: - if self._resize_c() != 0: - return INTPTR_MAX - - cdef Node* node = &self.nodes[node_id] - node.impurity = impurity - node.n_node_samples = n_node_samples - node.weighted_n_node_samples = weighted_n_node_samples - - if parent != _TREE_UNDEFINED: - if is_left: - self.nodes[parent].left_child = node_id - else: - self.nodes[parent].right_child = node_id - - if is_leaf: - node.left_child = _TREE_LEAF - node.right_child = _TREE_LEAF - node.feature = _TREE_UNDEFINED - node.threshold = _TREE_UNDEFINED - - else: - # left_child and right_child will be set later - node.feature = feature - node.threshold = threshold - node.missing_go_to_left = missing_go_to_left - - self.node_count += 1 - - return node_id - - cpdef cnp.ndarray predict(self, object X): - """Predict target for X.""" - out = self._get_value_ndarray().take(self.apply(X), axis=0, - mode='clip') - if self.n_outputs == 1: - out = out.reshape(X.shape[0], self.max_n_classes) - return out - - cpdef cnp.ndarray apply(self, object X): - """Finds the terminal region (=leaf node) for each sample in X.""" - if issparse(X): - return self._apply_sparse_csr(X) - else: - return self._apply_dense(X) - - cdef inline cnp.ndarray _apply_dense(self, object X): - """Finds the terminal region (=leaf node) for each sample in X.""" - - # Check input - if not isinstance(X, np.ndarray): - raise ValueError("X should be in np.ndarray format, got %s" - % type(X)) - - if X.dtype != DTYPE: - raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) - - # Extract input - cdef const DTYPE_t[:, :] X_ndarray = X - cdef SIZE_t n_samples = X.shape[0] - cdef DTYPE_t X_i_node_feature - - # Initialize output - cdef SIZE_t[:] out = np.zeros(n_samples, dtype=np.intp) - - # Initialize auxiliary data-structure - cdef Node* node = NULL - cdef SIZE_t i = 0 - - with nogil: - for i in range(n_samples): - node = self.nodes - # While node not a leaf - while node.left_child != _TREE_LEAF: - X_i_node_feature = X_ndarray[i, node.feature] - # ... and node.right_child != _TREE_LEAF: - if isnan(X_i_node_feature): - if node.missing_go_to_left: - node = &self.nodes[node.left_child] - else: - node = &self.nodes[node.right_child] - elif X_i_node_feature <= node.threshold: - node = &self.nodes[node.left_child] - else: - node = &self.nodes[node.right_child] - - out[i] = (node - self.nodes) # node offset - - return np.asarray(out) - - cdef inline cnp.ndarray _apply_sparse_csr(self, object X): - """Finds the terminal region (=leaf node) for each sample in sparse X. - """ - # Check input - if not isspmatrix_csr(X): - raise ValueError("X should be in csr_matrix format, got %s" - % type(X)) - - if X.dtype != DTYPE: - raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) - - # Extract input - cdef const DTYPE_t[:] X_data = X.data - cdef const INT32_t[:] X_indices = X.indices - cdef const INT32_t[:] X_indptr = X.indptr - - cdef SIZE_t n_samples = X.shape[0] - cdef SIZE_t n_features = X.shape[1] - - # Initialize output - cdef SIZE_t[:] out = np.zeros(n_samples, dtype=np.intp) - - # Initialize auxiliary data-structure - cdef DTYPE_t feature_value = 0. - cdef Node* node = NULL - cdef DTYPE_t* X_sample = NULL - cdef SIZE_t i = 0 - cdef INT32_t k = 0 - - # feature_to_sample as a data structure records the last seen sample - # for each feature; functionally, it is an efficient way to identify - # which features are nonzero in the present sample. - cdef SIZE_t* feature_to_sample = NULL - - safe_realloc(&X_sample, n_features) - safe_realloc(&feature_to_sample, n_features) - - with nogil: - memset(feature_to_sample, -1, n_features * sizeof(SIZE_t)) - - for i in range(n_samples): - node = self.nodes - - for k in range(X_indptr[i], X_indptr[i + 1]): - feature_to_sample[X_indices[k]] = i - X_sample[X_indices[k]] = X_data[k] - - # While node not a leaf - while node.left_child != _TREE_LEAF: - # ... and node.right_child != _TREE_LEAF: - if feature_to_sample[node.feature] == i: - feature_value = X_sample[node.feature] - - else: - feature_value = 0. - - if feature_value <= node.threshold: - node = &self.nodes[node.left_child] - else: - node = &self.nodes[node.right_child] - - out[i] = (node - self.nodes) # node offset - - # Free auxiliary arrays - free(X_sample) - free(feature_to_sample) - - return np.asarray(out) - - cpdef object decision_path(self, object X): - """Finds the decision path (=node) for each sample in X.""" - if issparse(X): - return self._decision_path_sparse_csr(X) - else: - return self._decision_path_dense(X) - - cdef inline object _decision_path_dense(self, object X): - """Finds the decision path (=node) for each sample in X.""" - - # Check input - if not isinstance(X, np.ndarray): - raise ValueError("X should be in np.ndarray format, got %s" - % type(X)) - - if X.dtype != DTYPE: - raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) - - # Extract input - cdef const DTYPE_t[:, :] X_ndarray = X - cdef SIZE_t n_samples = X.shape[0] - - # Initialize output - cdef SIZE_t[:] indptr = np.zeros(n_samples + 1, dtype=np.intp) - cdef SIZE_t[:] indices = np.zeros( - n_samples * (1 + self.max_depth), dtype=np.intp - ) - - # Initialize auxiliary data-structure - cdef Node* node = NULL - cdef SIZE_t i = 0 - - with nogil: - for i in range(n_samples): - node = self.nodes - indptr[i + 1] = indptr[i] - - # Add all external nodes - while node.left_child != _TREE_LEAF: - # ... and node.right_child != _TREE_LEAF: - indices[indptr[i + 1]] = (node - self.nodes) - indptr[i + 1] += 1 - - if X_ndarray[i, node.feature] <= node.threshold: - node = &self.nodes[node.left_child] - else: - node = &self.nodes[node.right_child] - - # Add the leave node - indices[indptr[i + 1]] = (node - self.nodes) - indptr[i + 1] += 1 - - indices = indices[:indptr[n_samples]] - cdef SIZE_t[:] data = np.ones(shape=len(indices), dtype=np.intp) - out = csr_matrix((data, indices, indptr), - shape=(n_samples, self.node_count)) - - return out - - cdef inline object _decision_path_sparse_csr(self, object X): - """Finds the decision path (=node) for each sample in X.""" - - # Check input - if not isspmatrix_csr(X): - raise ValueError("X should be in csr_matrix format, got %s" - % type(X)) - - if X.dtype != DTYPE: - raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) - - # Extract input - cdef const DTYPE_t[:] X_data = X.data - cdef const INT32_t[:] X_indices = X.indices - cdef const INT32_t[:] X_indptr = X.indptr - - cdef SIZE_t n_samples = X.shape[0] - cdef SIZE_t n_features = X.shape[1] - - # Initialize output - cdef SIZE_t[:] indptr = np.zeros(n_samples + 1, dtype=np.intp) - cdef SIZE_t[:] indices = np.zeros( - n_samples * (1 + self.max_depth), dtype=np.intp - ) - - # Initialize auxiliary data-structure - cdef DTYPE_t feature_value = 0. - cdef Node* node = NULL - cdef DTYPE_t* X_sample = NULL - cdef SIZE_t i = 0 - cdef INT32_t k = 0 - - # feature_to_sample as a data structure records the last seen sample - # for each feature; functionally, it is an efficient way to identify - # which features are nonzero in the present sample. - cdef SIZE_t* feature_to_sample = NULL - - safe_realloc(&X_sample, n_features) - safe_realloc(&feature_to_sample, n_features) - - with nogil: - memset(feature_to_sample, -1, n_features * sizeof(SIZE_t)) - - for i in range(n_samples): - node = self.nodes - indptr[i + 1] = indptr[i] - - for k in range(X_indptr[i], X_indptr[i + 1]): - feature_to_sample[X_indices[k]] = i - X_sample[X_indices[k]] = X_data[k] - - # While node not a leaf - while node.left_child != _TREE_LEAF: - # ... and node.right_child != _TREE_LEAF: - - indices[indptr[i + 1]] = (node - self.nodes) - indptr[i + 1] += 1 - - if feature_to_sample[node.feature] == i: - feature_value = X_sample[node.feature] - - else: - feature_value = 0. - - if feature_value <= node.threshold: - node = &self.nodes[node.left_child] - else: - node = &self.nodes[node.right_child] - - # Add the leave node - indices[indptr[i + 1]] = (node - self.nodes) - indptr[i + 1] += 1 - - # Free auxiliary arrays - free(X_sample) - free(feature_to_sample) - - indices = indices[:indptr[n_samples]] - cdef SIZE_t[:] data = np.ones(shape=len(indices), dtype=np.intp) - out = csr_matrix((data, indices, indptr), - shape=(n_samples, self.node_count)) - - return out - - cpdef compute_node_depths(self): - """Compute the depth of each node in a tree. - - .. versionadded:: 1.3 - - Returns - ------- - depths : ndarray of shape (self.node_count,), dtype=np.int64 - The depth of each node in the tree. - """ - cdef: - cnp.int64_t[::1] depths = np.empty(self.node_count, dtype=np.int64) - cnp.npy_intp[:] children_left = self.children_left - cnp.npy_intp[:] children_right = self.children_right - cnp.npy_intp node_id - cnp.npy_intp node_count = self.node_count - cnp.int64_t depth - - depths[0] = 1 # init root node - for node_id in range(node_count): - if children_left[node_id] != _TREE_LEAF: - depth = depths[node_id] + 1 - depths[children_left[node_id]] = depth - depths[children_right[node_id]] = depth - - return depths.base - - cpdef compute_feature_importances(self, normalize=True): - """Computes the importance of each feature (aka variable).""" - cdef Node* left - cdef Node* right - cdef Node* nodes = self.nodes - cdef Node* node = nodes - cdef Node* end_node = node + self.node_count - - cdef double normalizer = 0. - - cdef cnp.float64_t[:] importances = np.zeros(self.n_features) - - with nogil: - while node != end_node: - if node.left_child != _TREE_LEAF: - # ... and node.right_child != _TREE_LEAF: - left = &nodes[node.left_child] - right = &nodes[node.right_child] - - importances[node.feature] += ( - node.weighted_n_node_samples * node.impurity - - left.weighted_n_node_samples * left.impurity - - right.weighted_n_node_samples * right.impurity) - node += 1 - - for i in range(self.n_features): - importances[i] /= nodes[0].weighted_n_node_samples - - if normalize: - normalizer = np.sum(importances) - - if normalizer > 0.0: - # Avoid dividing by zero (e.g., when root is pure) - for i in range(self.n_features): - importances[i] /= normalizer - - return np.asarray(importances) - - cdef cnp.ndarray _get_value_ndarray(self): - """Wraps value as a 3-d NumPy array. - - The array keeps a reference to this Tree, which manages the underlying - memory. - """ - cdef cnp.npy_intp shape[3] - shape[0] = self.node_count - shape[1] = self.n_outputs - shape[2] = self.max_n_classes - cdef cnp.ndarray arr - arr = cnp.PyArray_SimpleNewFromData(3, shape, cnp.NPY_DOUBLE, self.value) - Py_INCREF(self) - if PyArray_SetBaseObject(arr, self) < 0: - raise ValueError("Can't initialize array.") - return arr - - cdef cnp.ndarray _get_node_ndarray(self): - """Wraps nodes as a NumPy struct array. - - The array keeps a reference to this Tree, which manages the underlying - memory. Individual fields are publicly accessible as properties of the - Tree. - """ - cdef cnp.npy_intp shape[1] - shape[0] = self.node_count - cdef cnp.npy_intp strides[1] - strides[0] = sizeof(Node) - cdef cnp.ndarray arr - Py_INCREF(NODE_DTYPE) - arr = PyArray_NewFromDescr( cnp.ndarray, - NODE_DTYPE, 1, shape, - strides, self.nodes, - cnp.NPY_ARRAY_DEFAULT, None) - Py_INCREF(self) - if PyArray_SetBaseObject(arr, self) < 0: - raise ValueError("Can't initialize array.") - return arr - - def compute_partial_dependence(self, DTYPE_t[:, ::1] X, - int[::1] target_features, - double[::1] out): - """Partial dependence of the response on the ``target_feature`` set. - - For each sample in ``X`` a tree traversal is performed. - Each traversal starts from the root with weight 1.0. - - At each non-leaf node that splits on a target feature, either - the left child or the right child is visited based on the feature - value of the current sample, and the weight is not modified. - At each non-leaf node that splits on a complementary feature, - both children are visited and the weight is multiplied by the fraction - of training samples which went to each child. - - At each leaf, the value of the node is multiplied by the current - weight (weights sum to 1 for all visited terminal nodes). - - Parameters - ---------- - X : view on 2d ndarray, shape (n_samples, n_target_features) - The grid points on which the partial dependence should be - evaluated. - target_features : view on 1d ndarray, shape (n_target_features) - The set of target features for which the partial dependence - should be evaluated. - out : view on 1d ndarray, shape (n_samples) - The value of the partial dependence function on each grid - point. - """ - cdef: - double[::1] weight_stack = np.zeros(self.node_count, - dtype=np.float64) - SIZE_t[::1] node_idx_stack = np.zeros(self.node_count, - dtype=np.intp) - SIZE_t sample_idx - SIZE_t feature_idx - int stack_size - double left_sample_frac - double current_weight - double total_weight # used for sanity check only - Node *current_node # use a pointer to avoid copying attributes - SIZE_t current_node_idx - bint is_target_feature - SIZE_t _TREE_LEAF = TREE_LEAF # to avoid python interactions - - for sample_idx in range(X.shape[0]): - # init stacks for current sample - stack_size = 1 - node_idx_stack[0] = 0 # root node - weight_stack[0] = 1 # all the samples are in the root node - total_weight = 0 - - while stack_size > 0: - # pop the stack - stack_size -= 1 - current_node_idx = node_idx_stack[stack_size] - current_node = &self.nodes[current_node_idx] - - if current_node.left_child == _TREE_LEAF: - # leaf node - out[sample_idx] += (weight_stack[stack_size] * - self.value[current_node_idx]) - total_weight += weight_stack[stack_size] - else: - # non-leaf node - - # determine if the split feature is a target feature - is_target_feature = False - for feature_idx in range(target_features.shape[0]): - if target_features[feature_idx] == current_node.feature: - is_target_feature = True - break - - if is_target_feature: - # In this case, we push left or right child on stack - if X[sample_idx, feature_idx] <= current_node.threshold: - node_idx_stack[stack_size] = current_node.left_child - else: - node_idx_stack[stack_size] = current_node.right_child - stack_size += 1 - else: - # In this case, we push both children onto the stack, - # and give a weight proportional to the number of - # samples going through each branch. - - # push left child - node_idx_stack[stack_size] = current_node.left_child - left_sample_frac = ( - self.nodes[current_node.left_child].weighted_n_node_samples / - current_node.weighted_n_node_samples) - current_weight = weight_stack[stack_size] - weight_stack[stack_size] = current_weight * left_sample_frac - stack_size += 1 - - # push right child - node_idx_stack[stack_size] = current_node.right_child - weight_stack[stack_size] = ( - current_weight * (1 - left_sample_frac)) - stack_size += 1 - - # Sanity check. Should never happen. - if not (0.999 < total_weight < 1.001): - raise ValueError("Total weight should be 1.0 but was %.9f" % - total_weight) - - -def _check_n_classes(n_classes, expected_dtype): - if n_classes.ndim != 1: - raise ValueError( - f"Wrong dimensions for n_classes from the pickle: " - f"expected 1, got {n_classes.ndim}" - ) - - if n_classes.dtype == expected_dtype: - return n_classes - - # Handles both different endianness and different bitness - if n_classes.dtype.kind == "i" and n_classes.dtype.itemsize in [4, 8]: - return n_classes.astype(expected_dtype, casting="same_kind") - - raise ValueError( - "n_classes from the pickle has an incompatible dtype:\n" - f"- expected: {expected_dtype}\n" - f"- got: {n_classes.dtype}" - ) - - -def _check_value_ndarray(value_ndarray, expected_dtype, expected_shape): - if value_ndarray.shape != expected_shape: - raise ValueError( - "Wrong shape for value array from the pickle: " - f"expected {expected_shape}, got {value_ndarray.shape}" - ) - - if not value_ndarray.flags.c_contiguous: - raise ValueError( - "value array from the pickle should be a C-contiguous array" - ) - - if value_ndarray.dtype == expected_dtype: - return value_ndarray - - # Handles different endianness - if value_ndarray.dtype.str.endswith('f8'): - return value_ndarray.astype(expected_dtype, casting='equiv') - - raise ValueError( - "value array from the pickle has an incompatible dtype:\n" - f"- expected: {expected_dtype}\n" - f"- got: {value_ndarray.dtype}" - ) - - -def _dtype_to_dict(dtype): - return {name: dt.str for name, (dt, *rest) in dtype.fields.items()} - - -def _dtype_dict_with_modified_bitness(dtype_dict): - # field names in Node struct with SIZE_t types (see sklearn/tree/_tree.pxd) - indexing_field_names = ["left_child", "right_child", "feature", "n_node_samples"] - - expected_dtype_size = str(struct.calcsize("P")) - allowed_dtype_size = "8" if expected_dtype_size == "4" else "4" - - allowed_dtype_dict = dtype_dict.copy() - for name in indexing_field_names: - allowed_dtype_dict[name] = allowed_dtype_dict[name].replace( - expected_dtype_size, allowed_dtype_size - ) - - return allowed_dtype_dict - - -def _all_compatible_dtype_dicts(dtype): - # The Cython code for decision trees uses platform-specific SIZE_t - # typed indexing fields that correspond to either i4 or i8 dtypes for - # the matching fields in the numpy array depending on the bitness of - # the platform (32 bit or 64 bit respectively). - # - # We need to cast the indexing fields of the NODE_DTYPE-dtyped array at - # pickle load time to enable cross-bitness deployment scenarios. We - # typically want to make it possible to run the expensive fit method of - # a tree estimator on a 64 bit server platform, pickle the estimator - # for deployment and run the predict method of a low power 32 bit edge - # platform. - # - # A similar thing happens for endianness, the machine where the pickle was - # saved can have a different endianness than the machine where the pickle - # is loaded - - dtype_dict = _dtype_to_dict(dtype) - dtype_dict_with_modified_bitness = _dtype_dict_with_modified_bitness(dtype_dict) - dtype_dict_with_modified_endianness = _dtype_to_dict(dtype.newbyteorder()) - dtype_dict_with_modified_bitness_and_endianness = _dtype_dict_with_modified_bitness( - dtype_dict_with_modified_endianness - ) - - return [ - dtype_dict, - dtype_dict_with_modified_bitness, - dtype_dict_with_modified_endianness, - dtype_dict_with_modified_bitness_and_endianness, - ] - - -def _check_node_ndarray(node_ndarray, expected_dtype): - if node_ndarray.ndim != 1: - raise ValueError( - "Wrong dimensions for node array from the pickle: " - f"expected 1, got {node_ndarray.ndim}" - ) - - if not node_ndarray.flags.c_contiguous: - raise ValueError( - "node array from the pickle should be a C-contiguous array" - ) - - node_ndarray_dtype = node_ndarray.dtype - if node_ndarray_dtype == expected_dtype: - return node_ndarray - - node_ndarray_dtype_dict = _dtype_to_dict(node_ndarray_dtype) - all_compatible_dtype_dicts = _all_compatible_dtype_dicts(expected_dtype) - - if node_ndarray_dtype_dict not in all_compatible_dtype_dicts: - raise ValueError( - "node array from the pickle has an incompatible dtype:\n" - f"- expected: {expected_dtype}\n" - f"- got : {node_ndarray_dtype}" - ) - - return node_ndarray.astype(expected_dtype, casting="same_kind") - - -# ============================================================================= -# Build Pruned Tree -# ============================================================================= - - -cdef class _CCPPruneController: - """Base class used by build_pruned_tree_ccp and ccp_pruning_path - to control pruning. - """ - cdef bint stop_pruning(self, DOUBLE_t effective_alpha) noexcept nogil: - """Return 1 to stop pruning and 0 to continue pruning""" - return 0 - - cdef void save_metrics(self, DOUBLE_t effective_alpha, - DOUBLE_t subtree_impurities) noexcept nogil: - """Save metrics when pruning""" - pass - - cdef void after_pruning(self, unsigned char[:] in_subtree) noexcept nogil: - """Called after pruning""" - pass - - -cdef class _AlphaPruner(_CCPPruneController): - """Use alpha to control when to stop pruning.""" - cdef DOUBLE_t ccp_alpha - cdef SIZE_t capacity - - def __cinit__(self, DOUBLE_t ccp_alpha): - self.ccp_alpha = ccp_alpha - self.capacity = 0 - - cdef bint stop_pruning(self, DOUBLE_t effective_alpha) noexcept nogil: - # The subtree on the previous iteration has the greatest ccp_alpha - # less than or equal to self.ccp_alpha - return self.ccp_alpha < effective_alpha - - cdef void after_pruning(self, unsigned char[:] in_subtree) noexcept nogil: - """Updates the number of leaves in subtree""" - for i in range(in_subtree.shape[0]): - if in_subtree[i]: - self.capacity += 1 - - -cdef class _PathFinder(_CCPPruneController): - """Record metrics used to return the cost complexity path.""" - cdef DOUBLE_t[:] ccp_alphas - cdef DOUBLE_t[:] impurities - cdef UINT32_t count - - def __cinit__(self, int node_count): - self.ccp_alphas = np.zeros(shape=(node_count), dtype=np.float64) - self.impurities = np.zeros(shape=(node_count), dtype=np.float64) - self.count = 0 - - cdef void save_metrics(self, - DOUBLE_t effective_alpha, - DOUBLE_t subtree_impurities) noexcept nogil: - self.ccp_alphas[self.count] = effective_alpha - self.impurities[self.count] = subtree_impurities - self.count += 1 - - -cdef struct CostComplexityPruningRecord: - SIZE_t node_idx - SIZE_t parent - -cdef _cost_complexity_prune(unsigned char[:] leaves_in_subtree, # OUT - Tree orig_tree, - _CCPPruneController controller): - """Perform cost complexity pruning. - - This function takes an already grown tree, `orig_tree` and outputs a - boolean mask `leaves_in_subtree` which are the leaves in the pruned tree. - During the pruning process, the controller is passed the effective alpha and - the subtree impurities. Furthermore, the controller signals when to stop - pruning. - - Parameters - ---------- - leaves_in_subtree : unsigned char[:] - Output for leaves of subtree - orig_tree : Tree - Original tree - ccp_controller : _CCPPruneController - Cost complexity controller - """ - - cdef: - SIZE_t i - SIZE_t n_nodes = orig_tree.node_count - # prior probability using weighted samples - DOUBLE_t[:] weighted_n_node_samples = orig_tree.weighted_n_node_samples - DOUBLE_t total_sum_weights = weighted_n_node_samples[0] - DOUBLE_t[:] impurity = orig_tree.impurity - # weighted impurity of each node - DOUBLE_t[:] r_node = np.empty(shape=n_nodes, dtype=np.float64) - - SIZE_t[:] child_l = orig_tree.children_left - SIZE_t[:] child_r = orig_tree.children_right - SIZE_t[:] parent = np.zeros(shape=n_nodes, dtype=np.intp) - - stack[CostComplexityPruningRecord] ccp_stack - CostComplexityPruningRecord stack_record - SIZE_t node_idx - stack[SIZE_t] node_indices_stack - - SIZE_t[:] n_leaves = np.zeros(shape=n_nodes, dtype=np.intp) - DOUBLE_t[:] r_branch = np.zeros(shape=n_nodes, dtype=np.float64) - DOUBLE_t current_r - SIZE_t leaf_idx - SIZE_t parent_idx - - # candidate nodes that can be pruned - unsigned char[:] candidate_nodes = np.zeros(shape=n_nodes, - dtype=np.uint8) - # nodes in subtree - unsigned char[:] in_subtree = np.ones(shape=n_nodes, dtype=np.uint8) - SIZE_t pruned_branch_node_idx - DOUBLE_t subtree_alpha - DOUBLE_t effective_alpha - SIZE_t n_pruned_leaves - DOUBLE_t r_diff - DOUBLE_t max_float64 = np.finfo(np.float64).max - - # find parent node ids and leaves - with nogil: - - for i in range(r_node.shape[0]): - r_node[i] = ( - weighted_n_node_samples[i] * impurity[i] / total_sum_weights) - - # Push the root node - ccp_stack.push({"node_idx": 0, "parent": _TREE_UNDEFINED}) - - while not ccp_stack.empty(): - stack_record = ccp_stack.top() - ccp_stack.pop() - - node_idx = stack_record.node_idx - parent[node_idx] = stack_record.parent - - if child_l[node_idx] == _TREE_LEAF: - # ... and child_r[node_idx] == _TREE_LEAF: - leaves_in_subtree[node_idx] = 1 - else: - ccp_stack.push({"node_idx": child_l[node_idx], "parent": node_idx}) - ccp_stack.push({"node_idx": child_r[node_idx], "parent": node_idx}) - - # computes number of leaves in all branches and the overall impurity of - # the branch. The overall impurity is the sum of r_node in its leaves. - for leaf_idx in range(leaves_in_subtree.shape[0]): - if not leaves_in_subtree[leaf_idx]: - continue - r_branch[leaf_idx] = r_node[leaf_idx] - - # bubble up values to ancestor nodes - current_r = r_node[leaf_idx] - while leaf_idx != 0: - parent_idx = parent[leaf_idx] - r_branch[parent_idx] += current_r - n_leaves[parent_idx] += 1 - leaf_idx = parent_idx - - for i in range(leaves_in_subtree.shape[0]): - candidate_nodes[i] = not leaves_in_subtree[i] - - # save metrics before pruning - controller.save_metrics(0.0, r_branch[0]) - - # while root node is not a leaf - while candidate_nodes[0]: - - # computes ccp_alpha for subtrees and finds the minimal alpha - effective_alpha = max_float64 - for i in range(n_nodes): - if not candidate_nodes[i]: - continue - subtree_alpha = (r_node[i] - r_branch[i]) / (n_leaves[i] - 1) - if subtree_alpha < effective_alpha: - effective_alpha = subtree_alpha - pruned_branch_node_idx = i - - if controller.stop_pruning(effective_alpha): - break - - node_indices_stack.push(pruned_branch_node_idx) - - # descendants of branch are not in subtree - while not node_indices_stack.empty(): - node_idx = node_indices_stack.top() - node_indices_stack.pop() - - if not in_subtree[node_idx]: - continue # branch has already been marked for pruning - candidate_nodes[node_idx] = 0 - leaves_in_subtree[node_idx] = 0 - in_subtree[node_idx] = 0 - - if child_l[node_idx] != _TREE_LEAF: - # ... and child_r[node_idx] != _TREE_LEAF: - node_indices_stack.push(child_l[node_idx]) - node_indices_stack.push(child_r[node_idx]) - leaves_in_subtree[pruned_branch_node_idx] = 1 - in_subtree[pruned_branch_node_idx] = 1 - - # updates number of leaves - n_pruned_leaves = n_leaves[pruned_branch_node_idx] - 1 - n_leaves[pruned_branch_node_idx] = 0 - - # computes the increase in r_branch to bubble up - r_diff = r_node[pruned_branch_node_idx] - r_branch[pruned_branch_node_idx] - r_branch[pruned_branch_node_idx] = r_node[pruned_branch_node_idx] - - # bubble up values to ancestors - node_idx = parent[pruned_branch_node_idx] - while node_idx != _TREE_UNDEFINED: - n_leaves[node_idx] -= n_pruned_leaves - r_branch[node_idx] += r_diff - node_idx = parent[node_idx] - - controller.save_metrics(effective_alpha, r_branch[0]) - - controller.after_pruning(in_subtree) - - -def _build_pruned_tree_ccp( - Tree tree, # OUT - Tree orig_tree, - DOUBLE_t ccp_alpha -): - """Build a pruned tree from the original tree using cost complexity - pruning. - - The values and nodes from the original tree are copied into the pruned - tree. - - Parameters - ---------- - tree : Tree - Location to place the pruned tree - orig_tree : Tree - Original tree - ccp_alpha : positive double - Complexity parameter. The subtree with the largest cost complexity - that is smaller than ``ccp_alpha`` will be chosen. By default, - no pruning is performed. - """ - - cdef: - SIZE_t n_nodes = orig_tree.node_count - unsigned char[:] leaves_in_subtree = np.zeros( - shape=n_nodes, dtype=np.uint8) - - pruning_controller = _AlphaPruner(ccp_alpha=ccp_alpha) - - _cost_complexity_prune(leaves_in_subtree, orig_tree, pruning_controller) - - _build_pruned_tree(tree, orig_tree, leaves_in_subtree, - pruning_controller.capacity) - - -def ccp_pruning_path(Tree orig_tree): - """Computes the cost complexity pruning path. - - Parameters - ---------- - tree : Tree - Original tree. - - Returns - ------- - path_info : dict - Information about pruning path with attributes: - - ccp_alphas : ndarray - Effective alphas of subtree during pruning. - - impurities : ndarray - Sum of the impurities of the subtree leaves for the - corresponding alpha value in ``ccp_alphas``. - """ - cdef: - unsigned char[:] leaves_in_subtree = np.zeros( - shape=orig_tree.node_count, dtype=np.uint8) - - path_finder = _PathFinder(orig_tree.node_count) - - _cost_complexity_prune(leaves_in_subtree, orig_tree, path_finder) - - cdef: - UINT32_t total_items = path_finder.count - DOUBLE_t[:] ccp_alphas = np.empty(shape=total_items, dtype=np.float64) - DOUBLE_t[:] impurities = np.empty(shape=total_items, dtype=np.float64) - UINT32_t count = 0 - - while count < total_items: - ccp_alphas[count] = path_finder.ccp_alphas[count] - impurities[count] = path_finder.impurities[count] - count += 1 - - return { - 'ccp_alphas': np.asarray(ccp_alphas), - 'impurities': np.asarray(impurities), - } - - -cdef struct BuildPrunedRecord: - SIZE_t start - SIZE_t depth - SIZE_t parent - bint is_left - -cdef _build_pruned_tree( - Tree tree, # OUT - Tree orig_tree, - const unsigned char[:] leaves_in_subtree, - SIZE_t capacity -): - """Build a pruned tree. - - Build a pruned tree from the original tree by transforming the nodes in - ``leaves_in_subtree`` into leaves. - - Parameters - ---------- - tree : Tree - Location to place the pruned tree - orig_tree : Tree - Original tree - leaves_in_subtree : unsigned char memoryview, shape=(node_count, ) - Boolean mask for leaves to include in subtree - capacity : SIZE_t - Number of nodes to initially allocate in pruned tree - """ - tree._resize(capacity) - - cdef: - SIZE_t orig_node_id - SIZE_t new_node_id - SIZE_t depth - SIZE_t parent - bint is_left - bint is_leaf - - # value_stride for original tree and new tree are the same - SIZE_t value_stride = orig_tree.value_stride - SIZE_t max_depth_seen = -1 - int rc = 0 - Node* node - double* orig_value_ptr - double* new_value_ptr - - stack[BuildPrunedRecord] prune_stack - BuildPrunedRecord stack_record - - with nogil: - # push root node onto stack - prune_stack.push({"start": 0, "depth": 0, "parent": _TREE_UNDEFINED, "is_left": 0}) - - while not prune_stack.empty(): - stack_record = prune_stack.top() - prune_stack.pop() - - orig_node_id = stack_record.start - depth = stack_record.depth - parent = stack_record.parent - is_left = stack_record.is_left - - is_leaf = leaves_in_subtree[orig_node_id] - node = &orig_tree.nodes[orig_node_id] - - new_node_id = tree._add_node( - parent, is_left, is_leaf, node.feature, node.threshold, - node.impurity, node.n_node_samples, - node.weighted_n_node_samples, node.missing_go_to_left) - - if new_node_id == INTPTR_MAX: - rc = -1 - break - - # copy value from original tree to new tree - orig_value_ptr = orig_tree.value + value_stride * orig_node_id - new_value_ptr = tree.value + value_stride * new_node_id - memcpy(new_value_ptr, orig_value_ptr, sizeof(double) * value_stride) - - if not is_leaf: - # Push right child on stack - prune_stack.push({"start": node.right_child, "depth": depth + 1, - "parent": new_node_id, "is_left": 0}) - # push left child on stack - prune_stack.push({"start": node.left_child, "depth": depth + 1, - "parent": new_node_id, "is_left": 1}) - - if depth > max_depth_seen: - max_depth_seen = depth - - if rc >= 0: - tree.max_depth = max_depth_seen - if rc == -1: - raise MemoryError("pruning tree") \ No newline at end of file diff --git a/ivy/functional/frontends/sklearn/_tree_ivy copy.pyx b/ivy/functional/frontends/sklearn/_tree_ivy copy.pyx deleted file mode 100644 index f436c919f8bc2..0000000000000 --- a/ivy/functional/frontends/sklearn/_tree_ivy copy.pyx +++ /dev/null @@ -1,1833 +0,0 @@ -# Authors: Gilles Louppe -# Peter Prettenhofer -# Brian Holt -# Noel Dawe -# Satrajit Gosh -# Lars Buitinck -# Arnaud Joly -# Joel Nothman -# Fares Hedayati -# Jacob Schreiber -# Nelson Liu -# -# License: BSD 3 clause - -from cpython cimport Py_INCREF, PyObject, PyTypeObject - -from libc.stdlib cimport free -from libc.string cimport memcpy -from libc.string cimport memset -from libc.stdint cimport INTPTR_MAX -from libc.math cimport isnan -from libcpp.vector cimport vector -from libcpp.algorithm cimport pop_heap -from libcpp.algorithm cimport push_heap -from libcpp cimport bool - -import struct - -import numpy as np -cimport numpy as cnp -cnp.import_array() - -from scipy.sparse import issparse -from scipy.sparse import csr_matrix -from scipy.sparse import isspmatrix_csr - -from ._utils cimport safe_realloc -from ._utils cimport sizet_ptr_to_ndarray - -cdef extern from "numpy/arrayobject.h": - object PyArray_NewFromDescr(PyTypeObject* subtype, cnp.dtype descr, - int nd, cnp.npy_intp* dims, - cnp.npy_intp* strides, - void* data, int flags, object obj) - int PyArray_SetBaseObject(cnp.ndarray arr, PyObject* obj) - -cdef extern from "" namespace "std" nogil: - cdef cppclass stack[T]: - ctypedef T value_type - stack() except + - bint empty() - void pop() - void push(T&) except + # Raise c++ exception for bad_alloc -> MemoryError - T& top() - -# ============================================================================= -# Types and constants -# ============================================================================= - -from numpy import float32 as DTYPE -from numpy import float64 as DOUBLE - -cdef double INFINITY = np.inf -cdef double EPSILON = np.finfo('double').eps - -# Some handy constants (BestFirstTreeBuilder) -cdef int IS_FIRST = 1 -cdef int IS_NOT_FIRST = 0 -cdef int IS_LEFT = 1 -cdef int IS_NOT_LEFT = 0 - -TREE_LEAF = -1 -TREE_UNDEFINED = -2 -cdef SIZE_t _TREE_LEAF = TREE_LEAF -cdef SIZE_t _TREE_UNDEFINED = TREE_UNDEFINED - -# Build the corresponding numpy dtype for Node. -# This works by casting `dummy` to an array of Node of length 1, which numpy -# can construct a `dtype`-object for. See https://stackoverflow.com/q/62448946 -# for a more detailed explanation. -cdef Node dummy -NODE_DTYPE = np.asarray((&dummy)).dtype - -# ============================================================================= -# TreeBuilder -# ============================================================================= - -cdef class TreeBuilder: - """Interface for different tree building strategies.""" - - cpdef build( - self, - Tree tree, - object X, - const DOUBLE_t[:, ::1] y, - const DOUBLE_t[:] sample_weight=None, - const unsigned char[::1] missing_values_in_feature_mask=None, - ): - """Build a decision tree from the training set (X, y).""" - pass - - cdef inline _check_input( - self, - object X, - const DOUBLE_t[:, ::1] y, - const DOUBLE_t[:] sample_weight, - ): - """Check input dtype, layout and format""" - if issparse(X): - X = X.tocsc() - X.sort_indices() - - if X.data.dtype != DTYPE: - X.data = np.ascontiguousarray(X.data, dtype=DTYPE) - - if X.indices.dtype != np.int32 or X.indptr.dtype != np.int32: - raise ValueError("No support for np.int64 index based " - "sparse matrices") - - elif X.dtype != DTYPE: - # since we have to copy we will make it fortran for efficiency - X = np.asfortranarray(X, dtype=DTYPE) - - # TODO: This check for y seems to be redundant, as it is also - # present in the BaseDecisionTree's fit method, and therefore - # can be removed. - if y.base.dtype != DOUBLE or not y.base.flags.contiguous: - y = np.ascontiguousarray(y, dtype=DOUBLE) - - if ( - sample_weight is not None and - ( - sample_weight.base.dtype != DOUBLE or - not sample_weight.base.flags.contiguous - ) - ): - sample_weight = np.asarray(sample_weight, dtype=DOUBLE, order="C") - - return X, y, sample_weight - -# Depth first builder --------------------------------------------------------- -# A record on the stack for depth-first tree growing -cdef struct StackRecord: - SIZE_t start - SIZE_t end - SIZE_t depth - SIZE_t parent - bint is_left - double impurity - SIZE_t n_constant_features - - - - - - - -cdef class DepthFirstTreeBuilder(TreeBuilder): - """Build a decision tree in depth-first fashion.""" - - def __cinit__(self, Splitter splitter, SIZE_t min_samples_split, - SIZE_t min_samples_leaf, double min_weight_leaf, - SIZE_t max_depth, double min_impurity_decrease): - self.splitter = splitter - self.min_samples_split = min_samples_split - self.min_samples_leaf = min_samples_leaf - self.min_weight_leaf = min_weight_leaf - self.max_depth = max_depth - self.min_impurity_decrease = min_impurity_decrease - - cpdef build( - self, - Tree tree, - object X, - const DOUBLE_t[:, ::1] y, - const DOUBLE_t[:] sample_weight=None, - const unsigned char[::1] missing_values_in_feature_mask=None, - ): - """Build a decision tree from the training set (X, y).""" - - # check input - X, y, sample_weight = self._check_input(X, y, sample_weight) - - # Initial capacity - cdef int init_capacity - - if tree.max_depth <= 10: - init_capacity = (2 ** (tree.max_depth + 1)) - 1 - else: - init_capacity = 2047 - - tree._resize(init_capacity) - - # Parameters - cdef Splitter splitter = self.splitter - cdef SIZE_t max_depth = self.max_depth - cdef SIZE_t min_samples_leaf = self.min_samples_leaf - cdef double min_weight_leaf = self.min_weight_leaf - cdef SIZE_t min_samples_split = self.min_samples_split - cdef double min_impurity_decrease = self.min_impurity_decrease - - # Recursive partition (without actual recursion) - splitter.init(X, y, sample_weight, missing_values_in_feature_mask) - - cdef SIZE_t start - cdef SIZE_t end - cdef SIZE_t depth - cdef SIZE_t parent - cdef bint is_left - cdef SIZE_t n_node_samples = splitter.n_samples - cdef double weighted_n_node_samples - cdef SplitRecord split - cdef SIZE_t node_id - - cdef double impurity = INFINITY - cdef SIZE_t n_constant_features - cdef bint is_leaf - cdef bint first = 1 - cdef SIZE_t max_depth_seen = -1 - cdef int rc = 0 - - cdef stack[StackRecord] builder_stack - cdef StackRecord stack_record - - with nogil: - # push root node onto stack - builder_stack.push({ - "start": 0, - "end": n_node_samples, - "depth": 0, - "parent": _TREE_UNDEFINED, - "is_left": 0, - "impurity": INFINITY, - "n_constant_features": 0}) - - while not builder_stack.empty(): - stack_record = builder_stack.top() - builder_stack.pop() - - start = stack_record.start - end = stack_record.end - depth = stack_record.depth - parent = stack_record.parent - is_left = stack_record.is_left - impurity = stack_record.impurity - n_constant_features = stack_record.n_constant_features - - n_node_samples = end - start - splitter.node_reset(start, end, &weighted_n_node_samples) - - is_leaf = (depth >= max_depth or - n_node_samples < min_samples_split or - n_node_samples < 2 * min_samples_leaf or - weighted_n_node_samples < 2 * min_weight_leaf) - - if first: - impurity = splitter.node_impurity() - first = 0 - - # impurity == 0 with tolerance due to rounding errors - is_leaf = is_leaf or impurity <= EPSILON - - if not is_leaf: - splitter.node_split(impurity, &split, &n_constant_features) - # If EPSILON=0 in the below comparison, float precision - # issues stop splitting, producing trees that are - # dissimilar to v0.18 - is_leaf = (is_leaf or split.pos >= end or - (split.improvement + EPSILON < - min_impurity_decrease)) - - node_id = tree._add_node(parent, is_left, is_leaf, split.feature, - split.threshold, impurity, n_node_samples, - weighted_n_node_samples, - split.missing_go_to_left) - - if node_id == INTPTR_MAX: - rc = -1 - break - - # Store value for all nodes, to facilitate tree/model - # inspection and interpretation - splitter.node_value(tree.value + node_id * tree.value_stride) - - if not is_leaf: - # Push right child on stack - builder_stack.push({ - "start": split.pos, - "end": end, - "depth": depth + 1, - "parent": node_id, - "is_left": 0, - "impurity": split.impurity_right, - "n_constant_features": n_constant_features}) - - # Push left child on stack - builder_stack.push({ - "start": start, - "end": split.pos, - "depth": depth + 1, - "parent": node_id, - "is_left": 1, - "impurity": split.impurity_left, - "n_constant_features": n_constant_features}) - - if depth > max_depth_seen: - max_depth_seen = depth - - if rc >= 0: - rc = tree._resize_c(tree.node_count) - - if rc >= 0: - tree.max_depth = max_depth_seen - if rc == -1: - raise MemoryError() - - -# Best first builder ---------------------------------------------------------- -cdef struct FrontierRecord: - # Record of information of a Node, the frontier for a split. Those records are - # maintained in a heap to access the Node with the best improvement in impurity, - # allowing growing trees greedily on this improvement. - SIZE_t node_id - SIZE_t start - SIZE_t end - SIZE_t pos - SIZE_t depth - bint is_leaf - double impurity - double impurity_left - double impurity_right - double improvement - -cdef inline bool _compare_records( - const FrontierRecord& left, - const FrontierRecord& right, -): - return left.improvement < right.improvement - -cdef inline void _add_to_frontier( - FrontierRecord rec, - vector[FrontierRecord]& frontier, -) noexcept nogil: - """Adds record `rec` to the priority queue `frontier`.""" - frontier.push_back(rec) - push_heap(frontier.begin(), frontier.end(), &_compare_records) - - -cdef class BestFirstTreeBuilder(TreeBuilder): - """Build a decision tree in best-first fashion. - - The best node to expand is given by the node at the frontier that has the - highest impurity improvement. - """ - cdef SIZE_t max_leaf_nodes - - def __cinit__(self, Splitter splitter, SIZE_t min_samples_split, - SIZE_t min_samples_leaf, min_weight_leaf, - SIZE_t max_depth, SIZE_t max_leaf_nodes, - double min_impurity_decrease): - self.splitter = splitter - self.min_samples_split = min_samples_split - self.min_samples_leaf = min_samples_leaf - self.min_weight_leaf = min_weight_leaf - self.max_depth = max_depth - self.max_leaf_nodes = max_leaf_nodes - self.min_impurity_decrease = min_impurity_decrease - - cpdef build( - self, - Tree tree, - object X, - const DOUBLE_t[:, ::1] y, - const DOUBLE_t[:] sample_weight=None, - const unsigned char[::1] missing_values_in_feature_mask=None, - ): - """Build a decision tree from the training set (X, y).""" - - # check input - X, y, sample_weight = self._check_input(X, y, sample_weight) - - # Parameters - cdef Splitter splitter = self.splitter - cdef SIZE_t max_leaf_nodes = self.max_leaf_nodes - - # Recursive partition (without actual recursion) - splitter.init(X, y, sample_weight, missing_values_in_feature_mask) - - cdef vector[FrontierRecord] frontier - cdef FrontierRecord record - cdef FrontierRecord split_node_left - cdef FrontierRecord split_node_right - - cdef SIZE_t n_node_samples = splitter.n_samples - cdef SIZE_t max_split_nodes = max_leaf_nodes - 1 - cdef bint is_leaf - cdef SIZE_t max_depth_seen = -1 - cdef int rc = 0 - cdef Node* node - - # Initial capacity - cdef SIZE_t init_capacity = max_split_nodes + max_leaf_nodes - tree._resize(init_capacity) - - with nogil: - # add root to frontier - rc = self._add_split_node(splitter, tree, 0, n_node_samples, - INFINITY, IS_FIRST, IS_LEFT, NULL, 0, - &split_node_left) - if rc >= 0: - _add_to_frontier(split_node_left, frontier) - - while not frontier.empty(): - pop_heap(frontier.begin(), frontier.end(), &_compare_records) - record = frontier.back() - frontier.pop_back() - - node = &tree.nodes[record.node_id] - is_leaf = (record.is_leaf or max_split_nodes <= 0) - - if is_leaf: - # Node is not expandable; set node as leaf - node.left_child = _TREE_LEAF - node.right_child = _TREE_LEAF - node.feature = _TREE_UNDEFINED - node.threshold = _TREE_UNDEFINED - - else: - # Node is expandable - - # Decrement number of split nodes available - max_split_nodes -= 1 - - # Compute left split node - rc = self._add_split_node(splitter, tree, - record.start, record.pos, - record.impurity_left, - IS_NOT_FIRST, IS_LEFT, node, - record.depth + 1, - &split_node_left) - if rc == -1: - break - - # tree.nodes may have changed - node = &tree.nodes[record.node_id] - - # Compute right split node - rc = self._add_split_node(splitter, tree, record.pos, - record.end, - record.impurity_right, - IS_NOT_FIRST, IS_NOT_LEFT, node, - record.depth + 1, - &split_node_right) - if rc == -1: - break - - # Add nodes to queue - _add_to_frontier(split_node_left, frontier) - _add_to_frontier(split_node_right, frontier) - - if record.depth > max_depth_seen: - max_depth_seen = record.depth - - if rc >= 0: - rc = tree._resize_c(tree.node_count) - - if rc >= 0: - tree.max_depth = max_depth_seen - - if rc == -1: - raise MemoryError() - - cdef inline int _add_split_node(self, Splitter splitter, Tree tree, - SIZE_t start, SIZE_t end, double impurity, - bint is_first, bint is_left, Node* parent, - SIZE_t depth, - FrontierRecord* res) except -1 nogil: - """Adds node w/ partition ``[start, end)`` to the frontier. """ - cdef SplitRecord split - cdef SIZE_t node_id - cdef SIZE_t n_node_samples - cdef SIZE_t n_constant_features = 0 - cdef double min_impurity_decrease = self.min_impurity_decrease - cdef double weighted_n_node_samples - cdef bint is_leaf - - splitter.node_reset(start, end, &weighted_n_node_samples) - - if is_first: - impurity = splitter.node_impurity() - - n_node_samples = end - start - is_leaf = (depth >= self.max_depth or - n_node_samples < self.min_samples_split or - n_node_samples < 2 * self.min_samples_leaf or - weighted_n_node_samples < 2 * self.min_weight_leaf or - impurity <= EPSILON # impurity == 0 with tolerance - ) - - if not is_leaf: - splitter.node_split(impurity, &split, &n_constant_features) - # If EPSILON=0 in the below comparison, float precision issues stop - # splitting early, producing trees that are dissimilar to v0.18 - is_leaf = (is_leaf or split.pos >= end or - split.improvement + EPSILON < min_impurity_decrease) - - node_id = tree._add_node(parent - tree.nodes - if parent != NULL - else _TREE_UNDEFINED, - is_left, is_leaf, - split.feature, split.threshold, impurity, n_node_samples, - weighted_n_node_samples, - split.missing_go_to_left) - if node_id == INTPTR_MAX: - return -1 - - # compute values also for split nodes (might become leafs later). - splitter.node_value(tree.value + node_id * tree.value_stride) - - res.node_id = node_id - res.start = start - res.end = end - res.depth = depth - res.impurity = impurity - - if not is_leaf: - # is split node - res.pos = split.pos - res.is_leaf = 0 - res.improvement = split.improvement - res.impurity_left = split.impurity_left - res.impurity_right = split.impurity_right - - else: - # is leaf => 0 improvement - res.pos = end - res.is_leaf = 1 - res.improvement = 0.0 - res.impurity_left = impurity - res.impurity_right = impurity - - return 0 - - -# ============================================================================= -# Tree -# ============================================================================= - -cdef class Tree: - """Array-based representation of a binary decision tree. - - The binary tree is represented as a number of parallel arrays. The i-th - element of each array holds information about the node `i`. Node 0 is the - tree's root. You can find a detailed description of all arrays in - `_tree.pxd`. NOTE: Some of the arrays only apply to either leaves or split - nodes, resp. In this case the values of nodes of the other type are - arbitrary! - - Attributes - ---------- - node_count : int - The number of nodes (internal nodes + leaves) in the tree. - - capacity : int - The current capacity (i.e., size) of the arrays, which is at least as - great as `node_count`. - - max_depth : int - The depth of the tree, i.e. the maximum depth of its leaves. - - children_left : array of int, shape [node_count] - children_left[i] holds the node id of the left child of node i. - For leaves, children_left[i] == TREE_LEAF. Otherwise, - children_left[i] > i. This child handles the case where - X[:, feature[i]] <= threshold[i]. - - children_right : array of int, shape [node_count] - children_right[i] holds the node id of the right child of node i. - For leaves, children_right[i] == TREE_LEAF. Otherwise, - children_right[i] > i. This child handles the case where - X[:, feature[i]] > threshold[i]. - - feature : array of int, shape [node_count] - feature[i] holds the feature to split on, for the internal node i. - - threshold : array of double, shape [node_count] - threshold[i] holds the threshold for the internal node i. - - value : array of double, shape [node_count, n_outputs, max_n_classes] - Contains the constant prediction value of each node. - - impurity : array of double, shape [node_count] - impurity[i] holds the impurity (i.e., the value of the splitting - criterion) at node i. - - n_node_samples : array of int, shape [node_count] - n_node_samples[i] holds the number of training samples reaching node i. - - weighted_n_node_samples : array of double, shape [node_count] - weighted_n_node_samples[i] holds the weighted number of training samples - reaching node i. - """ - # Wrap for outside world. - # WARNING: these reference the current `nodes` and `value` buffers, which - # must not be freed by a subsequent memory allocation. - # (i.e. through `_resize` or `__setstate__`) - @property - def n_classes(self): - return sizet_ptr_to_ndarray(self.n_classes, self.n_outputs) - - @property - def children_left(self): - return self._get_node_ndarray()['left_child'][:self.node_count] - - @property - def children_right(self): - return self._get_node_ndarray()['right_child'][:self.node_count] - - @property - def n_leaves(self): - return np.sum(np.logical_and( - self.children_left == -1, - self.children_right == -1)) - - @property - def feature(self): - return self._get_node_ndarray()['feature'][:self.node_count] - - @property - def threshold(self): - return self._get_node_ndarray()['threshold'][:self.node_count] - - @property - def impurity(self): - return self._get_node_ndarray()['impurity'][:self.node_count] - - @property - def n_node_samples(self): - return self._get_node_ndarray()['n_node_samples'][:self.node_count] - - @property - def weighted_n_node_samples(self): - return self._get_node_ndarray()['weighted_n_node_samples'][:self.node_count] - - @property - def missing_go_to_left(self): - return self._get_node_ndarray()['missing_go_to_left'][:self.node_count] - - @property - def value(self): - return self._get_value_ndarray()[:self.node_count] - - # TODO: Convert n_classes to cython.integral memory view once - # https://github.com/cython/cython/issues/5243 is fixed - def __cinit__(self, int n_features, cnp.ndarray n_classes, int n_outputs): - """Constructor.""" - cdef SIZE_t dummy = 0 - size_t_dtype = np.array(dummy).dtype - - n_classes = _check_n_classes(n_classes, size_t_dtype) - - # Input/Output layout - self.n_features = n_features - self.n_outputs = n_outputs - self.n_classes = NULL - safe_realloc(&self.n_classes, n_outputs) - - self.max_n_classes = np.max(n_classes) - self.value_stride = n_outputs * self.max_n_classes - - cdef SIZE_t k - for k in range(n_outputs): - self.n_classes[k] = n_classes[k] - - # Inner structures - self.max_depth = 0 - self.node_count = 0 - self.capacity = 0 - self.value = NULL - self.nodes = NULL - - def __dealloc__(self): - """Destructor.""" - # Free all inner structures - free(self.n_classes) - free(self.value) - free(self.nodes) - - def __reduce__(self): - """Reduce re-implementation, for pickling.""" - return (Tree, (self.n_features, - sizet_ptr_to_ndarray(self.n_classes, self.n_outputs), - self.n_outputs), self.__getstate__()) - - def __getstate__(self): - """Getstate re-implementation, for pickling.""" - d = {} - # capacity is inferred during the __setstate__ using nodes - d["max_depth"] = self.max_depth - d["node_count"] = self.node_count - d["nodes"] = self._get_node_ndarray() - d["values"] = self._get_value_ndarray() - return d - - def __setstate__(self, d): - """Setstate re-implementation, for unpickling.""" - self.max_depth = d["max_depth"] - self.node_count = d["node_count"] - - if 'nodes' not in d: - raise ValueError('You have loaded Tree version which ' - 'cannot be imported') - - node_ndarray = d['nodes'] - value_ndarray = d['values'] - - value_shape = (node_ndarray.shape[0], self.n_outputs, - self.max_n_classes) - - node_ndarray = _check_node_ndarray(node_ndarray, expected_dtype=NODE_DTYPE) - value_ndarray = _check_value_ndarray( - value_ndarray, - expected_dtype=np.dtype(np.float64), - expected_shape=value_shape - ) - - self.capacity = node_ndarray.shape[0] - if self._resize_c(self.capacity) != 0: - raise MemoryError("resizing tree to %d" % self.capacity) - - memcpy(self.nodes, cnp.PyArray_DATA(node_ndarray), - self.capacity * sizeof(Node)) - memcpy(self.value, cnp.PyArray_DATA(value_ndarray), - self.capacity * self.value_stride * sizeof(double)) - - cdef int _resize(self, SIZE_t capacity) except -1 nogil: - """Resize all inner arrays to `capacity`, if `capacity` == -1, then - double the size of the inner arrays. - - Returns -1 in case of failure to allocate memory (and raise MemoryError) - or 0 otherwise. - """ - if self._resize_c(capacity) != 0: - # Acquire gil only if we need to raise - with gil: - raise MemoryError() - - cdef int _resize_c(self, SIZE_t capacity=INTPTR_MAX) except -1 nogil: - """Guts of _resize - - Returns -1 in case of failure to allocate memory (and raise MemoryError) - or 0 otherwise. - """ - if capacity == self.capacity and self.nodes != NULL: - return 0 - - if capacity == INTPTR_MAX: - if self.capacity == 0: - capacity = 3 # default initial value - else: - capacity = 2 * self.capacity - - safe_realloc(&self.nodes, capacity) - safe_realloc(&self.value, capacity * self.value_stride) - - # value memory is initialised to 0 to enable classifier argmax - if capacity > self.capacity: - memset((self.value + self.capacity * self.value_stride), 0, - (capacity - self.capacity) * self.value_stride * - sizeof(double)) - - # if capacity smaller than node_count, adjust the counter - if capacity < self.node_count: - self.node_count = capacity - - self.capacity = capacity - return 0 - - cdef SIZE_t _add_node(self, SIZE_t parent, bint is_left, bint is_leaf, - SIZE_t feature, double threshold, double impurity, - SIZE_t n_node_samples, - double weighted_n_node_samples, - unsigned char missing_go_to_left) except -1 nogil: - """Add a node to the tree. - - The new node registers itself as the child of its parent. - - Returns (size_t)(-1) on error. - """ - cdef SIZE_t node_id = self.node_count - - if node_id >= self.capacity: - if self._resize_c() != 0: - return INTPTR_MAX - - cdef Node* node = &self.nodes[node_id] - node.impurity = impurity - node.n_node_samples = n_node_samples - node.weighted_n_node_samples = weighted_n_node_samples - - if parent != _TREE_UNDEFINED: - if is_left: - self.nodes[parent].left_child = node_id - else: - self.nodes[parent].right_child = node_id - - if is_leaf: - node.left_child = _TREE_LEAF - node.right_child = _TREE_LEAF - node.feature = _TREE_UNDEFINED - node.threshold = _TREE_UNDEFINED - - else: - # left_child and right_child will be set later - node.feature = feature - node.threshold = threshold - node.missing_go_to_left = missing_go_to_left - - self.node_count += 1 - - return node_id - - cpdef cnp.ndarray predict(self, object X): - """Predict target for X.""" - out = self._get_value_ndarray().take(self.apply(X), axis=0, - mode='clip') - if self.n_outputs == 1: - out = out.reshape(X.shape[0], self.max_n_classes) - return out - - cpdef cnp.ndarray apply(self, object X): - """Finds the terminal region (=leaf node) for each sample in X.""" - if issparse(X): - return self._apply_sparse_csr(X) - else: - return self._apply_dense(X) - - cdef inline cnp.ndarray _apply_dense(self, object X): - """Finds the terminal region (=leaf node) for each sample in X.""" - - # Check input - if not isinstance(X, np.ndarray): - raise ValueError("X should be in np.ndarray format, got %s" - % type(X)) - - if X.dtype != DTYPE: - raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) - - # Extract input - cdef const DTYPE_t[:, :] X_ndarray = X - cdef SIZE_t n_samples = X.shape[0] - cdef DTYPE_t X_i_node_feature - - # Initialize output - cdef SIZE_t[:] out = np.zeros(n_samples, dtype=np.intp) - - # Initialize auxiliary data-structure - cdef Node* node = NULL - cdef SIZE_t i = 0 - - with nogil: - for i in range(n_samples): - node = self.nodes - # While node not a leaf - while node.left_child != _TREE_LEAF: - X_i_node_feature = X_ndarray[i, node.feature] - # ... and node.right_child != _TREE_LEAF: - if isnan(X_i_node_feature): - if node.missing_go_to_left: - node = &self.nodes[node.left_child] - else: - node = &self.nodes[node.right_child] - elif X_i_node_feature <= node.threshold: - node = &self.nodes[node.left_child] - else: - node = &self.nodes[node.right_child] - - out[i] = (node - self.nodes) # node offset - - return np.asarray(out) - - cdef inline cnp.ndarray _apply_sparse_csr(self, object X): - """Finds the terminal region (=leaf node) for each sample in sparse X. - """ - # Check input - if not isspmatrix_csr(X): - raise ValueError("X should be in csr_matrix format, got %s" - % type(X)) - - if X.dtype != DTYPE: - raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) - - # Extract input - cdef const DTYPE_t[:] X_data = X.data - cdef const INT32_t[:] X_indices = X.indices - cdef const INT32_t[:] X_indptr = X.indptr - - cdef SIZE_t n_samples = X.shape[0] - cdef SIZE_t n_features = X.shape[1] - - # Initialize output - cdef SIZE_t[:] out = np.zeros(n_samples, dtype=np.intp) - - # Initialize auxiliary data-structure - cdef DTYPE_t feature_value = 0. - cdef Node* node = NULL - cdef DTYPE_t* X_sample = NULL - cdef SIZE_t i = 0 - cdef INT32_t k = 0 - - # feature_to_sample as a data structure records the last seen sample - # for each feature; functionally, it is an efficient way to identify - # which features are nonzero in the present sample. - cdef SIZE_t* feature_to_sample = NULL - - safe_realloc(&X_sample, n_features) - safe_realloc(&feature_to_sample, n_features) - - with nogil: - memset(feature_to_sample, -1, n_features * sizeof(SIZE_t)) - - for i in range(n_samples): - node = self.nodes - - for k in range(X_indptr[i], X_indptr[i + 1]): - feature_to_sample[X_indices[k]] = i - X_sample[X_indices[k]] = X_data[k] - - # While node not a leaf - while node.left_child != _TREE_LEAF: - # ... and node.right_child != _TREE_LEAF: - if feature_to_sample[node.feature] == i: - feature_value = X_sample[node.feature] - - else: - feature_value = 0. - - if feature_value <= node.threshold: - node = &self.nodes[node.left_child] - else: - node = &self.nodes[node.right_child] - - out[i] = (node - self.nodes) # node offset - - # Free auxiliary arrays - free(X_sample) - free(feature_to_sample) - - return np.asarray(out) - - cpdef object decision_path(self, object X): - """Finds the decision path (=node) for each sample in X.""" - if issparse(X): - return self._decision_path_sparse_csr(X) - else: - return self._decision_path_dense(X) - - cdef inline object _decision_path_dense(self, object X): - """Finds the decision path (=node) for each sample in X.""" - - # Check input - if not isinstance(X, np.ndarray): - raise ValueError("X should be in np.ndarray format, got %s" - % type(X)) - - if X.dtype != DTYPE: - raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) - - # Extract input - cdef const DTYPE_t[:, :] X_ndarray = X - cdef SIZE_t n_samples = X.shape[0] - - # Initialize output - cdef SIZE_t[:] indptr = np.zeros(n_samples + 1, dtype=np.intp) - cdef SIZE_t[:] indices = np.zeros( - n_samples * (1 + self.max_depth), dtype=np.intp - ) - - # Initialize auxiliary data-structure - cdef Node* node = NULL - cdef SIZE_t i = 0 - - with nogil: - for i in range(n_samples): - node = self.nodes - indptr[i + 1] = indptr[i] - - # Add all external nodes - while node.left_child != _TREE_LEAF: - # ... and node.right_child != _TREE_LEAF: - indices[indptr[i + 1]] = (node - self.nodes) - indptr[i + 1] += 1 - - if X_ndarray[i, node.feature] <= node.threshold: - node = &self.nodes[node.left_child] - else: - node = &self.nodes[node.right_child] - - # Add the leave node - indices[indptr[i + 1]] = (node - self.nodes) - indptr[i + 1] += 1 - - indices = indices[:indptr[n_samples]] - cdef SIZE_t[:] data = np.ones(shape=len(indices), dtype=np.intp) - out = csr_matrix((data, indices, indptr), - shape=(n_samples, self.node_count)) - - return out - - cdef inline object _decision_path_sparse_csr(self, object X): - """Finds the decision path (=node) for each sample in X.""" - - # Check input - if not isspmatrix_csr(X): - raise ValueError("X should be in csr_matrix format, got %s" - % type(X)) - - if X.dtype != DTYPE: - raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) - - # Extract input - cdef const DTYPE_t[:] X_data = X.data - cdef const INT32_t[:] X_indices = X.indices - cdef const INT32_t[:] X_indptr = X.indptr - - cdef SIZE_t n_samples = X.shape[0] - cdef SIZE_t n_features = X.shape[1] - - # Initialize output - cdef SIZE_t[:] indptr = np.zeros(n_samples + 1, dtype=np.intp) - cdef SIZE_t[:] indices = np.zeros( - n_samples * (1 + self.max_depth), dtype=np.intp - ) - - # Initialize auxiliary data-structure - cdef DTYPE_t feature_value = 0. - cdef Node* node = NULL - cdef DTYPE_t* X_sample = NULL - cdef SIZE_t i = 0 - cdef INT32_t k = 0 - - # feature_to_sample as a data structure records the last seen sample - # for each feature; functionally, it is an efficient way to identify - # which features are nonzero in the present sample. - cdef SIZE_t* feature_to_sample = NULL - - safe_realloc(&X_sample, n_features) - safe_realloc(&feature_to_sample, n_features) - - with nogil: - memset(feature_to_sample, -1, n_features * sizeof(SIZE_t)) - - for i in range(n_samples): - node = self.nodes - indptr[i + 1] = indptr[i] - - for k in range(X_indptr[i], X_indptr[i + 1]): - feature_to_sample[X_indices[k]] = i - X_sample[X_indices[k]] = X_data[k] - - # While node not a leaf - while node.left_child != _TREE_LEAF: - # ... and node.right_child != _TREE_LEAF: - - indices[indptr[i + 1]] = (node - self.nodes) - indptr[i + 1] += 1 - - if feature_to_sample[node.feature] == i: - feature_value = X_sample[node.feature] - - else: - feature_value = 0. - - if feature_value <= node.threshold: - node = &self.nodes[node.left_child] - else: - node = &self.nodes[node.right_child] - - # Add the leave node - indices[indptr[i + 1]] = (node - self.nodes) - indptr[i + 1] += 1 - - # Free auxiliary arrays - free(X_sample) - free(feature_to_sample) - - indices = indices[:indptr[n_samples]] - cdef SIZE_t[:] data = np.ones(shape=len(indices), dtype=np.intp) - out = csr_matrix((data, indices, indptr), - shape=(n_samples, self.node_count)) - - return out - - cpdef compute_node_depths(self): - """Compute the depth of each node in a tree. - - .. versionadded:: 1.3 - - Returns - ------- - depths : ndarray of shape (self.node_count,), dtype=np.int64 - The depth of each node in the tree. - """ - cdef: - cnp.int64_t[::1] depths = np.empty(self.node_count, dtype=np.int64) - cnp.npy_intp[:] children_left = self.children_left - cnp.npy_intp[:] children_right = self.children_right - cnp.npy_intp node_id - cnp.npy_intp node_count = self.node_count - cnp.int64_t depth - - depths[0] = 1 # init root node - for node_id in range(node_count): - if children_left[node_id] != _TREE_LEAF: - depth = depths[node_id] + 1 - depths[children_left[node_id]] = depth - depths[children_right[node_id]] = depth - - return depths.base - - cpdef compute_feature_importances(self, normalize=True): - """Computes the importance of each feature (aka variable).""" - cdef Node* left - cdef Node* right - cdef Node* nodes = self.nodes - cdef Node* node = nodes - cdef Node* end_node = node + self.node_count - - cdef double normalizer = 0. - - cdef cnp.float64_t[:] importances = np.zeros(self.n_features) - - with nogil: - while node != end_node: - if node.left_child != _TREE_LEAF: - # ... and node.right_child != _TREE_LEAF: - left = &nodes[node.left_child] - right = &nodes[node.right_child] - - importances[node.feature] += ( - node.weighted_n_node_samples * node.impurity - - left.weighted_n_node_samples * left.impurity - - right.weighted_n_node_samples * right.impurity) - node += 1 - - for i in range(self.n_features): - importances[i] /= nodes[0].weighted_n_node_samples - - if normalize: - normalizer = np.sum(importances) - - if normalizer > 0.0: - # Avoid dividing by zero (e.g., when root is pure) - for i in range(self.n_features): - importances[i] /= normalizer - - return np.asarray(importances) - - cdef cnp.ndarray _get_value_ndarray(self): - """Wraps value as a 3-d NumPy array. - - The array keeps a reference to this Tree, which manages the underlying - memory. - """ - cdef cnp.npy_intp shape[3] - shape[0] = self.node_count - shape[1] = self.n_outputs - shape[2] = self.max_n_classes - cdef cnp.ndarray arr - arr = cnp.PyArray_SimpleNewFromData(3, shape, cnp.NPY_DOUBLE, self.value) - Py_INCREF(self) - if PyArray_SetBaseObject(arr, self) < 0: - raise ValueError("Can't initialize array.") - return arr - - cdef cnp.ndarray _get_node_ndarray(self): - """Wraps nodes as a NumPy struct array. - - The array keeps a reference to this Tree, which manages the underlying - memory. Individual fields are publicly accessible as properties of the - Tree. - """ - cdef cnp.npy_intp shape[1] - shape[0] = self.node_count - cdef cnp.npy_intp strides[1] - strides[0] = sizeof(Node) - cdef cnp.ndarray arr - Py_INCREF(NODE_DTYPE) - arr = PyArray_NewFromDescr( cnp.ndarray, - NODE_DTYPE, 1, shape, - strides, self.nodes, - cnp.NPY_ARRAY_DEFAULT, None) - Py_INCREF(self) - if PyArray_SetBaseObject(arr, self) < 0: - raise ValueError("Can't initialize array.") - return arr - - def compute_partial_dependence(self, DTYPE_t[:, ::1] X, - int[::1] target_features, - double[::1] out): - """Partial dependence of the response on the ``target_feature`` set. - - For each sample in ``X`` a tree traversal is performed. - Each traversal starts from the root with weight 1.0. - - At each non-leaf node that splits on a target feature, either - the left child or the right child is visited based on the feature - value of the current sample, and the weight is not modified. - At each non-leaf node that splits on a complementary feature, - both children are visited and the weight is multiplied by the fraction - of training samples which went to each child. - - At each leaf, the value of the node is multiplied by the current - weight (weights sum to 1 for all visited terminal nodes). - - Parameters - ---------- - X : view on 2d ndarray, shape (n_samples, n_target_features) - The grid points on which the partial dependence should be - evaluated. - target_features : view on 1d ndarray, shape (n_target_features) - The set of target features for which the partial dependence - should be evaluated. - out : view on 1d ndarray, shape (n_samples) - The value of the partial dependence function on each grid - point. - """ - cdef: - double[::1] weight_stack = np.zeros(self.node_count, - dtype=np.float64) - SIZE_t[::1] node_idx_stack = np.zeros(self.node_count, - dtype=np.intp) - SIZE_t sample_idx - SIZE_t feature_idx - int stack_size - double left_sample_frac - double current_weight - double total_weight # used for sanity check only - Node *current_node # use a pointer to avoid copying attributes - SIZE_t current_node_idx - bint is_target_feature - SIZE_t _TREE_LEAF = TREE_LEAF # to avoid python interactions - - for sample_idx in range(X.shape[0]): - # init stacks for current sample - stack_size = 1 - node_idx_stack[0] = 0 # root node - weight_stack[0] = 1 # all the samples are in the root node - total_weight = 0 - - while stack_size > 0: - # pop the stack - stack_size -= 1 - current_node_idx = node_idx_stack[stack_size] - current_node = &self.nodes[current_node_idx] - - if current_node.left_child == _TREE_LEAF: - # leaf node - out[sample_idx] += (weight_stack[stack_size] * - self.value[current_node_idx]) - total_weight += weight_stack[stack_size] - else: - # non-leaf node - - # determine if the split feature is a target feature - is_target_feature = False - for feature_idx in range(target_features.shape[0]): - if target_features[feature_idx] == current_node.feature: - is_target_feature = True - break - - if is_target_feature: - # In this case, we push left or right child on stack - if X[sample_idx, feature_idx] <= current_node.threshold: - node_idx_stack[stack_size] = current_node.left_child - else: - node_idx_stack[stack_size] = current_node.right_child - stack_size += 1 - else: - # In this case, we push both children onto the stack, - # and give a weight proportional to the number of - # samples going through each branch. - - # push left child - node_idx_stack[stack_size] = current_node.left_child - left_sample_frac = ( - self.nodes[current_node.left_child].weighted_n_node_samples / - current_node.weighted_n_node_samples) - current_weight = weight_stack[stack_size] - weight_stack[stack_size] = current_weight * left_sample_frac - stack_size += 1 - - # push right child - node_idx_stack[stack_size] = current_node.right_child - weight_stack[stack_size] = ( - current_weight * (1 - left_sample_frac)) - stack_size += 1 - - # Sanity check. Should never happen. - if not (0.999 < total_weight < 1.001): - raise ValueError("Total weight should be 1.0 but was %.9f" % - total_weight) - - -def _check_n_classes(n_classes, expected_dtype): - if n_classes.ndim != 1: - raise ValueError( - f"Wrong dimensions for n_classes from the pickle: " - f"expected 1, got {n_classes.ndim}" - ) - - if n_classes.dtype == expected_dtype: - return n_classes - - # Handles both different endianness and different bitness - if n_classes.dtype.kind == "i" and n_classes.dtype.itemsize in [4, 8]: - return n_classes.astype(expected_dtype, casting="same_kind") - - raise ValueError( - "n_classes from the pickle has an incompatible dtype:\n" - f"- expected: {expected_dtype}\n" - f"- got: {n_classes.dtype}" - ) - - -def _check_value_ndarray(value_ndarray, expected_dtype, expected_shape): - if value_ndarray.shape != expected_shape: - raise ValueError( - "Wrong shape for value array from the pickle: " - f"expected {expected_shape}, got {value_ndarray.shape}" - ) - - if not value_ndarray.flags.c_contiguous: - raise ValueError( - "value array from the pickle should be a C-contiguous array" - ) - - if value_ndarray.dtype == expected_dtype: - return value_ndarray - - # Handles different endianness - if value_ndarray.dtype.str.endswith('f8'): - return value_ndarray.astype(expected_dtype, casting='equiv') - - raise ValueError( - "value array from the pickle has an incompatible dtype:\n" - f"- expected: {expected_dtype}\n" - f"- got: {value_ndarray.dtype}" - ) - - -def _dtype_to_dict(dtype): - return {name: dt.str for name, (dt, *rest) in dtype.fields.items()} - - -def _dtype_dict_with_modified_bitness(dtype_dict): - # field names in Node struct with SIZE_t types (see sklearn/tree/_tree.pxd) - indexing_field_names = ["left_child", "right_child", "feature", "n_node_samples"] - - expected_dtype_size = str(struct.calcsize("P")) - allowed_dtype_size = "8" if expected_dtype_size == "4" else "4" - - allowed_dtype_dict = dtype_dict.copy() - for name in indexing_field_names: - allowed_dtype_dict[name] = allowed_dtype_dict[name].replace( - expected_dtype_size, allowed_dtype_size - ) - - return allowed_dtype_dict - - -def _all_compatible_dtype_dicts(dtype): - # The Cython code for decision trees uses platform-specific SIZE_t - # typed indexing fields that correspond to either i4 or i8 dtypes for - # the matching fields in the numpy array depending on the bitness of - # the platform (32 bit or 64 bit respectively). - # - # We need to cast the indexing fields of the NODE_DTYPE-dtyped array at - # pickle load time to enable cross-bitness deployment scenarios. We - # typically want to make it possible to run the expensive fit method of - # a tree estimator on a 64 bit server platform, pickle the estimator - # for deployment and run the predict method of a low power 32 bit edge - # platform. - # - # A similar thing happens for endianness, the machine where the pickle was - # saved can have a different endianness than the machine where the pickle - # is loaded - - dtype_dict = _dtype_to_dict(dtype) - dtype_dict_with_modified_bitness = _dtype_dict_with_modified_bitness(dtype_dict) - dtype_dict_with_modified_endianness = _dtype_to_dict(dtype.newbyteorder()) - dtype_dict_with_modified_bitness_and_endianness = _dtype_dict_with_modified_bitness( - dtype_dict_with_modified_endianness - ) - - return [ - dtype_dict, - dtype_dict_with_modified_bitness, - dtype_dict_with_modified_endianness, - dtype_dict_with_modified_bitness_and_endianness, - ] - - -def _check_node_ndarray(node_ndarray, expected_dtype): - if node_ndarray.ndim != 1: - raise ValueError( - "Wrong dimensions for node array from the pickle: " - f"expected 1, got {node_ndarray.ndim}" - ) - - if not node_ndarray.flags.c_contiguous: - raise ValueError( - "node array from the pickle should be a C-contiguous array" - ) - - node_ndarray_dtype = node_ndarray.dtype - if node_ndarray_dtype == expected_dtype: - return node_ndarray - - node_ndarray_dtype_dict = _dtype_to_dict(node_ndarray_dtype) - all_compatible_dtype_dicts = _all_compatible_dtype_dicts(expected_dtype) - - if node_ndarray_dtype_dict not in all_compatible_dtype_dicts: - raise ValueError( - "node array from the pickle has an incompatible dtype:\n" - f"- expected: {expected_dtype}\n" - f"- got : {node_ndarray_dtype}" - ) - - return node_ndarray.astype(expected_dtype, casting="same_kind") - - -# ============================================================================= -# Build Pruned Tree -# ============================================================================= - - -cdef class _CCPPruneController: - """Base class used by build_pruned_tree_ccp and ccp_pruning_path - to control pruning. - """ - cdef bint stop_pruning(self, DOUBLE_t effective_alpha) noexcept nogil: - """Return 1 to stop pruning and 0 to continue pruning""" - return 0 - - cdef void save_metrics(self, DOUBLE_t effective_alpha, - DOUBLE_t subtree_impurities) noexcept nogil: - """Save metrics when pruning""" - pass - - cdef void after_pruning(self, unsigned char[:] in_subtree) noexcept nogil: - """Called after pruning""" - pass - - -cdef class _AlphaPruner(_CCPPruneController): - """Use alpha to control when to stop pruning.""" - cdef DOUBLE_t ccp_alpha - cdef SIZE_t capacity - - def __cinit__(self, DOUBLE_t ccp_alpha): - self.ccp_alpha = ccp_alpha - self.capacity = 0 - - cdef bint stop_pruning(self, DOUBLE_t effective_alpha) noexcept nogil: - # The subtree on the previous iteration has the greatest ccp_alpha - # less than or equal to self.ccp_alpha - return self.ccp_alpha < effective_alpha - - cdef void after_pruning(self, unsigned char[:] in_subtree) noexcept nogil: - """Updates the number of leaves in subtree""" - for i in range(in_subtree.shape[0]): - if in_subtree[i]: - self.capacity += 1 - - -cdef class _PathFinder(_CCPPruneController): - """Record metrics used to return the cost complexity path.""" - cdef DOUBLE_t[:] ccp_alphas - cdef DOUBLE_t[:] impurities - cdef UINT32_t count - - def __cinit__(self, int node_count): - self.ccp_alphas = np.zeros(shape=(node_count), dtype=np.float64) - self.impurities = np.zeros(shape=(node_count), dtype=np.float64) - self.count = 0 - - cdef void save_metrics(self, - DOUBLE_t effective_alpha, - DOUBLE_t subtree_impurities) noexcept nogil: - self.ccp_alphas[self.count] = effective_alpha - self.impurities[self.count] = subtree_impurities - self.count += 1 - - -cdef struct CostComplexityPruningRecord: - SIZE_t node_idx - SIZE_t parent - -cdef _cost_complexity_prune(unsigned char[:] leaves_in_subtree, # OUT - Tree orig_tree, - _CCPPruneController controller): - """Perform cost complexity pruning. - - This function takes an already grown tree, `orig_tree` and outputs a - boolean mask `leaves_in_subtree` which are the leaves in the pruned tree. - During the pruning process, the controller is passed the effective alpha and - the subtree impurities. Furthermore, the controller signals when to stop - pruning. - - Parameters - ---------- - leaves_in_subtree : unsigned char[:] - Output for leaves of subtree - orig_tree : Tree - Original tree - ccp_controller : _CCPPruneController - Cost complexity controller - """ - - cdef: - SIZE_t i - SIZE_t n_nodes = orig_tree.node_count - # prior probability using weighted samples - DOUBLE_t[:] weighted_n_node_samples = orig_tree.weighted_n_node_samples - DOUBLE_t total_sum_weights = weighted_n_node_samples[0] - DOUBLE_t[:] impurity = orig_tree.impurity - # weighted impurity of each node - DOUBLE_t[:] r_node = np.empty(shape=n_nodes, dtype=np.float64) - - SIZE_t[:] child_l = orig_tree.children_left - SIZE_t[:] child_r = orig_tree.children_right - SIZE_t[:] parent = np.zeros(shape=n_nodes, dtype=np.intp) - - stack[CostComplexityPruningRecord] ccp_stack - CostComplexityPruningRecord stack_record - SIZE_t node_idx - stack[SIZE_t] node_indices_stack - - SIZE_t[:] n_leaves = np.zeros(shape=n_nodes, dtype=np.intp) - DOUBLE_t[:] r_branch = np.zeros(shape=n_nodes, dtype=np.float64) - DOUBLE_t current_r - SIZE_t leaf_idx - SIZE_t parent_idx - - # candidate nodes that can be pruned - unsigned char[:] candidate_nodes = np.zeros(shape=n_nodes, - dtype=np.uint8) - # nodes in subtree - unsigned char[:] in_subtree = np.ones(shape=n_nodes, dtype=np.uint8) - SIZE_t pruned_branch_node_idx - DOUBLE_t subtree_alpha - DOUBLE_t effective_alpha - SIZE_t n_pruned_leaves - DOUBLE_t r_diff - DOUBLE_t max_float64 = np.finfo(np.float64).max - - # find parent node ids and leaves - with nogil: - - for i in range(r_node.shape[0]): - r_node[i] = ( - weighted_n_node_samples[i] * impurity[i] / total_sum_weights) - - # Push the root node - ccp_stack.push({"node_idx": 0, "parent": _TREE_UNDEFINED}) - - while not ccp_stack.empty(): - stack_record = ccp_stack.top() - ccp_stack.pop() - - node_idx = stack_record.node_idx - parent[node_idx] = stack_record.parent - - if child_l[node_idx] == _TREE_LEAF: - # ... and child_r[node_idx] == _TREE_LEAF: - leaves_in_subtree[node_idx] = 1 - else: - ccp_stack.push({"node_idx": child_l[node_idx], "parent": node_idx}) - ccp_stack.push({"node_idx": child_r[node_idx], "parent": node_idx}) - - # computes number of leaves in all branches and the overall impurity of - # the branch. The overall impurity is the sum of r_node in its leaves. - for leaf_idx in range(leaves_in_subtree.shape[0]): - if not leaves_in_subtree[leaf_idx]: - continue - r_branch[leaf_idx] = r_node[leaf_idx] - - # bubble up values to ancestor nodes - current_r = r_node[leaf_idx] - while leaf_idx != 0: - parent_idx = parent[leaf_idx] - r_branch[parent_idx] += current_r - n_leaves[parent_idx] += 1 - leaf_idx = parent_idx - - for i in range(leaves_in_subtree.shape[0]): - candidate_nodes[i] = not leaves_in_subtree[i] - - # save metrics before pruning - controller.save_metrics(0.0, r_branch[0]) - - # while root node is not a leaf - while candidate_nodes[0]: - - # computes ccp_alpha for subtrees and finds the minimal alpha - effective_alpha = max_float64 - for i in range(n_nodes): - if not candidate_nodes[i]: - continue - subtree_alpha = (r_node[i] - r_branch[i]) / (n_leaves[i] - 1) - if subtree_alpha < effective_alpha: - effective_alpha = subtree_alpha - pruned_branch_node_idx = i - - if controller.stop_pruning(effective_alpha): - break - - node_indices_stack.push(pruned_branch_node_idx) - - # descendants of branch are not in subtree - while not node_indices_stack.empty(): - node_idx = node_indices_stack.top() - node_indices_stack.pop() - - if not in_subtree[node_idx]: - continue # branch has already been marked for pruning - candidate_nodes[node_idx] = 0 - leaves_in_subtree[node_idx] = 0 - in_subtree[node_idx] = 0 - - if child_l[node_idx] != _TREE_LEAF: - # ... and child_r[node_idx] != _TREE_LEAF: - node_indices_stack.push(child_l[node_idx]) - node_indices_stack.push(child_r[node_idx]) - leaves_in_subtree[pruned_branch_node_idx] = 1 - in_subtree[pruned_branch_node_idx] = 1 - - # updates number of leaves - n_pruned_leaves = n_leaves[pruned_branch_node_idx] - 1 - n_leaves[pruned_branch_node_idx] = 0 - - # computes the increase in r_branch to bubble up - r_diff = r_node[pruned_branch_node_idx] - r_branch[pruned_branch_node_idx] - r_branch[pruned_branch_node_idx] = r_node[pruned_branch_node_idx] - - # bubble up values to ancestors - node_idx = parent[pruned_branch_node_idx] - while node_idx != _TREE_UNDEFINED: - n_leaves[node_idx] -= n_pruned_leaves - r_branch[node_idx] += r_diff - node_idx = parent[node_idx] - - controller.save_metrics(effective_alpha, r_branch[0]) - - controller.after_pruning(in_subtree) - - -def _build_pruned_tree_ccp( - Tree tree, # OUT - Tree orig_tree, - DOUBLE_t ccp_alpha -): - """Build a pruned tree from the original tree using cost complexity - pruning. - - The values and nodes from the original tree are copied into the pruned - tree. - - Parameters - ---------- - tree : Tree - Location to place the pruned tree - orig_tree : Tree - Original tree - ccp_alpha : positive double - Complexity parameter. The subtree with the largest cost complexity - that is smaller than ``ccp_alpha`` will be chosen. By default, - no pruning is performed. - """ - - cdef: - SIZE_t n_nodes = orig_tree.node_count - unsigned char[:] leaves_in_subtree = np.zeros( - shape=n_nodes, dtype=np.uint8) - - pruning_controller = _AlphaPruner(ccp_alpha=ccp_alpha) - - _cost_complexity_prune(leaves_in_subtree, orig_tree, pruning_controller) - - _build_pruned_tree(tree, orig_tree, leaves_in_subtree, - pruning_controller.capacity) - - -def ccp_pruning_path(Tree orig_tree): - """Computes the cost complexity pruning path. - - Parameters - ---------- - tree : Tree - Original tree. - - Returns - ------- - path_info : dict - Information about pruning path with attributes: - - ccp_alphas : ndarray - Effective alphas of subtree during pruning. - - impurities : ndarray - Sum of the impurities of the subtree leaves for the - corresponding alpha value in ``ccp_alphas``. - """ - cdef: - unsigned char[:] leaves_in_subtree = np.zeros( - shape=orig_tree.node_count, dtype=np.uint8) - - path_finder = _PathFinder(orig_tree.node_count) - - _cost_complexity_prune(leaves_in_subtree, orig_tree, path_finder) - - cdef: - UINT32_t total_items = path_finder.count - DOUBLE_t[:] ccp_alphas = np.empty(shape=total_items, dtype=np.float64) - DOUBLE_t[:] impurities = np.empty(shape=total_items, dtype=np.float64) - UINT32_t count = 0 - - while count < total_items: - ccp_alphas[count] = path_finder.ccp_alphas[count] - impurities[count] = path_finder.impurities[count] - count += 1 - - return { - 'ccp_alphas': np.asarray(ccp_alphas), - 'impurities': np.asarray(impurities), - } - - -cdef struct BuildPrunedRecord: - SIZE_t start - SIZE_t depth - SIZE_t parent - bint is_left - -cdef _build_pruned_tree( - Tree tree, # OUT - Tree orig_tree, - const unsigned char[:] leaves_in_subtree, - SIZE_t capacity -): - """Build a pruned tree. - - Build a pruned tree from the original tree by transforming the nodes in - ``leaves_in_subtree`` into leaves. - - Parameters - ---------- - tree : Tree - Location to place the pruned tree - orig_tree : Tree - Original tree - leaves_in_subtree : unsigned char memoryview, shape=(node_count, ) - Boolean mask for leaves to include in subtree - capacity : SIZE_t - Number of nodes to initially allocate in pruned tree - """ - tree._resize(capacity) - - cdef: - SIZE_t orig_node_id - SIZE_t new_node_id - SIZE_t depth - SIZE_t parent - bint is_left - bint is_leaf - - # value_stride for original tree and new tree are the same - SIZE_t value_stride = orig_tree.value_stride - SIZE_t max_depth_seen = -1 - int rc = 0 - Node* node - double* orig_value_ptr - double* new_value_ptr - - stack[BuildPrunedRecord] prune_stack - BuildPrunedRecord stack_record - - with nogil: - # push root node onto stack - prune_stack.push({"start": 0, "depth": 0, "parent": _TREE_UNDEFINED, "is_left": 0}) - - while not prune_stack.empty(): - stack_record = prune_stack.top() - prune_stack.pop() - - orig_node_id = stack_record.start - depth = stack_record.depth - parent = stack_record.parent - is_left = stack_record.is_left - - is_leaf = leaves_in_subtree[orig_node_id] - node = &orig_tree.nodes[orig_node_id] - - new_node_id = tree._add_node( - parent, is_left, is_leaf, node.feature, node.threshold, - node.impurity, node.n_node_samples, - node.weighted_n_node_samples, node.missing_go_to_left) - - if new_node_id == INTPTR_MAX: - rc = -1 - break - - # copy value from original tree to new tree - orig_value_ptr = orig_tree.value + value_stride * orig_node_id - new_value_ptr = tree.value + value_stride * new_node_id - memcpy(new_value_ptr, orig_value_ptr, sizeof(double) * value_stride) - - if not is_leaf: - # Push right child on stack - prune_stack.push({"start": node.right_child, "depth": depth + 1, - "parent": new_node_id, "is_left": 0}) - # push left child on stack - prune_stack.push({"start": node.left_child, "depth": depth + 1, - "parent": new_node_id, "is_left": 1}) - - if depth > max_depth_seen: - max_depth_seen = depth - - if rc >= 0: - tree.max_depth = max_depth_seen - if rc == -1: - raise MemoryError("pruning tree") \ No newline at end of file diff --git a/ivy/functional/frontends/sklearn/_tree_ivy.pyx b/ivy/functional/frontends/sklearn/_tree_ivy.pyx deleted file mode 100644 index f436c919f8bc2..0000000000000 --- a/ivy/functional/frontends/sklearn/_tree_ivy.pyx +++ /dev/null @@ -1,1833 +0,0 @@ -# Authors: Gilles Louppe -# Peter Prettenhofer -# Brian Holt -# Noel Dawe -# Satrajit Gosh -# Lars Buitinck -# Arnaud Joly -# Joel Nothman -# Fares Hedayati -# Jacob Schreiber -# Nelson Liu -# -# License: BSD 3 clause - -from cpython cimport Py_INCREF, PyObject, PyTypeObject - -from libc.stdlib cimport free -from libc.string cimport memcpy -from libc.string cimport memset -from libc.stdint cimport INTPTR_MAX -from libc.math cimport isnan -from libcpp.vector cimport vector -from libcpp.algorithm cimport pop_heap -from libcpp.algorithm cimport push_heap -from libcpp cimport bool - -import struct - -import numpy as np -cimport numpy as cnp -cnp.import_array() - -from scipy.sparse import issparse -from scipy.sparse import csr_matrix -from scipy.sparse import isspmatrix_csr - -from ._utils cimport safe_realloc -from ._utils cimport sizet_ptr_to_ndarray - -cdef extern from "numpy/arrayobject.h": - object PyArray_NewFromDescr(PyTypeObject* subtype, cnp.dtype descr, - int nd, cnp.npy_intp* dims, - cnp.npy_intp* strides, - void* data, int flags, object obj) - int PyArray_SetBaseObject(cnp.ndarray arr, PyObject* obj) - -cdef extern from "" namespace "std" nogil: - cdef cppclass stack[T]: - ctypedef T value_type - stack() except + - bint empty() - void pop() - void push(T&) except + # Raise c++ exception for bad_alloc -> MemoryError - T& top() - -# ============================================================================= -# Types and constants -# ============================================================================= - -from numpy import float32 as DTYPE -from numpy import float64 as DOUBLE - -cdef double INFINITY = np.inf -cdef double EPSILON = np.finfo('double').eps - -# Some handy constants (BestFirstTreeBuilder) -cdef int IS_FIRST = 1 -cdef int IS_NOT_FIRST = 0 -cdef int IS_LEFT = 1 -cdef int IS_NOT_LEFT = 0 - -TREE_LEAF = -1 -TREE_UNDEFINED = -2 -cdef SIZE_t _TREE_LEAF = TREE_LEAF -cdef SIZE_t _TREE_UNDEFINED = TREE_UNDEFINED - -# Build the corresponding numpy dtype for Node. -# This works by casting `dummy` to an array of Node of length 1, which numpy -# can construct a `dtype`-object for. See https://stackoverflow.com/q/62448946 -# for a more detailed explanation. -cdef Node dummy -NODE_DTYPE = np.asarray((&dummy)).dtype - -# ============================================================================= -# TreeBuilder -# ============================================================================= - -cdef class TreeBuilder: - """Interface for different tree building strategies.""" - - cpdef build( - self, - Tree tree, - object X, - const DOUBLE_t[:, ::1] y, - const DOUBLE_t[:] sample_weight=None, - const unsigned char[::1] missing_values_in_feature_mask=None, - ): - """Build a decision tree from the training set (X, y).""" - pass - - cdef inline _check_input( - self, - object X, - const DOUBLE_t[:, ::1] y, - const DOUBLE_t[:] sample_weight, - ): - """Check input dtype, layout and format""" - if issparse(X): - X = X.tocsc() - X.sort_indices() - - if X.data.dtype != DTYPE: - X.data = np.ascontiguousarray(X.data, dtype=DTYPE) - - if X.indices.dtype != np.int32 or X.indptr.dtype != np.int32: - raise ValueError("No support for np.int64 index based " - "sparse matrices") - - elif X.dtype != DTYPE: - # since we have to copy we will make it fortran for efficiency - X = np.asfortranarray(X, dtype=DTYPE) - - # TODO: This check for y seems to be redundant, as it is also - # present in the BaseDecisionTree's fit method, and therefore - # can be removed. - if y.base.dtype != DOUBLE or not y.base.flags.contiguous: - y = np.ascontiguousarray(y, dtype=DOUBLE) - - if ( - sample_weight is not None and - ( - sample_weight.base.dtype != DOUBLE or - not sample_weight.base.flags.contiguous - ) - ): - sample_weight = np.asarray(sample_weight, dtype=DOUBLE, order="C") - - return X, y, sample_weight - -# Depth first builder --------------------------------------------------------- -# A record on the stack for depth-first tree growing -cdef struct StackRecord: - SIZE_t start - SIZE_t end - SIZE_t depth - SIZE_t parent - bint is_left - double impurity - SIZE_t n_constant_features - - - - - - - -cdef class DepthFirstTreeBuilder(TreeBuilder): - """Build a decision tree in depth-first fashion.""" - - def __cinit__(self, Splitter splitter, SIZE_t min_samples_split, - SIZE_t min_samples_leaf, double min_weight_leaf, - SIZE_t max_depth, double min_impurity_decrease): - self.splitter = splitter - self.min_samples_split = min_samples_split - self.min_samples_leaf = min_samples_leaf - self.min_weight_leaf = min_weight_leaf - self.max_depth = max_depth - self.min_impurity_decrease = min_impurity_decrease - - cpdef build( - self, - Tree tree, - object X, - const DOUBLE_t[:, ::1] y, - const DOUBLE_t[:] sample_weight=None, - const unsigned char[::1] missing_values_in_feature_mask=None, - ): - """Build a decision tree from the training set (X, y).""" - - # check input - X, y, sample_weight = self._check_input(X, y, sample_weight) - - # Initial capacity - cdef int init_capacity - - if tree.max_depth <= 10: - init_capacity = (2 ** (tree.max_depth + 1)) - 1 - else: - init_capacity = 2047 - - tree._resize(init_capacity) - - # Parameters - cdef Splitter splitter = self.splitter - cdef SIZE_t max_depth = self.max_depth - cdef SIZE_t min_samples_leaf = self.min_samples_leaf - cdef double min_weight_leaf = self.min_weight_leaf - cdef SIZE_t min_samples_split = self.min_samples_split - cdef double min_impurity_decrease = self.min_impurity_decrease - - # Recursive partition (without actual recursion) - splitter.init(X, y, sample_weight, missing_values_in_feature_mask) - - cdef SIZE_t start - cdef SIZE_t end - cdef SIZE_t depth - cdef SIZE_t parent - cdef bint is_left - cdef SIZE_t n_node_samples = splitter.n_samples - cdef double weighted_n_node_samples - cdef SplitRecord split - cdef SIZE_t node_id - - cdef double impurity = INFINITY - cdef SIZE_t n_constant_features - cdef bint is_leaf - cdef bint first = 1 - cdef SIZE_t max_depth_seen = -1 - cdef int rc = 0 - - cdef stack[StackRecord] builder_stack - cdef StackRecord stack_record - - with nogil: - # push root node onto stack - builder_stack.push({ - "start": 0, - "end": n_node_samples, - "depth": 0, - "parent": _TREE_UNDEFINED, - "is_left": 0, - "impurity": INFINITY, - "n_constant_features": 0}) - - while not builder_stack.empty(): - stack_record = builder_stack.top() - builder_stack.pop() - - start = stack_record.start - end = stack_record.end - depth = stack_record.depth - parent = stack_record.parent - is_left = stack_record.is_left - impurity = stack_record.impurity - n_constant_features = stack_record.n_constant_features - - n_node_samples = end - start - splitter.node_reset(start, end, &weighted_n_node_samples) - - is_leaf = (depth >= max_depth or - n_node_samples < min_samples_split or - n_node_samples < 2 * min_samples_leaf or - weighted_n_node_samples < 2 * min_weight_leaf) - - if first: - impurity = splitter.node_impurity() - first = 0 - - # impurity == 0 with tolerance due to rounding errors - is_leaf = is_leaf or impurity <= EPSILON - - if not is_leaf: - splitter.node_split(impurity, &split, &n_constant_features) - # If EPSILON=0 in the below comparison, float precision - # issues stop splitting, producing trees that are - # dissimilar to v0.18 - is_leaf = (is_leaf or split.pos >= end or - (split.improvement + EPSILON < - min_impurity_decrease)) - - node_id = tree._add_node(parent, is_left, is_leaf, split.feature, - split.threshold, impurity, n_node_samples, - weighted_n_node_samples, - split.missing_go_to_left) - - if node_id == INTPTR_MAX: - rc = -1 - break - - # Store value for all nodes, to facilitate tree/model - # inspection and interpretation - splitter.node_value(tree.value + node_id * tree.value_stride) - - if not is_leaf: - # Push right child on stack - builder_stack.push({ - "start": split.pos, - "end": end, - "depth": depth + 1, - "parent": node_id, - "is_left": 0, - "impurity": split.impurity_right, - "n_constant_features": n_constant_features}) - - # Push left child on stack - builder_stack.push({ - "start": start, - "end": split.pos, - "depth": depth + 1, - "parent": node_id, - "is_left": 1, - "impurity": split.impurity_left, - "n_constant_features": n_constant_features}) - - if depth > max_depth_seen: - max_depth_seen = depth - - if rc >= 0: - rc = tree._resize_c(tree.node_count) - - if rc >= 0: - tree.max_depth = max_depth_seen - if rc == -1: - raise MemoryError() - - -# Best first builder ---------------------------------------------------------- -cdef struct FrontierRecord: - # Record of information of a Node, the frontier for a split. Those records are - # maintained in a heap to access the Node with the best improvement in impurity, - # allowing growing trees greedily on this improvement. - SIZE_t node_id - SIZE_t start - SIZE_t end - SIZE_t pos - SIZE_t depth - bint is_leaf - double impurity - double impurity_left - double impurity_right - double improvement - -cdef inline bool _compare_records( - const FrontierRecord& left, - const FrontierRecord& right, -): - return left.improvement < right.improvement - -cdef inline void _add_to_frontier( - FrontierRecord rec, - vector[FrontierRecord]& frontier, -) noexcept nogil: - """Adds record `rec` to the priority queue `frontier`.""" - frontier.push_back(rec) - push_heap(frontier.begin(), frontier.end(), &_compare_records) - - -cdef class BestFirstTreeBuilder(TreeBuilder): - """Build a decision tree in best-first fashion. - - The best node to expand is given by the node at the frontier that has the - highest impurity improvement. - """ - cdef SIZE_t max_leaf_nodes - - def __cinit__(self, Splitter splitter, SIZE_t min_samples_split, - SIZE_t min_samples_leaf, min_weight_leaf, - SIZE_t max_depth, SIZE_t max_leaf_nodes, - double min_impurity_decrease): - self.splitter = splitter - self.min_samples_split = min_samples_split - self.min_samples_leaf = min_samples_leaf - self.min_weight_leaf = min_weight_leaf - self.max_depth = max_depth - self.max_leaf_nodes = max_leaf_nodes - self.min_impurity_decrease = min_impurity_decrease - - cpdef build( - self, - Tree tree, - object X, - const DOUBLE_t[:, ::1] y, - const DOUBLE_t[:] sample_weight=None, - const unsigned char[::1] missing_values_in_feature_mask=None, - ): - """Build a decision tree from the training set (X, y).""" - - # check input - X, y, sample_weight = self._check_input(X, y, sample_weight) - - # Parameters - cdef Splitter splitter = self.splitter - cdef SIZE_t max_leaf_nodes = self.max_leaf_nodes - - # Recursive partition (without actual recursion) - splitter.init(X, y, sample_weight, missing_values_in_feature_mask) - - cdef vector[FrontierRecord] frontier - cdef FrontierRecord record - cdef FrontierRecord split_node_left - cdef FrontierRecord split_node_right - - cdef SIZE_t n_node_samples = splitter.n_samples - cdef SIZE_t max_split_nodes = max_leaf_nodes - 1 - cdef bint is_leaf - cdef SIZE_t max_depth_seen = -1 - cdef int rc = 0 - cdef Node* node - - # Initial capacity - cdef SIZE_t init_capacity = max_split_nodes + max_leaf_nodes - tree._resize(init_capacity) - - with nogil: - # add root to frontier - rc = self._add_split_node(splitter, tree, 0, n_node_samples, - INFINITY, IS_FIRST, IS_LEFT, NULL, 0, - &split_node_left) - if rc >= 0: - _add_to_frontier(split_node_left, frontier) - - while not frontier.empty(): - pop_heap(frontier.begin(), frontier.end(), &_compare_records) - record = frontier.back() - frontier.pop_back() - - node = &tree.nodes[record.node_id] - is_leaf = (record.is_leaf or max_split_nodes <= 0) - - if is_leaf: - # Node is not expandable; set node as leaf - node.left_child = _TREE_LEAF - node.right_child = _TREE_LEAF - node.feature = _TREE_UNDEFINED - node.threshold = _TREE_UNDEFINED - - else: - # Node is expandable - - # Decrement number of split nodes available - max_split_nodes -= 1 - - # Compute left split node - rc = self._add_split_node(splitter, tree, - record.start, record.pos, - record.impurity_left, - IS_NOT_FIRST, IS_LEFT, node, - record.depth + 1, - &split_node_left) - if rc == -1: - break - - # tree.nodes may have changed - node = &tree.nodes[record.node_id] - - # Compute right split node - rc = self._add_split_node(splitter, tree, record.pos, - record.end, - record.impurity_right, - IS_NOT_FIRST, IS_NOT_LEFT, node, - record.depth + 1, - &split_node_right) - if rc == -1: - break - - # Add nodes to queue - _add_to_frontier(split_node_left, frontier) - _add_to_frontier(split_node_right, frontier) - - if record.depth > max_depth_seen: - max_depth_seen = record.depth - - if rc >= 0: - rc = tree._resize_c(tree.node_count) - - if rc >= 0: - tree.max_depth = max_depth_seen - - if rc == -1: - raise MemoryError() - - cdef inline int _add_split_node(self, Splitter splitter, Tree tree, - SIZE_t start, SIZE_t end, double impurity, - bint is_first, bint is_left, Node* parent, - SIZE_t depth, - FrontierRecord* res) except -1 nogil: - """Adds node w/ partition ``[start, end)`` to the frontier. """ - cdef SplitRecord split - cdef SIZE_t node_id - cdef SIZE_t n_node_samples - cdef SIZE_t n_constant_features = 0 - cdef double min_impurity_decrease = self.min_impurity_decrease - cdef double weighted_n_node_samples - cdef bint is_leaf - - splitter.node_reset(start, end, &weighted_n_node_samples) - - if is_first: - impurity = splitter.node_impurity() - - n_node_samples = end - start - is_leaf = (depth >= self.max_depth or - n_node_samples < self.min_samples_split or - n_node_samples < 2 * self.min_samples_leaf or - weighted_n_node_samples < 2 * self.min_weight_leaf or - impurity <= EPSILON # impurity == 0 with tolerance - ) - - if not is_leaf: - splitter.node_split(impurity, &split, &n_constant_features) - # If EPSILON=0 in the below comparison, float precision issues stop - # splitting early, producing trees that are dissimilar to v0.18 - is_leaf = (is_leaf or split.pos >= end or - split.improvement + EPSILON < min_impurity_decrease) - - node_id = tree._add_node(parent - tree.nodes - if parent != NULL - else _TREE_UNDEFINED, - is_left, is_leaf, - split.feature, split.threshold, impurity, n_node_samples, - weighted_n_node_samples, - split.missing_go_to_left) - if node_id == INTPTR_MAX: - return -1 - - # compute values also for split nodes (might become leafs later). - splitter.node_value(tree.value + node_id * tree.value_stride) - - res.node_id = node_id - res.start = start - res.end = end - res.depth = depth - res.impurity = impurity - - if not is_leaf: - # is split node - res.pos = split.pos - res.is_leaf = 0 - res.improvement = split.improvement - res.impurity_left = split.impurity_left - res.impurity_right = split.impurity_right - - else: - # is leaf => 0 improvement - res.pos = end - res.is_leaf = 1 - res.improvement = 0.0 - res.impurity_left = impurity - res.impurity_right = impurity - - return 0 - - -# ============================================================================= -# Tree -# ============================================================================= - -cdef class Tree: - """Array-based representation of a binary decision tree. - - The binary tree is represented as a number of parallel arrays. The i-th - element of each array holds information about the node `i`. Node 0 is the - tree's root. You can find a detailed description of all arrays in - `_tree.pxd`. NOTE: Some of the arrays only apply to either leaves or split - nodes, resp. In this case the values of nodes of the other type are - arbitrary! - - Attributes - ---------- - node_count : int - The number of nodes (internal nodes + leaves) in the tree. - - capacity : int - The current capacity (i.e., size) of the arrays, which is at least as - great as `node_count`. - - max_depth : int - The depth of the tree, i.e. the maximum depth of its leaves. - - children_left : array of int, shape [node_count] - children_left[i] holds the node id of the left child of node i. - For leaves, children_left[i] == TREE_LEAF. Otherwise, - children_left[i] > i. This child handles the case where - X[:, feature[i]] <= threshold[i]. - - children_right : array of int, shape [node_count] - children_right[i] holds the node id of the right child of node i. - For leaves, children_right[i] == TREE_LEAF. Otherwise, - children_right[i] > i. This child handles the case where - X[:, feature[i]] > threshold[i]. - - feature : array of int, shape [node_count] - feature[i] holds the feature to split on, for the internal node i. - - threshold : array of double, shape [node_count] - threshold[i] holds the threshold for the internal node i. - - value : array of double, shape [node_count, n_outputs, max_n_classes] - Contains the constant prediction value of each node. - - impurity : array of double, shape [node_count] - impurity[i] holds the impurity (i.e., the value of the splitting - criterion) at node i. - - n_node_samples : array of int, shape [node_count] - n_node_samples[i] holds the number of training samples reaching node i. - - weighted_n_node_samples : array of double, shape [node_count] - weighted_n_node_samples[i] holds the weighted number of training samples - reaching node i. - """ - # Wrap for outside world. - # WARNING: these reference the current `nodes` and `value` buffers, which - # must not be freed by a subsequent memory allocation. - # (i.e. through `_resize` or `__setstate__`) - @property - def n_classes(self): - return sizet_ptr_to_ndarray(self.n_classes, self.n_outputs) - - @property - def children_left(self): - return self._get_node_ndarray()['left_child'][:self.node_count] - - @property - def children_right(self): - return self._get_node_ndarray()['right_child'][:self.node_count] - - @property - def n_leaves(self): - return np.sum(np.logical_and( - self.children_left == -1, - self.children_right == -1)) - - @property - def feature(self): - return self._get_node_ndarray()['feature'][:self.node_count] - - @property - def threshold(self): - return self._get_node_ndarray()['threshold'][:self.node_count] - - @property - def impurity(self): - return self._get_node_ndarray()['impurity'][:self.node_count] - - @property - def n_node_samples(self): - return self._get_node_ndarray()['n_node_samples'][:self.node_count] - - @property - def weighted_n_node_samples(self): - return self._get_node_ndarray()['weighted_n_node_samples'][:self.node_count] - - @property - def missing_go_to_left(self): - return self._get_node_ndarray()['missing_go_to_left'][:self.node_count] - - @property - def value(self): - return self._get_value_ndarray()[:self.node_count] - - # TODO: Convert n_classes to cython.integral memory view once - # https://github.com/cython/cython/issues/5243 is fixed - def __cinit__(self, int n_features, cnp.ndarray n_classes, int n_outputs): - """Constructor.""" - cdef SIZE_t dummy = 0 - size_t_dtype = np.array(dummy).dtype - - n_classes = _check_n_classes(n_classes, size_t_dtype) - - # Input/Output layout - self.n_features = n_features - self.n_outputs = n_outputs - self.n_classes = NULL - safe_realloc(&self.n_classes, n_outputs) - - self.max_n_classes = np.max(n_classes) - self.value_stride = n_outputs * self.max_n_classes - - cdef SIZE_t k - for k in range(n_outputs): - self.n_classes[k] = n_classes[k] - - # Inner structures - self.max_depth = 0 - self.node_count = 0 - self.capacity = 0 - self.value = NULL - self.nodes = NULL - - def __dealloc__(self): - """Destructor.""" - # Free all inner structures - free(self.n_classes) - free(self.value) - free(self.nodes) - - def __reduce__(self): - """Reduce re-implementation, for pickling.""" - return (Tree, (self.n_features, - sizet_ptr_to_ndarray(self.n_classes, self.n_outputs), - self.n_outputs), self.__getstate__()) - - def __getstate__(self): - """Getstate re-implementation, for pickling.""" - d = {} - # capacity is inferred during the __setstate__ using nodes - d["max_depth"] = self.max_depth - d["node_count"] = self.node_count - d["nodes"] = self._get_node_ndarray() - d["values"] = self._get_value_ndarray() - return d - - def __setstate__(self, d): - """Setstate re-implementation, for unpickling.""" - self.max_depth = d["max_depth"] - self.node_count = d["node_count"] - - if 'nodes' not in d: - raise ValueError('You have loaded Tree version which ' - 'cannot be imported') - - node_ndarray = d['nodes'] - value_ndarray = d['values'] - - value_shape = (node_ndarray.shape[0], self.n_outputs, - self.max_n_classes) - - node_ndarray = _check_node_ndarray(node_ndarray, expected_dtype=NODE_DTYPE) - value_ndarray = _check_value_ndarray( - value_ndarray, - expected_dtype=np.dtype(np.float64), - expected_shape=value_shape - ) - - self.capacity = node_ndarray.shape[0] - if self._resize_c(self.capacity) != 0: - raise MemoryError("resizing tree to %d" % self.capacity) - - memcpy(self.nodes, cnp.PyArray_DATA(node_ndarray), - self.capacity * sizeof(Node)) - memcpy(self.value, cnp.PyArray_DATA(value_ndarray), - self.capacity * self.value_stride * sizeof(double)) - - cdef int _resize(self, SIZE_t capacity) except -1 nogil: - """Resize all inner arrays to `capacity`, if `capacity` == -1, then - double the size of the inner arrays. - - Returns -1 in case of failure to allocate memory (and raise MemoryError) - or 0 otherwise. - """ - if self._resize_c(capacity) != 0: - # Acquire gil only if we need to raise - with gil: - raise MemoryError() - - cdef int _resize_c(self, SIZE_t capacity=INTPTR_MAX) except -1 nogil: - """Guts of _resize - - Returns -1 in case of failure to allocate memory (and raise MemoryError) - or 0 otherwise. - """ - if capacity == self.capacity and self.nodes != NULL: - return 0 - - if capacity == INTPTR_MAX: - if self.capacity == 0: - capacity = 3 # default initial value - else: - capacity = 2 * self.capacity - - safe_realloc(&self.nodes, capacity) - safe_realloc(&self.value, capacity * self.value_stride) - - # value memory is initialised to 0 to enable classifier argmax - if capacity > self.capacity: - memset((self.value + self.capacity * self.value_stride), 0, - (capacity - self.capacity) * self.value_stride * - sizeof(double)) - - # if capacity smaller than node_count, adjust the counter - if capacity < self.node_count: - self.node_count = capacity - - self.capacity = capacity - return 0 - - cdef SIZE_t _add_node(self, SIZE_t parent, bint is_left, bint is_leaf, - SIZE_t feature, double threshold, double impurity, - SIZE_t n_node_samples, - double weighted_n_node_samples, - unsigned char missing_go_to_left) except -1 nogil: - """Add a node to the tree. - - The new node registers itself as the child of its parent. - - Returns (size_t)(-1) on error. - """ - cdef SIZE_t node_id = self.node_count - - if node_id >= self.capacity: - if self._resize_c() != 0: - return INTPTR_MAX - - cdef Node* node = &self.nodes[node_id] - node.impurity = impurity - node.n_node_samples = n_node_samples - node.weighted_n_node_samples = weighted_n_node_samples - - if parent != _TREE_UNDEFINED: - if is_left: - self.nodes[parent].left_child = node_id - else: - self.nodes[parent].right_child = node_id - - if is_leaf: - node.left_child = _TREE_LEAF - node.right_child = _TREE_LEAF - node.feature = _TREE_UNDEFINED - node.threshold = _TREE_UNDEFINED - - else: - # left_child and right_child will be set later - node.feature = feature - node.threshold = threshold - node.missing_go_to_left = missing_go_to_left - - self.node_count += 1 - - return node_id - - cpdef cnp.ndarray predict(self, object X): - """Predict target for X.""" - out = self._get_value_ndarray().take(self.apply(X), axis=0, - mode='clip') - if self.n_outputs == 1: - out = out.reshape(X.shape[0], self.max_n_classes) - return out - - cpdef cnp.ndarray apply(self, object X): - """Finds the terminal region (=leaf node) for each sample in X.""" - if issparse(X): - return self._apply_sparse_csr(X) - else: - return self._apply_dense(X) - - cdef inline cnp.ndarray _apply_dense(self, object X): - """Finds the terminal region (=leaf node) for each sample in X.""" - - # Check input - if not isinstance(X, np.ndarray): - raise ValueError("X should be in np.ndarray format, got %s" - % type(X)) - - if X.dtype != DTYPE: - raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) - - # Extract input - cdef const DTYPE_t[:, :] X_ndarray = X - cdef SIZE_t n_samples = X.shape[0] - cdef DTYPE_t X_i_node_feature - - # Initialize output - cdef SIZE_t[:] out = np.zeros(n_samples, dtype=np.intp) - - # Initialize auxiliary data-structure - cdef Node* node = NULL - cdef SIZE_t i = 0 - - with nogil: - for i in range(n_samples): - node = self.nodes - # While node not a leaf - while node.left_child != _TREE_LEAF: - X_i_node_feature = X_ndarray[i, node.feature] - # ... and node.right_child != _TREE_LEAF: - if isnan(X_i_node_feature): - if node.missing_go_to_left: - node = &self.nodes[node.left_child] - else: - node = &self.nodes[node.right_child] - elif X_i_node_feature <= node.threshold: - node = &self.nodes[node.left_child] - else: - node = &self.nodes[node.right_child] - - out[i] = (node - self.nodes) # node offset - - return np.asarray(out) - - cdef inline cnp.ndarray _apply_sparse_csr(self, object X): - """Finds the terminal region (=leaf node) for each sample in sparse X. - """ - # Check input - if not isspmatrix_csr(X): - raise ValueError("X should be in csr_matrix format, got %s" - % type(X)) - - if X.dtype != DTYPE: - raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) - - # Extract input - cdef const DTYPE_t[:] X_data = X.data - cdef const INT32_t[:] X_indices = X.indices - cdef const INT32_t[:] X_indptr = X.indptr - - cdef SIZE_t n_samples = X.shape[0] - cdef SIZE_t n_features = X.shape[1] - - # Initialize output - cdef SIZE_t[:] out = np.zeros(n_samples, dtype=np.intp) - - # Initialize auxiliary data-structure - cdef DTYPE_t feature_value = 0. - cdef Node* node = NULL - cdef DTYPE_t* X_sample = NULL - cdef SIZE_t i = 0 - cdef INT32_t k = 0 - - # feature_to_sample as a data structure records the last seen sample - # for each feature; functionally, it is an efficient way to identify - # which features are nonzero in the present sample. - cdef SIZE_t* feature_to_sample = NULL - - safe_realloc(&X_sample, n_features) - safe_realloc(&feature_to_sample, n_features) - - with nogil: - memset(feature_to_sample, -1, n_features * sizeof(SIZE_t)) - - for i in range(n_samples): - node = self.nodes - - for k in range(X_indptr[i], X_indptr[i + 1]): - feature_to_sample[X_indices[k]] = i - X_sample[X_indices[k]] = X_data[k] - - # While node not a leaf - while node.left_child != _TREE_LEAF: - # ... and node.right_child != _TREE_LEAF: - if feature_to_sample[node.feature] == i: - feature_value = X_sample[node.feature] - - else: - feature_value = 0. - - if feature_value <= node.threshold: - node = &self.nodes[node.left_child] - else: - node = &self.nodes[node.right_child] - - out[i] = (node - self.nodes) # node offset - - # Free auxiliary arrays - free(X_sample) - free(feature_to_sample) - - return np.asarray(out) - - cpdef object decision_path(self, object X): - """Finds the decision path (=node) for each sample in X.""" - if issparse(X): - return self._decision_path_sparse_csr(X) - else: - return self._decision_path_dense(X) - - cdef inline object _decision_path_dense(self, object X): - """Finds the decision path (=node) for each sample in X.""" - - # Check input - if not isinstance(X, np.ndarray): - raise ValueError("X should be in np.ndarray format, got %s" - % type(X)) - - if X.dtype != DTYPE: - raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) - - # Extract input - cdef const DTYPE_t[:, :] X_ndarray = X - cdef SIZE_t n_samples = X.shape[0] - - # Initialize output - cdef SIZE_t[:] indptr = np.zeros(n_samples + 1, dtype=np.intp) - cdef SIZE_t[:] indices = np.zeros( - n_samples * (1 + self.max_depth), dtype=np.intp - ) - - # Initialize auxiliary data-structure - cdef Node* node = NULL - cdef SIZE_t i = 0 - - with nogil: - for i in range(n_samples): - node = self.nodes - indptr[i + 1] = indptr[i] - - # Add all external nodes - while node.left_child != _TREE_LEAF: - # ... and node.right_child != _TREE_LEAF: - indices[indptr[i + 1]] = (node - self.nodes) - indptr[i + 1] += 1 - - if X_ndarray[i, node.feature] <= node.threshold: - node = &self.nodes[node.left_child] - else: - node = &self.nodes[node.right_child] - - # Add the leave node - indices[indptr[i + 1]] = (node - self.nodes) - indptr[i + 1] += 1 - - indices = indices[:indptr[n_samples]] - cdef SIZE_t[:] data = np.ones(shape=len(indices), dtype=np.intp) - out = csr_matrix((data, indices, indptr), - shape=(n_samples, self.node_count)) - - return out - - cdef inline object _decision_path_sparse_csr(self, object X): - """Finds the decision path (=node) for each sample in X.""" - - # Check input - if not isspmatrix_csr(X): - raise ValueError("X should be in csr_matrix format, got %s" - % type(X)) - - if X.dtype != DTYPE: - raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) - - # Extract input - cdef const DTYPE_t[:] X_data = X.data - cdef const INT32_t[:] X_indices = X.indices - cdef const INT32_t[:] X_indptr = X.indptr - - cdef SIZE_t n_samples = X.shape[0] - cdef SIZE_t n_features = X.shape[1] - - # Initialize output - cdef SIZE_t[:] indptr = np.zeros(n_samples + 1, dtype=np.intp) - cdef SIZE_t[:] indices = np.zeros( - n_samples * (1 + self.max_depth), dtype=np.intp - ) - - # Initialize auxiliary data-structure - cdef DTYPE_t feature_value = 0. - cdef Node* node = NULL - cdef DTYPE_t* X_sample = NULL - cdef SIZE_t i = 0 - cdef INT32_t k = 0 - - # feature_to_sample as a data structure records the last seen sample - # for each feature; functionally, it is an efficient way to identify - # which features are nonzero in the present sample. - cdef SIZE_t* feature_to_sample = NULL - - safe_realloc(&X_sample, n_features) - safe_realloc(&feature_to_sample, n_features) - - with nogil: - memset(feature_to_sample, -1, n_features * sizeof(SIZE_t)) - - for i in range(n_samples): - node = self.nodes - indptr[i + 1] = indptr[i] - - for k in range(X_indptr[i], X_indptr[i + 1]): - feature_to_sample[X_indices[k]] = i - X_sample[X_indices[k]] = X_data[k] - - # While node not a leaf - while node.left_child != _TREE_LEAF: - # ... and node.right_child != _TREE_LEAF: - - indices[indptr[i + 1]] = (node - self.nodes) - indptr[i + 1] += 1 - - if feature_to_sample[node.feature] == i: - feature_value = X_sample[node.feature] - - else: - feature_value = 0. - - if feature_value <= node.threshold: - node = &self.nodes[node.left_child] - else: - node = &self.nodes[node.right_child] - - # Add the leave node - indices[indptr[i + 1]] = (node - self.nodes) - indptr[i + 1] += 1 - - # Free auxiliary arrays - free(X_sample) - free(feature_to_sample) - - indices = indices[:indptr[n_samples]] - cdef SIZE_t[:] data = np.ones(shape=len(indices), dtype=np.intp) - out = csr_matrix((data, indices, indptr), - shape=(n_samples, self.node_count)) - - return out - - cpdef compute_node_depths(self): - """Compute the depth of each node in a tree. - - .. versionadded:: 1.3 - - Returns - ------- - depths : ndarray of shape (self.node_count,), dtype=np.int64 - The depth of each node in the tree. - """ - cdef: - cnp.int64_t[::1] depths = np.empty(self.node_count, dtype=np.int64) - cnp.npy_intp[:] children_left = self.children_left - cnp.npy_intp[:] children_right = self.children_right - cnp.npy_intp node_id - cnp.npy_intp node_count = self.node_count - cnp.int64_t depth - - depths[0] = 1 # init root node - for node_id in range(node_count): - if children_left[node_id] != _TREE_LEAF: - depth = depths[node_id] + 1 - depths[children_left[node_id]] = depth - depths[children_right[node_id]] = depth - - return depths.base - - cpdef compute_feature_importances(self, normalize=True): - """Computes the importance of each feature (aka variable).""" - cdef Node* left - cdef Node* right - cdef Node* nodes = self.nodes - cdef Node* node = nodes - cdef Node* end_node = node + self.node_count - - cdef double normalizer = 0. - - cdef cnp.float64_t[:] importances = np.zeros(self.n_features) - - with nogil: - while node != end_node: - if node.left_child != _TREE_LEAF: - # ... and node.right_child != _TREE_LEAF: - left = &nodes[node.left_child] - right = &nodes[node.right_child] - - importances[node.feature] += ( - node.weighted_n_node_samples * node.impurity - - left.weighted_n_node_samples * left.impurity - - right.weighted_n_node_samples * right.impurity) - node += 1 - - for i in range(self.n_features): - importances[i] /= nodes[0].weighted_n_node_samples - - if normalize: - normalizer = np.sum(importances) - - if normalizer > 0.0: - # Avoid dividing by zero (e.g., when root is pure) - for i in range(self.n_features): - importances[i] /= normalizer - - return np.asarray(importances) - - cdef cnp.ndarray _get_value_ndarray(self): - """Wraps value as a 3-d NumPy array. - - The array keeps a reference to this Tree, which manages the underlying - memory. - """ - cdef cnp.npy_intp shape[3] - shape[0] = self.node_count - shape[1] = self.n_outputs - shape[2] = self.max_n_classes - cdef cnp.ndarray arr - arr = cnp.PyArray_SimpleNewFromData(3, shape, cnp.NPY_DOUBLE, self.value) - Py_INCREF(self) - if PyArray_SetBaseObject(arr, self) < 0: - raise ValueError("Can't initialize array.") - return arr - - cdef cnp.ndarray _get_node_ndarray(self): - """Wraps nodes as a NumPy struct array. - - The array keeps a reference to this Tree, which manages the underlying - memory. Individual fields are publicly accessible as properties of the - Tree. - """ - cdef cnp.npy_intp shape[1] - shape[0] = self.node_count - cdef cnp.npy_intp strides[1] - strides[0] = sizeof(Node) - cdef cnp.ndarray arr - Py_INCREF(NODE_DTYPE) - arr = PyArray_NewFromDescr( cnp.ndarray, - NODE_DTYPE, 1, shape, - strides, self.nodes, - cnp.NPY_ARRAY_DEFAULT, None) - Py_INCREF(self) - if PyArray_SetBaseObject(arr, self) < 0: - raise ValueError("Can't initialize array.") - return arr - - def compute_partial_dependence(self, DTYPE_t[:, ::1] X, - int[::1] target_features, - double[::1] out): - """Partial dependence of the response on the ``target_feature`` set. - - For each sample in ``X`` a tree traversal is performed. - Each traversal starts from the root with weight 1.0. - - At each non-leaf node that splits on a target feature, either - the left child or the right child is visited based on the feature - value of the current sample, and the weight is not modified. - At each non-leaf node that splits on a complementary feature, - both children are visited and the weight is multiplied by the fraction - of training samples which went to each child. - - At each leaf, the value of the node is multiplied by the current - weight (weights sum to 1 for all visited terminal nodes). - - Parameters - ---------- - X : view on 2d ndarray, shape (n_samples, n_target_features) - The grid points on which the partial dependence should be - evaluated. - target_features : view on 1d ndarray, shape (n_target_features) - The set of target features for which the partial dependence - should be evaluated. - out : view on 1d ndarray, shape (n_samples) - The value of the partial dependence function on each grid - point. - """ - cdef: - double[::1] weight_stack = np.zeros(self.node_count, - dtype=np.float64) - SIZE_t[::1] node_idx_stack = np.zeros(self.node_count, - dtype=np.intp) - SIZE_t sample_idx - SIZE_t feature_idx - int stack_size - double left_sample_frac - double current_weight - double total_weight # used for sanity check only - Node *current_node # use a pointer to avoid copying attributes - SIZE_t current_node_idx - bint is_target_feature - SIZE_t _TREE_LEAF = TREE_LEAF # to avoid python interactions - - for sample_idx in range(X.shape[0]): - # init stacks for current sample - stack_size = 1 - node_idx_stack[0] = 0 # root node - weight_stack[0] = 1 # all the samples are in the root node - total_weight = 0 - - while stack_size > 0: - # pop the stack - stack_size -= 1 - current_node_idx = node_idx_stack[stack_size] - current_node = &self.nodes[current_node_idx] - - if current_node.left_child == _TREE_LEAF: - # leaf node - out[sample_idx] += (weight_stack[stack_size] * - self.value[current_node_idx]) - total_weight += weight_stack[stack_size] - else: - # non-leaf node - - # determine if the split feature is a target feature - is_target_feature = False - for feature_idx in range(target_features.shape[0]): - if target_features[feature_idx] == current_node.feature: - is_target_feature = True - break - - if is_target_feature: - # In this case, we push left or right child on stack - if X[sample_idx, feature_idx] <= current_node.threshold: - node_idx_stack[stack_size] = current_node.left_child - else: - node_idx_stack[stack_size] = current_node.right_child - stack_size += 1 - else: - # In this case, we push both children onto the stack, - # and give a weight proportional to the number of - # samples going through each branch. - - # push left child - node_idx_stack[stack_size] = current_node.left_child - left_sample_frac = ( - self.nodes[current_node.left_child].weighted_n_node_samples / - current_node.weighted_n_node_samples) - current_weight = weight_stack[stack_size] - weight_stack[stack_size] = current_weight * left_sample_frac - stack_size += 1 - - # push right child - node_idx_stack[stack_size] = current_node.right_child - weight_stack[stack_size] = ( - current_weight * (1 - left_sample_frac)) - stack_size += 1 - - # Sanity check. Should never happen. - if not (0.999 < total_weight < 1.001): - raise ValueError("Total weight should be 1.0 but was %.9f" % - total_weight) - - -def _check_n_classes(n_classes, expected_dtype): - if n_classes.ndim != 1: - raise ValueError( - f"Wrong dimensions for n_classes from the pickle: " - f"expected 1, got {n_classes.ndim}" - ) - - if n_classes.dtype == expected_dtype: - return n_classes - - # Handles both different endianness and different bitness - if n_classes.dtype.kind == "i" and n_classes.dtype.itemsize in [4, 8]: - return n_classes.astype(expected_dtype, casting="same_kind") - - raise ValueError( - "n_classes from the pickle has an incompatible dtype:\n" - f"- expected: {expected_dtype}\n" - f"- got: {n_classes.dtype}" - ) - - -def _check_value_ndarray(value_ndarray, expected_dtype, expected_shape): - if value_ndarray.shape != expected_shape: - raise ValueError( - "Wrong shape for value array from the pickle: " - f"expected {expected_shape}, got {value_ndarray.shape}" - ) - - if not value_ndarray.flags.c_contiguous: - raise ValueError( - "value array from the pickle should be a C-contiguous array" - ) - - if value_ndarray.dtype == expected_dtype: - return value_ndarray - - # Handles different endianness - if value_ndarray.dtype.str.endswith('f8'): - return value_ndarray.astype(expected_dtype, casting='equiv') - - raise ValueError( - "value array from the pickle has an incompatible dtype:\n" - f"- expected: {expected_dtype}\n" - f"- got: {value_ndarray.dtype}" - ) - - -def _dtype_to_dict(dtype): - return {name: dt.str for name, (dt, *rest) in dtype.fields.items()} - - -def _dtype_dict_with_modified_bitness(dtype_dict): - # field names in Node struct with SIZE_t types (see sklearn/tree/_tree.pxd) - indexing_field_names = ["left_child", "right_child", "feature", "n_node_samples"] - - expected_dtype_size = str(struct.calcsize("P")) - allowed_dtype_size = "8" if expected_dtype_size == "4" else "4" - - allowed_dtype_dict = dtype_dict.copy() - for name in indexing_field_names: - allowed_dtype_dict[name] = allowed_dtype_dict[name].replace( - expected_dtype_size, allowed_dtype_size - ) - - return allowed_dtype_dict - - -def _all_compatible_dtype_dicts(dtype): - # The Cython code for decision trees uses platform-specific SIZE_t - # typed indexing fields that correspond to either i4 or i8 dtypes for - # the matching fields in the numpy array depending on the bitness of - # the platform (32 bit or 64 bit respectively). - # - # We need to cast the indexing fields of the NODE_DTYPE-dtyped array at - # pickle load time to enable cross-bitness deployment scenarios. We - # typically want to make it possible to run the expensive fit method of - # a tree estimator on a 64 bit server platform, pickle the estimator - # for deployment and run the predict method of a low power 32 bit edge - # platform. - # - # A similar thing happens for endianness, the machine where the pickle was - # saved can have a different endianness than the machine where the pickle - # is loaded - - dtype_dict = _dtype_to_dict(dtype) - dtype_dict_with_modified_bitness = _dtype_dict_with_modified_bitness(dtype_dict) - dtype_dict_with_modified_endianness = _dtype_to_dict(dtype.newbyteorder()) - dtype_dict_with_modified_bitness_and_endianness = _dtype_dict_with_modified_bitness( - dtype_dict_with_modified_endianness - ) - - return [ - dtype_dict, - dtype_dict_with_modified_bitness, - dtype_dict_with_modified_endianness, - dtype_dict_with_modified_bitness_and_endianness, - ] - - -def _check_node_ndarray(node_ndarray, expected_dtype): - if node_ndarray.ndim != 1: - raise ValueError( - "Wrong dimensions for node array from the pickle: " - f"expected 1, got {node_ndarray.ndim}" - ) - - if not node_ndarray.flags.c_contiguous: - raise ValueError( - "node array from the pickle should be a C-contiguous array" - ) - - node_ndarray_dtype = node_ndarray.dtype - if node_ndarray_dtype == expected_dtype: - return node_ndarray - - node_ndarray_dtype_dict = _dtype_to_dict(node_ndarray_dtype) - all_compatible_dtype_dicts = _all_compatible_dtype_dicts(expected_dtype) - - if node_ndarray_dtype_dict not in all_compatible_dtype_dicts: - raise ValueError( - "node array from the pickle has an incompatible dtype:\n" - f"- expected: {expected_dtype}\n" - f"- got : {node_ndarray_dtype}" - ) - - return node_ndarray.astype(expected_dtype, casting="same_kind") - - -# ============================================================================= -# Build Pruned Tree -# ============================================================================= - - -cdef class _CCPPruneController: - """Base class used by build_pruned_tree_ccp and ccp_pruning_path - to control pruning. - """ - cdef bint stop_pruning(self, DOUBLE_t effective_alpha) noexcept nogil: - """Return 1 to stop pruning and 0 to continue pruning""" - return 0 - - cdef void save_metrics(self, DOUBLE_t effective_alpha, - DOUBLE_t subtree_impurities) noexcept nogil: - """Save metrics when pruning""" - pass - - cdef void after_pruning(self, unsigned char[:] in_subtree) noexcept nogil: - """Called after pruning""" - pass - - -cdef class _AlphaPruner(_CCPPruneController): - """Use alpha to control when to stop pruning.""" - cdef DOUBLE_t ccp_alpha - cdef SIZE_t capacity - - def __cinit__(self, DOUBLE_t ccp_alpha): - self.ccp_alpha = ccp_alpha - self.capacity = 0 - - cdef bint stop_pruning(self, DOUBLE_t effective_alpha) noexcept nogil: - # The subtree on the previous iteration has the greatest ccp_alpha - # less than or equal to self.ccp_alpha - return self.ccp_alpha < effective_alpha - - cdef void after_pruning(self, unsigned char[:] in_subtree) noexcept nogil: - """Updates the number of leaves in subtree""" - for i in range(in_subtree.shape[0]): - if in_subtree[i]: - self.capacity += 1 - - -cdef class _PathFinder(_CCPPruneController): - """Record metrics used to return the cost complexity path.""" - cdef DOUBLE_t[:] ccp_alphas - cdef DOUBLE_t[:] impurities - cdef UINT32_t count - - def __cinit__(self, int node_count): - self.ccp_alphas = np.zeros(shape=(node_count), dtype=np.float64) - self.impurities = np.zeros(shape=(node_count), dtype=np.float64) - self.count = 0 - - cdef void save_metrics(self, - DOUBLE_t effective_alpha, - DOUBLE_t subtree_impurities) noexcept nogil: - self.ccp_alphas[self.count] = effective_alpha - self.impurities[self.count] = subtree_impurities - self.count += 1 - - -cdef struct CostComplexityPruningRecord: - SIZE_t node_idx - SIZE_t parent - -cdef _cost_complexity_prune(unsigned char[:] leaves_in_subtree, # OUT - Tree orig_tree, - _CCPPruneController controller): - """Perform cost complexity pruning. - - This function takes an already grown tree, `orig_tree` and outputs a - boolean mask `leaves_in_subtree` which are the leaves in the pruned tree. - During the pruning process, the controller is passed the effective alpha and - the subtree impurities. Furthermore, the controller signals when to stop - pruning. - - Parameters - ---------- - leaves_in_subtree : unsigned char[:] - Output for leaves of subtree - orig_tree : Tree - Original tree - ccp_controller : _CCPPruneController - Cost complexity controller - """ - - cdef: - SIZE_t i - SIZE_t n_nodes = orig_tree.node_count - # prior probability using weighted samples - DOUBLE_t[:] weighted_n_node_samples = orig_tree.weighted_n_node_samples - DOUBLE_t total_sum_weights = weighted_n_node_samples[0] - DOUBLE_t[:] impurity = orig_tree.impurity - # weighted impurity of each node - DOUBLE_t[:] r_node = np.empty(shape=n_nodes, dtype=np.float64) - - SIZE_t[:] child_l = orig_tree.children_left - SIZE_t[:] child_r = orig_tree.children_right - SIZE_t[:] parent = np.zeros(shape=n_nodes, dtype=np.intp) - - stack[CostComplexityPruningRecord] ccp_stack - CostComplexityPruningRecord stack_record - SIZE_t node_idx - stack[SIZE_t] node_indices_stack - - SIZE_t[:] n_leaves = np.zeros(shape=n_nodes, dtype=np.intp) - DOUBLE_t[:] r_branch = np.zeros(shape=n_nodes, dtype=np.float64) - DOUBLE_t current_r - SIZE_t leaf_idx - SIZE_t parent_idx - - # candidate nodes that can be pruned - unsigned char[:] candidate_nodes = np.zeros(shape=n_nodes, - dtype=np.uint8) - # nodes in subtree - unsigned char[:] in_subtree = np.ones(shape=n_nodes, dtype=np.uint8) - SIZE_t pruned_branch_node_idx - DOUBLE_t subtree_alpha - DOUBLE_t effective_alpha - SIZE_t n_pruned_leaves - DOUBLE_t r_diff - DOUBLE_t max_float64 = np.finfo(np.float64).max - - # find parent node ids and leaves - with nogil: - - for i in range(r_node.shape[0]): - r_node[i] = ( - weighted_n_node_samples[i] * impurity[i] / total_sum_weights) - - # Push the root node - ccp_stack.push({"node_idx": 0, "parent": _TREE_UNDEFINED}) - - while not ccp_stack.empty(): - stack_record = ccp_stack.top() - ccp_stack.pop() - - node_idx = stack_record.node_idx - parent[node_idx] = stack_record.parent - - if child_l[node_idx] == _TREE_LEAF: - # ... and child_r[node_idx] == _TREE_LEAF: - leaves_in_subtree[node_idx] = 1 - else: - ccp_stack.push({"node_idx": child_l[node_idx], "parent": node_idx}) - ccp_stack.push({"node_idx": child_r[node_idx], "parent": node_idx}) - - # computes number of leaves in all branches and the overall impurity of - # the branch. The overall impurity is the sum of r_node in its leaves. - for leaf_idx in range(leaves_in_subtree.shape[0]): - if not leaves_in_subtree[leaf_idx]: - continue - r_branch[leaf_idx] = r_node[leaf_idx] - - # bubble up values to ancestor nodes - current_r = r_node[leaf_idx] - while leaf_idx != 0: - parent_idx = parent[leaf_idx] - r_branch[parent_idx] += current_r - n_leaves[parent_idx] += 1 - leaf_idx = parent_idx - - for i in range(leaves_in_subtree.shape[0]): - candidate_nodes[i] = not leaves_in_subtree[i] - - # save metrics before pruning - controller.save_metrics(0.0, r_branch[0]) - - # while root node is not a leaf - while candidate_nodes[0]: - - # computes ccp_alpha for subtrees and finds the minimal alpha - effective_alpha = max_float64 - for i in range(n_nodes): - if not candidate_nodes[i]: - continue - subtree_alpha = (r_node[i] - r_branch[i]) / (n_leaves[i] - 1) - if subtree_alpha < effective_alpha: - effective_alpha = subtree_alpha - pruned_branch_node_idx = i - - if controller.stop_pruning(effective_alpha): - break - - node_indices_stack.push(pruned_branch_node_idx) - - # descendants of branch are not in subtree - while not node_indices_stack.empty(): - node_idx = node_indices_stack.top() - node_indices_stack.pop() - - if not in_subtree[node_idx]: - continue # branch has already been marked for pruning - candidate_nodes[node_idx] = 0 - leaves_in_subtree[node_idx] = 0 - in_subtree[node_idx] = 0 - - if child_l[node_idx] != _TREE_LEAF: - # ... and child_r[node_idx] != _TREE_LEAF: - node_indices_stack.push(child_l[node_idx]) - node_indices_stack.push(child_r[node_idx]) - leaves_in_subtree[pruned_branch_node_idx] = 1 - in_subtree[pruned_branch_node_idx] = 1 - - # updates number of leaves - n_pruned_leaves = n_leaves[pruned_branch_node_idx] - 1 - n_leaves[pruned_branch_node_idx] = 0 - - # computes the increase in r_branch to bubble up - r_diff = r_node[pruned_branch_node_idx] - r_branch[pruned_branch_node_idx] - r_branch[pruned_branch_node_idx] = r_node[pruned_branch_node_idx] - - # bubble up values to ancestors - node_idx = parent[pruned_branch_node_idx] - while node_idx != _TREE_UNDEFINED: - n_leaves[node_idx] -= n_pruned_leaves - r_branch[node_idx] += r_diff - node_idx = parent[node_idx] - - controller.save_metrics(effective_alpha, r_branch[0]) - - controller.after_pruning(in_subtree) - - -def _build_pruned_tree_ccp( - Tree tree, # OUT - Tree orig_tree, - DOUBLE_t ccp_alpha -): - """Build a pruned tree from the original tree using cost complexity - pruning. - - The values and nodes from the original tree are copied into the pruned - tree. - - Parameters - ---------- - tree : Tree - Location to place the pruned tree - orig_tree : Tree - Original tree - ccp_alpha : positive double - Complexity parameter. The subtree with the largest cost complexity - that is smaller than ``ccp_alpha`` will be chosen. By default, - no pruning is performed. - """ - - cdef: - SIZE_t n_nodes = orig_tree.node_count - unsigned char[:] leaves_in_subtree = np.zeros( - shape=n_nodes, dtype=np.uint8) - - pruning_controller = _AlphaPruner(ccp_alpha=ccp_alpha) - - _cost_complexity_prune(leaves_in_subtree, orig_tree, pruning_controller) - - _build_pruned_tree(tree, orig_tree, leaves_in_subtree, - pruning_controller.capacity) - - -def ccp_pruning_path(Tree orig_tree): - """Computes the cost complexity pruning path. - - Parameters - ---------- - tree : Tree - Original tree. - - Returns - ------- - path_info : dict - Information about pruning path with attributes: - - ccp_alphas : ndarray - Effective alphas of subtree during pruning. - - impurities : ndarray - Sum of the impurities of the subtree leaves for the - corresponding alpha value in ``ccp_alphas``. - """ - cdef: - unsigned char[:] leaves_in_subtree = np.zeros( - shape=orig_tree.node_count, dtype=np.uint8) - - path_finder = _PathFinder(orig_tree.node_count) - - _cost_complexity_prune(leaves_in_subtree, orig_tree, path_finder) - - cdef: - UINT32_t total_items = path_finder.count - DOUBLE_t[:] ccp_alphas = np.empty(shape=total_items, dtype=np.float64) - DOUBLE_t[:] impurities = np.empty(shape=total_items, dtype=np.float64) - UINT32_t count = 0 - - while count < total_items: - ccp_alphas[count] = path_finder.ccp_alphas[count] - impurities[count] = path_finder.impurities[count] - count += 1 - - return { - 'ccp_alphas': np.asarray(ccp_alphas), - 'impurities': np.asarray(impurities), - } - - -cdef struct BuildPrunedRecord: - SIZE_t start - SIZE_t depth - SIZE_t parent - bint is_left - -cdef _build_pruned_tree( - Tree tree, # OUT - Tree orig_tree, - const unsigned char[:] leaves_in_subtree, - SIZE_t capacity -): - """Build a pruned tree. - - Build a pruned tree from the original tree by transforming the nodes in - ``leaves_in_subtree`` into leaves. - - Parameters - ---------- - tree : Tree - Location to place the pruned tree - orig_tree : Tree - Original tree - leaves_in_subtree : unsigned char memoryview, shape=(node_count, ) - Boolean mask for leaves to include in subtree - capacity : SIZE_t - Number of nodes to initially allocate in pruned tree - """ - tree._resize(capacity) - - cdef: - SIZE_t orig_node_id - SIZE_t new_node_id - SIZE_t depth - SIZE_t parent - bint is_left - bint is_leaf - - # value_stride for original tree and new tree are the same - SIZE_t value_stride = orig_tree.value_stride - SIZE_t max_depth_seen = -1 - int rc = 0 - Node* node - double* orig_value_ptr - double* new_value_ptr - - stack[BuildPrunedRecord] prune_stack - BuildPrunedRecord stack_record - - with nogil: - # push root node onto stack - prune_stack.push({"start": 0, "depth": 0, "parent": _TREE_UNDEFINED, "is_left": 0}) - - while not prune_stack.empty(): - stack_record = prune_stack.top() - prune_stack.pop() - - orig_node_id = stack_record.start - depth = stack_record.depth - parent = stack_record.parent - is_left = stack_record.is_left - - is_leaf = leaves_in_subtree[orig_node_id] - node = &orig_tree.nodes[orig_node_id] - - new_node_id = tree._add_node( - parent, is_left, is_leaf, node.feature, node.threshold, - node.impurity, node.n_node_samples, - node.weighted_n_node_samples, node.missing_go_to_left) - - if new_node_id == INTPTR_MAX: - rc = -1 - break - - # copy value from original tree to new tree - orig_value_ptr = orig_tree.value + value_stride * orig_node_id - new_value_ptr = tree.value + value_stride * new_node_id - memcpy(new_value_ptr, orig_value_ptr, sizeof(double) * value_stride) - - if not is_leaf: - # Push right child on stack - prune_stack.push({"start": node.right_child, "depth": depth + 1, - "parent": new_node_id, "is_left": 0}) - # push left child on stack - prune_stack.push({"start": node.left_child, "depth": depth + 1, - "parent": new_node_id, "is_left": 1}) - - if depth > max_depth_seen: - max_depth_seen = depth - - if rc >= 0: - tree.max_depth = max_depth_seen - if rc == -1: - raise MemoryError("pruning tree") \ No newline at end of file diff --git a/ivy/functional/frontends/sklearn/tree copy.py b/ivy/functional/frontends/sklearn/tree copy.py deleted file mode 100644 index 55ab869bd01f7..0000000000000 --- a/ivy/functional/frontends/sklearn/tree copy.py +++ /dev/null @@ -1,822 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - -import numpy as np - - - -from scipy.sparse import issparse -from scipy.sparse import csr_matrix -from scipy.sparse import isspmatrix_csr - - - - - - - - - - - - - - - - - - - - -# ============================================================================= -# Types and constants -# ============================================================================= - -from numpy import float32 as DTYPE -from numpy import float64 as DOUBLE - - -# Define constants -INFINITY = np.inf -EPSILON = np.finfo(np.double).eps - -# Some handy constants (BestFirstTreeBuilder) -IS_FIRST = 1 -IS_NOT_FIRST = 0 -IS_LEFT = 1 -IS_NOT_LEFT = 0 - -TREE_LEAF = -1 -TREE_UNDEFINED = -2 -_TREE_LEAF = TREE_LEAF -_TREE_UNDEFINED = TREE_UNDEFINED - -# Since you're dealing with Cython-specific types and features, -# it's important to provide a dummy definition for Node. -class Node: - def __init__(self): - self.left_child = None - self.right_child = None - self.feature = None - self.threshold = None - self.impurity = None - self.n_node_samples = None - self.weighted_n_node_samples = None - self.missing_go_to_left = None - -dummy = Node() -# Create a numpy dtype for Node using the dummy object -NODE_DTYPE = np.asarray([dummy], dtype=object).dtype -# ============================================================================= -# TreeBuilder -# ============================================================================= - -class TreeBuilder: - """Interface for different tree building strategies.""" - def __init__(self): - self.splitter = None - self.min_samples_split = None - self.min_samples_leaf = None - self.min_weight_leaf = None - self.max_depth = None - self.min_impurity_decrease = None - - def build( - self, - tree, - X, - y, - sample_weight=None, - missing_values_in_feature_mask=None, - ): - """Build a decision tree from the training set (X, y).""" - pass - - def _check_input( - self, - X, - y, - sample_weight, - ): - """Check input dtype, layout, and format""" - if issparse(X): - X = X.tocsc() #tocsc() is a method provided by the scipy.sparse module in the SciPy library. It's used to convert a sparse matrix to the Compressed Sparse Column (CSC) format. - X.sort_indices() #This is done to ensure that the indices of non-zero elements within the matrix are sorted in ascending order. - - if X.data.dtype != DTYPE: - X.data = np.ascontiguousarray(X.data, dtype=DTYPE) - - if X.indices.dtype != np.int32 or X.indptr.dtype != np.int32: - raise ValueError("No support for np.int64 index-based sparse matrices") - - elif X.dtype != DTYPE: - # since we have to copy, we will make it Fortran for efficiency - X = np.asfortranarray(X, dtype=DTYPE) - - if y.base.dtype != DTYPE or not y.base.flags.contiguous: - y = np.ascontiguousarray(y, dtype=DTYPE) - - if ( - sample_weight is not None and - ( - sample_weight.base.dtype != DOUBLE or - not sample_weight.base.flags.contiguous - ) - ): - sample_weight = np.asarray(sample_weight, dtype=DOUBLE, order="C") - - return X, y, sample_weight - - - - -# Depth first builder --------------------------------------------------------- -# A record on the stack for depth-first tree growing -class StackRecord: - def __init__(self, start, end, depth, parent, is_left, impurity, n_constant_features): - self.start = start - self.end = end - self.depth = depth - self.parent = parent - self.is_left = is_left - self.impurity = impurity - self.n_constant_features = n_constant_features - - - - - - - - -class DepthFirstTreeBuilder(TreeBuilder): - """Build a decision tree in depth-first fashion.""" - - def __init__( - self, splitter, min_samples_split, - min_samples_leaf, min_weight_leaf, - max_depth, min_impurity_decrease - ): - self.splitter = splitter - self.min_samples_split = min_samples_split - self.min_samples_leaf = min_samples_leaf - self.min_weight_leaf = min_weight_leaf - self.max_depth = max_depth - self.min_impurity_decrease = min_impurity_decrease - - def build( - self, - tree, - X, - y, - sample_weight=None, - missing_values_in_feature_mask=None - ): - """Build a decision tree from the training set (X, y).""" - - # Check input - X, y, sample_weight = self._check_input(X, y, sample_weight) - - # Initial capacity - init_capacity = (2 ** (tree.max_depth + 1)) - 1 if tree.max_depth <= 10 else 2047 - - tree._resize(init_capacity) - - # Parameters - splitter = self.splitter - max_depth = self.max_depth - min_samples_leaf = self.min_samples_leaf - min_weight_leaf = self.min_weight_leaf - min_samples_split = self.min_samples_split - min_impurity_decrease = self.min_impurity_decrease - - # Recursive partition (without actual recursion) - splitter.init(X, y, sample_weight, missing_values_in_feature_mask) - - stack = [] - - # Push root node onto stack - stack.append( - StackRecord( - start=0, - end=splitter.n_samples, - depth=0, - parent=_TREE_UNDEFINED, - is_left=False, - impurity=INFINITY, - n_constant_features=0 - ) - ) - weighted_n_node_samples = np.zeros(1, dtype=np.double) - while stack: - stack_record = stack.pop() - - start = stack_record.start - end = stack_record.end - depth = stack_record.depth - parent = stack_record.parent - is_left = stack_record.is_left - impurity = stack_record.impurity - n_constant_features = stack_record.n_constant_features - - n_node_samples = end - start - splitter.node_reset(start, end, weighted_n_node_samples) - - is_leaf = ( - depth >= max_depth - or n_node_samples < min_samples_split - or n_node_samples < 2 * min_samples_leaf - or np.sum(sample_weight[start:end]) < 2 * min_weight_leaf - ) - - if is_left: - impurity = splitter.node_impurity() - - is_leaf = is_leaf or impurity <= EPSILON - - if not is_leaf: - split = SplitRecord() # No idea what is SplitRecord in original code. Maybe this never gets called, not sure - splitter.node_split(impurity, split, n_constant_features) - is_leaf = ( - is_leaf - or split.pos >= end - or (split.improvement + EPSILON < min_impurity_decrease) - ) - - node_id = tree._add_node( - parent, - is_left, - is_leaf, - split.feature if not is_leaf else 0, - split.threshold if not is_leaf else 0, - impurity, - n_node_samples, - np.sum(sample_weight[start:end]), - split.missing_go_to_left, - ) - - if node_id == np.iinfo(np.intp).max: - raise MemoryError() - - splitter.node_value(tree.value + node_id * tree.value_stride) - - if not is_leaf: - # Push right child on stack - stack.append( - StackRecord( - start=split.pos, - end=end, - depth=depth + 1, - parent=node_id, - is_left=False, - impurity=split.impurity_right, - n_constant_features=n_constant_features, - ) - ) - # Push left child on stack - stack.append( - StackRecord( - start=start, - end=split.pos, - depth=depth + 1, - parent=node_id, - is_left=True, - impurity=split.impurity_left, - n_constant_features=n_constant_features, - ) - ) - - -class Tree: - def __init__(self, n_features, n_classes, n_outputs): - """Constructor.""" - self.n_features = None - self.n_classes = None - self.n_outputs = None - self.max_n_classes = None - self.max_depth = None - self.node_count = None - self.capacity = None - self.nodes = [] #replaced it with array since this array will contain nodes - self.value = None - self.value_stride = None - - dummy = 0 - size_t_dtype = np.array(dummy).dtype - - n_classes = _check_n_classes(n_classes, size_t_dtype) - - # Input/Output layout - self.n_features = n_features - self.n_outputs = n_outputs - self.n_classes = np.zeros(n_outputs, dtype=size_t_dtype) - - self.max_n_classes = np.max(n_classes) - self.value_stride = n_outputs * self.max_n_classes - - for k in range(n_outputs): - self.n_classes[k] = n_classes[k] - - # Inner structures - self.max_depth = 0 - self.node_count = 0 - self.capacity = 0 - self.value = None - self.nodes = None - - def __del__(self): - """Destructor.""" - # Free all inner structures - self.n_classes = None - self.value = None - self.nodes = None - - def __reduce__(self): - """Reduce re-implementation, for pickling.""" - raise NotImplementedError - - def __getstate__(self): - """Getstate re-implementation, for pickling.""" - d = {} - # capacity is inferred during the __setstate__ using nodes - d["max_depth"] = self.max_depth - d["node_count"] = self.node_count - d["nodes"] = self._get_node_ndarray() - d["values"] = self._get_value_ndarray() - return d - - def __setstate__(self, d): - """Setstate re-implementation, for unpickling.""" - raise NotImplementedError - - def _resize(self, capacity): - """ - Resize all inner arrays to `capacity`. If `capacity` is -1, then double the size of the inner arrays. - Returns -1 in case of failure to allocate memory (and raise MemoryError), or 0 otherwise. - """ - if self._resize_c(capacity) != 0: - # Raise MemoryError if resizing fails - raise MemoryError() - - def _resize_c(self, capacity=float('inf')): - # """ - # Guts of _resize - # Returns -1 in case of failure to allocate memory (and raise MemoryError), - # or 0 otherwise. - # """ - # if capacity == self.capacity and self.nodes is not None: - # return 0 - - # if capacity == float('inf'): - # if self.capacity == 0: - # capacity = 3 # default initial value - # else: - # capacity = 2 * self.capacity - - # # This section is relevant if the code is dealing with C arrays. - # # In Python, resizing arrays is handled automatically by lists or numpy arrays. - # # You won't need to explicitly reallocate memory or initialize values like this. - # self.nodes = [None] * capacity #doubtfull either the Node classes get reallocated or something else happends - # self.value = [0.0] * (capacity * self.value_stride) #doubtfull either the value classes get reallocated or something else happends - - # # value memory is initialized to 0 to enable classifier argmax - # if capacity > self.capacity: - # self.value += [0.0] * ((capacity - self.capacity) * self.value_stride) - - # # if capacity smaller than node_count, adjust the counter - # if capacity < self.node_count: - # self.node_count = capacity - - # self.capacity = capacity - # return 0 - raise NotImplementedError - - - def _add_node(self, parent, is_left, is_leaf, feature, threshold, impurity, - n_node_samples, weighted_n_node_samples, missing_go_to_left): - """ - Add a node to the tree. - - The new node registers itself as the child of its parent. - - Returns -1 on error. - """ - node_id = self.node_count - - #no need to resize since python reallocates lists dynamically - # if node_id >= self.capacity: - # if self._resize_c() != 0: - # return -1 #throw error if resize not possible - - node = Node() #self.nodes contains a list of nodes, it returns the node at node_id location - self.nodes.append(node) - node.impurity = impurity - node.n_node_samples = n_node_samples - node.weighted_n_node_samples = weighted_n_node_samples - - if parent != _TREE_UNDEFINED: - if is_left: - self.nodes[parent].left_child = node_id - else: - self.nodes[parent].right_child = node_id - - if is_leaf: - node.left_child = _TREE_LEAF - node.right_child = _TREE_LEAF - node.feature = _TREE_UNDEFINED - node.threshold = _TREE_UNDEFINED - - else: - # left_child and right_child will be set later - node.feature = feature - node.threshold = threshold - node.missing_go_to_left = missing_go_to_left - - self.node_count += 1 - - return node_id - - def predict(self, X): - # Apply the model to the input data X - predictions = self.apply(X) - # Get the internal data as a NumPy array - internal_data = self._get_value_ndarray() - # Use the predictions to index the internal data - out = internal_data[predictions] #not sure if this accurately translates to .take(self.apply(X), axis=0, mode='clip') - # Reshape the output if the model is single-output - if self.n_outputs == 1: - out = out.reshape(X.shape[0], self.max_n_classes) - return out - - def apply(self, X): - """Finds the terminal region (=leaf node) for each sample in X.""" - if issparse(X): - return self._apply_sparse_csr(X) - else: - return self._apply_dense(X) - - def _apply_dense(self, X): - if not isinstance(X, torch.Tensor): - raise ValueError("X should be a torch.Tensor, got %s" % type(X)) - - if X.dtype != torch.float32: - raise ValueError("X.dtype should be torch.float32, got %s" % X.dtype) - - X_tensor = X - n_samples = X.shape[0] - out = torch.zeros(n_samples, dtype=torch.int64) - - for i in range(n_samples): - node = self.nodes - - while node.left_child != _TREE_LEAF: - X_i_node_feature = X_tensor[i, node.feature] - - if torch.isnan(X_i_node_feature): - if node.missing_go_to_left: - node = self.nodes[node.left_child] - else: - node = self.nodes[node.right_child] - elif X_i_node_feature <= node.threshold: - node = self.nodes[node.left_child] - else: - node = self.nodes[node.right_child] - - out[i] = node - self.nodes - - return out - - def _apply_sparse_csr(self, X): - """Finds the terminal region (=leaf node) for each sample in sparse X.""" - if not isinstance(X, csr_matrix): - raise ValueError("X should be in csr_matrix format, got %s" % type(X)) - - if X.dtype != np.float32: - raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) - - n_samples, n_features = X.shape - - # Initialize output - out = np.zeros(n_samples, dtype=np.intp) - - # Initialize auxiliary data structures - feature_to_sample = np.full(n_features, -1, dtype=np.intp) - X_sample = np.zeros(n_features, dtype=np.float32) - - for i in range(n_samples): - node = self.nodes - - for k in range(X.indptr[i], X.indptr[i + 1]): - feature_to_sample[X.indices[k]] = i - X_sample[X.indices[k]] = X.data[k] - - while node.left_child != _TREE_LEAF: - if feature_to_sample[node.feature] == i: - feature_value = X_sample[node.feature] - else: - feature_value = 0.0 - - if feature_value <= node.threshold: - node = self.nodes[node.left_child] - else: - node = self.nodes[node.right_child] - - out[i] = node - self.nodes # node offset - - return out - - def decision_path(self, X): - """Finds the decision path (=node) for each sample in X.""" - if issparse(X): - return self._decision_path_sparse_csr(X) - else: - return self._decision_path_dense(X) - - def _decision_path_dense(self, X): - """Finds the decision path (=node) for each sample in X.""" - - # Check input - if not isinstance(X, np.ndarray): - raise ValueError("X should be in np.ndarray format, got %s" % type(X)) - - if X.dtype != DTYPE: - raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) - - # Extract input - X_ndarray = X - n_samples = X.shape[0] - - # Initialize output - indptr = np.zeros(n_samples + 1, dtype=np.intp) - indices = np.zeros(n_samples * (1 + self.max_depth), dtype=np.intp) - - # Initialize auxiliary data-structure - node = None - i = 0 - - for i in range(n_samples): - node = self.nodes - indptr[i + 1] = indptr[i] - - # Add all external nodes - while node.left_child != _TREE_LEAF: - # ... and node.right_child != _TREE_LEAF: - indices[indptr[i + 1]] = node - self.nodes - indptr[i + 1] += 1 - - if X_ndarray[i, node.feature] <= node.threshold: - node = self.nodes[node.left_child] - else: - node = self.nodes[node.right_child] - - # Add the leaf node - indices[indptr[i + 1]] = node - self.nodes - indptr[i + 1] += 1 - - indices = indices[:indptr[n_samples]] - data = np.ones(shape=len(indices), dtype=np.intp) - out = csr_matrix((data, indices, indptr), shape=(n_samples, self.node_count)) - - return out - - def _decision_path_sparse_csr(self, X): - """Finds the decision path (=node) for each sample in X.""" - - # Check input - if not isspmatrix_csr(X): - raise ValueError("X should be in csr_matrix format, got %s" % type(X)) - - if X.dtype != DTYPE: - raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) - - # Extract input - X_data = X.data - X_indices = X.indices - X_indptr = X.indptr - - n_samples = X.shape[0] - n_features = X.shape[1] - - # Initialize output - indptr = np.zeros(n_samples + 1, dtype=np.intp) - indices = np.zeros(n_samples * (1 + self.max_depth), dtype=np.intp) - - # Initialize auxiliary data-structure - feature_value = 0.0 - node = None - X_sample = np.zeros(n_features, dtype=DTYPE) - feature_to_sample = np.full(n_features, -1, dtype=np.intp) - - for i in range(n_samples): - node = self.nodes - indptr[i + 1] = indptr[i] - - for k in range(X_indptr[i], X_indptr[i + 1]): - feature_to_sample[X_indices[k]] = i - X_sample[X_indices[k]] = X_data[k] - - # While node not a leaf - while node.left_child != _TREE_LEAF: - indices[indptr[i + 1]] = node - self.nodes - indptr[i + 1] += 1 - - if feature_to_sample[node.feature] == i: - feature_value = X_sample[node.feature] - else: - feature_value = 0.0 - - if feature_value <= node.threshold: - node = self.nodes[node.left_child] - else: - node = self.nodes[node.right_child] - - # Add the leaf node - indices[indptr[i + 1]] = node - self.nodes - indptr[i + 1] += 1 - - indices = indices[:indptr[n_samples]] - data = np.ones(shape=len(indices), dtype=np.intp) - out = csr_matrix((data, indices, indptr), shape=(n_samples, self.node_count)) - - return out - - def compute_node_depths(self): - """Compute the depth of each node in a tree. - - .. versionadded:: 1.3 - - Returns - ------- - depths : ndarray of shape (self.node_count,), dtype=np.int64 - The depth of each node in the tree. - """ - depths = np.empty(self.node_count, dtype=np.int64) - children_left = self.children_left - children_right = self.children_right - node_count = self.node_count - - depths[0] = 1 # init root node - for node_id in range(node_count): - if children_left[node_id] != _TREE_LEAF: - depth = depths[node_id] + 1 - depths[children_left[node_id]] = depth - depths[children_right[node_id]] = depth - - return depths.base - - def compute_feature_importances(self, normalize=True): - """Computes the importance of each feature (aka variable).""" - nodes = self.nodes - node = nodes - end_node = node + self.node_count - - importances = np.zeros(self.n_features, dtype=np.float64) - - while node != end_node: - if node.left_child != _TREE_LEAF: - left = nodes[node.left_child] - right = nodes[node.right_child] - - importances[node.feature] += ( - node.weighted_n_node_samples * node.impurity - - left.weighted_n_node_samples * left.impurity - - right.weighted_n_node_samples * right.impurity) - node += 1 - - for i in range(self.n_features): - importances[i] /= nodes[0].weighted_n_node_samples - - if normalize: - normalizer = np.sum(importances) - - if normalizer > 0.0: - # Avoid dividing by zero (e.g., when root is pure) - importances /= normalizer - - return importances - - def _get_value_ndarray(self): - """Wraps value as a 3-d NumPy array. - - The array keeps a reference to this Tree, which manages the underlying - memory. - """ - shape = (self.node_count, self.n_outputs, self.max_n_classes) - arr = np.ndarray(shape, dtype=np.float64, buffer=self.value) - arr.base = self - return arr - - def _get_node_ndarray(self): - """Wraps nodes as a NumPy struct array. - - The array keeps a reference to this Tree, which manages the underlying - memory. Individual fields are publicly accessible as properties of the - Tree. - """ - shape = (self.node_count,) - dtype = np.dtype([ - ('left_child', np.intp), - ('right_child', np.intp), - ('feature', np.intp), - ('threshold', np.float64), - ('impurity', np.float64), - ('n_node_samples', np.intp), - ('weighted_n_node_samples', np.float64), - ('missing_go_to_left', np.uint8) - ]) - arr = np.ndarray(shape, dtype=dtype, buffer=self.nodes) - arr.base = self - return arr - - def compute_partial_dependence(self, X, target_features, out): - out.fill(0.0) # Initialize the output array - - _TREE_LEAF = self._TREE_LEAF # The value for leaf nodes - - for sample_idx in range(X.shape[0]): - stack_size = 1 - node_idx_stack = [0] # root node - weight_stack = [1.0] # all samples are in the root node - total_weight = 0.0 - - while stack_size > 0: - stack_size -= 1 - current_node_idx = node_idx_stack[stack_size] - current_node = self.nodes[current_node_idx] - - if current_node.left_child == _TREE_LEAF: - # Leaf node - out[sample_idx] += weight_stack[stack_size] * self.value[current_node_idx] - total_weight += weight_stack[stack_size] - else: - is_target_feature = any(target_feature == current_node.feature for target_feature in target_features) - if is_target_feature: - if X[sample_idx, current_node.feature] <= current_node.threshold: - node_idx_stack.append(current_node.left_child) - weight_stack.append(weight_stack[stack_size]) - stack_size += 1 - else: - node_idx_stack.append(current_node.right_child) - weight_stack.append(weight_stack[stack_size]) - stack_size += 1 - else: - left_sample_frac = self.nodes[current_node.left_child].weighted_n_node_samples / current_node.weighted_n_node_samples - current_weight = weight_stack[stack_size] - node_idx_stack.extend([current_node.left_child, current_node.right_child]) - weight_stack.extend([current_weight * left_sample_frac, current_weight * (1 - left_sample_frac)]) - stack_size += 2 - - if not (0.999 < total_weight < 1.001): - raise ValueError(f"Total weight should be 1.0 but was {total_weight:.9f}") - - -def _check_n_classes(n_classes, expected_dtype): - if n_classes.ndim != 1: - raise ValueError( - f"Wrong dimensions for n_classes from the pickle: " - f"expected 1, got {n_classes.ndim}" - ) - - if n_classes.dtype == expected_dtype: - return n_classes - - # Handles both different endianness and different bitness - if n_classes.dtype.kind == "i" and n_classes.dtype.itemsize in [4, 8]: - return n_classes.astype(expected_dtype, casting="same_kind") - - raise ValueError( - "n_classes from the pickle has an incompatible dtype:\n" - f"- expected: {expected_dtype}\n" - f"- got: {n_classes.dtype}" - ) - - - - - - diff --git a/ivy/functional/frontends/sklearn/tree ivy copy.py b/ivy/functional/frontends/sklearn/tree ivy copy.py deleted file mode 100644 index fa547997eb761..0000000000000 --- a/ivy/functional/frontends/sklearn/tree ivy copy.py +++ /dev/null @@ -1,840 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - -import numpy as np - - - -from scipy.sparse import issparse -from scipy.sparse import csr_matrix -from scipy.sparse import isspmatrix_csr - - - - - - - - - - - - - - - - - - - - -# ============================================================================= -# Types and constants -# ============================================================================= - -from numpy import float32 as DTYPE -from numpy import float64 as DOUBLE - - -# Define constants -INFINITY = np.inf -EPSILON = np.finfo(np.double).eps - -# Some handy constants (BestFirstTreeBuilder) -IS_FIRST = 1 -IS_NOT_FIRST = 0 -IS_LEFT = 1 -IS_NOT_LEFT = 0 - -TREE_LEAF = -1 -TREE_UNDEFINED = -2 -_TREE_LEAF = TREE_LEAF -_TREE_UNDEFINED = TREE_UNDEFINED - -# Since you're dealing with Cython-specific types and features, -# it's important to provide a dummy definition for Node. -class Node: - def __init__(self): - self.left_child = None - self.right_child = None - self.feature = None - self.threshold = None - self.impurity = None - self.n_node_samples = None - self.weighted_n_node_samples = None - self.missing_go_to_left = None - -dummy = Node() -# Create a numpy dtype for Node using the dummy object -NODE_DTYPE = np.asarray([dummy], dtype=object).dtype -# ============================================================================= -# TreeBuilder -# ============================================================================= - -class TreeBuilder: - """Interface for different tree building strategies.""" - def __init__(self): - self.splitter = None - self.min_samples_split = None - self.min_samples_leaf = None - self.min_weight_leaf = None - self.max_depth = None - self.min_impurity_decrease = None - - def build( - self, - tree, - X, - y, - sample_weight=None, - missing_values_in_feature_mask=None, - ): - """Build a decision tree from the training set (X, y).""" - pass - - def _check_input( - self, - X, - y, - sample_weight, - ): - """Check input dtype, layout, and format""" - if issparse(X): - X = X.tocsc() #tocsc() is a method provided by the scipy.sparse module in the SciPy library. It's used to convert a sparse matrix to the Compressed Sparse Column (CSC) format. - X.sort_indices() #This is done to ensure that the indices of non-zero elements within the matrix are sorted in ascending order. - - if X.data.dtype != DTYPE: - X.data = np.ascontiguousarray(X.data, dtype=DTYPE) - - if X.indices.dtype != np.int32 or X.indptr.dtype != np.int32: - raise ValueError("No support for np.int64 index-based sparse matrices") - - elif X.dtype != DTYPE: - # since we have to copy, we will make it Fortran for efficiency - X = np.asfortranarray(X, dtype=DTYPE) - - if y.base.dtype != DTYPE or not y.base.flags.contiguous: - y = np.ascontiguousarray(y, dtype=DTYPE) - - if ( - sample_weight is not None and - ( - sample_weight.base.dtype != DOUBLE or - not sample_weight.base.flags.contiguous - ) - ): - sample_weight = np.asarray(sample_weight, dtype=DOUBLE, order="C") - - return X, y, sample_weight - - - - -# Depth first builder --------------------------------------------------------- -# A record on the stack for depth-first tree growing -class StackRecord: - def __init__(self, start, end, depth, parent, is_left, impurity, n_constant_features): - self.start = start - self.end = end - self.depth = depth - self.parent = parent - self.is_left = is_left - self.impurity = impurity - self.n_constant_features = n_constant_features - - -class SplitRecord: - def __init__(self, feature, pos, threshold, improvement, - impurity_left, impurity_right, missing_go_to_left, - n_missing): - self.feature = feature - self.pos = pos - self.threshold = threshold - self.improvement = improvement - self.impurity_left = impurity_left - self.impurity_right = impurity_right - self.missing_go_to_left = missing_go_to_left - self.n_missing = n_missing - - - - - -class DepthFirstTreeBuilder(TreeBuilder): - """Build a decision tree in depth-first fashion.""" - - def __init__( - self, splitter, min_samples_split, - min_samples_leaf, min_weight_leaf, - max_depth, min_impurity_decrease - ): - self.splitter = splitter - self.min_samples_split = min_samples_split - self.min_samples_leaf = min_samples_leaf - self.min_weight_leaf = min_weight_leaf - self.max_depth = max_depth - self.min_impurity_decrease = min_impurity_decrease - - def build( - self, - tree, - X, - y, - sample_weight=None, - missing_values_in_feature_mask=None - ): - """Build a decision tree from the training set (X, y).""" - - # Check input - X, y, sample_weight = self._check_input(X, y, sample_weight) - - # Initial capacity - init_capacity = (2 ** (tree.max_depth + 1)) - 1 if tree.max_depth <= 10 else 2047 - - tree._resize(init_capacity) - - # Parameters - splitter = self.splitter - max_depth = self.max_depth - min_samples_leaf = self.min_samples_leaf - min_weight_leaf = self.min_weight_leaf - min_samples_split = self.min_samples_split - min_impurity_decrease = self.min_impurity_decrease - - # Recursive partition (without actual recursion) - splitter.init(X, y, sample_weight, missing_values_in_feature_mask) - - stack = [] - - # Push root node onto stack - stack.append( - StackRecord( - start=0, - end=splitter.n_samples, - depth=0, - parent=_TREE_UNDEFINED, - is_left=False, - impurity=INFINITY, - n_constant_features=0 - ) - ) - weighted_n_node_samples = np.zeros(1, dtype=np.double) - while stack: - stack_record = stack.pop() - - start = stack_record.start - end = stack_record.end - depth = stack_record.depth - parent = stack_record.parent - is_left = stack_record.is_left - impurity = stack_record.impurity - n_constant_features = stack_record.n_constant_features - - n_node_samples = end - start - splitter.node_reset(start, end, weighted_n_node_samples) - - is_leaf = ( - depth >= max_depth - or n_node_samples < min_samples_split - or n_node_samples < 2 * min_samples_leaf - or np.sum(sample_weight[start:end]) < 2 * min_weight_leaf - ) - - if is_left: - impurity = splitter.node_impurity() - - is_leaf = is_leaf or impurity <= EPSILON - - if not is_leaf: - split = SplitRecord() # No idea what is SplitRecord in original code. Maybe this never gets called, not sure - splitter.node_split(impurity, split, n_constant_features) - is_leaf = ( - is_leaf - or split.pos >= end - or (split.improvement + EPSILON < min_impurity_decrease) - ) - - node_id = tree._add_node( - parent, - is_left, - is_leaf, - split.feature if not is_leaf else 0, - split.threshold if not is_leaf else 0, - impurity, - n_node_samples, - np.sum(sample_weight[start:end]), - split.missing_go_to_left, - ) - - if node_id == np.iinfo(np.intp).max: - raise MemoryError() - - splitter.node_value(tree.value + node_id * tree.value_stride) - - if not is_leaf: - # Push right child on stack - stack.append( - StackRecord( - start=split.pos, - end=end, - depth=depth + 1, - parent=node_id, - is_left=False, - impurity=split.impurity_right, - n_constant_features=n_constant_features, - ) - ) - # Push left child on stack - stack.append( - StackRecord( - start=start, - end=split.pos, - depth=depth + 1, - parent=node_id, - is_left=True, - impurity=split.impurity_left, - n_constant_features=n_constant_features, - ) - ) - - -class Tree: - def __init__(self, n_features, n_classes, n_outputs): - """Constructor.""" - self.n_features = None - self.n_outputs = None - self.n_classes = None - self.max_n_classes = None - self.max_depth = None - self.node_count = None - self.capacity = None - self.nodes = None - self.value = None - self.value_stride = None - - dummy = 0 - size_t_dtype = np.array(dummy).dtype - - n_classes = _check_n_classes(n_classes, size_t_dtype) - - # Input/Output layout - self.n_features = n_features - self.n_outputs = n_outputs - self.n_classes = np.zeros(n_outputs, dtype=size_t_dtype) - - self.max_n_classes = np.max(n_classes) - self.value_stride = n_outputs * self.max_n_classes - - for k in range(n_outputs): - self.n_classes[k] = n_classes[k] - - # Inner structures - self.max_depth = 0 - self.node_count = 0 - self.capacity = 0 - self.value = None - self.nodes = None - - def __del__(self): - """Destructor.""" - # Free all inner structures - self.n_classes = None - self.value = None - self.nodes = None - - #NOT CONSIDERING PICKINLING FOR NOW - def __reduce__(self): - """Reduce re-implementation, for pickling.""" - raise NotImplementedError - #NOT CONSIDERING PICKINLING FOR NOW - def __getstate__(self): - """Getstate re-implementation, for pickling.""" - d = {} - # capacity is inferred during the __setstate__ using nodes - d["max_depth"] = self.max_depth - d["node_count"] = self.node_count - d["nodes"] = self._get_node_ndarray() - d["values"] = self._get_value_ndarray() - return d - #NOT CONSIDERING PICKINLING FOR NOW - def __setstate__(self, d): - """Setstate re-implementation, for unpickling.""" - raise NotImplementedError - - def _resize(self, capacity): - """ - Resize all inner arrays to `capacity`. If `capacity` is -1, then double the size of the inner arrays. - Returns -1 in case of failure to allocate memory (and raise MemoryError), or 0 otherwise. - """ - if self._resize_c(capacity) != 0: - # Raise MemoryError if resizing fails - raise MemoryError() - - def _resize_c(self, capacity=float('inf')): - """ - Guts of _resize - Returns -1 in case of failure to allocate memory (and raise MemoryError), - or 0 otherwise. - """ - if capacity == self.capacity and self.nodes is not None: - return 0 - - if capacity == float('inf'): - if self.capacity == 0: - capacity = 3 # default initial value - else: - capacity = 2 * self.capacity - - # This section is relevant if the code is dealing with C arrays. - # In Python, resizing arrays is handled automatically by lists or numpy arrays. - # You won't need to explicitly reallocate memory or initialize values like this. - - # replaced safe_realloc(&self.nodes, capacity) with the following - new_nodes = np.empty(capacity, dtype=object) - for i in range(len(self.nodes)): - new_nodes[i] = self.nodes[i] - self.nodes = new_nodes - - # replaced safe_realloc(&self.value, capacity * self.value_stride) with the following - new_value = np.empty(capacity * self.value_stride, dtype=object) - for i in range(len(self.value)): - new_value[i] = self.value[i] - self.value = new_value - - # if capacity smaller than node_count, adjust the counter - if capacity < self.node_count: - self.node_count = capacity - - self.capacity = capacity - return 0 - - - def _add_node(self, parent, is_left, is_leaf, feature, threshold, impurity, - n_node_samples, weighted_n_node_samples, missing_go_to_left): - """ - Add a node to the tree. - - The new node registers itself as the child of its parent. - - Returns -1 on error. - """ - node_id = self.node_count - - if node_id >= self.capacity: - if self._resize_c() != 0: - return -1 - - node = self.nodes[node_id] - node.impurity = impurity - node.n_node_samples = n_node_samples - node.weighted_n_node_samples = weighted_n_node_samples - - if parent != _TREE_UNDEFINED: - if is_left: - self.nodes[parent].left_child = node_id - else: - self.nodes[parent].right_child = node_id - - if is_leaf: - node.left_child = _TREE_LEAF - node.right_child = _TREE_LEAF - node.feature = _TREE_UNDEFINED - node.threshold = _TREE_UNDEFINED - - else: - # left_child and right_child will be set later - node.feature = feature - node.threshold = threshold - node.missing_go_to_left = missing_go_to_left - - self.node_count += 1 - - return node_id - - def predict(self, X): - """Predict target for X.""" - out = self._get_value_ndarray()[self.apply(X), :, :] - - if self.n_outputs == 1: - out = out.reshape(X.shape[0], self.max_n_classes) - - return out - - def apply(self, X): - """Finds the terminal region (=leaf node) for each sample in X.""" - if issparse(X): - return self._apply_sparse_csr(X) - else: - return self._apply_dense(X) - - def _apply_dense(self, X): - """Finds the terminal region (=leaf node) for each sample in X.""" - - # Check input - if not isinstance(X, np.ndarray): - raise ValueError("X should be in np.ndarray format, got %s" % type(X)) - - if X.dtype != np.float32: - raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) - - # Extract input - n_samples, n_features = X.shape - - # Initialize output - out = np.zeros(n_samples, dtype=np.intp) - - with np.nditer(X, flags=['c_index', 'multi_index'], op_flags=['readonly'], order='C') as it: - for x_value, index in it: - node = self.nodes - # While node not a leaf - while node.left_child != _TREE_LEAF: - X_i_node_feature = x_value - # ... and node.right_child != _TREE_LEAF: - if np.isnan(X_i_node_feature): - if node.missing_go_to_left: - node = self.nodes[node.left_child] - else: - node = self.nodes[node.right_child] - elif X_i_node_feature <= node.threshold: - node = self.nodes[node.left_child] - else: - node = self.nodes[node.right_child] - - out[index[0]] = node - self.nodes # node offset - - return out - - def _apply_sparse_csr(self, X): - """Finds the terminal region (=leaf node) for each sample in sparse X.""" - if not isinstance(X, csr_matrix): - raise ValueError("X should be in csr_matrix format, got %s" % type(X)) - - if X.dtype != np.float32: - raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) - - n_samples, n_features = X.shape - - # Initialize output - out = np.zeros(n_samples, dtype=np.intp) - - # Initialize auxiliary data structures - feature_to_sample = np.full(n_features, -1, dtype=np.intp) - X_sample = np.zeros(n_features, dtype=np.float32) - - for i in range(n_samples): - node = self.nodes - - for k in range(X.indptr[i], X.indptr[i + 1]): - feature_to_sample[X.indices[k]] = i - X_sample[X.indices[k]] = X.data[k] - - while node.left_child != _TREE_LEAF: - if feature_to_sample[node.feature] == i: - feature_value = X_sample[node.feature] - else: - feature_value = 0.0 - - if feature_value <= node.threshold: - node = self.nodes[node.left_child] - else: - node = self.nodes[node.right_child] - - out[i] = node - self.nodes # node offset - - return out - - def decision_path(self, X): - """Finds the decision path (=node) for each sample in X.""" - if issparse(X): - return self._decision_path_sparse_csr(X) - else: - return self._decision_path_dense(X) - - def _decision_path_dense(self, X): - """Finds the decision path (=node) for each sample in X.""" - - # Check input - if not isinstance(X, np.ndarray): - raise ValueError("X should be in np.ndarray format, got %s" % type(X)) - - if X.dtype != DTYPE: - raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) - - # Extract input - X_ndarray = X - n_samples = X.shape[0] - - # Initialize output - indptr = np.zeros(n_samples + 1, dtype=np.intp) - indices = np.zeros(n_samples * (1 + self.max_depth), dtype=np.intp) - - # Initialize auxiliary data-structure - node = None - i = 0 - - for i in range(n_samples): - node = self.nodes - indptr[i + 1] = indptr[i] - - # Add all external nodes - while node.left_child != _TREE_LEAF: - # ... and node.right_child != _TREE_LEAF: - indices[indptr[i + 1]] = node - self.nodes - indptr[i + 1] += 1 - - if X_ndarray[i, node.feature] <= node.threshold: - node = self.nodes[node.left_child] - else: - node = self.nodes[node.right_child] - - # Add the leaf node - indices[indptr[i + 1]] = node - self.nodes - indptr[i + 1] += 1 - - indices = indices[:indptr[n_samples]] - data = np.ones(shape=len(indices), dtype=np.intp) - out = csr_matrix((data, indices, indptr), shape=(n_samples, self.node_count)) - - return out - - def _decision_path_sparse_csr(self, X): - """Finds the decision path (=node) for each sample in X.""" - - # Check input - if not isspmatrix_csr(X): - raise ValueError("X should be in csr_matrix format, got %s" % type(X)) - - if X.dtype != DTYPE: - raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) - - # Extract input - X_data = X.data - X_indices = X.indices - X_indptr = X.indptr - - n_samples = X.shape[0] - n_features = X.shape[1] - - # Initialize output - indptr = np.zeros(n_samples + 1, dtype=np.intp) - indices = np.zeros(n_samples * (1 + self.max_depth), dtype=np.intp) - - # Initialize auxiliary data-structure - feature_value = 0.0 - node = None - X_sample = np.zeros(n_features, dtype=DTYPE) - feature_to_sample = np.full(n_features, -1, dtype=np.intp) - - for i in range(n_samples): - node = self.nodes - indptr[i + 1] = indptr[i] - - for k in range(X_indptr[i], X_indptr[i + 1]): - feature_to_sample[X_indices[k]] = i - X_sample[X_indices[k]] = X_data[k] - - # While node not a leaf - while node.left_child != _TREE_LEAF: - indices[indptr[i + 1]] = node - self.nodes - indptr[i + 1] += 1 - - if feature_to_sample[node.feature] == i: - feature_value = X_sample[node.feature] - else: - feature_value = 0.0 - - if feature_value <= node.threshold: - node = self.nodes[node.left_child] - else: - node = self.nodes[node.right_child] - - # Add the leaf node - indices[indptr[i + 1]] = node - self.nodes - indptr[i + 1] += 1 - - indices = indices[:indptr[n_samples]] - data = np.ones(shape=len(indices), dtype=np.intp) - out = csr_matrix((data, indices, indptr), shape=(n_samples, self.node_count)) - - return out - - def compute_node_depths(self): - """Compute the depth of each node in a tree. - - .. versionadded:: 1.3 - - Returns - ------- - depths : ndarray of shape (self.node_count,), dtype=np.int64 - The depth of each node in the tree. - """ - depths = np.empty(self.node_count, dtype=np.int64) - children_left = self.children_left - children_right = self.children_right - node_count = self.node_count - - depths[0] = 1 # init root node - for node_id in range(node_count): - if children_left[node_id] != _TREE_LEAF: - depth = depths[node_id] + 1 - depths[children_left[node_id]] = depth - depths[children_right[node_id]] = depth - - return depths.base - - def compute_feature_importances(self, normalize=True): - """Computes the importance of each feature (aka variable).""" - nodes = self.nodes - node = nodes - end_node = node + self.node_count - - importances = np.zeros(self.n_features, dtype=np.float64) - - while node != end_node: - if node.left_child != _TREE_LEAF: - left = nodes[node.left_child] - right = nodes[node.right_child] - - importances[node.feature] += ( - node.weighted_n_node_samples * node.impurity - - left.weighted_n_node_samples * left.impurity - - right.weighted_n_node_samples * right.impurity) - node += 1 - - for i in range(self.n_features): - importances[i] /= nodes[0].weighted_n_node_samples - - if normalize: - normalizer = np.sum(importances) - - if normalizer > 0.0: - # Avoid dividing by zero (e.g., when root is pure) - importances /= normalizer - - return importances - - def _get_value_ndarray(self): - """Wraps value as a 3-d NumPy array. - - The array keeps a reference to this Tree, which manages the underlying - memory. - """ - shape = (self.node_count, self.n_outputs, self.max_n_classes) - arr = np.ndarray(shape, dtype=np.float64, buffer=self.value) - arr.base = self - return arr - - def _get_node_ndarray(self): - """Wraps nodes as a NumPy struct array. - - The array keeps a reference to this Tree, which manages the underlying - memory. Individual fields are publicly accessible as properties of the - Tree. - """ - shape = (self.node_count,) - dtype = np.dtype([ - ('left_child', np.intp), - ('right_child', np.intp), - ('feature', np.intp), - ('threshold', np.float64), - ('impurity', np.float64), - ('n_node_samples', np.intp), - ('weighted_n_node_samples', np.float64), - ('missing_go_to_left', np.uint8) - ]) - arr = np.ndarray(shape, dtype=dtype, buffer=self.nodes) - arr.base = self - return arr - - def compute_partial_dependence(self, X, target_features, out): - out.fill(0.0) # Initialize the output array - - _TREE_LEAF = self._TREE_LEAF # The value for leaf nodes - - for sample_idx in range(X.shape[0]): - stack_size = 1 - node_idx_stack = [0] # root node - weight_stack = [1.0] # all samples are in the root node - total_weight = 0.0 - - while stack_size > 0: - stack_size -= 1 - current_node_idx = node_idx_stack[stack_size] - current_node = self.nodes[current_node_idx] - - if current_node.left_child == _TREE_LEAF: - # Leaf node - out[sample_idx] += weight_stack[stack_size] * self.value[current_node_idx] - total_weight += weight_stack[stack_size] - else: - is_target_feature = any(target_feature == current_node.feature for target_feature in target_features) - if is_target_feature: - if X[sample_idx, current_node.feature] <= current_node.threshold: - node_idx_stack.append(current_node.left_child) - weight_stack.append(weight_stack[stack_size]) - stack_size += 1 - else: - node_idx_stack.append(current_node.right_child) - weight_stack.append(weight_stack[stack_size]) - stack_size += 1 - else: - left_sample_frac = self.nodes[current_node.left_child].weighted_n_node_samples / current_node.weighted_n_node_samples - current_weight = weight_stack[stack_size] - node_idx_stack.extend([current_node.left_child, current_node.right_child]) - weight_stack.extend([current_weight * left_sample_frac, current_weight * (1 - left_sample_frac)]) - stack_size += 2 - - if not (0.999 < total_weight < 1.001): - raise ValueError(f"Total weight should be 1.0 but was {total_weight:.9f}") - - -def _check_n_classes(n_classes, expected_dtype): - if n_classes.ndim != 1: - raise ValueError( - f"Wrong dimensions for n_classes from the pickle: " - f"expected 1, got {n_classes.ndim}" - ) - - if n_classes.dtype == expected_dtype: - return n_classes - - # Handles both different endianness and different bitness - if n_classes.dtype.kind == "i" and n_classes.dtype.itemsize in [4, 8]: - return n_classes.astype(expected_dtype, casting="same_kind") - - raise ValueError( - "n_classes from the pickle has an incompatible dtype:\n" - f"- expected: {expected_dtype}\n" - f"- got: {n_classes.dtype}" - ) - - - - - - diff --git a/ivy/functional/frontends/sklearn/tree ivy.py b/ivy/functional/frontends/sklearn/tree ivy.py deleted file mode 100644 index fa547997eb761..0000000000000 --- a/ivy/functional/frontends/sklearn/tree ivy.py +++ /dev/null @@ -1,840 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - -import numpy as np - - - -from scipy.sparse import issparse -from scipy.sparse import csr_matrix -from scipy.sparse import isspmatrix_csr - - - - - - - - - - - - - - - - - - - - -# ============================================================================= -# Types and constants -# ============================================================================= - -from numpy import float32 as DTYPE -from numpy import float64 as DOUBLE - - -# Define constants -INFINITY = np.inf -EPSILON = np.finfo(np.double).eps - -# Some handy constants (BestFirstTreeBuilder) -IS_FIRST = 1 -IS_NOT_FIRST = 0 -IS_LEFT = 1 -IS_NOT_LEFT = 0 - -TREE_LEAF = -1 -TREE_UNDEFINED = -2 -_TREE_LEAF = TREE_LEAF -_TREE_UNDEFINED = TREE_UNDEFINED - -# Since you're dealing with Cython-specific types and features, -# it's important to provide a dummy definition for Node. -class Node: - def __init__(self): - self.left_child = None - self.right_child = None - self.feature = None - self.threshold = None - self.impurity = None - self.n_node_samples = None - self.weighted_n_node_samples = None - self.missing_go_to_left = None - -dummy = Node() -# Create a numpy dtype for Node using the dummy object -NODE_DTYPE = np.asarray([dummy], dtype=object).dtype -# ============================================================================= -# TreeBuilder -# ============================================================================= - -class TreeBuilder: - """Interface for different tree building strategies.""" - def __init__(self): - self.splitter = None - self.min_samples_split = None - self.min_samples_leaf = None - self.min_weight_leaf = None - self.max_depth = None - self.min_impurity_decrease = None - - def build( - self, - tree, - X, - y, - sample_weight=None, - missing_values_in_feature_mask=None, - ): - """Build a decision tree from the training set (X, y).""" - pass - - def _check_input( - self, - X, - y, - sample_weight, - ): - """Check input dtype, layout, and format""" - if issparse(X): - X = X.tocsc() #tocsc() is a method provided by the scipy.sparse module in the SciPy library. It's used to convert a sparse matrix to the Compressed Sparse Column (CSC) format. - X.sort_indices() #This is done to ensure that the indices of non-zero elements within the matrix are sorted in ascending order. - - if X.data.dtype != DTYPE: - X.data = np.ascontiguousarray(X.data, dtype=DTYPE) - - if X.indices.dtype != np.int32 or X.indptr.dtype != np.int32: - raise ValueError("No support for np.int64 index-based sparse matrices") - - elif X.dtype != DTYPE: - # since we have to copy, we will make it Fortran for efficiency - X = np.asfortranarray(X, dtype=DTYPE) - - if y.base.dtype != DTYPE or not y.base.flags.contiguous: - y = np.ascontiguousarray(y, dtype=DTYPE) - - if ( - sample_weight is not None and - ( - sample_weight.base.dtype != DOUBLE or - not sample_weight.base.flags.contiguous - ) - ): - sample_weight = np.asarray(sample_weight, dtype=DOUBLE, order="C") - - return X, y, sample_weight - - - - -# Depth first builder --------------------------------------------------------- -# A record on the stack for depth-first tree growing -class StackRecord: - def __init__(self, start, end, depth, parent, is_left, impurity, n_constant_features): - self.start = start - self.end = end - self.depth = depth - self.parent = parent - self.is_left = is_left - self.impurity = impurity - self.n_constant_features = n_constant_features - - -class SplitRecord: - def __init__(self, feature, pos, threshold, improvement, - impurity_left, impurity_right, missing_go_to_left, - n_missing): - self.feature = feature - self.pos = pos - self.threshold = threshold - self.improvement = improvement - self.impurity_left = impurity_left - self.impurity_right = impurity_right - self.missing_go_to_left = missing_go_to_left - self.n_missing = n_missing - - - - - -class DepthFirstTreeBuilder(TreeBuilder): - """Build a decision tree in depth-first fashion.""" - - def __init__( - self, splitter, min_samples_split, - min_samples_leaf, min_weight_leaf, - max_depth, min_impurity_decrease - ): - self.splitter = splitter - self.min_samples_split = min_samples_split - self.min_samples_leaf = min_samples_leaf - self.min_weight_leaf = min_weight_leaf - self.max_depth = max_depth - self.min_impurity_decrease = min_impurity_decrease - - def build( - self, - tree, - X, - y, - sample_weight=None, - missing_values_in_feature_mask=None - ): - """Build a decision tree from the training set (X, y).""" - - # Check input - X, y, sample_weight = self._check_input(X, y, sample_weight) - - # Initial capacity - init_capacity = (2 ** (tree.max_depth + 1)) - 1 if tree.max_depth <= 10 else 2047 - - tree._resize(init_capacity) - - # Parameters - splitter = self.splitter - max_depth = self.max_depth - min_samples_leaf = self.min_samples_leaf - min_weight_leaf = self.min_weight_leaf - min_samples_split = self.min_samples_split - min_impurity_decrease = self.min_impurity_decrease - - # Recursive partition (without actual recursion) - splitter.init(X, y, sample_weight, missing_values_in_feature_mask) - - stack = [] - - # Push root node onto stack - stack.append( - StackRecord( - start=0, - end=splitter.n_samples, - depth=0, - parent=_TREE_UNDEFINED, - is_left=False, - impurity=INFINITY, - n_constant_features=0 - ) - ) - weighted_n_node_samples = np.zeros(1, dtype=np.double) - while stack: - stack_record = stack.pop() - - start = stack_record.start - end = stack_record.end - depth = stack_record.depth - parent = stack_record.parent - is_left = stack_record.is_left - impurity = stack_record.impurity - n_constant_features = stack_record.n_constant_features - - n_node_samples = end - start - splitter.node_reset(start, end, weighted_n_node_samples) - - is_leaf = ( - depth >= max_depth - or n_node_samples < min_samples_split - or n_node_samples < 2 * min_samples_leaf - or np.sum(sample_weight[start:end]) < 2 * min_weight_leaf - ) - - if is_left: - impurity = splitter.node_impurity() - - is_leaf = is_leaf or impurity <= EPSILON - - if not is_leaf: - split = SplitRecord() # No idea what is SplitRecord in original code. Maybe this never gets called, not sure - splitter.node_split(impurity, split, n_constant_features) - is_leaf = ( - is_leaf - or split.pos >= end - or (split.improvement + EPSILON < min_impurity_decrease) - ) - - node_id = tree._add_node( - parent, - is_left, - is_leaf, - split.feature if not is_leaf else 0, - split.threshold if not is_leaf else 0, - impurity, - n_node_samples, - np.sum(sample_weight[start:end]), - split.missing_go_to_left, - ) - - if node_id == np.iinfo(np.intp).max: - raise MemoryError() - - splitter.node_value(tree.value + node_id * tree.value_stride) - - if not is_leaf: - # Push right child on stack - stack.append( - StackRecord( - start=split.pos, - end=end, - depth=depth + 1, - parent=node_id, - is_left=False, - impurity=split.impurity_right, - n_constant_features=n_constant_features, - ) - ) - # Push left child on stack - stack.append( - StackRecord( - start=start, - end=split.pos, - depth=depth + 1, - parent=node_id, - is_left=True, - impurity=split.impurity_left, - n_constant_features=n_constant_features, - ) - ) - - -class Tree: - def __init__(self, n_features, n_classes, n_outputs): - """Constructor.""" - self.n_features = None - self.n_outputs = None - self.n_classes = None - self.max_n_classes = None - self.max_depth = None - self.node_count = None - self.capacity = None - self.nodes = None - self.value = None - self.value_stride = None - - dummy = 0 - size_t_dtype = np.array(dummy).dtype - - n_classes = _check_n_classes(n_classes, size_t_dtype) - - # Input/Output layout - self.n_features = n_features - self.n_outputs = n_outputs - self.n_classes = np.zeros(n_outputs, dtype=size_t_dtype) - - self.max_n_classes = np.max(n_classes) - self.value_stride = n_outputs * self.max_n_classes - - for k in range(n_outputs): - self.n_classes[k] = n_classes[k] - - # Inner structures - self.max_depth = 0 - self.node_count = 0 - self.capacity = 0 - self.value = None - self.nodes = None - - def __del__(self): - """Destructor.""" - # Free all inner structures - self.n_classes = None - self.value = None - self.nodes = None - - #NOT CONSIDERING PICKINLING FOR NOW - def __reduce__(self): - """Reduce re-implementation, for pickling.""" - raise NotImplementedError - #NOT CONSIDERING PICKINLING FOR NOW - def __getstate__(self): - """Getstate re-implementation, for pickling.""" - d = {} - # capacity is inferred during the __setstate__ using nodes - d["max_depth"] = self.max_depth - d["node_count"] = self.node_count - d["nodes"] = self._get_node_ndarray() - d["values"] = self._get_value_ndarray() - return d - #NOT CONSIDERING PICKINLING FOR NOW - def __setstate__(self, d): - """Setstate re-implementation, for unpickling.""" - raise NotImplementedError - - def _resize(self, capacity): - """ - Resize all inner arrays to `capacity`. If `capacity` is -1, then double the size of the inner arrays. - Returns -1 in case of failure to allocate memory (and raise MemoryError), or 0 otherwise. - """ - if self._resize_c(capacity) != 0: - # Raise MemoryError if resizing fails - raise MemoryError() - - def _resize_c(self, capacity=float('inf')): - """ - Guts of _resize - Returns -1 in case of failure to allocate memory (and raise MemoryError), - or 0 otherwise. - """ - if capacity == self.capacity and self.nodes is not None: - return 0 - - if capacity == float('inf'): - if self.capacity == 0: - capacity = 3 # default initial value - else: - capacity = 2 * self.capacity - - # This section is relevant if the code is dealing with C arrays. - # In Python, resizing arrays is handled automatically by lists or numpy arrays. - # You won't need to explicitly reallocate memory or initialize values like this. - - # replaced safe_realloc(&self.nodes, capacity) with the following - new_nodes = np.empty(capacity, dtype=object) - for i in range(len(self.nodes)): - new_nodes[i] = self.nodes[i] - self.nodes = new_nodes - - # replaced safe_realloc(&self.value, capacity * self.value_stride) with the following - new_value = np.empty(capacity * self.value_stride, dtype=object) - for i in range(len(self.value)): - new_value[i] = self.value[i] - self.value = new_value - - # if capacity smaller than node_count, adjust the counter - if capacity < self.node_count: - self.node_count = capacity - - self.capacity = capacity - return 0 - - - def _add_node(self, parent, is_left, is_leaf, feature, threshold, impurity, - n_node_samples, weighted_n_node_samples, missing_go_to_left): - """ - Add a node to the tree. - - The new node registers itself as the child of its parent. - - Returns -1 on error. - """ - node_id = self.node_count - - if node_id >= self.capacity: - if self._resize_c() != 0: - return -1 - - node = self.nodes[node_id] - node.impurity = impurity - node.n_node_samples = n_node_samples - node.weighted_n_node_samples = weighted_n_node_samples - - if parent != _TREE_UNDEFINED: - if is_left: - self.nodes[parent].left_child = node_id - else: - self.nodes[parent].right_child = node_id - - if is_leaf: - node.left_child = _TREE_LEAF - node.right_child = _TREE_LEAF - node.feature = _TREE_UNDEFINED - node.threshold = _TREE_UNDEFINED - - else: - # left_child and right_child will be set later - node.feature = feature - node.threshold = threshold - node.missing_go_to_left = missing_go_to_left - - self.node_count += 1 - - return node_id - - def predict(self, X): - """Predict target for X.""" - out = self._get_value_ndarray()[self.apply(X), :, :] - - if self.n_outputs == 1: - out = out.reshape(X.shape[0], self.max_n_classes) - - return out - - def apply(self, X): - """Finds the terminal region (=leaf node) for each sample in X.""" - if issparse(X): - return self._apply_sparse_csr(X) - else: - return self._apply_dense(X) - - def _apply_dense(self, X): - """Finds the terminal region (=leaf node) for each sample in X.""" - - # Check input - if not isinstance(X, np.ndarray): - raise ValueError("X should be in np.ndarray format, got %s" % type(X)) - - if X.dtype != np.float32: - raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) - - # Extract input - n_samples, n_features = X.shape - - # Initialize output - out = np.zeros(n_samples, dtype=np.intp) - - with np.nditer(X, flags=['c_index', 'multi_index'], op_flags=['readonly'], order='C') as it: - for x_value, index in it: - node = self.nodes - # While node not a leaf - while node.left_child != _TREE_LEAF: - X_i_node_feature = x_value - # ... and node.right_child != _TREE_LEAF: - if np.isnan(X_i_node_feature): - if node.missing_go_to_left: - node = self.nodes[node.left_child] - else: - node = self.nodes[node.right_child] - elif X_i_node_feature <= node.threshold: - node = self.nodes[node.left_child] - else: - node = self.nodes[node.right_child] - - out[index[0]] = node - self.nodes # node offset - - return out - - def _apply_sparse_csr(self, X): - """Finds the terminal region (=leaf node) for each sample in sparse X.""" - if not isinstance(X, csr_matrix): - raise ValueError("X should be in csr_matrix format, got %s" % type(X)) - - if X.dtype != np.float32: - raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) - - n_samples, n_features = X.shape - - # Initialize output - out = np.zeros(n_samples, dtype=np.intp) - - # Initialize auxiliary data structures - feature_to_sample = np.full(n_features, -1, dtype=np.intp) - X_sample = np.zeros(n_features, dtype=np.float32) - - for i in range(n_samples): - node = self.nodes - - for k in range(X.indptr[i], X.indptr[i + 1]): - feature_to_sample[X.indices[k]] = i - X_sample[X.indices[k]] = X.data[k] - - while node.left_child != _TREE_LEAF: - if feature_to_sample[node.feature] == i: - feature_value = X_sample[node.feature] - else: - feature_value = 0.0 - - if feature_value <= node.threshold: - node = self.nodes[node.left_child] - else: - node = self.nodes[node.right_child] - - out[i] = node - self.nodes # node offset - - return out - - def decision_path(self, X): - """Finds the decision path (=node) for each sample in X.""" - if issparse(X): - return self._decision_path_sparse_csr(X) - else: - return self._decision_path_dense(X) - - def _decision_path_dense(self, X): - """Finds the decision path (=node) for each sample in X.""" - - # Check input - if not isinstance(X, np.ndarray): - raise ValueError("X should be in np.ndarray format, got %s" % type(X)) - - if X.dtype != DTYPE: - raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) - - # Extract input - X_ndarray = X - n_samples = X.shape[0] - - # Initialize output - indptr = np.zeros(n_samples + 1, dtype=np.intp) - indices = np.zeros(n_samples * (1 + self.max_depth), dtype=np.intp) - - # Initialize auxiliary data-structure - node = None - i = 0 - - for i in range(n_samples): - node = self.nodes - indptr[i + 1] = indptr[i] - - # Add all external nodes - while node.left_child != _TREE_LEAF: - # ... and node.right_child != _TREE_LEAF: - indices[indptr[i + 1]] = node - self.nodes - indptr[i + 1] += 1 - - if X_ndarray[i, node.feature] <= node.threshold: - node = self.nodes[node.left_child] - else: - node = self.nodes[node.right_child] - - # Add the leaf node - indices[indptr[i + 1]] = node - self.nodes - indptr[i + 1] += 1 - - indices = indices[:indptr[n_samples]] - data = np.ones(shape=len(indices), dtype=np.intp) - out = csr_matrix((data, indices, indptr), shape=(n_samples, self.node_count)) - - return out - - def _decision_path_sparse_csr(self, X): - """Finds the decision path (=node) for each sample in X.""" - - # Check input - if not isspmatrix_csr(X): - raise ValueError("X should be in csr_matrix format, got %s" % type(X)) - - if X.dtype != DTYPE: - raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) - - # Extract input - X_data = X.data - X_indices = X.indices - X_indptr = X.indptr - - n_samples = X.shape[0] - n_features = X.shape[1] - - # Initialize output - indptr = np.zeros(n_samples + 1, dtype=np.intp) - indices = np.zeros(n_samples * (1 + self.max_depth), dtype=np.intp) - - # Initialize auxiliary data-structure - feature_value = 0.0 - node = None - X_sample = np.zeros(n_features, dtype=DTYPE) - feature_to_sample = np.full(n_features, -1, dtype=np.intp) - - for i in range(n_samples): - node = self.nodes - indptr[i + 1] = indptr[i] - - for k in range(X_indptr[i], X_indptr[i + 1]): - feature_to_sample[X_indices[k]] = i - X_sample[X_indices[k]] = X_data[k] - - # While node not a leaf - while node.left_child != _TREE_LEAF: - indices[indptr[i + 1]] = node - self.nodes - indptr[i + 1] += 1 - - if feature_to_sample[node.feature] == i: - feature_value = X_sample[node.feature] - else: - feature_value = 0.0 - - if feature_value <= node.threshold: - node = self.nodes[node.left_child] - else: - node = self.nodes[node.right_child] - - # Add the leaf node - indices[indptr[i + 1]] = node - self.nodes - indptr[i + 1] += 1 - - indices = indices[:indptr[n_samples]] - data = np.ones(shape=len(indices), dtype=np.intp) - out = csr_matrix((data, indices, indptr), shape=(n_samples, self.node_count)) - - return out - - def compute_node_depths(self): - """Compute the depth of each node in a tree. - - .. versionadded:: 1.3 - - Returns - ------- - depths : ndarray of shape (self.node_count,), dtype=np.int64 - The depth of each node in the tree. - """ - depths = np.empty(self.node_count, dtype=np.int64) - children_left = self.children_left - children_right = self.children_right - node_count = self.node_count - - depths[0] = 1 # init root node - for node_id in range(node_count): - if children_left[node_id] != _TREE_LEAF: - depth = depths[node_id] + 1 - depths[children_left[node_id]] = depth - depths[children_right[node_id]] = depth - - return depths.base - - def compute_feature_importances(self, normalize=True): - """Computes the importance of each feature (aka variable).""" - nodes = self.nodes - node = nodes - end_node = node + self.node_count - - importances = np.zeros(self.n_features, dtype=np.float64) - - while node != end_node: - if node.left_child != _TREE_LEAF: - left = nodes[node.left_child] - right = nodes[node.right_child] - - importances[node.feature] += ( - node.weighted_n_node_samples * node.impurity - - left.weighted_n_node_samples * left.impurity - - right.weighted_n_node_samples * right.impurity) - node += 1 - - for i in range(self.n_features): - importances[i] /= nodes[0].weighted_n_node_samples - - if normalize: - normalizer = np.sum(importances) - - if normalizer > 0.0: - # Avoid dividing by zero (e.g., when root is pure) - importances /= normalizer - - return importances - - def _get_value_ndarray(self): - """Wraps value as a 3-d NumPy array. - - The array keeps a reference to this Tree, which manages the underlying - memory. - """ - shape = (self.node_count, self.n_outputs, self.max_n_classes) - arr = np.ndarray(shape, dtype=np.float64, buffer=self.value) - arr.base = self - return arr - - def _get_node_ndarray(self): - """Wraps nodes as a NumPy struct array. - - The array keeps a reference to this Tree, which manages the underlying - memory. Individual fields are publicly accessible as properties of the - Tree. - """ - shape = (self.node_count,) - dtype = np.dtype([ - ('left_child', np.intp), - ('right_child', np.intp), - ('feature', np.intp), - ('threshold', np.float64), - ('impurity', np.float64), - ('n_node_samples', np.intp), - ('weighted_n_node_samples', np.float64), - ('missing_go_to_left', np.uint8) - ]) - arr = np.ndarray(shape, dtype=dtype, buffer=self.nodes) - arr.base = self - return arr - - def compute_partial_dependence(self, X, target_features, out): - out.fill(0.0) # Initialize the output array - - _TREE_LEAF = self._TREE_LEAF # The value for leaf nodes - - for sample_idx in range(X.shape[0]): - stack_size = 1 - node_idx_stack = [0] # root node - weight_stack = [1.0] # all samples are in the root node - total_weight = 0.0 - - while stack_size > 0: - stack_size -= 1 - current_node_idx = node_idx_stack[stack_size] - current_node = self.nodes[current_node_idx] - - if current_node.left_child == _TREE_LEAF: - # Leaf node - out[sample_idx] += weight_stack[stack_size] * self.value[current_node_idx] - total_weight += weight_stack[stack_size] - else: - is_target_feature = any(target_feature == current_node.feature for target_feature in target_features) - if is_target_feature: - if X[sample_idx, current_node.feature] <= current_node.threshold: - node_idx_stack.append(current_node.left_child) - weight_stack.append(weight_stack[stack_size]) - stack_size += 1 - else: - node_idx_stack.append(current_node.right_child) - weight_stack.append(weight_stack[stack_size]) - stack_size += 1 - else: - left_sample_frac = self.nodes[current_node.left_child].weighted_n_node_samples / current_node.weighted_n_node_samples - current_weight = weight_stack[stack_size] - node_idx_stack.extend([current_node.left_child, current_node.right_child]) - weight_stack.extend([current_weight * left_sample_frac, current_weight * (1 - left_sample_frac)]) - stack_size += 2 - - if not (0.999 < total_weight < 1.001): - raise ValueError(f"Total weight should be 1.0 but was {total_weight:.9f}") - - -def _check_n_classes(n_classes, expected_dtype): - if n_classes.ndim != 1: - raise ValueError( - f"Wrong dimensions for n_classes from the pickle: " - f"expected 1, got {n_classes.ndim}" - ) - - if n_classes.dtype == expected_dtype: - return n_classes - - # Handles both different endianness and different bitness - if n_classes.dtype.kind == "i" and n_classes.dtype.itemsize in [4, 8]: - return n_classes.astype(expected_dtype, casting="same_kind") - - raise ValueError( - "n_classes from the pickle has an incompatible dtype:\n" - f"- expected: {expected_dtype}\n" - f"- got: {n_classes.dtype}" - ) - - - - - - diff --git a/ivy/functional/frontends/sklearn/tree.py b/ivy/functional/frontends/sklearn/tree.py deleted file mode 100644 index 55ab869bd01f7..0000000000000 --- a/ivy/functional/frontends/sklearn/tree.py +++ /dev/null @@ -1,822 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - -import numpy as np - - - -from scipy.sparse import issparse -from scipy.sparse import csr_matrix -from scipy.sparse import isspmatrix_csr - - - - - - - - - - - - - - - - - - - - -# ============================================================================= -# Types and constants -# ============================================================================= - -from numpy import float32 as DTYPE -from numpy import float64 as DOUBLE - - -# Define constants -INFINITY = np.inf -EPSILON = np.finfo(np.double).eps - -# Some handy constants (BestFirstTreeBuilder) -IS_FIRST = 1 -IS_NOT_FIRST = 0 -IS_LEFT = 1 -IS_NOT_LEFT = 0 - -TREE_LEAF = -1 -TREE_UNDEFINED = -2 -_TREE_LEAF = TREE_LEAF -_TREE_UNDEFINED = TREE_UNDEFINED - -# Since you're dealing with Cython-specific types and features, -# it's important to provide a dummy definition for Node. -class Node: - def __init__(self): - self.left_child = None - self.right_child = None - self.feature = None - self.threshold = None - self.impurity = None - self.n_node_samples = None - self.weighted_n_node_samples = None - self.missing_go_to_left = None - -dummy = Node() -# Create a numpy dtype for Node using the dummy object -NODE_DTYPE = np.asarray([dummy], dtype=object).dtype -# ============================================================================= -# TreeBuilder -# ============================================================================= - -class TreeBuilder: - """Interface for different tree building strategies.""" - def __init__(self): - self.splitter = None - self.min_samples_split = None - self.min_samples_leaf = None - self.min_weight_leaf = None - self.max_depth = None - self.min_impurity_decrease = None - - def build( - self, - tree, - X, - y, - sample_weight=None, - missing_values_in_feature_mask=None, - ): - """Build a decision tree from the training set (X, y).""" - pass - - def _check_input( - self, - X, - y, - sample_weight, - ): - """Check input dtype, layout, and format""" - if issparse(X): - X = X.tocsc() #tocsc() is a method provided by the scipy.sparse module in the SciPy library. It's used to convert a sparse matrix to the Compressed Sparse Column (CSC) format. - X.sort_indices() #This is done to ensure that the indices of non-zero elements within the matrix are sorted in ascending order. - - if X.data.dtype != DTYPE: - X.data = np.ascontiguousarray(X.data, dtype=DTYPE) - - if X.indices.dtype != np.int32 or X.indptr.dtype != np.int32: - raise ValueError("No support for np.int64 index-based sparse matrices") - - elif X.dtype != DTYPE: - # since we have to copy, we will make it Fortran for efficiency - X = np.asfortranarray(X, dtype=DTYPE) - - if y.base.dtype != DTYPE or not y.base.flags.contiguous: - y = np.ascontiguousarray(y, dtype=DTYPE) - - if ( - sample_weight is not None and - ( - sample_weight.base.dtype != DOUBLE or - not sample_weight.base.flags.contiguous - ) - ): - sample_weight = np.asarray(sample_weight, dtype=DOUBLE, order="C") - - return X, y, sample_weight - - - - -# Depth first builder --------------------------------------------------------- -# A record on the stack for depth-first tree growing -class StackRecord: - def __init__(self, start, end, depth, parent, is_left, impurity, n_constant_features): - self.start = start - self.end = end - self.depth = depth - self.parent = parent - self.is_left = is_left - self.impurity = impurity - self.n_constant_features = n_constant_features - - - - - - - - -class DepthFirstTreeBuilder(TreeBuilder): - """Build a decision tree in depth-first fashion.""" - - def __init__( - self, splitter, min_samples_split, - min_samples_leaf, min_weight_leaf, - max_depth, min_impurity_decrease - ): - self.splitter = splitter - self.min_samples_split = min_samples_split - self.min_samples_leaf = min_samples_leaf - self.min_weight_leaf = min_weight_leaf - self.max_depth = max_depth - self.min_impurity_decrease = min_impurity_decrease - - def build( - self, - tree, - X, - y, - sample_weight=None, - missing_values_in_feature_mask=None - ): - """Build a decision tree from the training set (X, y).""" - - # Check input - X, y, sample_weight = self._check_input(X, y, sample_weight) - - # Initial capacity - init_capacity = (2 ** (tree.max_depth + 1)) - 1 if tree.max_depth <= 10 else 2047 - - tree._resize(init_capacity) - - # Parameters - splitter = self.splitter - max_depth = self.max_depth - min_samples_leaf = self.min_samples_leaf - min_weight_leaf = self.min_weight_leaf - min_samples_split = self.min_samples_split - min_impurity_decrease = self.min_impurity_decrease - - # Recursive partition (without actual recursion) - splitter.init(X, y, sample_weight, missing_values_in_feature_mask) - - stack = [] - - # Push root node onto stack - stack.append( - StackRecord( - start=0, - end=splitter.n_samples, - depth=0, - parent=_TREE_UNDEFINED, - is_left=False, - impurity=INFINITY, - n_constant_features=0 - ) - ) - weighted_n_node_samples = np.zeros(1, dtype=np.double) - while stack: - stack_record = stack.pop() - - start = stack_record.start - end = stack_record.end - depth = stack_record.depth - parent = stack_record.parent - is_left = stack_record.is_left - impurity = stack_record.impurity - n_constant_features = stack_record.n_constant_features - - n_node_samples = end - start - splitter.node_reset(start, end, weighted_n_node_samples) - - is_leaf = ( - depth >= max_depth - or n_node_samples < min_samples_split - or n_node_samples < 2 * min_samples_leaf - or np.sum(sample_weight[start:end]) < 2 * min_weight_leaf - ) - - if is_left: - impurity = splitter.node_impurity() - - is_leaf = is_leaf or impurity <= EPSILON - - if not is_leaf: - split = SplitRecord() # No idea what is SplitRecord in original code. Maybe this never gets called, not sure - splitter.node_split(impurity, split, n_constant_features) - is_leaf = ( - is_leaf - or split.pos >= end - or (split.improvement + EPSILON < min_impurity_decrease) - ) - - node_id = tree._add_node( - parent, - is_left, - is_leaf, - split.feature if not is_leaf else 0, - split.threshold if not is_leaf else 0, - impurity, - n_node_samples, - np.sum(sample_weight[start:end]), - split.missing_go_to_left, - ) - - if node_id == np.iinfo(np.intp).max: - raise MemoryError() - - splitter.node_value(tree.value + node_id * tree.value_stride) - - if not is_leaf: - # Push right child on stack - stack.append( - StackRecord( - start=split.pos, - end=end, - depth=depth + 1, - parent=node_id, - is_left=False, - impurity=split.impurity_right, - n_constant_features=n_constant_features, - ) - ) - # Push left child on stack - stack.append( - StackRecord( - start=start, - end=split.pos, - depth=depth + 1, - parent=node_id, - is_left=True, - impurity=split.impurity_left, - n_constant_features=n_constant_features, - ) - ) - - -class Tree: - def __init__(self, n_features, n_classes, n_outputs): - """Constructor.""" - self.n_features = None - self.n_classes = None - self.n_outputs = None - self.max_n_classes = None - self.max_depth = None - self.node_count = None - self.capacity = None - self.nodes = [] #replaced it with array since this array will contain nodes - self.value = None - self.value_stride = None - - dummy = 0 - size_t_dtype = np.array(dummy).dtype - - n_classes = _check_n_classes(n_classes, size_t_dtype) - - # Input/Output layout - self.n_features = n_features - self.n_outputs = n_outputs - self.n_classes = np.zeros(n_outputs, dtype=size_t_dtype) - - self.max_n_classes = np.max(n_classes) - self.value_stride = n_outputs * self.max_n_classes - - for k in range(n_outputs): - self.n_classes[k] = n_classes[k] - - # Inner structures - self.max_depth = 0 - self.node_count = 0 - self.capacity = 0 - self.value = None - self.nodes = None - - def __del__(self): - """Destructor.""" - # Free all inner structures - self.n_classes = None - self.value = None - self.nodes = None - - def __reduce__(self): - """Reduce re-implementation, for pickling.""" - raise NotImplementedError - - def __getstate__(self): - """Getstate re-implementation, for pickling.""" - d = {} - # capacity is inferred during the __setstate__ using nodes - d["max_depth"] = self.max_depth - d["node_count"] = self.node_count - d["nodes"] = self._get_node_ndarray() - d["values"] = self._get_value_ndarray() - return d - - def __setstate__(self, d): - """Setstate re-implementation, for unpickling.""" - raise NotImplementedError - - def _resize(self, capacity): - """ - Resize all inner arrays to `capacity`. If `capacity` is -1, then double the size of the inner arrays. - Returns -1 in case of failure to allocate memory (and raise MemoryError), or 0 otherwise. - """ - if self._resize_c(capacity) != 0: - # Raise MemoryError if resizing fails - raise MemoryError() - - def _resize_c(self, capacity=float('inf')): - # """ - # Guts of _resize - # Returns -1 in case of failure to allocate memory (and raise MemoryError), - # or 0 otherwise. - # """ - # if capacity == self.capacity and self.nodes is not None: - # return 0 - - # if capacity == float('inf'): - # if self.capacity == 0: - # capacity = 3 # default initial value - # else: - # capacity = 2 * self.capacity - - # # This section is relevant if the code is dealing with C arrays. - # # In Python, resizing arrays is handled automatically by lists or numpy arrays. - # # You won't need to explicitly reallocate memory or initialize values like this. - # self.nodes = [None] * capacity #doubtfull either the Node classes get reallocated or something else happends - # self.value = [0.0] * (capacity * self.value_stride) #doubtfull either the value classes get reallocated or something else happends - - # # value memory is initialized to 0 to enable classifier argmax - # if capacity > self.capacity: - # self.value += [0.0] * ((capacity - self.capacity) * self.value_stride) - - # # if capacity smaller than node_count, adjust the counter - # if capacity < self.node_count: - # self.node_count = capacity - - # self.capacity = capacity - # return 0 - raise NotImplementedError - - - def _add_node(self, parent, is_left, is_leaf, feature, threshold, impurity, - n_node_samples, weighted_n_node_samples, missing_go_to_left): - """ - Add a node to the tree. - - The new node registers itself as the child of its parent. - - Returns -1 on error. - """ - node_id = self.node_count - - #no need to resize since python reallocates lists dynamically - # if node_id >= self.capacity: - # if self._resize_c() != 0: - # return -1 #throw error if resize not possible - - node = Node() #self.nodes contains a list of nodes, it returns the node at node_id location - self.nodes.append(node) - node.impurity = impurity - node.n_node_samples = n_node_samples - node.weighted_n_node_samples = weighted_n_node_samples - - if parent != _TREE_UNDEFINED: - if is_left: - self.nodes[parent].left_child = node_id - else: - self.nodes[parent].right_child = node_id - - if is_leaf: - node.left_child = _TREE_LEAF - node.right_child = _TREE_LEAF - node.feature = _TREE_UNDEFINED - node.threshold = _TREE_UNDEFINED - - else: - # left_child and right_child will be set later - node.feature = feature - node.threshold = threshold - node.missing_go_to_left = missing_go_to_left - - self.node_count += 1 - - return node_id - - def predict(self, X): - # Apply the model to the input data X - predictions = self.apply(X) - # Get the internal data as a NumPy array - internal_data = self._get_value_ndarray() - # Use the predictions to index the internal data - out = internal_data[predictions] #not sure if this accurately translates to .take(self.apply(X), axis=0, mode='clip') - # Reshape the output if the model is single-output - if self.n_outputs == 1: - out = out.reshape(X.shape[0], self.max_n_classes) - return out - - def apply(self, X): - """Finds the terminal region (=leaf node) for each sample in X.""" - if issparse(X): - return self._apply_sparse_csr(X) - else: - return self._apply_dense(X) - - def _apply_dense(self, X): - if not isinstance(X, torch.Tensor): - raise ValueError("X should be a torch.Tensor, got %s" % type(X)) - - if X.dtype != torch.float32: - raise ValueError("X.dtype should be torch.float32, got %s" % X.dtype) - - X_tensor = X - n_samples = X.shape[0] - out = torch.zeros(n_samples, dtype=torch.int64) - - for i in range(n_samples): - node = self.nodes - - while node.left_child != _TREE_LEAF: - X_i_node_feature = X_tensor[i, node.feature] - - if torch.isnan(X_i_node_feature): - if node.missing_go_to_left: - node = self.nodes[node.left_child] - else: - node = self.nodes[node.right_child] - elif X_i_node_feature <= node.threshold: - node = self.nodes[node.left_child] - else: - node = self.nodes[node.right_child] - - out[i] = node - self.nodes - - return out - - def _apply_sparse_csr(self, X): - """Finds the terminal region (=leaf node) for each sample in sparse X.""" - if not isinstance(X, csr_matrix): - raise ValueError("X should be in csr_matrix format, got %s" % type(X)) - - if X.dtype != np.float32: - raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) - - n_samples, n_features = X.shape - - # Initialize output - out = np.zeros(n_samples, dtype=np.intp) - - # Initialize auxiliary data structures - feature_to_sample = np.full(n_features, -1, dtype=np.intp) - X_sample = np.zeros(n_features, dtype=np.float32) - - for i in range(n_samples): - node = self.nodes - - for k in range(X.indptr[i], X.indptr[i + 1]): - feature_to_sample[X.indices[k]] = i - X_sample[X.indices[k]] = X.data[k] - - while node.left_child != _TREE_LEAF: - if feature_to_sample[node.feature] == i: - feature_value = X_sample[node.feature] - else: - feature_value = 0.0 - - if feature_value <= node.threshold: - node = self.nodes[node.left_child] - else: - node = self.nodes[node.right_child] - - out[i] = node - self.nodes # node offset - - return out - - def decision_path(self, X): - """Finds the decision path (=node) for each sample in X.""" - if issparse(X): - return self._decision_path_sparse_csr(X) - else: - return self._decision_path_dense(X) - - def _decision_path_dense(self, X): - """Finds the decision path (=node) for each sample in X.""" - - # Check input - if not isinstance(X, np.ndarray): - raise ValueError("X should be in np.ndarray format, got %s" % type(X)) - - if X.dtype != DTYPE: - raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) - - # Extract input - X_ndarray = X - n_samples = X.shape[0] - - # Initialize output - indptr = np.zeros(n_samples + 1, dtype=np.intp) - indices = np.zeros(n_samples * (1 + self.max_depth), dtype=np.intp) - - # Initialize auxiliary data-structure - node = None - i = 0 - - for i in range(n_samples): - node = self.nodes - indptr[i + 1] = indptr[i] - - # Add all external nodes - while node.left_child != _TREE_LEAF: - # ... and node.right_child != _TREE_LEAF: - indices[indptr[i + 1]] = node - self.nodes - indptr[i + 1] += 1 - - if X_ndarray[i, node.feature] <= node.threshold: - node = self.nodes[node.left_child] - else: - node = self.nodes[node.right_child] - - # Add the leaf node - indices[indptr[i + 1]] = node - self.nodes - indptr[i + 1] += 1 - - indices = indices[:indptr[n_samples]] - data = np.ones(shape=len(indices), dtype=np.intp) - out = csr_matrix((data, indices, indptr), shape=(n_samples, self.node_count)) - - return out - - def _decision_path_sparse_csr(self, X): - """Finds the decision path (=node) for each sample in X.""" - - # Check input - if not isspmatrix_csr(X): - raise ValueError("X should be in csr_matrix format, got %s" % type(X)) - - if X.dtype != DTYPE: - raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) - - # Extract input - X_data = X.data - X_indices = X.indices - X_indptr = X.indptr - - n_samples = X.shape[0] - n_features = X.shape[1] - - # Initialize output - indptr = np.zeros(n_samples + 1, dtype=np.intp) - indices = np.zeros(n_samples * (1 + self.max_depth), dtype=np.intp) - - # Initialize auxiliary data-structure - feature_value = 0.0 - node = None - X_sample = np.zeros(n_features, dtype=DTYPE) - feature_to_sample = np.full(n_features, -1, dtype=np.intp) - - for i in range(n_samples): - node = self.nodes - indptr[i + 1] = indptr[i] - - for k in range(X_indptr[i], X_indptr[i + 1]): - feature_to_sample[X_indices[k]] = i - X_sample[X_indices[k]] = X_data[k] - - # While node not a leaf - while node.left_child != _TREE_LEAF: - indices[indptr[i + 1]] = node - self.nodes - indptr[i + 1] += 1 - - if feature_to_sample[node.feature] == i: - feature_value = X_sample[node.feature] - else: - feature_value = 0.0 - - if feature_value <= node.threshold: - node = self.nodes[node.left_child] - else: - node = self.nodes[node.right_child] - - # Add the leaf node - indices[indptr[i + 1]] = node - self.nodes - indptr[i + 1] += 1 - - indices = indices[:indptr[n_samples]] - data = np.ones(shape=len(indices), dtype=np.intp) - out = csr_matrix((data, indices, indptr), shape=(n_samples, self.node_count)) - - return out - - def compute_node_depths(self): - """Compute the depth of each node in a tree. - - .. versionadded:: 1.3 - - Returns - ------- - depths : ndarray of shape (self.node_count,), dtype=np.int64 - The depth of each node in the tree. - """ - depths = np.empty(self.node_count, dtype=np.int64) - children_left = self.children_left - children_right = self.children_right - node_count = self.node_count - - depths[0] = 1 # init root node - for node_id in range(node_count): - if children_left[node_id] != _TREE_LEAF: - depth = depths[node_id] + 1 - depths[children_left[node_id]] = depth - depths[children_right[node_id]] = depth - - return depths.base - - def compute_feature_importances(self, normalize=True): - """Computes the importance of each feature (aka variable).""" - nodes = self.nodes - node = nodes - end_node = node + self.node_count - - importances = np.zeros(self.n_features, dtype=np.float64) - - while node != end_node: - if node.left_child != _TREE_LEAF: - left = nodes[node.left_child] - right = nodes[node.right_child] - - importances[node.feature] += ( - node.weighted_n_node_samples * node.impurity - - left.weighted_n_node_samples * left.impurity - - right.weighted_n_node_samples * right.impurity) - node += 1 - - for i in range(self.n_features): - importances[i] /= nodes[0].weighted_n_node_samples - - if normalize: - normalizer = np.sum(importances) - - if normalizer > 0.0: - # Avoid dividing by zero (e.g., when root is pure) - importances /= normalizer - - return importances - - def _get_value_ndarray(self): - """Wraps value as a 3-d NumPy array. - - The array keeps a reference to this Tree, which manages the underlying - memory. - """ - shape = (self.node_count, self.n_outputs, self.max_n_classes) - arr = np.ndarray(shape, dtype=np.float64, buffer=self.value) - arr.base = self - return arr - - def _get_node_ndarray(self): - """Wraps nodes as a NumPy struct array. - - The array keeps a reference to this Tree, which manages the underlying - memory. Individual fields are publicly accessible as properties of the - Tree. - """ - shape = (self.node_count,) - dtype = np.dtype([ - ('left_child', np.intp), - ('right_child', np.intp), - ('feature', np.intp), - ('threshold', np.float64), - ('impurity', np.float64), - ('n_node_samples', np.intp), - ('weighted_n_node_samples', np.float64), - ('missing_go_to_left', np.uint8) - ]) - arr = np.ndarray(shape, dtype=dtype, buffer=self.nodes) - arr.base = self - return arr - - def compute_partial_dependence(self, X, target_features, out): - out.fill(0.0) # Initialize the output array - - _TREE_LEAF = self._TREE_LEAF # The value for leaf nodes - - for sample_idx in range(X.shape[0]): - stack_size = 1 - node_idx_stack = [0] # root node - weight_stack = [1.0] # all samples are in the root node - total_weight = 0.0 - - while stack_size > 0: - stack_size -= 1 - current_node_idx = node_idx_stack[stack_size] - current_node = self.nodes[current_node_idx] - - if current_node.left_child == _TREE_LEAF: - # Leaf node - out[sample_idx] += weight_stack[stack_size] * self.value[current_node_idx] - total_weight += weight_stack[stack_size] - else: - is_target_feature = any(target_feature == current_node.feature for target_feature in target_features) - if is_target_feature: - if X[sample_idx, current_node.feature] <= current_node.threshold: - node_idx_stack.append(current_node.left_child) - weight_stack.append(weight_stack[stack_size]) - stack_size += 1 - else: - node_idx_stack.append(current_node.right_child) - weight_stack.append(weight_stack[stack_size]) - stack_size += 1 - else: - left_sample_frac = self.nodes[current_node.left_child].weighted_n_node_samples / current_node.weighted_n_node_samples - current_weight = weight_stack[stack_size] - node_idx_stack.extend([current_node.left_child, current_node.right_child]) - weight_stack.extend([current_weight * left_sample_frac, current_weight * (1 - left_sample_frac)]) - stack_size += 2 - - if not (0.999 < total_weight < 1.001): - raise ValueError(f"Total weight should be 1.0 but was {total_weight:.9f}") - - -def _check_n_classes(n_classes, expected_dtype): - if n_classes.ndim != 1: - raise ValueError( - f"Wrong dimensions for n_classes from the pickle: " - f"expected 1, got {n_classes.ndim}" - ) - - if n_classes.dtype == expected_dtype: - return n_classes - - # Handles both different endianness and different bitness - if n_classes.dtype.kind == "i" and n_classes.dtype.itemsize in [4, 8]: - return n_classes.astype(expected_dtype, casting="same_kind") - - raise ValueError( - "n_classes from the pickle has an incompatible dtype:\n" - f"- expected: {expected_dtype}\n" - f"- got: {n_classes.dtype}" - ) - - - - - - From bd339b34e52a2c6471d18d31ef9936c034d2a427 Mon Sep 17 00:00:00 2001 From: umairjavaid Date: Sun, 3 Sep 2023 05:43:57 +0000 Subject: [PATCH 005/117] added apply dense --- .../frontends/sklearn/tree/_tree_ivy.pyx | 2 ++ ivy/functional/frontends/sklearn/tree/tree.py | 18 +++++++++--------- ivy/functional/frontends/sklearn/tree/try.py | 4 ++-- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/ivy/functional/frontends/sklearn/tree/_tree_ivy.pyx b/ivy/functional/frontends/sklearn/tree/_tree_ivy.pyx index f436c919f8bc2..2095718c8a4bf 100644 --- a/ivy/functional/frontends/sklearn/tree/_tree_ivy.pyx +++ b/ivy/functional/frontends/sklearn/tree/_tree_ivy.pyx @@ -26,6 +26,8 @@ from libcpp cimport bool import struct +import ivy + import numpy as np cimport numpy as cnp cnp.import_array() diff --git a/ivy/functional/frontends/sklearn/tree/tree.py b/ivy/functional/frontends/sklearn/tree/tree.py index 55ab869bd01f7..40776a54c1e80 100644 --- a/ivy/functional/frontends/sklearn/tree/tree.py +++ b/ivy/functional/frontends/sklearn/tree/tree.py @@ -1,4 +1,4 @@ - +import ivy @@ -481,23 +481,23 @@ def apply(self, X): return self._apply_dense(X) def _apply_dense(self, X): - if not isinstance(X, torch.Tensor): - raise ValueError("X should be a torch.Tensor, got %s" % type(X)) + if not isinstance(X, ivy.data_classes.array.array.Array): + raise ValueError("X should be a ivy.data_classes.array.array.Array, got %s" % type(X)) - if X.dtype != torch.float32: - raise ValueError("X.dtype should be torch.float32, got %s" % X.dtype) + if X.dtype != "float32": + raise ValueError("X.dtype should be float32, got %s" % X.dtype) X_tensor = X n_samples = X.shape[0] - out = torch.zeros(n_samples, dtype=torch.int64) + out = ivy.zeros(n_samples, dtype="float32") for i in range(n_samples): - node = self.nodes + node = self.nodes[0] # Start at the root node while node.left_child != _TREE_LEAF: X_i_node_feature = X_tensor[i, node.feature] - if torch.isnan(X_i_node_feature): + if ivy.isnan(X_i_node_feature): if node.missing_go_to_left: node = self.nodes[node.left_child] else: @@ -507,7 +507,7 @@ def _apply_dense(self, X): else: node = self.nodes[node.right_child] - out[i] = node - self.nodes + out[i] = self.nodes.index(node) # Get the index of the terminal node return out diff --git a/ivy/functional/frontends/sklearn/tree/try.py b/ivy/functional/frontends/sklearn/tree/try.py index a953aa4ef01d2..fea9ebc3c29c4 100644 --- a/ivy/functional/frontends/sklearn/tree/try.py +++ b/ivy/functional/frontends/sklearn/tree/try.py @@ -1,5 +1,5 @@ import ivy -a = ivy.array([1,2,3]) - +a = ivy.array([1,2,3],dtype="float32") +print(a.dtype == "float32") print(isinstance(a, ivy.data_classes.array.array.Array)) \ No newline at end of file From 1d70bf057345b53b77ace46f0419a7d7e33e55ef Mon Sep 17 00:00:00 2001 From: umairjavaid Date: Sun, 3 Sep 2023 23:50:16 +0000 Subject: [PATCH 006/117] fixed translation --- ivy/functional/frontends/sklearn/tree/tree.py | 64 ++++++++++--------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/ivy/functional/frontends/sklearn/tree/tree.py b/ivy/functional/frontends/sklearn/tree/tree.py index 40776a54c1e80..b0fd72cd90151 100644 --- a/ivy/functional/frontends/sklearn/tree/tree.py +++ b/ivy/functional/frontends/sklearn/tree/tree.py @@ -511,42 +511,45 @@ def _apply_dense(self, X): return out + #not so sure about sparse implimentation yet def _apply_sparse_csr(self, X): """Finds the terminal region (=leaf node) for each sample in sparse X.""" - if not isinstance(X, csr_matrix): - raise ValueError("X should be in csr_matrix format, got %s" % type(X)) + if not isinstance(X, ivy.data_classes.array.array.Array): + raise ValueError("X should be a ivy.data_classes.array.array.Array, got %s" % type(X)) - if X.dtype != np.float32: - raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + if X.dtype != "float32": + raise ValueError("X.dtype should be float32, got %s" % X.dtype) n_samples, n_features = X.shape # Initialize output - out = np.zeros(n_samples, dtype=np.intp) + out = ivy.zeros(n_samples, dtype="float32") - # Initialize auxiliary data structures - feature_to_sample = np.full(n_features, -1, dtype=np.intp) - X_sample = np.zeros(n_features, dtype=np.float32) + # Initialize auxiliary data structures on CPU + feature_to_sample = ivy.full((n_features,), -1, dtype="float32") + X_sample = ivy.zeros((n_features,), dtype="float32") for i in range(n_samples): - node = self.nodes + node = self.nodes[0] # Start from the root node for k in range(X.indptr[i], X.indptr[i + 1]): feature_to_sample[X.indices[k]] = i X_sample[X.indices[k]] = X.data[k] - while node.left_child != _TREE_LEAF: + while node.left_child is not None: if feature_to_sample[node.feature] == i: feature_value = X_sample[node.feature] else: - feature_value = 0.0 + feature_value = ivy.array(0,dtype="float32") #feature value is computed during training - if feature_value <= node.threshold: - node = self.nodes[node.left_child] + threshold = ivy.array(node.threshold, dtype="float32") + if feature_value <= threshold: + node = node.left_child else: - node = self.nodes[node.right_child] + node = node.right_child - out[i] = node - self.nodes # node offset + # Get the index of the leaf node + out[i] = self.nodes.index(node) return out @@ -561,49 +564,48 @@ def _decision_path_dense(self, X): """Finds the decision path (=node) for each sample in X.""" # Check input - if not isinstance(X, np.ndarray): - raise ValueError("X should be in np.ndarray format, got %s" % type(X)) + if not isinstance(X, ivy.data_classes.array.array.Array): + raise ValueError("X should be a ivy.data_classes.array.array.Array, got %s" % type(X)) - if X.dtype != DTYPE: - raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + if X.dtype != "float32": + raise ValueError("X.dtype should be float32, got %s" % X.dtype) # Extract input - X_ndarray = X n_samples = X.shape[0] # Initialize output - indptr = np.zeros(n_samples + 1, dtype=np.intp) - indices = np.zeros(n_samples * (1 + self.max_depth), dtype=np.intp) + indptr = ivy.zeros(n_samples + 1, dtype="float32") + indices = ivy.zeros(n_samples * (1 + self.max_depth), dtype="float32") # Initialize auxiliary data-structure - node = None i = 0 for i in range(n_samples): - node = self.nodes + node = self.nodes[0] indptr[i + 1] = indptr[i] # Add all external nodes while node.left_child != _TREE_LEAF: - # ... and node.right_child != _TREE_LEAF: - indices[indptr[i + 1]] = node - self.nodes + indices[indptr[i + 1]] = node indptr[i + 1] += 1 - if X_ndarray[i, node.feature] <= node.threshold: + if X[i, node.feature] <= node.threshold: node = self.nodes[node.left_child] else: node = self.nodes[node.right_child] # Add the leaf node - indices[indptr[i + 1]] = node - self.nodes + indices[indptr[i + 1]] = node indptr[i + 1] += 1 indices = indices[:indptr[n_samples]] - data = np.ones(shape=len(indices), dtype=np.intp) + data = ivy.ones(indices.shape, dtype="float32") + #csr_matrix is not implemented out = csr_matrix((data, indices, indptr), shape=(n_samples, self.node_count)) return out + #not tested def _decision_path_sparse_csr(self, X): """Finds the decision path (=node) for each sample in X.""" @@ -672,10 +674,10 @@ def compute_node_depths(self): Returns ------- - depths : ndarray of shape (self.node_count,), dtype=np.int64 + depths : ivy of shape (self.node_count,), dtype="int32" The depth of each node in the tree. """ - depths = np.empty(self.node_count, dtype=np.int64) + depths = ivy.zeros(self.node_count, dtype="int32") children_left = self.children_left children_right = self.children_right node_count = self.node_count From 1f3d95cc7e89a4373375f2af50f69739a7731f09 Mon Sep 17 00:00:00 2001 From: umairjavaid Date: Mon, 4 Sep 2023 07:11:23 +0000 Subject: [PATCH 007/117] fixed tree functions --- ivy/functional/frontends/sklearn/tree/tree.py | 280 +++++++----------- 1 file changed, 114 insertions(+), 166 deletions(-) diff --git a/ivy/functional/frontends/sklearn/tree/tree.py b/ivy/functional/frontends/sklearn/tree/tree.py index b0fd72cd90151..c849f6b79f4c4 100644 --- a/ivy/functional/frontends/sklearn/tree/tree.py +++ b/ivy/functional/frontends/sklearn/tree/tree.py @@ -1,69 +1,12 @@ import ivy - - - - - - - - - - - - - - - - - - - - - - - - - - -import numpy as np - - - from scipy.sparse import issparse from scipy.sparse import csr_matrix from scipy.sparse import isspmatrix_csr - - - - - - - - - - - - - - - - - - - -# ============================================================================= -# Types and constants -# ============================================================================= - -from numpy import float32 as DTYPE -from numpy import float64 as DOUBLE - - # Define constants -INFINITY = np.inf -EPSILON = np.finfo(np.double).eps +INFINITY = ivy.inf +EPSILON = ivy.finfo(ivy.double).eps # Some handy constants (BestFirstTreeBuilder) IS_FIRST = 1 @@ -76,8 +19,7 @@ _TREE_LEAF = TREE_LEAF _TREE_UNDEFINED = TREE_UNDEFINED -# Since you're dealing with Cython-specific types and features, -# it's important to provide a dummy definition for Node. + class Node: def __init__(self): self.left_child = None @@ -89,9 +31,7 @@ def __init__(self): self.weighted_n_node_samples = None self.missing_go_to_left = None -dummy = Node() -# Create a numpy dtype for Node using the dummy object -NODE_DTYPE = np.asarray([dummy], dtype=object).dtype + # ============================================================================= # TreeBuilder # ============================================================================= @@ -375,45 +315,9 @@ def __setstate__(self, d): raise NotImplementedError def _resize(self, capacity): - """ - Resize all inner arrays to `capacity`. If `capacity` is -1, then double the size of the inner arrays. - Returns -1 in case of failure to allocate memory (and raise MemoryError), or 0 otherwise. - """ - if self._resize_c(capacity) != 0: - # Raise MemoryError if resizing fails - raise MemoryError() + raise NotImplementedError def _resize_c(self, capacity=float('inf')): - # """ - # Guts of _resize - # Returns -1 in case of failure to allocate memory (and raise MemoryError), - # or 0 otherwise. - # """ - # if capacity == self.capacity and self.nodes is not None: - # return 0 - - # if capacity == float('inf'): - # if self.capacity == 0: - # capacity = 3 # default initial value - # else: - # capacity = 2 * self.capacity - - # # This section is relevant if the code is dealing with C arrays. - # # In Python, resizing arrays is handled automatically by lists or numpy arrays. - # # You won't need to explicitly reallocate memory or initialize values like this. - # self.nodes = [None] * capacity #doubtfull either the Node classes get reallocated or something else happends - # self.value = [0.0] * (capacity * self.value_stride) #doubtfull either the value classes get reallocated or something else happends - - # # value memory is initialized to 0 to enable classifier argmax - # if capacity > self.capacity: - # self.value += [0.0] * ((capacity - self.capacity) * self.value_stride) - - # # if capacity smaller than node_count, adjust the counter - # if capacity < self.node_count: - # self.node_count = capacity - - # self.capacity = capacity - # return 0 raise NotImplementedError @@ -433,7 +337,7 @@ def _add_node(self, parent, is_left, is_leaf, feature, threshold, impurity, # if self._resize_c() != 0: # return -1 #throw error if resize not possible - node = Node() #self.nodes contains a list of nodes, it returns the node at node_id location + node = Node() #self.nodes contains a list of nodes, it returns the node at node_id location self.nodes.append(node) node.impurity = impurity node.n_node_samples = n_node_samples @@ -691,110 +595,154 @@ def compute_node_depths(self): return depths.base + + ''' + This code is typically used after fitting a decision tree model to assess the importance of each feature in making predictions. + The feature importances indicate the contribution of each feature to the overall decision-making process of the tree. + ''' def compute_feature_importances(self, normalize=True): - """Computes the importance of each feature (aka variable).""" - nodes = self.nodes - node = nodes - end_node = node + self.node_count + # Compute the importance of each feature (variable). - importances = np.zeros(self.n_features, dtype=np.float64) + # Create an array to store feature importances. + importances = ivy.zeros(self.n_features) - while node != end_node: - if node.left_child != _TREE_LEAF: - left = nodes[node.left_child] - right = nodes[node.right_child] + for node in self.nodes: + if node.left_child is not None and node.right_child is not None: + left = node.left_child + right = node.right_child - importances[node.feature] += ( + # Calculate the importance for the feature associated with this node. + importance = ( node.weighted_n_node_samples * node.impurity - left.weighted_n_node_samples * left.impurity - - right.weighted_n_node_samples * right.impurity) - node += 1 + right.weighted_n_node_samples * right.impurity + ) - for i in range(self.n_features): - importances[i] /= nodes[0].weighted_n_node_samples + importances[node.feature] += importance if normalize: - normalizer = np.sum(importances) + total_importance = ivy.sum(importances) - if normalizer > 0.0: - # Avoid dividing by zero (e.g., when root is pure) - importances /= normalizer + if total_importance > 0.0: + # Normalize feature importances to sum up to 1.0 + importances /= total_importance return importances def _get_value_ndarray(self): - """Wraps value as a 3-d NumPy array. + """Wraps value as a 3-dimensional NumPy array. The array keeps a reference to this Tree, which manages the underlying memory. """ - shape = (self.node_count, self.n_outputs, self.max_n_classes) - arr = np.ndarray(shape, dtype=np.float64, buffer=self.value) - arr.base = self + shape = ( + int(self.node_count), + int(self.n_outputs), + int(self.max_n_classes) + ) + arr = ivy.array(self.value, dtype="float32").reshape(shape) return arr - def _get_node_ndarray(self): - """Wraps nodes as a NumPy struct array. - - The array keeps a reference to this Tree, which manages the underlying - memory. Individual fields are publicly accessible as properties of the - Tree. + ''' + this code creates a NumPy structured array that wraps the internal nodes of a decision tree. + This array can be used to access and manipulate the tree's nodes efficiently in Python. + ''' + def _get_node_tensor(self): + """Wraps nodes as a PyTorch tensor. + + The tensor keeps a reference to this Tree, which manages the underlying + memory. Individual fields are publicly accessible as properties of the Tree. """ - shape = (self.node_count,) - dtype = np.dtype([ - ('left_child', np.intp), - ('right_child', np.intp), - ('feature', np.intp), - ('threshold', np.float64), - ('impurity', np.float64), - ('n_node_samples', np.intp), - ('weighted_n_node_samples', np.float64), - ('missing_go_to_left', np.uint8) - ]) - arr = np.ndarray(shape, dtype=dtype, buffer=self.nodes) - arr.base = self - return arr + + # Create a tensor with a custom data type for the tree nodes + nodes_tensor = ivy.zeros(self.node_count, dtype="float32") + + # Fill the tensor with node data + for i, node in enumerate(self.nodes): + nodes_tensor[i] = ivy.array(( + node.impurity, + node.n_node_samples, + node.weighted_n_node_samples, + node.left_child, + node.right_child, + node.feature, + node.threshold, + node.missing_go_to_left, + ), dtype="float32") + + # Attach a reference to the tree + nodes_tensor.tree_reference = self + + return nodes_tensor + ''' + This code effectively computes the partial dependence values for a set of grid points based on a given decision tree. + Partial dependence helps understand how the model's predictions change with variations in specific features while keeping other features constant. + ''' def compute_partial_dependence(self, X, target_features, out): - out.fill(0.0) # Initialize the output array + """ + Partial dependence of the response on the target_feature set. + + For each sample in X, a tree traversal is performed. + Each traversal starts from the root with weight 1.0. + + At each non-leaf node that splits on a target feature, either the left child or the right child is visited based on the feature value of the current sample, and the weight is not modified. + At each non-leaf node that splits on a complementary feature, both children are visited, and the weight is multiplied by the fraction of training samples that went to each child. - _TREE_LEAF = self._TREE_LEAF # The value for leaf nodes + At each leaf, the value of the node is multiplied by the current weight (weights sum to 1 for all visited terminal nodes). + + Parameters + ---------- + X : numpy.ndarray, shape (n_samples, n_target_features) + The grid points on which the partial dependence should be evaluated. + target_features : numpy.ndarray, shape (n_target_features) + The set of target features for which the partial dependence should be evaluated. + out : numpy.ndarray, shape (n_samples) + The value of the partial dependence function on each grid point. + """ + weight_stack = ivy.zeros(self.node_count, dtype="float32") + node_idx_stack = np.zeros(self.node_count, dtype="int32") + stack_size = 0 for sample_idx in range(X.shape[0]): + # Init stacks for the current sample stack_size = 1 - node_idx_stack = [0] # root node - weight_stack = [1.0] # all samples are in the root node - total_weight = 0.0 + node_idx_stack[0] = 0 # Root node + weight_stack[0] = 1.0 # All samples are in the root node while stack_size > 0: + # Pop the stack stack_size -= 1 current_node_idx = node_idx_stack[stack_size] current_node = self.nodes[current_node_idx] - if current_node.left_child == _TREE_LEAF: - # Leaf node + if current_node.left_child == -1: # Leaf node out[sample_idx] += weight_stack[stack_size] * self.value[current_node_idx] - total_weight += weight_stack[stack_size] - else: - is_target_feature = any(target_feature == current_node.feature for target_feature in target_features) + else: # Non-leaf node + is_target_feature = current_node.feature in target_features if is_target_feature: + # Push left or right child on the stack if X[sample_idx, current_node.feature] <= current_node.threshold: - node_idx_stack.append(current_node.left_child) - weight_stack.append(weight_stack[stack_size]) - stack_size += 1 + node_idx_stack[stack_size] = current_node.left_child else: - node_idx_stack.append(current_node.right_child) - weight_stack.append(weight_stack[stack_size]) - stack_size += 1 + node_idx_stack[stack_size] = current_node.right_child + stack_size += 1 else: - left_sample_frac = self.nodes[current_node.left_child].weighted_n_node_samples / current_node.weighted_n_node_samples + # Push both children onto the stack and give a weight proportional to the number of samples going through each branch + left_child = current_node.left_child + right_child = current_node.right_child + left_sample_frac = self.nodes[left_child].weighted_n_node_samples / current_node.weighted_n_node_samples current_weight = weight_stack[stack_size] - node_idx_stack.extend([current_node.left_child, current_node.right_child]) - weight_stack.extend([current_weight * left_sample_frac, current_weight * (1 - left_sample_frac)]) - stack_size += 2 + weight_stack[stack_size] = current_weight * left_sample_frac + stack_size += 1 + + node_idx_stack[stack_size] = right_child + weight_stack[stack_size] = current_weight * (1 - left_sample_frac) + stack_size += 1 - if not (0.999 < total_weight < 1.001): - raise ValueError(f"Total weight should be 1.0 but was {total_weight:.9f}") + # Sanity check. Should never happen. + if not (0.999 < np.sum(weight_stack) < 1.001): + raise ValueError("Total weight should be 1.0 but was %.9f" % np.sum(weight_stack)) def _check_n_classes(n_classes, expected_dtype): From b428c52dde23c8fc0267e272e2f384a0717c5137 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Haider=20Sultan=20=20=E2=9A=A1?= Date: Mon, 4 Sep 2023 21:04:40 +0500 Subject: [PATCH 008/117] Update _classes.py --- .../frontends/sklearn/tree/_classes.py | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/ivy/functional/frontends/sklearn/tree/_classes.py b/ivy/functional/frontends/sklearn/tree/_classes.py index 79e76b73df506..d2e8dc8b17c30 100644 --- a/ivy/functional/frontends/sklearn/tree/_classes.py +++ b/ivy/functional/frontends/sklearn/tree/_classes.py @@ -49,12 +49,6 @@ def _support_missing_values(self, X): def _compute_missing_values_in_feature_mask(self, X): raise NotImplementedError - def _support_missing_values(self, X): - raise NotImplementedError - - def _compute_missing_values_in_feature_mask(self, X): - raise NotImplementedError - def _fit( self, X, @@ -68,16 +62,6 @@ def _fit( def _validate_X_predict(self, X, check_input): raise NotImplementedError - - def predict(self, X, check_input=True): - raise NotImplementedError - - def apply(self, X, check_input=True): - raise NotImplementedError - - def _validate_X_predict(self, X, check_input): - raise NotImplementedError - def predict(self, X, check_input=True): raise NotImplementedError @@ -90,13 +74,6 @@ def decision_path(self, X, check_input=True): def _prune_tree(self): raise NotImplementedError - - def cost_complexity_pruning_path(self, X, y, sample_weight=None): - raise NotImplementedError - - def _prune_tree(self): - raise NotImplementedError - def cost_complexity_pruning_path(self, X, y, sample_weight=None): raise NotImplementedError From 5f27d6fa8149a06417f1cb282ec3a2b329128fe1 Mon Sep 17 00:00:00 2001 From: umairjavaid Date: Wed, 6 Sep 2023 05:51:21 +0000 Subject: [PATCH 009/117] implemented compute partial in tree module --- ivy/functional/frontends/sklearn/tree/tree.py | 34 +- ivy/functional/frontends/sklearn/tree/try.py | 513 +++++++++++++++++- 2 files changed, 523 insertions(+), 24 deletions(-) diff --git a/ivy/functional/frontends/sklearn/tree/tree.py b/ivy/functional/frontends/sklearn/tree/tree.py index c849f6b79f4c4..6459eb8969e85 100644 --- a/ivy/functional/frontends/sklearn/tree/tree.py +++ b/ivy/functional/frontends/sklearn/tree/tree.py @@ -265,18 +265,17 @@ def __init__(self, n_features, n_classes, n_outputs): self.nodes = [] #replaced it with array since this array will contain nodes self.value = None self.value_stride = None - - dummy = 0 - size_t_dtype = np.array(dummy).dtype + + size_t_dtype = "float32" n_classes = _check_n_classes(n_classes, size_t_dtype) # Input/Output layout self.n_features = n_features self.n_outputs = n_outputs - self.n_classes = np.zeros(n_outputs, dtype=size_t_dtype) + self.n_classes = ivy.zeros(n_outputs, dtype=size_t_dtype) - self.max_n_classes = np.max(n_classes) + self.max_n_classes = ivy.max(n_classes) self.value_stride = n_outputs * self.max_n_classes for k in range(n_outputs): @@ -320,9 +319,7 @@ def _resize(self, capacity): def _resize_c(self, capacity=float('inf')): raise NotImplementedError - - def _add_node(self, parent, is_left, is_leaf, feature, threshold, impurity, - n_node_samples, weighted_n_node_samples, missing_go_to_left): + def _add_node(self, parent, is_left, is_leaf, feature, threshold, impurity, n_node_samples, weighted_n_node_samples, missing_go_to_left): """ Add a node to the tree. @@ -332,11 +329,6 @@ def _add_node(self, parent, is_left, is_leaf, feature, threshold, impurity, """ node_id = self.node_count - #no need to resize since python reallocates lists dynamically - # if node_id >= self.capacity: - # if self._resize_c() != 0: - # return -1 #throw error if resize not possible - node = Node() #self.nodes contains a list of nodes, it returns the node at node_id location self.nodes.append(node) node.impurity = impurity @@ -518,7 +510,7 @@ def _decision_path_sparse_csr(self, X): raise ValueError("X should be in csr_matrix format, got %s" % type(X)) if X.dtype != DTYPE: - raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + raise ValueError("X.dtype should be float32, got %s" % X.dtype) # Extract input X_data = X.data @@ -529,14 +521,14 @@ def _decision_path_sparse_csr(self, X): n_features = X.shape[1] # Initialize output - indptr = np.zeros(n_samples + 1, dtype=np.intp) - indices = np.zeros(n_samples * (1 + self.max_depth), dtype=np.intp) + indptr = ivy.zeros(n_samples + 1, dtype="int32") + indices = ivy.zeros(n_samples * (1 + self.max_depth), dtype="int32") # Initialize auxiliary data-structure feature_value = 0.0 node = None - X_sample = np.zeros(n_features, dtype=DTYPE) - feature_to_sample = np.full(n_features, -1, dtype=np.intp) + X_sample = ivy.zeros(n_features, dtype=DTYPE) + feature_to_sample = ivy.full(n_features, -1, dtype="int32") for i in range(n_samples): node = self.nodes @@ -566,7 +558,7 @@ def _decision_path_sparse_csr(self, X): indptr[i + 1] += 1 indices = indices[:indptr[n_samples]] - data = np.ones(shape=len(indices), dtype=np.intp) + data = ivy.ones(shape=len(indices), dtype="int32") out = csr_matrix((data, indices, indptr), shape=(n_samples, self.node_count)) return out @@ -741,8 +733,8 @@ def compute_partial_dependence(self, X, target_features, out): stack_size += 1 # Sanity check. Should never happen. - if not (0.999 < np.sum(weight_stack) < 1.001): - raise ValueError("Total weight should be 1.0 but was %.9f" % np.sum(weight_stack)) + if not (0.999 < ivy.sum(weight_stack) < 1.001): + raise ValueError("Total weight should be 1.0 but was %.9f" % ivy.sum(weight_stack)) def _check_n_classes(n_classes, expected_dtype): diff --git a/ivy/functional/frontends/sklearn/tree/try.py b/ivy/functional/frontends/sklearn/tree/try.py index fea9ebc3c29c4..422b633dcdeaf 100644 --- a/ivy/functional/frontends/sklearn/tree/try.py +++ b/ivy/functional/frontends/sklearn/tree/try.py @@ -1,5 +1,512 @@ import ivy -a = ivy.array([1,2,3],dtype="float32") -print(a.dtype == "float32") -print(isinstance(a, ivy.data_classes.array.array.Array)) \ No newline at end of file +class Tree: + def __init__(self, n_features, n_classes, n_outputs): + """Constructor.""" + self.n_features = None + self.n_classes = None + self.n_outputs = None + self.max_n_classes = None + self.max_depth = None + self.node_count = None + self.capacity = None + self.nodes = [] # replaced it with array since this array will contain nodes + self.value = None + self.value_stride = None + + size_t_dtype = "float32" + + n_classes = _check_n_classes(n_classes, size_t_dtype) + + # Input/Output layout + self.n_features = n_features + self.n_outputs = n_outputs + self.n_classes = ivy.zeros(n_outputs, dtype=size_t_dtype) + + self.max_n_classes = ivy.max(n_classes) + self.value_stride = n_outputs * self.max_n_classes + + for k in range(n_outputs): + self.n_classes[k] = n_classes[k] + + # Inner structures + self.max_depth = 0 + self.node_count = 0 + self.capacity = 0 + self.value = None + self.nodes = None + + def __del__(self): + """Destructor.""" + # Free all inner structures + self.n_classes = None + self.value = None + self.nodes = None + + def __reduce__(self): + """Reduce re-implementation, for pickling.""" + raise NotImplementedError + + def __getstate__(self): + """Getstate re-implementation, for pickling.""" + d = {} + # capacity is inferred during the __setstate__ using nodes + d["max_depth"] = self.max_depth + d["node_count"] = self.node_count + d["nodes"] = self._get_node_ndarray() + d["values"] = self._get_value_ndarray() + return d + + def __setstate__(self, d): + """Setstate re-implementation, for unpickling.""" + raise NotImplementedError + + def _resize(self, capacity): + raise NotImplementedError + + def _resize_c(self, capacity=float('inf')): + raise NotImplementedError + + def _add_node(self, parent, is_left, is_leaf, feature, threshold, impurity, n_node_samples, weighted_n_node_samples, missing_go_to_left): + """ + Add a node to the tree. + + The new node registers itself as the child of its parent. + + Returns -1 on error. + """ + node_id = self.node_count + + node = Node() #self.nodes contains a list of nodes, it returns the node at node_id location + self.nodes.append(node) + node.impurity = impurity + node.n_node_samples = n_node_samples + node.weighted_n_node_samples = weighted_n_node_samples + + if parent != _TREE_UNDEFINED: + if is_left: + self.nodes[parent].left_child = node_id + else: + self.nodes[parent].right_child = node_id + + if is_leaf: + node.left_child = _TREE_LEAF + node.right_child = _TREE_LEAF + node.feature = _TREE_UNDEFINED + node.threshold = _TREE_UNDEFINED + + else: + # left_child and right_child will be set later + node.feature = feature + node.threshold = threshold + node.missing_go_to_left = missing_go_to_left + + self.node_count += 1 + + return node_id + + def predict(self, X): + # Apply the model to the input data X + predictions = self.apply(X) + # Get the internal data as a NumPy array + internal_data = self._get_value_ndarray() + # Use the predictions to index the internal data + out = internal_data[predictions] #not sure if this accurately translates to .take(self.apply(X), axis=0, mode='clip') + # Reshape the output if the model is single-output + if self.n_outputs == 1: + out = out.reshape(X.shape[0], self.max_n_classes) + return out + + def apply(self, X): + """Finds the terminal region (=leaf node) for each sample in X.""" + if issparse(X): + return self._apply_sparse_csr(X) + else: + return self._apply_dense(X) + + def _apply_dense(self, X): + if not isinstance(X, ivy.data_classes.array.array.Array): + raise ValueError("X should be a ivy.data_classes.array.array.Array, got %s" % type(X)) + + if X.dtype != "float32": + raise ValueError("X.dtype should be float32, got %s" % X.dtype) + + X_tensor = X + n_samples = X.shape[0] + out = ivy.zeros(n_samples, dtype="float32") + + for i in range(n_samples): + node = self.nodes[0] # Start at the root node + + while node.left_child != _TREE_LEAF: + X_i_node_feature = X_tensor[i, node.feature] + + if ivy.isnan(X_i_node_feature): + if node.missing_go_to_left: + node = self.nodes[node.left_child] + else: + node = self.nodes[node.right_child] + elif X_i_node_feature <= node.threshold: + node = self.nodes[node.left_child] + else: + node = self.nodes[node.right_child] + + out[i] = self.nodes.index(node) # Get the index of the terminal node + + return out + + #not so sure about sparse implimentation yet + def _apply_sparse_csr(self, X): + """Finds the terminal region (=leaf node) for each sample in sparse X.""" + if not isinstance(X, ivy.data_classes.array.array.Array): + raise ValueError("X should be a ivy.data_classes.array.array.Array, got %s" % type(X)) + + if X.dtype != "float32": + raise ValueError("X.dtype should be float32, got %s" % X.dtype) + + n_samples, n_features = X.shape + + # Initialize output + out = ivy.zeros(n_samples, dtype="float32") + + # Initialize auxiliary data structures on CPU + feature_to_sample = ivy.full((n_features,), -1, dtype="float32") + X_sample = ivy.zeros((n_features,), dtype="float32") + + for i in range(n_samples): + node = self.nodes[0] # Start from the root node + + for k in range(X.indptr[i], X.indptr[i + 1]): + feature_to_sample[X.indices[k]] = i + X_sample[X.indices[k]] = X.data[k] + + while node.left_child is not None: + if feature_to_sample[node.feature] == i: + feature_value = X_sample[node.feature] + else: + feature_value = ivy.array( 0, dtype="float32") #feature value is computed during training + + threshold = ivy.array(node.threshold, dtype="float32") + if feature_value <= threshold: + node = node.left_child + else: + node = node.right_child + + # Get the index of the leaf node + out[i] = self.nodes.index(node) + + return out + + def decision_path(self, X): + """Finds the decision path (=node) for each sample in X.""" + if issparse(X): + return self._decision_path_sparse_csr(X) + else: + return self._decision_path_dense(X) + + def _decision_path_dense(self, X): + """Finds the decision path (=node) for each sample in X.""" + + # Check input + if not isinstance(X, ivy.data_classes.array.array.Array): + raise ValueError("X should be a ivy.data_classes.array.array.Array, got %s" % type(X)) + + if X.dtype != "float32": + raise ValueError("X.dtype should be float32, got %s" % X.dtype) + + # Extract input + n_samples = X.shape[0] + + # Initialize output + indptr = ivy.zeros(n_samples + 1, dtype="float32") + indices = ivy.zeros(n_samples * (1 + self.max_depth), dtype="float32") + + # Initialize auxiliary data-structure + i = 0 + + for i in range(n_samples): + node = self.nodes[0] + indptr[i + 1] = indptr[i] + + # Add all external nodes + while node.left_child != _TREE_LEAF: + indices[indptr[i + 1]] = node + indptr[i + 1] += 1 + + if X[i, node.feature] <= node.threshold: + node = self.nodes[node.left_child] + else: + node = self.nodes[node.right_child] + + # Add the leaf node + indices[indptr[i + 1]] = node + indptr[i + 1] += 1 + + indices = indices[:indptr[n_samples]] + data = ivy.ones(indices.shape, dtype="float32") + #csr_matrix is not implemented + out = csr_matrix((data, indices, indptr), shape=(n_samples, self.node_count)) + + return out + + #not tested + def _decision_path_sparse_csr(self, X): + """Finds the decision path (=node) for each sample in X.""" + + # Check input + if not isspmatrix_csr(X): + raise ValueError("X should be in csr_matrix format, got %s" % type(X)) + + if X.dtype != DTYPE: + raise ValueError("X.dtype should be np.float32, got %s" % X.dtype) + + # Extract input + X_data = X.data + X_indices = X.indices + X_indptr = X.indptr + + n_samples = X.shape[0] + n_features = X.shape[1] + + # Initialize output + indptr = np.zeros(n_samples + 1, dtype=np.intp) + indices = np.zeros(n_samples * (1 + self.max_depth), dtype=np.intp) + + # Initialize auxiliary data-structure + feature_value = 0.0 + node = None + X_sample = np.zeros(n_features, dtype=DTYPE) + feature_to_sample = np.full(n_features, -1, dtype=np.intp) + + for i in range(n_samples): + node = self.nodes + indptr[i + 1] = indptr[i] + + for k in range(X_indptr[i], X_indptr[i + 1]): + feature_to_sample[X_indices[k]] = i + X_sample[X_indices[k]] = X_data[k] + + # While node not a leaf + while node.left_child != _TREE_LEAF: + indices[indptr[i + 1]] = node - self.nodes + indptr[i + 1] += 1 + + if feature_to_sample[node.feature] == i: + feature_value = X_sample[node.feature] + else: + feature_value = 0.0 + + if feature_value <= node.threshold: + node = self.nodes[node.left_child] + else: + node = self.nodes[node.right_child] + + # Add the leaf node + indices[indptr[i + 1]] = node - self.nodes + indptr[i + 1] += 1 + + indices = indices[:indptr[n_samples]] + data = np.ones(shape=len(indices), dtype=np.intp) + out = csr_matrix((data, indices, indptr), shape=(n_samples, self.node_count)) + + return out + + def compute_node_depths(self): + """Compute the depth of each node in a tree. + + .. versionadded:: 1.3 + + Returns + ------- + depths : ivy of shape (self.node_count,), dtype="int32" + The depth of each node in the tree. + """ + depths = ivy.zeros(self.node_count, dtype="int32") + children_left = self.children_left + children_right = self.children_right + node_count = self.node_count + + depths[0] = 1 # init root node + for node_id in range(node_count): + if children_left[node_id] != _TREE_LEAF: + depth = depths[node_id] + 1 + depths[children_left[node_id]] = depth + depths[children_right[node_id]] = depth + + return depths.base + + + ''' + This code is typically used after fitting a decision tree model to assess the importance of each feature in making predictions. + The feature importances indicate the contribution of each feature to the overall decision-making process of the tree. + ''' + def compute_feature_importances(self, normalize=True): + # Compute the importance of each feature (variable). + + # Create an array to store feature importances. + importances = ivy.zeros(self.n_features) + + for node in self.nodes: + if node.left_child is not None and node.right_child is not None: + left = node.left_child + right = node.right_child + + # Calculate the importance for the feature associated with this node. + importance = ( + node.weighted_n_node_samples * node.impurity - + left.weighted_n_node_samples * left.impurity - + right.weighted_n_node_samples * right.impurity + ) + + importances[node.feature] += importance + + if normalize: + total_importance = ivy.sum(importances) + + if total_importance > 0.0: + # Normalize feature importances to sum up to 1.0 + importances /= total_importance + + return importances + + def _get_value_ndarray(self): + """Wraps value as a 3-dimensional NumPy array. + + The array keeps a reference to this Tree, which manages the underlying + memory. + """ + shape = ( + int(self.node_count), + int(self.n_outputs), + int(self.max_n_classes) + ) + arr = ivy.array(self.value, dtype="float32").reshape(shape) + return arr + + ''' + this code creates a ivy structured array that wraps the internal nodes of a decision tree. + This array can be used to access and manipulate the tree's nodes efficiently in Python. + ''' + def _get_node_tensor(self): + """Wraps nodes as a PyTorch tensor. + + The tensor keeps a reference to this Tree, which manages the underlying + memory. Individual fields are publicly accessible as properties of the Tree. + """ + + # Create a tensor with a custom data type for the tree nodes + nodes_tensor = ivy.zeros(self.node_count, dtype="float32") + + # Fill the tensor with node data + for i, node in enumerate(self.nodes): + nodes_tensor[i] = ivy.array(( + node.impurity, + node.n_node_samples, + node.weighted_n_node_samples, + node.left_child, + node.right_child, + node.feature, + node.threshold, + node.missing_go_to_left, + ), dtype="float32") + + # Attach a reference to the tree + nodes_tensor.tree_reference = self + + return nodes_tensor + + ''' + This code effectively computes the partial dependence values for a set of grid points based on a given decision tree. + Partial dependence helps understand how the model's predictions change with variations in specific features while keeping other features constant. + ''' + def compute_partial_dependence(self, X, target_features, out): + """ + Partial dependence of the response on the target_feature set. + + For each sample in X, a tree traversal is performed. + Each traversal starts from the root with weight 1.0. + + At each non-leaf node that splits on a target feature, either the left child or the right child is visited based on the feature value of the current sample, and the weight is not modified. + At each non-leaf node that splits on a complementary feature, both children are visited, and the weight is multiplied by the fraction of training samples that went to each child. + + At each leaf, the value of the node is multiplied by the current weight (weights sum to 1 for all visited terminal nodes). + + Parameters + ---------- + X : numpy.ndarray, shape (n_samples, n_target_features) + The grid points on which the partial dependence should be evaluated. + target_features : numpy.ndarray, shape (n_target_features) + The set of target features for which the partial dependence should be evaluated. + out : numpy.ndarray, shape (n_samples) + The value of the partial dependence function on each grid point. + """ + weight_stack = ivy.zeros(self.node_count, dtype="float32") + node_idx_stack = ivy.zeros(self.node_count, dtype="int32") + stack_size = 0 + + for sample_idx in range(X.shape[0]): + # Init stacks for the current sample + stack_size = 1 + node_idx_stack[0] = 0 # Root node + weight_stack[0] = 1.0 # All samples are in the root node + + while stack_size > 0: + # Pop the stack + stack_size -= 1 + current_node_idx = node_idx_stack[stack_size] + current_node = self.nodes[current_node_idx] + + if current_node.left_child == -1: # Leaf node + out[sample_idx] += weight_stack[stack_size] * self.value[current_node_idx] + else: # Non-leaf node + is_target_feature = current_node.feature in target_features + if is_target_feature: + # Push left or right child on the stack + if X[sample_idx, current_node.feature] <= current_node.threshold: + node_idx_stack[stack_size] = current_node.left_child + else: + node_idx_stack[stack_size] = current_node.right_child + stack_size += 1 + else: + # Push both children onto the stack and give a weight proportional to the number of samples going through each branch + left_child = current_node.left_child + right_child = current_node.right_child + left_sample_frac = self.nodes[left_child].weighted_n_node_samples / current_node.weighted_n_node_samples + current_weight = weight_stack[stack_size] + weight_stack[stack_size] = current_weight * left_sample_frac + stack_size += 1 + + node_idx_stack[stack_size] = right_child + weight_stack[stack_size] = current_weight * (1 - left_sample_frac) + stack_size += 1 + + # Sanity check. Should never happen. + if not (0.999 < np.sum(weight_stack) < 1.001): + raise ValueError("Total weight should be 1.0 but was %.9f" % np.sum(weight_stack)) + + +def _check_n_classes(n_classes, expected_dtype): + if n_classes.ndim != 1: + raise ValueError( + f"Wrong dimensions for n_classes from the pickle: " + f"expected 1, got {n_classes.ndim}" + ) + + if n_classes.dtype == expected_dtype: + return n_classes + + # Handles both different endianness and different bitness + if n_classes.dtype.kind == "i" and n_classes.dtype.itemsize in [4, 8]: + return n_classes.astype(expected_dtype, casting="same_kind") + + raise ValueError( + "n_classes from the pickle has an incompatible dtype:\n" + f"- expected: {expected_dtype}\n" + f"- got: {n_classes.dtype}" + ) + + + + + + From 2601ef5801895bcb5e42ade0380e47f87b4e9a18 Mon Sep 17 00:00:00 2001 From: umairjavaid Date: Thu, 7 Sep 2023 11:04:18 +0000 Subject: [PATCH 010/117] added splitter --- .../frontends/sklearn/tree/splitter.py | 115 ++++++++++++++++++ ivy/functional/frontends/sklearn/tree/tree.py | 96 ++++----------- 2 files changed, 136 insertions(+), 75 deletions(-) create mode 100644 ivy/functional/frontends/sklearn/tree/splitter.py diff --git a/ivy/functional/frontends/sklearn/tree/splitter.py b/ivy/functional/frontends/sklearn/tree/splitter.py new file mode 100644 index 0000000000000..24533e3fe1629 --- /dev/null +++ b/ivy/functional/frontends/sklearn/tree/splitter.py @@ -0,0 +1,115 @@ +#splitter.init +#splitter.n_samples +#splitter.node_reset +#splitter.node_impurity +#splitter.node_split +#splitter.node_value + +import ivy + + +def init_split(split_record, start_pos): + split_record.impurity_left = float('inf') + split_record.impurity_right = float('inf') + split_record.pos = start_pos + split_record.feature = 0 + split_record.threshold = 0.0 + split_record.improvement = float('-inf') + split_record.missing_go_to_left = False + split_record.n_missing = 0 + + +class Splitter: + def __init__(self, criterion, max_features, min_samples_leaf, min_weight_leaf, random_state): + self.criterion = criterion + self.random_state = random_state + + self.n_samples = 0 + self.n_features = 0 + + self.max_features = max_features + self.min_samples_leaf = min_samples_leaf + self.min_weight_leaf = min_weight_leaf + + self.rand_r_state = random_state.randint(0, 0x7FFFFFFF) + + self.samples = None + self.weighted_n_samples = 0.0 + + self.features = None + self.feature_values = None + self.constant_features = None + + self.y = None + self.sample_weight = None + + def init(self, X, y, sample_weight, missing_values_in_feature_mask=None): + n_samples = X.shape[0] + + # Create a list to store indices of positively weighted samples + samples = [] + + weighted_n_samples = 0.0 + + for i in range(n_samples): + # Only work with positively weighted samples + if sample_weight is None or sample_weight[i] != 0.0: + samples.append(i) + + if sample_weight is not None: + weighted_n_samples += sample_weight[i] + else: + weighted_n_samples += 1.0 + + # Set the attributes + self.n_samples = len(samples) + self.weighted_n_samples = weighted_n_samples + + n_features = X.shape[1] + self.features = ivy.arange(n_features) + self.n_features = n_features + + self.samples = ivy.array(samples, dtype="int32") + self.feature_values = ivy.empty(n_samples, dtype="float32") + self.constant_features = ivy.empty(n_features, dtype="int32") + + self.y = y + self.sample_weight = sample_weight + + if missing_values_in_feature_mask is not None: + self.criterion.init_sum_missing() + + return 0 + + def __reduce__(self): + return (type(self), (self.criterion, self.max_features, self.min_samples_leaf, + self.min_weight_leaf, self.random_state), self.__getstate__()) + + def node_reset(self, start, end, weighted_n_node_samples): + self.start = start + self.end = end + + self.criterion.init( + self.y, + self.sample_weight, + self.weighted_n_samples, + self.samples, + start, + end + ) + + weighted_n_node_samples[0] = self.criterion.weighted_n_node_samples + return 0 + + def node_split(self, impurity, split, n_constant_features): + # This is a placeholder method; actual implementation required. + # You should add your computation logic here to find the best split. + pass + + def node_value(self, dest): + """Copy the value of node samples[start:end] into dest.""" + self.criterion.node_value(dest) + + def node_impurity(self): + """Return the impurity of the current node.""" + return self.criterion.node_impurity() \ No newline at end of file diff --git a/ivy/functional/frontends/sklearn/tree/tree.py b/ivy/functional/frontends/sklearn/tree/tree.py index 6459eb8969e85..41d325a55e5a3 100644 --- a/ivy/functional/frontends/sklearn/tree/tree.py +++ b/ivy/functional/frontends/sklearn/tree/tree.py @@ -4,6 +4,9 @@ from scipy.sparse import csr_matrix from scipy.sparse import isspmatrix_csr +from ._splitter import SplitRecord + + # Define constants INFINITY = ivy.inf EPSILON = ivy.finfo(ivy.double).eps @@ -68,35 +71,35 @@ def _check_input( X = X.tocsc() #tocsc() is a method provided by the scipy.sparse module in the SciPy library. It's used to convert a sparse matrix to the Compressed Sparse Column (CSC) format. X.sort_indices() #This is done to ensure that the indices of non-zero elements within the matrix are sorted in ascending order. - if X.data.dtype != DTYPE: - X.data = np.ascontiguousarray(X.data, dtype=DTYPE) + if X.data.dtype != "float32": + X.data = ivy.ascontiguousarray(X.data, dtype="float32") - if X.indices.dtype != np.int32 or X.indptr.dtype != np.int32: + if X.indices.dtype != "int32" or X.indptr.dtype != "int32": raise ValueError("No support for np.int64 index-based sparse matrices") - elif X.dtype != DTYPE: + elif X.dtype != "float32": # since we have to copy, we will make it Fortran for efficiency - X = np.asfortranarray(X, dtype=DTYPE) + X = ivy.asfortranarray(X, dtype="float32") - if y.base.dtype != DTYPE or not y.base.flags.contiguous: - y = np.ascontiguousarray(y, dtype=DTYPE) + if y.base.dtype != "float32" or not y.base.flags.contiguous: + y = ivy.ascontiguousarray(y, dtype="float32") if ( sample_weight is not None and ( - sample_weight.base.dtype != DOUBLE or + sample_weight.base.dtype != "float32" or not sample_weight.base.flags.contiguous ) ): - sample_weight = np.asarray(sample_weight, dtype=DOUBLE, order="C") + sample_weight = ivy.asarray(sample_weight, dtype="float32", order="C") return X, y, sample_weight - # Depth first builder --------------------------------------------------------- # A record on the stack for depth-first tree growing + class StackRecord: def __init__(self, start, end, depth, parent, is_left, impurity, n_constant_features): self.start = start @@ -108,12 +111,6 @@ def __init__(self, start, end, depth, parent, is_left, impurity, n_constant_feat self.n_constant_features = n_constant_features - - - - - - class DepthFirstTreeBuilder(TreeBuilder): """Build a decision tree in depth-first fashion.""" @@ -142,11 +139,6 @@ def build( # Check input X, y, sample_weight = self._check_input(X, y, sample_weight) - # Initial capacity - init_capacity = (2 ** (tree.max_depth + 1)) - 1 if tree.max_depth <= 10 else 2047 - - tree._resize(init_capacity) - # Parameters splitter = self.splitter max_depth = self.max_depth @@ -172,7 +164,7 @@ def build( n_constant_features=0 ) ) - weighted_n_node_samples = np.zeros(1, dtype=np.double) + weighted_n_node_samples = ivy.zeros(1, dtype="float32") while stack: stack_record = stack.pop() @@ -191,7 +183,7 @@ def build( depth >= max_depth or n_node_samples < min_samples_split or n_node_samples < 2 * min_samples_leaf - or np.sum(sample_weight[start:end]) < 2 * min_weight_leaf + or ivy.sum(sample_weight[start:end]) < 2 * min_weight_leaf ) if is_left: @@ -216,13 +208,10 @@ def build( split.threshold if not is_leaf else 0, impurity, n_node_samples, - np.sum(sample_weight[start:end]), + ivy.sum(sample_weight[start:end]), split.missing_go_to_left, ) - if node_id == np.iinfo(np.intp).max: - raise MemoryError() - splitter.node_value(tree.value + node_id * tree.value_stride) if not is_leaf: @@ -363,7 +352,7 @@ def predict(self, X): # Get the internal data as a NumPy array internal_data = self._get_value_ndarray() # Use the predictions to index the internal data - out = internal_data[predictions] #not sure if this accurately translates to .take(self.apply(X), axis=0, mode='clip') + out = internal_data[predictions] # not sure if this accurately translates to .take(self.apply(X), axis=0, mode='clip') # Reshape the output if the model is single-output if self.n_outputs == 1: out = out.reshape(X.shape[0], self.max_n_classes) @@ -436,7 +425,7 @@ def _apply_sparse_csr(self, X): if feature_to_sample[node.feature] == i: feature_value = X_sample[node.feature] else: - feature_value = ivy.array(0,dtype="float32") #feature value is computed during training + feature_value = ivy.array(0, dtype="float32") # feature value is computed during training threshold = ivy.array(node.threshold, dtype="float32") if feature_value <= threshold: @@ -450,14 +439,12 @@ def _apply_sparse_csr(self, X): return out def decision_path(self, X): - """Finds the decision path (=node) for each sample in X.""" if issparse(X): return self._decision_path_sparse_csr(X) else: return self._decision_path_dense(X) def _decision_path_dense(self, X): - """Finds the decision path (=node) for each sample in X.""" # Check input if not isinstance(X, ivy.data_classes.array.array.Array): @@ -503,13 +490,12 @@ def _decision_path_dense(self, X): #not tested def _decision_path_sparse_csr(self, X): - """Finds the decision path (=node) for each sample in X.""" # Check input if not isspmatrix_csr(X): raise ValueError("X should be in csr_matrix format, got %s" % type(X)) - if X.dtype != DTYPE: + if X.dtype != "float32": raise ValueError("X.dtype should be float32, got %s" % X.dtype) # Extract input @@ -527,7 +513,7 @@ def _decision_path_sparse_csr(self, X): # Initialize auxiliary data-structure feature_value = 0.0 node = None - X_sample = ivy.zeros(n_features, dtype=DTYPE) + X_sample = ivy.zeros(n_features, dtype="float32") feature_to_sample = ivy.full(n_features, -1, dtype="int32") for i in range(n_samples): @@ -564,15 +550,6 @@ def _decision_path_sparse_csr(self, X): return out def compute_node_depths(self): - """Compute the depth of each node in a tree. - - .. versionadded:: 1.3 - - Returns - ------- - depths : ivy of shape (self.node_count,), dtype="int32" - The depth of each node in the tree. - """ depths = ivy.zeros(self.node_count, dtype="int32") children_left = self.children_left children_right = self.children_right @@ -622,11 +599,6 @@ def compute_feature_importances(self, normalize=True): return importances def _get_value_ndarray(self): - """Wraps value as a 3-dimensional NumPy array. - - The array keeps a reference to this Tree, which manages the underlying - memory. - """ shape = ( int(self.node_count), int(self.n_outputs), @@ -640,12 +612,6 @@ def _get_value_ndarray(self): This array can be used to access and manipulate the tree's nodes efficiently in Python. ''' def _get_node_tensor(self): - """Wraps nodes as a PyTorch tensor. - - The tensor keeps a reference to this Tree, which manages the underlying - memory. Individual fields are publicly accessible as properties of the Tree. - """ - # Create a tensor with a custom data type for the tree nodes nodes_tensor = ivy.zeros(self.node_count, dtype="float32") @@ -672,28 +638,8 @@ def _get_node_tensor(self): Partial dependence helps understand how the model's predictions change with variations in specific features while keeping other features constant. ''' def compute_partial_dependence(self, X, target_features, out): - """ - Partial dependence of the response on the target_feature set. - - For each sample in X, a tree traversal is performed. - Each traversal starts from the root with weight 1.0. - - At each non-leaf node that splits on a target feature, either the left child or the right child is visited based on the feature value of the current sample, and the weight is not modified. - At each non-leaf node that splits on a complementary feature, both children are visited, and the weight is multiplied by the fraction of training samples that went to each child. - - At each leaf, the value of the node is multiplied by the current weight (weights sum to 1 for all visited terminal nodes). - - Parameters - ---------- - X : numpy.ndarray, shape (n_samples, n_target_features) - The grid points on which the partial dependence should be evaluated. - target_features : numpy.ndarray, shape (n_target_features) - The set of target features for which the partial dependence should be evaluated. - out : numpy.ndarray, shape (n_samples) - The value of the partial dependence function on each grid point. - """ weight_stack = ivy.zeros(self.node_count, dtype="float32") - node_idx_stack = np.zeros(self.node_count, dtype="int32") + node_idx_stack = ivy.zeros(self.node_count, dtype="int32") stack_size = 0 for sample_idx in range(X.shape[0]): From cd87ce9d198afb5e3152977664328a4c3bc99914 Mon Sep 17 00:00:00 2001 From: umairjavaid Date: Fri, 8 Sep 2023 13:41:28 +0000 Subject: [PATCH 011/117] added splitter --- .../frontends/sklearn/tree/splitter.py | 350 +++++++++++++++++- 1 file changed, 349 insertions(+), 1 deletion(-) diff --git a/ivy/functional/frontends/sklearn/tree/splitter.py b/ivy/functional/frontends/sklearn/tree/splitter.py index 24533e3fe1629..311d90f02b2c8 100644 --- a/ivy/functional/frontends/sklearn/tree/splitter.py +++ b/ivy/functional/frontends/sklearn/tree/splitter.py @@ -20,6 +20,11 @@ def init_split(split_record, start_pos): class Splitter: + """Abstract splitter class. + + Splitters are called by tree builders to find the best splits on both + sparse and dense data, one split at a time. + """ def __init__(self, criterion, max_features, min_samples_leaf, min_weight_leaf, random_state): self.criterion = criterion self.random_state = random_state @@ -112,4 +117,347 @@ def node_value(self, dest): def node_impurity(self): """Return the impurity of the current node.""" - return self.criterion.node_impurity() \ No newline at end of file + return self.criterion.node_impurity() + +class BestSplitter(Splitter): + """Splitter for finding the best split on dense data.""" + + def __init__(self, X, y, sample_weight, missing_values_in_feature_mask): + super().__init__(X, y, sample_weight, missing_values_in_feature_mask) + self.partitioner = DensePartitioner( + X, self.samples, self.feature_values, missing_values_in_feature_mask + ) + + def node_split(self, impurity, split, n_constant_features): + return node_split_best( + self, + self.partitioner, + self.criterion, + impurity, + split, + n_constant_features, + ) + +class DensePartitioner: + """Partitioner specialized for dense data. + + Note that this partitioner is agnostic to the splitting strategy (best vs. random). + """ + + def __init__(self, X, samples, feature_values, missing_values_in_feature_mask): + self.X = X + self.samples = samples + self.feature_values = feature_values + self.missing_values_in_feature_mask = missing_values_in_feature_mask + + def init_node_split(self, start, end): + """Initialize splitter at the beginning of node_split.""" + self.start = start + self.end = end + self.n_missing = 0 + + def sort_samples_and_feature_values(self, current_feature): + i = 0 + current_end = 0 + feature_values = self.feature_values + X = self.X + samples = self.samples + n_missing = 0 + missing_values_in_feature_mask = self.missing_values_in_feature_mask + + if missing_values_in_feature_mask is not None and missing_values_in_feature_mask[current_feature]: + i, current_end = self.start, self.end - 1 + while i <= current_end: + if isnan(X[samples[current_end], current_feature]): + n_missing += 1 + current_end -= 1 + continue + + if isnan(X[samples[i], current_feature]): + samples[i], samples[current_end] = samples[current_end], samples[i] + n_missing += 1 + current_end -= 1 + + feature_values[i] = X[samples[i], current_feature] + i += 1 + else: + for i in range(self.start, self.end): + feature_values[i] = X[samples[i], current_feature] + + # Sorting algorithm not shown here; implement the sorting logic separately. + + self.n_missing = n_missing + + + +def sort(feature_values, samples, n): + if n == 0: + return + maxd = 2 * int(math.log(n)) + introsort(feature_values, samples, n, maxd) + +def introsort(feature_values, samples, n, maxd): + while n > 1: + if maxd <= 0: # max depth limit exceeded ("gone quadratic") + # Implement or import heapsort function + heapsort(feature_values, samples, n) + return + maxd -= 1 + + pivot = median3(feature_values, n) + + # Three-way partition. + i = l = 0 + r = n + while i < r: + if feature_values[i] < pivot: + swap(feature_values, samples, i, l) + i += 1 + l += 1 + elif feature_values[i] > pivot: + r -= 1 + swap(feature_values, samples, i, r) + else: + i += 1 + + introsort(feature_values[:l], samples[:l], l, maxd) + feature_values = feature_values[r:] + samples = samples[r:] + n -= r + +def heapsort(feature_values, samples, n): + # Heapify + start = (n - 2) // 2 + end = n + while True: + sift_down(feature_values, samples, start, end) + if start == 0: + break + start -= 1 + + # Sort by shrinking the heap, putting the max element immediately after it + end = n - 1 + while end > 0: + swap(feature_values, samples, 0, end) + sift_down(feature_values, samples, 0, end) + end -= 1 + +def sift_down(feature_values, samples, start, end): + # Restore heap order in feature_values[start:end] by moving the max element to start. + root = start + while True: + child = root * 2 + 1 + + # Find the max of root, left child, right child + maxind = root + if child < end and feature_values[samples[maxind]] < feature_values[samples[child]]: + maxind = child + if child + 1 < end and feature_values[samples[maxind]] < feature_values[samples[child + 1]]: + maxind = child + 1 + + if maxind == root: + break + else: + swap(feature_values, samples, root, maxind) + root = maxind + + +# Define the swap function here +def swap(feature_values, samples, i, j): + feature_values[samples[i]], feature_values[samples[j]] = feature_values[samples[j]], feature_values[samples[i]] + samples[i], samples[j] = samples[j], samples[i] + + +def median3(feature_values, n): + # Median of three pivot selection, after Bentley and McIlroy (1993). + # Engineering a sort function. SP&E. Requires 8/3 comparisons on average. + a, b, c = feature_values[0], feature_values[n // 2], feature_values[n - 1] + if a < b: + if b < c: + return b + elif a < c: + return c + else: + return a + elif b < c: + if a < c: + return a + else: + return c + else: + return b + +import random + +def node_split_best(splitter, partitioner, criterion, impurity, split, n_constant_features): + start = splitter.start + end = splitter.end + end_non_missing = end + n_missing = 0 + has_missing = False + n_searches = 2 if has_missing else 1 + best_split = SplitRecord() + best_proxy_improvement = -float("inf") + + features = list(splitter.features) + constant_features = list(splitter.constant_features) + n_features = splitter.n_features + feature_values = list(splitter.feature_values) + max_features = splitter.max_features + min_samples_leaf = splitter.min_samples_leaf + min_weight_leaf = splitter.min_weight_leaf + random_state = random.Random(splitter.rand_r_state) + + n_visited_features = 0 + n_found_constants = 0 + n_drawn_constants = 0 + n_known_constants = n_constant_features[0] + n_total_constants = n_known_constants + + while f_i > n_total_constants and (n_visited_features < max_features or n_visited_features <= n_found_constants + n_drawn_constants): + n_visited_features += 1 + f_j = random.randint(n_drawn_constants, f_i - n_found_constants - 1) + + if f_j < n_known_constants: + features[n_drawn_constants], features[f_j] = features[f_j], features[n_drawn_constants] + n_drawn_constants += 1 + continue + + f_j += n_found_constants + current_split.feature = features[f_j] + partitioner.sort_samples_and_feature_values(current_split.feature) + n_missing = partitioner.n_missing + end_non_missing = end - n_missing + + if end_non_missing == start or feature_values[end_non_missing - 1] <= feature_values[start] + FEATURE_THRESHOLD: + features[f_j], features[n_total_constants] = features[n_total_constants], features[f_j] + n_found_constants += 1 + n_total_constants += 1 + continue + + f_i -= 1 + features[f_i], features[f_j] = features[f_j], features[f_i] + has_missing = n_missing != 0 + + if has_missing: + criterion.init_missing(n_missing) + + n_searches = 2 if has_missing else 1 + + for i in range(n_searches): + missing_go_to_left = i == 1 + criterion.missing_go_to_left = missing_go_to_left + criterion.reset() + p = start + + while p < end_non_missing: + partitioner.next_p(p_prev, p) + + if p >= end_non_missing: + continue + + if missing_go_to_left: + n_left = p - start + n_missing + n_right = end_non_missing - p + else: + n_left = p - start + n_right = end_non_missing - p + n_missing + + if n_left < min_samples_leaf or n_right < min_samples_leaf: + continue + + current_split.pos = p + criterion.update(current_split.pos) + + if criterion.weighted_n_left < min_weight_leaf or criterion.weighted_n_right < min_weight_leaf: + continue + + current_proxy_improvement = criterion.proxy_impurity_improvement() + + if current_proxy_improvement > best_proxy_improvement: + best_proxy_improvement = current_proxy_improvement + current_split.threshold = (feature_values[p_prev] / 2.0 + feature_values[p] / 2.0) + + if current_split.threshold == feature_values[p] or current_split.threshold == float("inf") or current_split.threshold == -float("inf"): + current_split.threshold = feature_values[p_prev] + + current_split.n_missing = n_missing + if n_missing == 0: + current_split.missing_go_to_left = n_left > n_right + else: + current_split.missing_go_to_left = missing_go_to_left + + best_split = current_split + + if has_missing: + n_left, n_right = end - start - n_missing, n_missing + p = end - n_missing + missing_go_to_left = False + + if not (n_left < min_samples_leaf or n_right < min_samples_leaf): + criterion.missing_go_to_left = missing_go_to_left + criterion.update(p) + + if not (criterion.weighted_n_left < min_weight_leaf or criterion.weighted_n_right < min_weight_leaf): + current_proxy_improvement = criterion.proxy_impurity_improvement() + + if current_proxy_improvement > best_proxy_improvement: + best_proxy_improvement = current_proxy_improvement + current_split.threshold = float("inf") + current_split.missing_go_to_left = missing_go_to_left + current_split.n_missing = n_missing + current_split.pos = p + best_split = current_split + + if best_split.pos < end: + partitioner.partition_samples_final( + best_split.pos, + best_split.threshold, + best_split.feature, + best_split.n_missing + ) + + if best_split.n_missing != 0: + criterion.init_missing(best_split.n_missing) + criterion.missing_go_to_left = best_split.missing_go_to_left + criterion.reset() + criterion.update(best_split.pos) + criterion.children_impurity( + best_split.impurity_left, + best_split.impurity_right + ) + best_split.improvement = criterion.impurity_improvement( + impurity, + best_split.impurity_left, + best_split.impurity_right + ) + shift_missing_values_to_left_if_required( + best_split, + samples, + end + ) + + memcpy(features, constant_features, sizeof(SIZE_t) * n_known_constants) + memcpy( + constant_features[n_known_constants:], + features[n_known_constants:n_known_constants + n_found_constants], + sizeof(SIZE_t) * n_found_constants + ) + + split[0] = best_split + n_constant_features[0] = n_total_constants + return 0 + +def shift_missing_values_to_left_if_required(best, samples, end): + # The partitioner partitions the data such that the missing values are in + # samples[-n_missing:] for the criterion to consume. If the missing values + # are going to the right node, then the missing values are already in the + # correct position. If the missing values go left, then we move the missing + # values to samples[best.pos:best.pos+n_missing] and update `best.pos`. + if best.n_missing > 0 and best.missing_go_to_left: + for p in range(best.n_missing): + i = best.pos + p + current_end = end - 1 - p + samples[i], samples[current_end] = samples[current_end], samples[i] + best.pos += best.n_missing + From 032a7481769c63bbc40ae53751e10452349a9daa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Haider=20Sultan=20=20=E2=9A=A1?= Date: Sat, 9 Sep 2023 19:43:27 +0000 Subject: [PATCH 012/117] Update: Decision Tree --- ivy/functional/frontends/sklearn/__init__.py | 1 + ivy/functional/frontends/sklearn/_config.py | 300 ++++++ ivy/functional/frontends/sklearn/base.py | 17 + .../frontends/sklearn/tree/_classes.py | 250 ++++- .../frontends/sklearn/tree/_criterion.py | 941 ++++++++++++++++++ .../frontends/sklearn/tree/_tree.py | 532 +++++----- .../frontends/sklearn/tree/_utils.py | 84 ++ .../frontends/sklearn/tree/_utils.pyx | 468 +++++++++ .../frontends/sklearn/tree/splitter.py | 473 ++++++--- .../frontends/sklearn/tree/tree ivy.py | 549 +++++----- ivy/functional/frontends/sklearn/tree/tree.py | 455 +++++---- .../frontends/sklearn/utils/_array_api.py | 247 +++++ .../frontends/sklearn/utils/_isfinite.py | 40 + .../frontends/sklearn/utils/_isfinite.pyx | 50 + .../frontends/sklearn/utils/class_weight.py | 188 ++++ .../frontends/sklearn/utils/fixes.py | 4 + .../frontends/sklearn/utils/multiclass.py | 28 + .../frontends/sklearn/utils/validation.py | 793 +++++++++++++++ 18 files changed, 4548 insertions(+), 872 deletions(-) create mode 100644 ivy/functional/frontends/sklearn/_config.py create mode 100644 ivy/functional/frontends/sklearn/tree/_criterion.py create mode 100644 ivy/functional/frontends/sklearn/tree/_utils.py create mode 100644 ivy/functional/frontends/sklearn/tree/_utils.pyx create mode 100644 ivy/functional/frontends/sklearn/utils/_array_api.py create mode 100644 ivy/functional/frontends/sklearn/utils/_isfinite.py create mode 100644 ivy/functional/frontends/sklearn/utils/_isfinite.pyx create mode 100644 ivy/functional/frontends/sklearn/utils/class_weight.py create mode 100644 ivy/functional/frontends/sklearn/utils/fixes.py diff --git a/ivy/functional/frontends/sklearn/__init__.py b/ivy/functional/frontends/sklearn/__init__.py index 278ccd9274970..b2d3aa9383126 100644 --- a/ivy/functional/frontends/sklearn/__init__.py +++ b/ivy/functional/frontends/sklearn/__init__.py @@ -1,5 +1,6 @@ import ivy from ivy.functional.frontends.numpy import array +from ._config import get_config, set_config, config_context _int8 = ivy.IntDtype("int8") _int16 = ivy.IntDtype("int16") diff --git a/ivy/functional/frontends/sklearn/_config.py b/ivy/functional/frontends/sklearn/_config.py new file mode 100644 index 0000000000000..7bf9871d3438c --- /dev/null +++ b/ivy/functional/frontends/sklearn/_config.py @@ -0,0 +1,300 @@ +"""Global configuration state and functions for management.""" +import os +from contextlib import contextmanager as contextmanager +import threading + +_global_config = { + "assume_finite": bool(os.environ.get("SKLEARN_ASSUME_FINITE", False)), + "working_memory": int(os.environ.get("SKLEARN_WORKING_MEMORY", 1024)), + "print_changed_only": True, + "display": "diagram", + "pairwise_dist_chunk_size": int( + os.environ.get("SKLEARN_PAIRWISE_DIST_CHUNK_SIZE", 256) + ), + "enable_cython_pairwise_dist": True, + "array_api_dispatch": False, + "transform_output": "default", +} +_threadlocal = threading.local() + + +def _get_threadlocal_config(): + """ + Get a threadlocal **mutable** configuration. + + If the configuration does not exist, copy the default global + configuration. + """ + if not hasattr(_threadlocal, "global_config"): + _threadlocal.global_config = _global_config.copy() + return _threadlocal.global_config + + +def get_config(): + """ + Retrieve current values for configuration set by :func:`set_config`. + + Returns + ------- + config : dict + Keys are parameter names that can be passed to :func:`set_config`. + + See Also + -------- + config_context : Context manager for global scikit-learn configuration. + set_config : Set global scikit-learn configuration. + """ + # Return a copy of the threadlocal configuration so that users will + # not be able to modify the configuration with the returned dict. + return _get_threadlocal_config().copy() + + +def set_config( + assume_finite=None, + working_memory=None, + print_changed_only=None, + display=None, + pairwise_dist_chunk_size=None, + enable_cython_pairwise_dist=None, + array_api_dispatch=None, + transform_output=None, +): + """ + Set global scikit-learn configuration. + + .. versionadded:: 0.19 + + Parameters + ---------- + assume_finite : bool, default=None + If True, validation for finiteness will be skipped, + saving time, but leading to potential crashes. If + False, validation for finiteness will be performed, + avoiding error. Global default: False. + + .. versionadded:: 0.19 + + working_memory : int, default=None + If set, scikit-learn will attempt to limit the size of temporary arrays + to this number of MiB (per job when parallelised), often saving both + computation time and memory on expensive operations that can be + performed in chunks. Global default: 1024. + + .. versionadded:: 0.20 + + print_changed_only : bool, default=None + If True, only the parameters that were set to non-default + values will be printed when printing an estimator. For example, + ``print(SVC())`` while True will only print 'SVC()' while the default + behaviour would be to print 'SVC(C=1.0, cache_size=200, ...)' with + all the non-changed parameters. + + .. versionadded:: 0.21 + + display : {'text', 'diagram'}, default=None + If 'diagram', estimators will be displayed as a diagram in a Jupyter + lab or notebook context. If 'text', estimators will be displayed as + text. Default is 'diagram'. + + .. versionadded:: 0.23 + + pairwise_dist_chunk_size : int, default=None + The number of row vectors per chunk for the accelerated pairwise- + distances reduction backend. Default is 256 (suitable for most of + modern laptops' caches and architectures). + + Intended for easier benchmarking and testing of scikit-learn internals. + End users are not expected to benefit from customizing this configuration + setting. + + .. versionadded:: 1.1 + + enable_cython_pairwise_dist : bool, default=None + Use the accelerated pairwise-distances reduction backend when + possible. Global default: True. + + Intended for easier benchmarking and testing of scikit-learn internals. + End users are not expected to benefit from customizing this configuration + setting. + + .. versionadded:: 1.1 + + array_api_dispatch : bool, default=None + Use Array API dispatching when inputs follow the Array API standard. + Default is False. + + See the :ref:`User Guide ` for more details. + + .. versionadded:: 1.2 + + transform_output : str, default=None + Configure output of `transform` and `fit_transform`. + + See :ref:`sphx_glr_auto_examples_miscellaneous_plot_set_output.py` + for an example on how to use the API. + + - `"default"`: Default output format of a transformer + - `"pandas"`: DataFrame output + - `None`: Transform configuration is unchanged + + .. versionadded:: 1.2 + + See Also + -------- + config_context : Context manager for global scikit-learn configuration. + get_config : Retrieve current values of the global configuration. + """ + local_config = _get_threadlocal_config() + + if assume_finite is not None: + local_config["assume_finite"] = assume_finite + if working_memory is not None: + local_config["working_memory"] = working_memory + if print_changed_only is not None: + local_config["print_changed_only"] = print_changed_only + if display is not None: + local_config["display"] = display + if pairwise_dist_chunk_size is not None: + local_config["pairwise_dist_chunk_size"] = pairwise_dist_chunk_size + if enable_cython_pairwise_dist is not None: + local_config["enable_cython_pairwise_dist"] = enable_cython_pairwise_dist + if array_api_dispatch is not None: + local_config["array_api_dispatch"] = array_api_dispatch + if transform_output is not None: + local_config["transform_output"] = transform_output + + +@contextmanager +def config_context( + *, + assume_finite=None, + working_memory=None, + print_changed_only=None, + display=None, + pairwise_dist_chunk_size=None, + enable_cython_pairwise_dist=None, + array_api_dispatch=None, + transform_output=None, +): + """ + Context manager for global scikit-learn configuration. + + Parameters + ---------- + assume_finite : bool, default=None + If True, validation for finiteness will be skipped, + saving time, but leading to potential crashes. If + False, validation for finiteness will be performed, + avoiding error. If None, the existing value won't change. + The default value is False. + + working_memory : int, default=None + If set, scikit-learn will attempt to limit the size of temporary arrays + to this number of MiB (per job when parallelised), often saving both + computation time and memory on expensive operations that can be + performed in chunks. If None, the existing value won't change. + The default value is 1024. + + print_changed_only : bool, default=None + If True, only the parameters that were set to non-default + values will be printed when printing an estimator. For example, + ``print(SVC())`` while True will only print 'SVC()', but would print + 'SVC(C=1.0, cache_size=200, ...)' with all the non-changed parameters + when False. If None, the existing value won't change. + The default value is True. + + .. versionchanged:: 0.23 + Default changed from False to True. + + display : {'text', 'diagram'}, default=None + If 'diagram', estimators will be displayed as a diagram in a Jupyter + lab or notebook context. If 'text', estimators will be displayed as + text. If None, the existing value won't change. + The default value is 'diagram'. + + .. versionadded:: 0.23 + + pairwise_dist_chunk_size : int, default=None + The number of row vectors per chunk for the accelerated pairwise- + distances reduction backend. Default is 256 (suitable for most of + modern laptops' caches and architectures). + + Intended for easier benchmarking and testing of scikit-learn internals. + End users are not expected to benefit from customizing this configuration + setting. + + .. versionadded:: 1.1 + + enable_cython_pairwise_dist : bool, default=None + Use the accelerated pairwise-distances reduction backend when + possible. Global default: True. + + Intended for easier benchmarking and testing of scikit-learn internals. + End users are not expected to benefit from customizing this configuration + setting. + + .. versionadded:: 1.1 + + array_api_dispatch : bool, default=None + Use Array API dispatching when inputs follow the Array API standard. + Default is False. + + See the :ref:`User Guide ` for more details. + + .. versionadded:: 1.2 + + transform_output : str, default=None + Configure output of `transform` and `fit_transform`. + + See :ref:`sphx_glr_auto_examples_miscellaneous_plot_set_output.py` + for an example on how to use the API. + + - `"default"`: Default output format of a transformer + - `"pandas"`: DataFrame output + - `None`: Transform configuration is unchanged + + .. versionadded:: 1.2 + + Yields + ------ + None. + + See Also + -------- + set_config : Set global scikit-learn configuration. + get_config : Retrieve current values of the global configuration. + + Notes + ----- + All settings, not just those presently modified, will be returned to + their previous values when the context manager is exited. + + Examples + -------- + >>> import sklearn + >>> from sklearn.utils.validation import assert_all_finite + >>> with sklearn.config_context(assume_finite=True): + ... assert_all_finite([float('nan')]) + >>> with sklearn.config_context(assume_finite=True): + ... with sklearn.config_context(assume_finite=False): + ... assert_all_finite([float('nan')]) + Traceback (most recent call last): + ... + ValueError: Input contains NaN... + """ + old_config = get_config() + set_config( + assume_finite=assume_finite, + working_memory=working_memory, + print_changed_only=print_changed_only, + display=display, + pairwise_dist_chunk_size=pairwise_dist_chunk_size, + enable_cython_pairwise_dist=enable_cython_pairwise_dist, + array_api_dispatch=array_api_dispatch, + transform_output=transform_output, + ) + + try: + yield + finally: + set_config(**old_config) diff --git a/ivy/functional/frontends/sklearn/base.py b/ivy/functional/frontends/sklearn/base.py index 8fa319eb2b587..729932b2d875e 100644 --- a/ivy/functional/frontends/sklearn/base.py +++ b/ivy/functional/frontends/sklearn/base.py @@ -36,3 +36,20 @@ def predict(self, X): class MultiOutputMixin: def _more_tags(self): return {"multioutput": True} + + +def is_classifier(estimator): + """ + Return True if the given estimator is (probably) a classifier. + + Parameters + ---------- + estimator : object + Estimator object to test. + + Returns + ------- + out : bool + True if estimator is a classifier and False otherwise. + """ + return getattr(estimator, "_estimator_type", None) == "classifier" diff --git a/ivy/functional/frontends/sklearn/tree/_classes.py b/ivy/functional/frontends/sklearn/tree/_classes.py index d2e8dc8b17c30..a5b77c1394be8 100644 --- a/ivy/functional/frontends/sklearn/tree/_classes.py +++ b/ivy/functional/frontends/sklearn/tree/_classes.py @@ -1,9 +1,43 @@ from abc import ABCMeta, abstractmethod +from scipy.sparse import issparse +import ivy +from ..utils.multiclass import check_classification_targets +from ..utils.class_weight import compute_sample_weight +from ..base import is_classifier +from _tree import DTYPE, DOUBLE, Tree, BestFirstTreeBuilder, DepthFirstTreeBuilder from ..base import ( BaseEstimator, ClassifierMixin, MultiOutputMixin, ) +import numbers +import warnings +from ..utils.validation import _check_sample_weight +from _criterion import Criterion, Gini, Entropy, MSE, MAE, FriedmanMSE, Poisson +from splitter import ( + Splitter, + BestSplitter, + RandomSplitter, + RandomSparseSplitter, + BestSparseSplitter, +) + +CRITERIA_CLF = { + "gini": Gini, + "log_loss": Entropy, + "entropy": Entropy, +} +CRITERIA_REG = { + "squared_error": MSE, + "friedman_mse": FriedmanMSE, + "absolute_error": MAE, + "poisson": Poisson, +} +DENSE_SPLITTERS = {"best": BestSplitter, "random": RandomSplitter} +SPARSE_SPLITTERS = { + "best": BestSparseSplitter, + "random": RandomSparseSplitter, +} class BaseDecisionTree(MultiOutputMixin, BaseEstimator, metaclass=ABCMeta): @@ -57,7 +91,221 @@ def _fit( check_input=True, missing_values_in_feature_mask=None, ): - raise NotImplementedError + if check_input: + check_X_params = dict( + dtype=DTYPE, accept_sparse="csc", force_all_finite=False + ) + check_y_params = dict(ensure_2d=False, dtype=None) + X, y = self._validate_data( + X, y, validate_separately=(check_X_params, check_y_params) + ) + + missing_values_in_feature_mask = ( + self._compute_missing_values_in_feature_mask(X) + ) + if issparse(X): + X.sort_indices() + + if X.indices.dtype != ivy.int64 or X.indptr.dtype != ivy.int64: + raise ValueError( + "No support for ivy.int64 index based sparse matrices" + ) + + if self.criterion == "poisson": + if ivy.any(y < 0): + raise ValueError( + "Some value(s) of y are negative which is" + " not allowed for Poisson regression." + ) + if ivy.sum(y) <= 0: + raise ValueError( + "Sum of y is not positive which is " + "necessary for Poisson regression." + ) + + # Determine output settings + n_samples, self.n_features_in_ = X.shape + is_classification = is_classifier(self) + + y = ivy.atleast_1d(y) + expanded_class_weight = None + + if y.ndim == 1: + # reshape is necessary to preserve the data contiguity against vs + # [:, ivy.newaxis] that does not. + y = ivy.reshape(y, (-1, 1)) + + self.n_outputs_ = y.shape[1] + + if is_classification: + check_classification_targets(y) + y = ivy.copy.copy(y) + + self.classes_ = [] + self.n_classes_ = [] + + if self.class_weight is not None: + y_original = ivy.copy.copy(y) + + y_encoded = ivy.zeros(y.shape, dtype=int) + for k in range(self.n_outputs_): + classes_k, y_encoded[:, k] = ivy.unique_all( + y[:, k], return_inverse=True + ) + self.classes_.append(classes_k) + self.n_classes_.append(classes_k.shape[0]) + y = y_encoded + + if self.class_weight is not None: + expanded_class_weight = compute_sample_weight( + self.class_weight, y_original + ) + + self.n_classes_ = ivy.array(self.n_classes_, dtype=ivy.intp) + + if getattr(y, "dtype", None) != DOUBLE or not y.flags.contiguous: + y = ivy.ascontiguousarray(y, dtype=DOUBLE) + + max_depth = ( + ivy.iinfo(ivy.int32).max if self.max_depth is None else self.max_depth + ) + + if isinstance(self.min_samples_leaf, numbers.Integral): + min_samples_leaf = self.min_samples_leaf + else: # float + min_samples_leaf = int(ivy.ceil(self.min_samples_leaf * n_samples)) + + if isinstance(self.min_samples_split, numbers.Integral): + min_samples_split = self.min_samples_split + else: # float + min_samples_split = int(ivy.ceil(self.min_samples_split * n_samples)) + min_samples_split = max(2, min_samples_split) + + min_samples_split = max(min_samples_split, 2 * min_samples_leaf) + + if isinstance(self.max_features, str): + if self.max_features == "auto": + if is_classification: + max_features = max(1, int(ivy.sqrt(self.n_features_in_))) + warnings.warn( + "`max_features='auto'` has been deprecated in 1.1 " + "and will be removed in 1.3. To keep the past behaviour, " + "explicitly set `max_features='sqrt'`.", + FutureWarning, + ) + else: + max_features = self.n_features_in_ + warnings.warn( + "`max_features='auto'` has been deprecated in 1.1 " + "and will be removed in 1.3. To keep the past behaviour, " + "explicitly set `max_features=1.0'`.", + FutureWarning, + ) + elif self.max_features == "sqrt": + max_features = max(1, int(ivy.sqrt(self.n_features_in_))) + elif self.max_features == "log2": + max_features = max(1, int(ivy.log2(self.n_features_in_))) + elif self.max_features is None: + max_features = self.n_features_in_ + elif isinstance(self.max_features, numbers.Integral): + max_features = self.max_features + else: # float + if self.max_features > 0.0: + max_features = max(1, int(self.max_features * self.n_features_in_)) + else: + max_features = 0 + + self.max_features_ = max_features + + max_leaf_nodes = -1 if self.max_leaf_nodes is None else self.max_leaf_nodes + + if len(y) != n_samples: + raise ValueError( + "Number of labels=%d does not match number of samples=%d" + % (len(y), n_samples) + ) + + if sample_weight is not None: + sample_weight = _check_sample_weight(sample_weight, X, DOUBLE) + + if expanded_class_weight is not None: + if sample_weight is not None: + sample_weight = sample_weight * expanded_class_weight + else: + sample_weight = expanded_class_weight + + # Set min_weight_leaf from min_weight_fraction_leaf + if sample_weight is None: + min_weight_leaf = self.min_weight_fraction_leaf * n_samples + else: + min_weight_leaf = self.min_weight_fraction_leaf * ivy.sum(sample_weight) + + # Build tree + criterion = self.criterion + if not isinstance(criterion, Criterion): + if is_classification: + criterion = CRITERIA_CLF[self.criterion]( + self.n_outputs_, self.n_classes_ + ) + else: + criterion = CRITERIA_REG[self.criterion](self.n_outputs_, n_samples) + else: + # Make a deepcopy in case the criterion has mutable attributes that + # might be shared and modified concurrently during parallel fitting + criterion = ivy.copy.deepcopy(criterion) + + SPLITTERS = SPARSE_SPLITTERS if issparse(X) else DENSE_SPLITTERS + + splitter = self.splitter + if not isinstance(self.splitter, Splitter): + splitter = SPLITTERS[self.splitter]( + criterion, + self.max_features_, + min_samples_leaf, + min_weight_leaf, + self.random_state, # TODO: It was just random_state, no idea why it was not recognized! + ) + + if is_classifier(self): + self.tree_ = Tree(self.n_features_in_, self.n_classes_, self.n_outputs_) + else: + self.tree_ = Tree( + self.n_features_in_, + # TODO: tree shouldn't need this in this case + ivy.array([1] * self.n_outputs_, dtype=ivy.intp), + self.n_outputs_, + ) + + # Use BestFirst if max_leaf_nodes given; use DepthFirst otherwise + if max_leaf_nodes < 0: + builder = DepthFirstTreeBuilder( + splitter, + min_samples_split, + min_samples_leaf, + min_weight_leaf, + max_depth, + self.min_impurity_decrease, + ) + else: + builder = BestFirstTreeBuilder( + splitter, + min_samples_split, + min_samples_leaf, + min_weight_leaf, + max_depth, + max_leaf_nodes, + self.min_impurity_decrease, + ) + + builder.build(self.tree_, X, y, sample_weight, missing_values_in_feature_mask) + + if self.n_outputs_ == 1 and is_classifier(self): + self.n_classes_ = self.n_classes_[0] + self.classes_ = self.classes_[0] + + self._prune_tree() + + return self def _validate_X_predict(self, X, check_input): raise NotImplementedError diff --git a/ivy/functional/frontends/sklearn/tree/_criterion.py b/ivy/functional/frontends/sklearn/tree/_criterion.py new file mode 100644 index 0000000000000..737fa9b27f3e2 --- /dev/null +++ b/ivy/functional/frontends/sklearn/tree/_criterion.py @@ -0,0 +1,941 @@ +import ivy +from _utils import WeightedMedianCalculator +import numpy as np + + +EPSILON = 10 * np.finfo("double").eps + + +class Criterion: + def __getstate__(self): + return {} + + def __setstate__(self, d): + pass + + def init(self, y, sample_weight, weighted_n_samples, sample_indices, start, end): + pass + + def init_missing(self, n_missing): + pass + + def reset(self): + pass + + def reverse_reset(self): + pass + + def update(self, new_pos): + pass + + def node_impurity(self): + pass + + def children_impurity(self, impurity_left, impurity_right): + pass + + def node_value(self, dest): + pass + + def proxy_impurity_improvement(self): + """ + Compute a proxy of the impurity reduction. + + This method is used to speed up the search for the best split. + It is a proxy quantity such that the split that maximizes this + value also maximizes the impurity improvement. It neglects all + constant terms of the impurity decrease for a given split. + + The absolute impurity improvement is only computed by the + impurity_improvement method once the best split has been found. + """ + impurity_left, impurity_right = self.children_impurity() + return ( + -self.weighted_n_right * impurity_right + - self.weighted_n_left * impurity_left + ) + + def impurity_improvement(self, impurity_parent, impurity_left, impurity_right): + """ + Compute the improvement in impurity. + + This method computes the improvement in impurity when a split occurs. + The weighted impurity improvement equation is the following: + + N_t / N * (impurity - N_t_R / N_t * right_impurity + - N_t_L / N_t * left_impurity) + + where N is the total number of samples, N_t is the number of samples + at the current node, N_t_L is the number of samples in the left child, + and N_t_R is the number of samples in the right child. + + Parameters + ---------- + impurity_parent : double + The initial impurity of the parent node before the split + + impurity_left : double + The impurity of the left child + + impurity_right : double + The impurity of the right child + + Return + ------ + double : improvement in impurity after the split occurs + """ + return (self.weighted_n_node_samples / self.weighted_n_samples) * ( + impurity_parent + - (self.weighted_n_right / self.weighted_n_node_samples) * impurity_right + - (self.weighted_n_left / self.weighted_n_node_samples) * impurity_left + ) + + def init_sum_missing(self): + pass + + +class FriedmanMSE: + def __init__(self, n_outputs): + self.n_outputs = n_outputs + + def proxy_impurity_improvement(self): + total_sum_left = 0.0 + total_sum_right = 0.0 + + for k in range(self.n_outputs): + total_sum_left += self.sum_left[k] + total_sum_right += self.sum_right[k] + + diff = ( + self.weighted_n_right * total_sum_left + - self.weighted_n_left * total_sum_right + ) + + return diff * diff / (self.weighted_n_left * self.weighted_n_right) + + def impurity_improvement(self, impurity_parent, impurity_left, impurity_right): + total_sum_left = 0.0 + total_sum_right = 0.0 + + for k in range(self.n_outputs): + total_sum_left += self.sum_left[k] + total_sum_right += self.sum_right[k] + + diff = ( + self.weighted_n_right * total_sum_left + - self.weighted_n_left * total_sum_right + ) / self.n_outputs + + return ( + diff + * diff + / ( + self.weighted_n_left + * self.weighted_n_right + * self.weighted_n_node_samples + ) + ) + + +class Poisson: + def __init__(self, n_outputs): + self.n_outputs = n_outputs + + def node_impurity(self): + return self.poisson_loss( + self.start, self.end, self.sum_total, self.weighted_n_node_samples + ) + + def proxy_impurity_improvement(self): + proxy_impurity_left = 0.0 + proxy_impurity_right = 0.0 + + for k in range(self.n_outputs): + if (self.sum_left[k] <= EPSILON) or (self.sum_right[k] <= EPSILON): + return -ivy.inf + else: + y_mean_left = self.sum_left[k] / self.weighted_n_left + y_mean_right = self.sum_right[k] / self.weighted_n_right + proxy_impurity_left -= self.sum_left[k] * ivy.log(y_mean_left) + proxy_impurity_right -= self.sum_right[k] * ivy.log(y_mean_right) + + return -proxy_impurity_left - proxy_impurity_right + + def children_impurity(self, impurity_left, impurity_right): + start = self.start + pos = self.pos + end = self.end + + impurity_left[0] = self.poisson_loss( + start, pos, self.sum_left, self.weighted_n_left + ) + impurity_right[0] = self.poisson_loss( + pos, end, self.sum_right, self.weighted_n_right + ) + + def poisson_loss(self, start, end, y_sum, weight_sum): + y_mean = 0.0 + poisson_loss = 0.0 + w = 1.0 + + for k in range(self.n_outputs): + if y_sum[k] <= EPSILON: + return ivy.inf + + y_mean = y_sum[k] / weight_sum + + for p in range(start, end): + i = self.sample_indices[p] + + if self.sample_weight is not None: + w = self.sample_weight[i] + + poisson_loss += w * self.y[i, k] * ivy.log(self.y[i, k] / y_mean) + + return poisson_loss / (weight_sum * self.n_outputs) + + +class ClassificationCriterion(Criterion): + def __init__(self, n_outputs, n_classes): + self.start = 0 + self.pos = 0 + self.end = 0 + self.missing_go_to_left = 0 + + self.n_outputs = n_outputs + self.n_samples = 0 + self.n_node_samples = 0 + self.weighted_n_node_samples = 0.0 + self.weighted_n_left = 0.0 + self.weighted_n_right = 0.0 + self.weighted_n_missing = 0.0 + + self.n_classes = ivy.empty(n_outputs, dtype=ivy.intp) + + max_n_classes = 0 + + # For each target, set the number of unique classes in that target, + # and also compute the maximal stride of all targets + for k in range(n_outputs): + self.n_classes[k] = n_classes[k] + + if n_classes[k] > max_n_classes: + max_n_classes = n_classes[k] + + self.max_n_classes = max_n_classes + + # Count labels for each output + self.sum_total = ivy.zeros((n_outputs, max_n_classes), dtype=ivy.float64) + self.sum_left = ivy.zeros((n_outputs, max_n_classes), dtype=ivy.float64) + self.sum_right = ivy.zeros((n_outputs, max_n_classes), dtype=ivy.float64) + + def init(self, y, sample_weight, weighted_n_samples, sample_indices, start, end): + self.y = y + self.sample_weight = sample_weight + self.sample_indices = sample_indices + self.start = start + self.end = end + self.n_node_samples = end - start + self.weighted_n_samples = weighted_n_samples + self.weighted_n_node_samples = 0.0 + + for k in range(self.n_outputs): + self.sum_total[k, :] = 0.0 + + for p in range(start, end): + i = sample_indices[p] + w = 1.0 + + if sample_weight is not None: + w = sample_weight[i] + + for k in range(self.n_outputs): + c = int(self.y[i, k]) + self.sum_total[k, c] += w + + self.weighted_n_node_samples += w + + self.reset() + return 0 + + def init_sum_missing(self): + self.sum_missing = ivy.zeros( + (self.n_outputs, self.max_n_classes), dtype=ivy.float64 + ) + + def init_missing(self, n_missing): + w = 1.0 + + self.n_missing = n_missing + if n_missing == 0: + return + + self.sum_missing[:, :] = 0.0 + self.weighted_n_missing = 0.0 + + for p in range(self.end - n_missing, self.end): + i = self.sample_indices[p] + if self.sample_weight is not None: + w = self.sample_weight[i] + + for k in range(self.n_outputs): + c = int(self.y[i, k]) + self.sum_missing[k, c] += w + + self.weighted_n_missing += w + + def reset(self): + self.pos = self.start + _move_sums_classification( + self, + self.sum_left, + self.sum_right, + self.weighted_n_left, + self.weighted_n_right, + self.missing_go_to_left, + ) + return 0 + + def reverse_reset(self): + self.pos = self.end + _move_sums_classification( + self, + self.sum_right, + self.sum_left, + self.weighted_n_right, + self.weighted_n_left, + not self.missing_go_to_left, + ) + return 0 + + def update(self, new_pos): + pos = self.pos + + end_non_missing = self.end - self.n_missing + + sample_indices = self.sample_indices + sample_weight = self.sample_weight + + for k in range(self.n_outputs): + for c in range(self.max_n_classes): + self.sum_left[k, c] = 0.0 + self.sum_right[k, c] = 0.0 + + if (new_pos - pos) <= (end_non_missing - new_pos): + for p in range(pos, new_pos): + i = sample_indices[p] + + w = 1.0 + + if sample_weight is not None: + w = sample_weight[i] + + for k in range(self.n_outputs): + c = int(self.y[i, k]) + self.sum_left[k, c] += w + + self.weighted_n_left += w + + else: + self.reverse_reset() + + for p in range(end_non_missing - 1, new_pos - 1, -1): + i = sample_indices[p] + + w = 1.0 + + if sample_weight is not None: + w = sample_weight[i] + + for k in range(self.n_outputs): + c = int(self.y[i, k]) + self.sum_left[k, c] -= w + + self.weighted_n_left -= w + + self.weighted_n_right = self.weighted_n_node_samples - self.weighted_n_left + + for k in range(self.n_outputs): + for c in range(self.max_n_classes): + self.sum_right[k, c] = self.sum_total[k, c] - self.sum_left[k, c] + + self.pos = new_pos + return 0 + + def node_impurity(self): + pass + + def children_impurity(self, impurity_left, impurity_right): + pass + + def node_value(self, dest): + for k in range(self.n_outputs): + dest[:] = self.sum_total[k, :] + dest += self.max_n_classes + + +class RegressionCriterion(Criterion): + """ + Abstract regression criterion for regression problems. + + This handles cases where the target is a continuous value and is + evaluated by computing the variance of the target values left and + right of the split point. + """ + + def __init__(self, n_outputs, n_samples): + """ + Initialize parameters for this criterion. + + Parameters + ---------- + n_outputs : int + The number of targets to be predicted + + n_samples : int + The total number of samples to fit on + """ + self.start = 0 + self.pos = 0 + self.end = 0 + self.n_outputs = n_outputs + self.n_samples = n_samples + self.n_node_samples = 0 + self.weighted_n_node_samples = 0.0 + self.weighted_n_left = 0.0 + self.weighted_n_right = 0.0 + self.weighted_n_missing = 0.0 + self.sq_sum_total = 0.0 + self.sum_total = ivy.zeros(n_outputs, dtype=ivy.float64) + self.sum_left = ivy.zeros(n_outputs, dtype=ivy.float64) + self.sum_right = ivy.zeros(n_outputs, dtype=ivy.float64) + + def init(self, y, sample_weight, weighted_n_samples, sample_indices, start, end): + """ + Initialize the criterion. + + This initializes the criterion at node sample_indices[start:end] + and children sample_indices[start:start] and + sample_indices[start:end]. + """ + self.y = y + self.sample_weight = sample_weight + self.sample_indices = sample_indices + self.start = start + self.end = end + self.n_node_samples = end - start + self.weighted_n_samples = weighted_n_samples + self.weighted_n_node_samples = 0.0 + self.sq_sum_total = 0.0 + self.sum_total.fill(0.0) + + for p in range(start, end): + i = sample_indices[p] + w = sample_weight[i] if sample_weight is not None else 1.0 + for k in range(self.n_outputs): + y_ik = self.y[i, k] + w_y_ik = w * y_ik + self.sum_total[k] += w_y_ik + self.sq_sum_total += w_y_ik * y_ik + + self.weighted_n_node_samples += w + + self.reset() + return 0 + + def init_sum_missing(self): + """Initialize sum_missing to hold sums for missing values.""" + self.sum_missing = ivy.zeros(self.n_outputs, dtype=ivy.float64) + + def init_missing(self, n_missing): + """Initialize sum_missing if there are missing values.""" + self.n_missing = n_missing + if n_missing == 0: + return + + self.sum_missing.fill(0.0) + self.weighted_n_missing = 0.0 + + for p in range(self.end - n_missing, self.end): + i = self.sample_indices[p] + w = self.sample_weight[i] if self.sample_weight is not None else 1.0 + + for k in range(self.n_outputs): + y_ik = self.y[i, k] + w_y_ik = w * y_ik + self.sum_missing[k] += w_y_ik + + self.weighted_n_missing += w + + def reset(self): + """Reset the criterion at pos=start.""" + self.pos = self.start + self._move_sums_regression( + self.sum_left, + self.sum_right, + self.weighted_n_left, + self.weighted_n_right, + self.missing_go_to_left, + ) + return 0 + + def reverse_reset(self): + """Reset the criterion at pos=end.""" + self.pos = self.end + self._move_sums_regression( + self.sum_right, + self.sum_left, + self.weighted_n_right, + self.weighted_n_left, + not self.missing_go_to_left, + ) + return 0 + + def update(self, new_pos): + """Update statistics by moving sample_indices[pos:new_pos] to the left.""" + sample_weight = self.sample_weight + sample_indices = self.sample_indices + pos = self.pos + + end_non_missing = self.end - self.n_missing + + for k in range(self.n_outputs): + self.sum_left[k] = 0.0 + + w = 1.0 + + if (new_pos - pos) <= (end_non_missing - new_pos): + for p in range(pos, new_pos): + i = sample_indices[p] + w = sample_weight[i] if sample_weight is not None else 1.0 + for k in range(self.n_outputs): + self.sum_left[k] += w * self.y[i, k] + self.weighted_n_left += w + else: + self.reverse_reset() + for p in range(end_non_missing - 1, new_pos - 1, -1): + i = sample_indices[p] + w = sample_weight[i] if sample_weight is not None else 1.0 + for k in range(self.n_outputs): + self.sum_left[k] -= w * self.y[i, k] + self.weighted_n_left -= w + + self.weighted_n_right = self.weighted_n_node_samples - self.weighted_n_left + + for k in range(self.n_outputs): + self.sum_right[k] = self.sum_total[k] - self.sum_left[k] + + self.pos = new_pos + return 0 + + def node_impurity(self): + pass + + def children_impurity(self): + pass + + def node_value(self, dest): + for k in range(self.n_outputs): + dest[k] = self.sum_total[k] / self.weighted_n_node_samples + + def _move_sums_regression( + self, sum_1, sum_2, weighted_n_1, weighted_n_2, put_missing_in_1 + ): + """ + Distribute sum_total and sum_missing into sum_1 and sum_2. + + If there are missing values and: + - put_missing_in_1 is True, then missing values go to sum_1. + - put_missing_in_1 is False, then missing values go to sum_2. + """ + has_missing = self.n_missing != 0 + + if has_missing and put_missing_in_1: + for k in range(self.n_outputs): + sum_1[k] = self.sum_missing[k] + + weighted_n_1[0] = self.weighted_n_missing + weighted_n_2[0] = self.weighted_n_node_samples - self.weighted_n_missing + else: + for k in range(self.n_outputs): + sum_1[k] = 0.0 + + weighted_n_1[0] = 0.0 + weighted_n_2[0] = self.weighted_n_node_samples + + for k in range(self.n_outputs): + sum_2[k] = self.sum_total[k] - sum_1[k] + + +class Gini(ClassificationCriterion): + def node_impurity(self): + gini = 0.0 + for k in range(self.n_outputs): + sq_count = 0.0 + for c in range(self.n_classes[k]): + count_k = self.sum_total[k, c] + sq_count += count_k * count_k + gini += 1.0 - sq_count / ( + self.weighted_n_node_samples * self.weighted_n_node_samples + ) + return gini / self.n_outputs + + def children_impurity(self): + gini_left = 0.0 + gini_right = 0.0 + for k in range(self.n_outputs): + sq_count_left = 0.0 + sq_count_right = 0.0 + for c in range(self.n_classes[k]): + count_k_left = self.sum_left[k, c] + sq_count_left += count_k_left * count_k_left + count_k_right = self.sum_right[k, c] + sq_count_right += count_k_right * count_k_right + gini_left += 1.0 - sq_count_left / ( + self.weighted_n_left * self.weighted_n_left + ) + gini_right += 1.0 - sq_count_right / ( + self.weighted_n_right * self.weighted_n_right + ) + return gini_left / self.n_outputs, gini_right / self.n_outputs + + +class Entropy(ClassificationCriterion): + """ + Cross Entropy impurity criterion for classification. + + This handles cases where the target is a classification taking values + 0, 1, ... K-2, K-1. If node m represents a region Rm with Nm observations, + then let + + count_k = 1 / Nm * sum_{x_i in Rm} I(yi = k) + + be the proportion of class k observations in node m. + + The cross-entropy is then defined as + + cross-entropy = -sum_{k=0}^{K-1} count_k * log(count_k) + """ + + def node_impurity(self): + """ + Evaluate the impurity of the current node. + + Evaluate the cross-entropy criterion as impurity of the current + node, i.e., the impurity of sample_indices[start:end]. The + smaller the impurity the better. + """ + entropy = 0.0 + + for k in range(self.n_outputs): + for c in range(self.n_classes[k]): + count_k = self.sum_total[k, c] + if count_k > 0.0: + count_k /= self.weighted_n_node_samples + entropy -= count_k * ivy.log(count_k) + + return entropy / self.n_outputs + + def children_impurity(self): + """ + Evaluate the impurity in children nodes. + + i.e., the impurity of the left child (sample_indices[start:pos]) and the + impurity of the right child (sample_indices[pos:end]). + + Returns + ------- + impurity_left : float + The impurity of the left child + impurity_right : float + The impurity of the right child + """ + entropy_left = 0.0 + entropy_right = 0.0 + + for k in range(self.n_outputs): + for c in range(self.n_classes[k]): + count_k_left = self.sum_left[k, c] + count_k_right = self.sum_right[k, c] + + if count_k_left > 0.0: + count_k_left /= self.weighted_n_left + entropy_left -= count_k_left * ivy.log(count_k_left) + + if count_k_right > 0.0: + count_k_right /= self.weighted_n_right + entropy_right -= count_k_right * ivy.log(count_k_right) + + return entropy_left / self.n_outputs, entropy_right / self.n_outputs + + +class MSE(RegressionCriterion): + """ + Mean squared error impurity criterion. + + MSE = var_left + var_right + """ + + def node_impurity(self): + impurity = self.sq_sum_total / self.weighted_n_node_samples + for k in range(self.n_outputs): + impurity -= (self.sum_total[k] / self.weighted_n_node_samples) ** 2.0 + return impurity / self.n_outputs + + def proxy_impurity_improvement(self): + proxy_impurity_left = 0.0 + proxy_impurity_right = 0.0 + for k in range(self.n_outputs): + proxy_impurity_left += self.sum_left[k] * self.sum_left[k] + proxy_impurity_right += self.sum_right[k] * self.sum_right[k] + return ( + proxy_impurity_left / self.weighted_n_left + + proxy_impurity_right / self.weighted_n_right + ) + + def children_impurity(self): + sample_weight = self.sample_weight + sample_indices = self.sample_indices + pos = self.pos + start = self.start + + sq_sum_left = 0.0 + sq_sum_right = self.sq_sum_total + + for p in range(start, pos): + i = sample_indices[p] + + if sample_weight is not None: + w = sample_weight[i] + else: + w = 1.0 + + for k in range(self.n_outputs): + sq_sum_left += w * self.y[i, k] * self.y[i, k] + + sq_sum_right = self.sq_sum_total - sq_sum_left + + impurity_left = sq_sum_left / self.weighted_n_left + impurity_right = sq_sum_right / self.weighted_n_right + + for k in range(self.n_outputs): + impurity_left -= (self.sum_left[k] / self.weighted_n_left) ** 2.0 + impurity_right -= (self.sum_right[k] / self.weighted_n_right) ** 2.0 + + impurity_left /= self.n_outputs + impurity_right /= self.n_outputs + + return impurity_left, impurity_right + + def node_value(self, dest): + for k in range(self.n_outputs): + dest[k] = self.sum_total[k] / self.weighted_n_node_samples + + +class MAE(RegressionCriterion): + """ + Mean absolute error impurity criterion. + + MAE = (1 / n) * (sum_i |y_i - f_i|), where y_i is the true + value and f_i is the predicted value. + """ + + def __init__(self, n_outputs, n_samples): + super().__init__(n_outputs, n_samples) + self.left_child = ivy.empty(n_outputs, dtype=object) + self.right_child = ivy.empty(n_outputs, dtype=object) + for k in range(n_outputs): + self.left_child[k] = WeightedMedianCalculator(n_samples) + self.right_child[k] = WeightedMedianCalculator(n_samples) + + def init(self, y, sample_weight, weighted_n_samples, sample_indices, start, end): + super().init(y, sample_weight, weighted_n_samples, sample_indices, start, end) + + left_child = self.left_child + right_child = self.right_child + + for k in range(self.n_outputs): + left_child[k].reset() + right_child[k].reset() + + for p in range(start, end): + i = sample_indices[p] + w = sample_weight[i] if sample_weight is not None else 1.0 + for k in range(self.n_outputs): + right_child[k].push(self.y[i, k], w) + + for k in range(self.n_outputs): + self.node_medians[k] = right_child[k].get_median() + + def init_missing(self, n_missing): + if n_missing != 0: + raise ValueError("Missing values are not supported for MAE.") + + def reset(self): + super().reset() + left_child = self.left_child + right_child = self.right_child + + self.weighted_n_left = 0.0 + self.weighted_n_right = self.weighted_n_node_samples + + for k in range(self.n_outputs): + for i in range(left_child[k].size()): + value, weight = left_child[k].pop() + right_child[k].push(value, weight) + + def reverse_reset(self): + super().reverse_reset() + left_child = self.left_child + right_child = self.right_child + + self.weighted_n_right = 0.0 + self.weighted_n_left = self.weighted_n_node_samples + + for k in range(self.n_outputs): + for i in range(right_child[k].size()): + value, weight = right_child[k].pop() + left_child[k].push(value, weight) + + def update(self, new_pos): + super().update(new_pos) + sample_weight = self.sample_weight + sample_indices = self.sample_indices + + left_child = self.left_child + right_child = self.right_child + + pos = self.pos + end = self.end + + for k in range(self.n_outputs): + for p in range(pos, end): + i = sample_indices[p] + w = sample_weight[i] if sample_weight is not None else 1.0 + left_child[k].remove(self.y[i, k], w) + right_child[k].push(self.y[i, k], w) + + self.weighted_n_right = self.weighted_n_node_samples - self.weighted_n_left + + def node_value(self, dest): + for k in range(self.n_outputs): + dest[k] = self.node_medians[k] + + def node_impurity(self): + sample_weight = self.sample_weight + sample_indices = self.sample_indices + impurity = 0.0 + + for k in range(self.n_outputs): + for p in range(self.start, self.end): + i = sample_indices[p] + + if sample_weight is not None: + w = sample_weight[i] + else: + w = 1.0 + + impurity += abs(self.y[i, k] - self.node_medians[k]) * w + + return impurity / (self.weighted_n_node_samples * self.n_outputs) + + def children_impurity(self): + sample_weight = self.sample_weight + sample_indices = self.sample_indices + + start = self.start + pos = self.pos + end = self.end + + impurity_left = 0.0 + impurity_right = 0.0 + + for k in range(self.n_outputs): + for p in range(start, pos): + i = sample_indices[p] + + if sample_weight is not None: + w = sample_weight[i] + else: + w = 1.0 + + impurity_left += abs(self.y[i, k] - self.node_medians[k]) * w + + impurity_left /= self.weighted_n_left * self.n_outputs + + for p in range(pos, end): + i = sample_indices[p] + + if sample_weight is not None: + w = sample_weight[i] + else: + w = 1.0 + + impurity_right += abs(self.y[i, k] - self.node_medians[k]) * w + + impurity_right /= self.weighted_n_right * self.n_outputs + + return impurity_left, impurity_right + + +# --- Helpers --- # +# --------------- # + + +def _move_sums_classification( + criterion, sum_1, sum_2, weighted_n_1, weighted_n_2, put_missing_in_1 +): + """ + Distribute sum_total and sum_missing into sum_1 and sum_2. + + If there are missing values and: + - put_missing_in_1 is True, then missing values go to sum_1. Specifically: + sum_1 = sum_missing + sum_2 = sum_total - sum_missing + + - put_missing_in_1 is False, then missing values go to sum_2. Specifically: + sum_1 = 0 + sum_2 = sum_total + """ + for k in range(criterion.n_outputs): + if criterion.n_missing != 0 and put_missing_in_1: + sum_1[k, :] = criterion.sum_missing[k, :] + sum_2[k, :] = criterion.sum_total[k, :] - criterion.sum_missing[k, :] + weighted_n_1[0] = criterion.weighted_n_missing + weighted_n_2[0] = ( + criterion.weighted_n_node_samples - criterion.weighted_n_missing + ) + else: + sum_1[k, :] = 0.0 # Set all elements in sum_1 to 0 + sum_2[k, :] = criterion.sum_total[k, :] + weighted_n_1[0] = 0.0 + weighted_n_2[0] = criterion.weighted_n_node_samples + + +def _move_sums_regression( + criterion, sum_1, sum_2, weighted_n_1, weighted_n_2, put_missing_in_1 +): + """ + Distribute sum_total and sum_missing into sum_1 and sum_2. + + If there are missing values and: + - put_missing_in_1 is True, then missing values go to sum_1. Specifically: + sum_1 = sum_missing + sum_2 = sum_total - sum_missing + + - put_missing_in_1 is False, then missing values go to sum_2. Specifically: + sum_1 = 0 + sum_2 = sum_total + """ + if criterion.n_missing != 0 and put_missing_in_1: + sum_1[:] = criterion.sum_missing[:] + sum_2[:] = criterion.sum_total[:] - criterion.sum_missing[:] + weighted_n_1[0] = criterion.weighted_n_missing + weighted_n_2[0] = ( + criterion.weighted_n_node_samples - criterion.weighted_n_missing + ) + else: + sum_1[:] = 0.0 # Set all elements in sum_1 to 0 + sum_2[:] = criterion.sum_total[:] + weighted_n_1[0] = 0.0 + weighted_n_2[0] = criterion.weighted_n_node_samples diff --git a/ivy/functional/frontends/sklearn/tree/_tree.py b/ivy/functional/frontends/sklearn/tree/_tree.py index 4d532ebb66125..758381dc5e10a 100644 --- a/ivy/functional/frontends/sklearn/tree/_tree.py +++ b/ivy/functional/frontends/sklearn/tree/_tree.py @@ -1,58 +1,14 @@ import ivy - - - - - - - - - - - - - - - - - - - - - - - - - import numpy as np - from scipy.sparse import issparse from scipy.sparse import csr_matrix from scipy.sparse import isspmatrix_csr - - - - - - - - - - - - - - - - - - # ============================================================================= # Types and constants # ============================================================================= @@ -60,21 +16,24 @@ from numpy import float32 as DTYPE from numpy import float64 as DOUBLE - +EPSILON = np.finfo(np.double).eps # Define constants INFINITY = np.inf -EPSILON = np.finfo(np.double).eps - # Some handy constants (BestFirstTreeBuilder) IS_FIRST = 1 -IS_NOT_FIRST = 0 IS_LEFT = 1 +IS_NOT_FIRST = 0 IS_NOT_LEFT = 0 - TREE_LEAF = -1 TREE_UNDEFINED = -2 -_TREE_LEAF = TREE_LEAF -_TREE_UNDEFINED = TREE_UNDEFINED + + +class SplitRecord: + def __init__(): + raise NotImplementedError( + "No idea what SplitRecord is, just created to remove error!" + ) + # Since you're dealing with Cython-specific types and features, # it's important to provide a dummy definition for Node. @@ -89,15 +48,15 @@ def __init__(self): self.weighted_n_node_samples = None self.missing_go_to_left = None -dummy = Node() -# Create a numpy dtype for Node using the dummy object -NODE_DTYPE = np.asarray([dummy], dtype=object).dtype + # ============================================================================= # TreeBuilder # ============================================================================= + class TreeBuilder: """Interface for different tree building strategies.""" + def __init__(self): self.splitter = None self.min_samples_split = None @@ -105,7 +64,7 @@ def __init__(self): self.min_weight_leaf = None self.max_depth = None self.min_impurity_decrease = None - + def build( self, tree, @@ -123,10 +82,12 @@ def _check_input( y, sample_weight, ): - """Check input dtype, layout, and format""" + """Check input dtype, layout, and format.""" if issparse(X): - X = X.tocsc() #tocsc() is a method provided by the scipy.sparse module in the SciPy library. It's used to convert a sparse matrix to the Compressed Sparse Column (CSC) format. - X.sort_indices() #This is done to ensure that the indices of non-zero elements within the matrix are sorted in ascending order. + X = ( + X.tocsc() + ) # tocsc() is a method provided by the scipy.sparse module in the SciPy library. It's used to convert a sparse matrix to the Compressed Sparse Column (CSC) format. + X.sort_indices() # This is done to ensure that the indices of non-zero elements within the matrix are sorted in ascending order. if X.data.dtype != DTYPE: X.data = np.ascontiguousarray(X.data, dtype=DTYPE) @@ -141,24 +102,21 @@ def _check_input( if y.base.dtype != DTYPE or not y.base.flags.contiguous: y = np.ascontiguousarray(y, dtype=DTYPE) - if ( - sample_weight is not None and - ( - sample_weight.base.dtype != DOUBLE or - not sample_weight.base.flags.contiguous - ) + if sample_weight is not None and ( + sample_weight.base.dtype != DOUBLE + or not sample_weight.base.flags.contiguous ): sample_weight = np.asarray(sample_weight, dtype=DOUBLE, order="C") return X, y, sample_weight - - # Depth first builder --------------------------------------------------------- # A record on the stack for depth-first tree growing class StackRecord: - def __init__(self, start, end, depth, parent, is_left, impurity, n_constant_features): + def __init__( + self, start, end, depth, parent, is_left, impurity, n_constant_features + ): self.start = start self.end = end self.depth = depth @@ -168,150 +126,6 @@ def __init__(self, start, end, depth, parent, is_left, impurity, n_constant_feat self.n_constant_features = n_constant_features - - - - - - -class DepthFirstTreeBuilder(TreeBuilder): - """Build a decision tree in depth-first fashion.""" - - def __init__( - self, splitter, min_samples_split, - min_samples_leaf, min_weight_leaf, - max_depth, min_impurity_decrease - ): - self.splitter = splitter - self.min_samples_split = min_samples_split - self.min_samples_leaf = min_samples_leaf - self.min_weight_leaf = min_weight_leaf - self.max_depth = max_depth - self.min_impurity_decrease = min_impurity_decrease - - def build( - self, - tree, - X, - y, - sample_weight=None, - missing_values_in_feature_mask=None - ): - """Build a decision tree from the training set (X, y).""" - - # Check input - X, y, sample_weight = self._check_input(X, y, sample_weight) - - # Initial capacity - init_capacity = (2 ** (tree.max_depth + 1)) - 1 if tree.max_depth <= 10 else 2047 - - tree._resize(init_capacity) - - # Parameters - splitter = self.splitter - max_depth = self.max_depth - min_samples_leaf = self.min_samples_leaf - min_weight_leaf = self.min_weight_leaf - min_samples_split = self.min_samples_split - min_impurity_decrease = self.min_impurity_decrease - - # Recursive partition (without actual recursion) - splitter.init(X, y, sample_weight, missing_values_in_feature_mask) - - stack = [] - - # Push root node onto stack - stack.append( - StackRecord( - start=0, - end=splitter.n_samples, - depth=0, - parent=_TREE_UNDEFINED, - is_left=False, - impurity=INFINITY, - n_constant_features=0 - ) - ) - weighted_n_node_samples = np.zeros(1, dtype=np.double) - while stack: - stack_record = stack.pop() - - start = stack_record.start - end = stack_record.end - depth = stack_record.depth - parent = stack_record.parent - is_left = stack_record.is_left - impurity = stack_record.impurity - n_constant_features = stack_record.n_constant_features - - n_node_samples = end - start - splitter.node_reset(start, end, weighted_n_node_samples) - - is_leaf = ( - depth >= max_depth - or n_node_samples < min_samples_split - or n_node_samples < 2 * min_samples_leaf - or np.sum(sample_weight[start:end]) < 2 * min_weight_leaf - ) - - if is_left: - impurity = splitter.node_impurity() - - is_leaf = is_leaf or impurity <= EPSILON - - if not is_leaf: - split = SplitRecord() # No idea what is SplitRecord in original code. Maybe this never gets called, not sure - splitter.node_split(impurity, split, n_constant_features) - is_leaf = ( - is_leaf - or split.pos >= end - or (split.improvement + EPSILON < min_impurity_decrease) - ) - - node_id = tree._add_node( - parent, - is_left, - is_leaf, - split.feature if not is_leaf else 0, - split.threshold if not is_leaf else 0, - impurity, - n_node_samples, - np.sum(sample_weight[start:end]), - split.missing_go_to_left, - ) - - if node_id == np.iinfo(np.intp).max: - raise MemoryError() - - splitter.node_value(tree.value + node_id * tree.value_stride) - - if not is_leaf: - # Push right child on stack - stack.append( - StackRecord( - start=split.pos, - end=end, - depth=depth + 1, - parent=node_id, - is_left=False, - impurity=split.impurity_right, - n_constant_features=n_constant_features, - ) - ) - # Push left child on stack - stack.append( - StackRecord( - start=start, - end=split.pos, - depth=depth + 1, - parent=node_id, - is_left=True, - impurity=split.impurity_left, - n_constant_features=n_constant_features, - ) - ) - - class Tree: def __init__(self, n_features, n_classes, n_outputs): """Constructor.""" @@ -322,7 +136,7 @@ def __init__(self, n_features, n_classes, n_outputs): self.max_depth = None self.node_count = None self.capacity = None - self.nodes = [] #replaced it with array since this array will contain nodes + self.nodes = [] # replaced it with array since this array will contain nodes self.value = None self.value_stride = None @@ -376,14 +190,16 @@ def __setstate__(self, d): def _resize(self, capacity): """ - Resize all inner arrays to `capacity`. If `capacity` is -1, then double the size of the inner arrays. + Resize all inner arrays to `capacity`. + + If `capacity` is -1, then double the size of the inner arrays. Returns -1 in case of failure to allocate memory (and raise MemoryError), or 0 otherwise. """ if self._resize_c(capacity) != 0: # Raise MemoryError if resizing fails raise MemoryError() - def _resize_c(self, capacity=float('inf')): + def _resize_c(self, capacity=float("inf")): # """ # Guts of _resize # Returns -1 in case of failure to allocate memory (and raise MemoryError), @@ -416,9 +232,18 @@ def _resize_c(self, capacity=float('inf')): # return 0 raise NotImplementedError - - def _add_node(self, parent, is_left, is_leaf, feature, threshold, impurity, - n_node_samples, weighted_n_node_samples, missing_go_to_left): + def _add_node( + self, + parent, + is_left, + is_leaf, + feature, + threshold, + impurity, + n_node_samples, + weighted_n_node_samples, + missing_go_to_left, + ): """ Add a node to the tree. @@ -428,12 +253,14 @@ def _add_node(self, parent, is_left, is_leaf, feature, threshold, impurity, """ node_id = self.node_count - #no need to resize since python reallocates lists dynamically + # no need to resize since python reallocates lists dynamically # if node_id >= self.capacity: # if self._resize_c() != 0: # return -1 #throw error if resize not possible - node = Node() #self.nodes contains a list of nodes, it returns the node at node_id location + node = ( + Node() + ) # self.nodes contains a list of nodes, it returns the node at node_id location self.nodes.append(node) node.impurity = impurity node.n_node_samples = n_node_samples @@ -467,19 +294,21 @@ def predict(self, X): # Get the internal data as a NumPy array internal_data = self._get_value_ndarray() # Use the predictions to index the internal data - out = internal_data[predictions] #not sure if this accurately translates to .take(self.apply(X), axis=0, mode='clip') + out = internal_data[ + predictions + ] # not sure if this accurately translates to .take(self.apply(X), axis=0, mode='clip') # Reshape the output if the model is single-output if self.n_outputs == 1: out = out.reshape(X.shape[0], self.max_n_classes) return out - + def apply(self, X): """Finds the terminal region (=leaf node) for each sample in X.""" if issparse(X): return self._apply_sparse_csr(X) else: return self._apply_dense(X) - + def _apply_dense(self, X): if not isinstance(X, ivy.data_classes.array.array.Array): raise ValueError("X should be a torch.Tensor, got %s" % type(X)) @@ -510,7 +339,7 @@ def _apply_dense(self, X): out[i] = node - self.nodes return out - + def _apply_sparse_csr(self, X): """Finds the terminal region (=leaf node) for each sample in sparse X.""" if not isinstance(X, csr_matrix): @@ -549,7 +378,7 @@ def _apply_sparse_csr(self, X): out[i] = node - self.nodes # node offset return out - + def decision_path(self, X): """Finds the decision path (=node) for each sample in X.""" if issparse(X): @@ -598,12 +427,12 @@ def _decision_path_dense(self, X): indices[indptr[i + 1]] = node - self.nodes indptr[i + 1] += 1 - indices = indices[:indptr[n_samples]] + indices = indices[: indptr[n_samples]] data = np.ones(shape=len(indices), dtype=np.intp) out = csr_matrix((data, indices, indptr), shape=(n_samples, self.node_count)) return out - + def _decision_path_sparse_csr(self, X): """Finds the decision path (=node) for each sample in X.""" @@ -659,14 +488,15 @@ def _decision_path_sparse_csr(self, X): indices[indptr[i + 1]] = node - self.nodes indptr[i + 1] += 1 - indices = indices[:indptr[n_samples]] + indices = indices[: indptr[n_samples]] data = np.ones(shape=len(indices), dtype=np.intp) out = csr_matrix((data, indices, indptr), shape=(n_samples, self.node_count)) return out def compute_node_depths(self): - """Compute the depth of each node in a tree. + """ + Compute the depth of each node in a tree. .. versionadded:: 1.3 @@ -703,9 +533,10 @@ def compute_feature_importances(self, normalize=True): right = nodes[node.right_child] importances[node.feature] += ( - node.weighted_n_node_samples * node.impurity - - left.weighted_n_node_samples * left.impurity - - right.weighted_n_node_samples * right.impurity) + node.weighted_n_node_samples * node.impurity + - left.weighted_n_node_samples * left.impurity + - right.weighted_n_node_samples * right.impurity + ) node += 1 for i in range(self.n_features): @@ -719,40 +550,44 @@ def compute_feature_importances(self, normalize=True): importances /= normalizer return importances - + def _get_value_ndarray(self): - """Wraps value as a 3-d NumPy array. + """ + Wraps value as a 3-d NumPy array. - The array keeps a reference to this Tree, which manages the underlying - memory. + The array keeps a reference to this Tree, which manages the + underlying memory. """ shape = (self.node_count, self.n_outputs, self.max_n_classes) arr = np.ndarray(shape, dtype=np.float64, buffer=self.value) arr.base = self return arr - + def _get_node_ndarray(self): - """Wraps nodes as a NumPy struct array. + """ + Wraps nodes as a NumPy struct array. - The array keeps a reference to this Tree, which manages the underlying - memory. Individual fields are publicly accessible as properties of the - Tree. + The array keeps a reference to this Tree, which manages the + underlying memory. Individual fields are publicly accessible as + properties of the Tree. """ shape = (self.node_count,) - dtype = np.dtype([ - ('left_child', np.intp), - ('right_child', np.intp), - ('feature', np.intp), - ('threshold', np.float64), - ('impurity', np.float64), - ('n_node_samples', np.intp), - ('weighted_n_node_samples', np.float64), - ('missing_go_to_left', np.uint8) - ]) + dtype = np.dtype( + [ + ("left_child", np.intp), + ("right_child", np.intp), + ("feature", np.intp), + ("threshold", np.float64), + ("impurity", np.float64), + ("n_node_samples", np.intp), + ("weighted_n_node_samples", np.float64), + ("missing_go_to_left", np.uint8), + ] + ) arr = np.ndarray(shape, dtype=dtype, buffer=self.nodes) arr.base = self return arr - + def compute_partial_dependence(self, X, target_features, out): out.fill(0.0) # Initialize the output array @@ -771,12 +606,20 @@ def compute_partial_dependence(self, X, target_features, out): if current_node.left_child == _TREE_LEAF: # Leaf node - out[sample_idx] += weight_stack[stack_size] * self.value[current_node_idx] + out[sample_idx] += ( + weight_stack[stack_size] * self.value[current_node_idx] + ) total_weight += weight_stack[stack_size] else: - is_target_feature = any(target_feature == current_node.feature for target_feature in target_features) + is_target_feature = any( + target_feature == current_node.feature + for target_feature in target_features + ) if is_target_feature: - if X[sample_idx, current_node.feature] <= current_node.threshold: + if ( + X[sample_idx, current_node.feature] + <= current_node.threshold + ): node_idx_stack.append(current_node.left_child) weight_stack.append(weight_stack[stack_size]) stack_size += 1 @@ -785,20 +628,182 @@ def compute_partial_dependence(self, X, target_features, out): weight_stack.append(weight_stack[stack_size]) stack_size += 1 else: - left_sample_frac = self.nodes[current_node.left_child].weighted_n_node_samples / current_node.weighted_n_node_samples + left_sample_frac = ( + self.nodes[current_node.left_child].weighted_n_node_samples + / current_node.weighted_n_node_samples + ) current_weight = weight_stack[stack_size] - node_idx_stack.extend([current_node.left_child, current_node.right_child]) - weight_stack.extend([current_weight * left_sample_frac, current_weight * (1 - left_sample_frac)]) + node_idx_stack.extend( + [current_node.left_child, current_node.right_child] + ) + weight_stack.extend( + [ + current_weight * left_sample_frac, + current_weight * (1 - left_sample_frac), + ] + ) stack_size += 2 if not (0.999 < total_weight < 1.001): - raise ValueError(f"Total weight should be 1.0 but was {total_weight:.9f}") + raise ValueError( + f"Total weight should be 1.0 but was {total_weight:.9f}" + ) + + +class BestFirstTreeBuilder: + def __init__(): + raise NotImplementedError + + +class DepthFirstTreeBuilder(TreeBuilder): + """Build a decision tree in depth-first fashion.""" + + def __init__( + self, + splitter, + min_samples_split, + min_samples_leaf, + min_weight_leaf, + max_depth, + min_impurity_decrease, + ): + self.splitter = splitter + self.min_samples_split = min_samples_split + self.min_samples_leaf = min_samples_leaf + self.min_weight_leaf = min_weight_leaf + self.max_depth = max_depth + self.min_impurity_decrease = min_impurity_decrease + + def build( + self, tree, X, y, sample_weight=None, missing_values_in_feature_mask=None + ): + """Build a decision tree from the training set (X, y).""" + + # Check input + X, y, sample_weight = self._check_input(X, y, sample_weight) + + # Initial capacity + init_capacity = ( + (2 ** (tree.max_depth + 1)) - 1 if tree.max_depth <= 10 else 2047 + ) + + tree._resize(init_capacity) + + # Parameters + splitter = self.splitter + max_depth = self.max_depth + min_samples_leaf = self.min_samples_leaf + min_weight_leaf = self.min_weight_leaf + min_samples_split = self.min_samples_split + min_impurity_decrease = self.min_impurity_decrease + + # Recursive partition (without actual recursion) + splitter.init(X, y, sample_weight, missing_values_in_feature_mask) + + stack = [] + + # Push root node onto stack + stack.append( + StackRecord( + start=0, + end=splitter.n_samples, + depth=0, + parent=_TREE_UNDEFINED, + is_left=False, + impurity=INFINITY, + n_constant_features=0, + ) + ) + weighted_n_node_samples = np.zeros(1, dtype=np.double) + while stack: + stack_record = stack.pop() + + start = stack_record.start + end = stack_record.end + depth = stack_record.depth + parent = stack_record.parent + is_left = stack_record.is_left + impurity = stack_record.impurity + n_constant_features = stack_record.n_constant_features + + n_node_samples = end - start + splitter.node_reset(start, end, weighted_n_node_samples) + + is_leaf = ( + depth >= max_depth + or n_node_samples < min_samples_split + or n_node_samples < 2 * min_samples_leaf + or np.sum(sample_weight[start:end]) < 2 * min_weight_leaf + ) + + if is_left: + impurity = splitter.node_impurity() + + is_leaf = is_leaf or impurity <= EPSILON + + if not is_leaf: + split = ( + SplitRecord() + ) # No idea what is SplitRecord in original code. Maybe this never gets called, not sure + splitter.node_split(impurity, split, n_constant_features) + is_leaf = ( + is_leaf + or split.pos >= end + or (split.improvement + EPSILON < min_impurity_decrease) + ) + + node_id = tree._add_node( + parent, + is_left, + is_leaf, + split.feature if not is_leaf else 0, + split.threshold if not is_leaf else 0, + impurity, + n_node_samples, + np.sum(sample_weight[start:end]), + split.missing_go_to_left, + ) + + if node_id == np.iinfo(np.intp).max: + raise MemoryError() + + splitter.node_value(tree.value + node_id * tree.value_stride) + + if not is_leaf: + # Push right child on stack + stack.append( + StackRecord( + start=split.pos, + end=end, + depth=depth + 1, + parent=node_id, + is_left=False, + impurity=split.impurity_right, + n_constant_features=n_constant_features, + ) + ) + # Push left child on stack + stack.append( + StackRecord( + start=start, + end=split.pos, + depth=depth + 1, + parent=node_id, + is_left=True, + impurity=split.impurity_left, + n_constant_features=n_constant_features, + ) + ) + + +# --- Helpers --- # +# --------------- # def _check_n_classes(n_classes, expected_dtype): if n_classes.ndim != 1: raise ValueError( - f"Wrong dimensions for n_classes from the pickle: " + "Wrong dimensions for n_classes from the pickle: " f"expected 1, got {n_classes.ndim}" ) @@ -816,7 +821,8 @@ def _check_n_classes(n_classes, expected_dtype): ) - - - - +dummy = Node() +# Create a numpy dtype for Node using the dummy object +NODE_DTYPE = np.asarray([dummy], dtype=object).dtype +_TREE_LEAF = TREE_LEAF +_TREE_UNDEFINED = TREE_UNDEFINED diff --git a/ivy/functional/frontends/sklearn/tree/_utils.py b/ivy/functional/frontends/sklearn/tree/_utils.py new file mode 100644 index 0000000000000..b5cfaa5f46d47 --- /dev/null +++ b/ivy/functional/frontends/sklearn/tree/_utils.py @@ -0,0 +1,84 @@ +class WeightedMedianCalculator: + def __init__(self, initial_capacity): + self.initial_capacity = initial_capacity + self.samples = [] + self.total_weight = 0 + self.k = 0 + self.sum_w_0_k = 0 + + def size(self): + return len(self.samples) + + def reset(self): + self.samples = [] + self.total_weight = 0 + self.k = 0 + self.sum_w_0_k = 0 + + def push(self, data, weight): + original_median = self.get_median() if self.size() != 0 else 0.0 + self.samples.append((data, weight)) + self.update_median_parameters_post_push(data, weight, original_median) + + def update_median_parameters_post_push(self, data, weight, original_median): + if self.size() == 1: + self.k = 1 + self.total_weight = weight + self.sum_w_0_k = self.total_weight + return + + self.total_weight += weight + + if data < original_median: + self.k += 1 + self.sum_w_0_k += weight + + while ( + self.k > 1 + and (self.sum_w_0_k - self.samples[self.k - 1][1]) + >= self.total_weight / 2.0 + ): + self.k -= 1 + self.sum_w_0_k -= self.samples[self.k][1] + + def remove(self, data, weight): + original_median = self.get_median() if self.size() != 0 else 0.0 + self.samples = [(d, w) for (d, w) in self.samples if d != data or w != weight] + self.update_median_parameters_post_remove(data, weight, original_median) + + def pop(self): + original_median = self.get_median() if self.size() != 0 else 0.0 + if self.size() == 0: + return None, None + data, weight = self.samples.pop(0) + self.update_median_parameters_post_remove(data, weight, original_median) + return data, weight + + def update_median_parameters_post_remove(self, data, weight, original_median): + if not self.samples: + self.k = 0 + self.total_weight = 0 + self.sum_w_0_k = 0 + return + + if self.size() == 1: + self.k = 1 + self.total_weight -= weight + self.sum_w_0_k = self.total_weight + return + + self.total_weight -= weight + + if data < original_median: + self.k -= 1 + self.sum_w_0_k -= weight + + while self.k < self.size() and self.sum_w_0_k < self.total_weight / 2.0: + self.k += 1 + self.sum_w_0_k += self.samples[self.k - 1][1] + + def get_median(self): + if self.sum_w_0_k == (self.total_weight / 2.0): + return (self.samples[self.k][0] + self.samples[self.k - 1][0]) / 2.0 + if self.sum_w_0_k > (self.total_weight / 2.0): + return self.samples[self.k - 1][0] diff --git a/ivy/functional/frontends/sklearn/tree/_utils.pyx b/ivy/functional/frontends/sklearn/tree/_utils.pyx new file mode 100644 index 0000000000000..e59fb28c28fd1 --- /dev/null +++ b/ivy/functional/frontends/sklearn/tree/_utils.pyx @@ -0,0 +1,468 @@ +# Authors: Gilles Louppe +# Peter Prettenhofer +# Arnaud Joly +# Jacob Schreiber +# Nelson Liu +# +# +# License: BSD 3 clause + +from libc.stdlib cimport free +from libc.stdlib cimport realloc +from libc.math cimport log as ln +from libc.math cimport isnan + +import numpy as np +cimport numpy as cnp +cnp.import_array() + +from ..utils._random cimport our_rand_r + +# ============================================================================= +# Helper functions +# ============================================================================= + +cdef realloc_ptr safe_realloc(realloc_ptr* p, size_t nelems) except * nogil: + # sizeof(realloc_ptr[0]) would be more like idiomatic C, but causes Cython + # 0.20.1 to crash. + cdef size_t nbytes = nelems * sizeof(p[0][0]) + if nbytes / sizeof(p[0][0]) != nelems: + # Overflow in the multiplication + with gil: + raise MemoryError("could not allocate (%d * %d) bytes" + % (nelems, sizeof(p[0][0]))) + cdef realloc_ptr tmp = realloc(p[0], nbytes) + if tmp == NULL: + with gil: + raise MemoryError("could not allocate %d bytes" % nbytes) + + p[0] = tmp + return tmp # for convenience + + +def _realloc_test(): + # Helper for tests. Tries to allocate (-1) / 2 * sizeof(size_t) + # bytes, which will always overflow. + cdef SIZE_t* p = NULL + safe_realloc(&p, (-1) / 2) + if p != NULL: + free(p) + assert False + + +cdef inline cnp.ndarray sizet_ptr_to_ndarray(SIZE_t* data, SIZE_t size): + """Return copied data as 1D numpy array of intp's.""" + cdef cnp.npy_intp shape[1] + shape[0] = size + return cnp.PyArray_SimpleNewFromData(1, shape, cnp.NPY_INTP, data).copy() + + +cdef inline SIZE_t rand_int(SIZE_t low, SIZE_t high, + UINT32_t* random_state) noexcept nogil: + """Generate a random integer in [low; end).""" + return low + our_rand_r(random_state) % (high - low) + + +cdef inline double rand_uniform(double low, double high, + UINT32_t* random_state) noexcept nogil: + """Generate a random double in [low; high).""" + return ((high - low) * our_rand_r(random_state) / + RAND_R_MAX) + low + + +cdef inline double log(double x) noexcept nogil: + return ln(x) / ln(2.0) + +# ============================================================================= +# WeightedPQueue data structure +# ============================================================================= + +cdef class WeightedPQueue: + """A priority queue class, always sorted in increasing order. + + Attributes + ---------- + capacity : SIZE_t + The capacity of the priority queue. + + array_ptr : SIZE_t + The water mark of the priority queue; the priority queue grows from + left to right in the array ``array_``. ``array_ptr`` is always + less than ``capacity``. + + array_ : WeightedPQueueRecord* + The array of priority queue records. The minimum element is on the + left at index 0, and the maximum element is on the right at index + ``array_ptr-1``. + """ + + def __cinit__(self, SIZE_t capacity): + self.capacity = capacity + self.array_ptr = 0 + safe_realloc(&self.array_, capacity) + + def __dealloc__(self): + free(self.array_) + + cdef int reset(self) except -1 nogil: + """Reset the WeightedPQueue to its state at construction + + Return -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + """ + self.array_ptr = 0 + # Since safe_realloc can raise MemoryError, use `except *` + safe_realloc(&self.array_, self.capacity) + return 0 + + cdef bint is_empty(self) noexcept nogil: + return self.array_ptr <= 0 + + cdef SIZE_t size(self) noexcept nogil: + return self.array_ptr + + cdef int push(self, DOUBLE_t data, DOUBLE_t weight) except -1 nogil: + """Push record on the array. + + Return -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + """ + cdef SIZE_t array_ptr = self.array_ptr + cdef WeightedPQueueRecord* array = NULL + cdef SIZE_t i + + # Resize if capacity not sufficient + if array_ptr >= self.capacity: + self.capacity *= 2 + # Since safe_realloc can raise MemoryError, use `except -1` + safe_realloc(&self.array_, self.capacity) + + # Put element as last element of array + array = self.array_ + array[array_ptr].data = data + array[array_ptr].weight = weight + + # bubble last element up according until it is sorted + # in ascending order + i = array_ptr + while(i != 0 and array[i].data < array[i-1].data): + array[i], array[i-1] = array[i-1], array[i] + i -= 1 + + # Increase element count + self.array_ptr = array_ptr + 1 + return 0 + + cdef int remove(self, DOUBLE_t data, DOUBLE_t weight) noexcept nogil: + """Remove a specific value/weight record from the array. + Returns 0 if successful, -1 if record not found.""" + cdef SIZE_t array_ptr = self.array_ptr + cdef WeightedPQueueRecord* array = self.array_ + cdef SIZE_t idx_to_remove = -1 + cdef SIZE_t i + + if array_ptr <= 0: + return -1 + + # find element to remove + for i in range(array_ptr): + if array[i].data == data and array[i].weight == weight: + idx_to_remove = i + break + + if idx_to_remove == -1: + return -1 + + # shift the elements after the removed element + # to the left. + for i in range(idx_to_remove, array_ptr-1): + array[i] = array[i+1] + + self.array_ptr = array_ptr - 1 + return 0 + + cdef int pop(self, DOUBLE_t* data, DOUBLE_t* weight) noexcept nogil: + """Remove the top (minimum) element from array. + Returns 0 if successful, -1 if nothing to remove.""" + cdef SIZE_t array_ptr = self.array_ptr + cdef WeightedPQueueRecord* array = self.array_ + cdef SIZE_t i + + if array_ptr <= 0: + return -1 + + data[0] = array[0].data + weight[0] = array[0].weight + + # shift the elements after the removed element + # to the left. + for i in range(0, array_ptr-1): + array[i] = array[i+1] + + self.array_ptr = array_ptr - 1 + return 0 + + cdef int peek(self, DOUBLE_t* data, DOUBLE_t* weight) noexcept nogil: + """Write the top element from array to a pointer. + Returns 0 if successful, -1 if nothing to write.""" + cdef WeightedPQueueRecord* array = self.array_ + if self.array_ptr <= 0: + return -1 + # Take first value + data[0] = array[0].data + weight[0] = array[0].weight + return 0 + + cdef DOUBLE_t get_weight_from_index(self, SIZE_t index) noexcept nogil: + """Given an index between [0,self.current_capacity], access + the appropriate heap and return the requested weight""" + cdef WeightedPQueueRecord* array = self.array_ + + # get weight at index + return array[index].weight + + cdef DOUBLE_t get_value_from_index(self, SIZE_t index) noexcept nogil: + """Given an index between [0,self.current_capacity], access + the appropriate heap and return the requested value""" + cdef WeightedPQueueRecord* array = self.array_ + + # get value at index + return array[index].data + +# ============================================================================= +# WeightedMedianCalculator data structure +# ============================================================================= + +cdef class WeightedMedianCalculator: + """A class to handle calculation of the weighted median from streams of + data. To do so, it maintains a parameter ``k`` such that the sum of the + weights in the range [0,k) is greater than or equal to half of the total + weight. By minimizing the value of ``k`` that fulfills this constraint, + calculating the median is done by either taking the value of the sample + at index ``k-1`` of ``samples`` (samples[k-1].data) or the average of + the samples at index ``k-1`` and ``k`` of ``samples`` + ((samples[k-1] + samples[k]) / 2). + + Attributes + ---------- + initial_capacity : SIZE_t + The initial capacity of the WeightedMedianCalculator. + + samples : WeightedPQueue + Holds the samples (consisting of values and their weights) used in the + weighted median calculation. + + total_weight : DOUBLE_t + The sum of the weights of items in ``samples``. Represents the total + weight of all samples used in the median calculation. + + k : SIZE_t + Index used to calculate the median. + + sum_w_0_k : DOUBLE_t + The sum of the weights from samples[0:k]. Used in the weighted + median calculation; minimizing the value of ``k`` such that + ``sum_w_0_k`` >= ``total_weight / 2`` provides a mechanism for + calculating the median in constant time. + + """ + + def __cinit__(self, SIZE_t initial_capacity): + self.initial_capacity = initial_capacity + self.samples = WeightedPQueue(initial_capacity) + self.total_weight = 0 + self.k = 0 + self.sum_w_0_k = 0 + + cdef SIZE_t size(self) noexcept nogil: + """Return the number of samples in the + WeightedMedianCalculator""" + return self.samples.size() + + cdef int reset(self) except -1 nogil: + """Reset the WeightedMedianCalculator to its state at construction + + Return -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + """ + # samples.reset (WeightedPQueue.reset) uses safe_realloc, hence + # except -1 + self.samples.reset() + self.total_weight = 0 + self.k = 0 + self.sum_w_0_k = 0 + return 0 + + cdef int push(self, DOUBLE_t data, DOUBLE_t weight) except -1 nogil: + """Push a value and its associated weight to the WeightedMedianCalculator + + Return -1 in case of failure to allocate memory (and raise MemoryError) + or 0 otherwise. + """ + cdef int return_value + cdef DOUBLE_t original_median = 0.0 + + if self.size() != 0: + original_median = self.get_median() + # samples.push (WeightedPQueue.push) uses safe_realloc, hence except -1 + return_value = self.samples.push(data, weight) + self.update_median_parameters_post_push(data, weight, + original_median) + return return_value + + cdef int update_median_parameters_post_push( + self, DOUBLE_t data, DOUBLE_t weight, + DOUBLE_t original_median) noexcept nogil: + """Update the parameters used in the median calculation, + namely `k` and `sum_w_0_k` after an insertion""" + + # trivial case of one element. + if self.size() == 1: + self.k = 1 + self.total_weight = weight + self.sum_w_0_k = self.total_weight + return 0 + + # get the original weighted median + self.total_weight += weight + + if data < original_median: + # inserting below the median, so increment k and + # then update self.sum_w_0_k accordingly by adding + # the weight that was added. + self.k += 1 + # update sum_w_0_k by adding the weight added + self.sum_w_0_k += weight + + # minimize k such that sum(W[0:k]) >= total_weight / 2 + # minimum value of k is 1 + while(self.k > 1 and ((self.sum_w_0_k - + self.samples.get_weight_from_index(self.k-1)) + >= self.total_weight / 2.0)): + self.k -= 1 + self.sum_w_0_k -= self.samples.get_weight_from_index(self.k) + return 0 + + if data >= original_median: + # inserting above or at the median + # minimize k such that sum(W[0:k]) >= total_weight / 2 + while(self.k < self.samples.size() and + (self.sum_w_0_k < self.total_weight / 2.0)): + self.k += 1 + self.sum_w_0_k += self.samples.get_weight_from_index(self.k-1) + return 0 + + cdef int remove(self, DOUBLE_t data, DOUBLE_t weight) noexcept nogil: + """Remove a value from the MedianHeap, removing it + from consideration in the median calculation + """ + cdef int return_value + cdef DOUBLE_t original_median = 0.0 + + if self.size() != 0: + original_median = self.get_median() + + return_value = self.samples.remove(data, weight) + self.update_median_parameters_post_remove(data, weight, + original_median) + return return_value + + cdef int pop(self, DOUBLE_t* data, DOUBLE_t* weight) noexcept nogil: + """Pop a value from the MedianHeap, starting from the + left and moving to the right. + """ + cdef int return_value + cdef double original_median = 0.0 + + if self.size() != 0: + original_median = self.get_median() + + # no elements to pop + if self.samples.size() == 0: + return -1 + + return_value = self.samples.pop(data, weight) + self.update_median_parameters_post_remove(data[0], + weight[0], + original_median) + return return_value + + cdef int update_median_parameters_post_remove( + self, DOUBLE_t data, DOUBLE_t weight, + double original_median) noexcept nogil: + """Update the parameters used in the median calculation, + namely `k` and `sum_w_0_k` after a removal""" + # reset parameters because it there are no elements + if self.samples.size() == 0: + self.k = 0 + self.total_weight = 0 + self.sum_w_0_k = 0 + return 0 + + # trivial case of one element. + if self.samples.size() == 1: + self.k = 1 + self.total_weight -= weight + self.sum_w_0_k = self.total_weight + return 0 + + # get the current weighted median + self.total_weight -= weight + + if data < original_median: + # removing below the median, so decrement k and + # then update self.sum_w_0_k accordingly by subtracting + # the removed weight + + self.k -= 1 + # update sum_w_0_k by removing the weight at index k + self.sum_w_0_k -= weight + + # minimize k such that sum(W[0:k]) >= total_weight / 2 + # by incrementing k and updating sum_w_0_k accordingly + # until the condition is met. + while(self.k < self.samples.size() and + (self.sum_w_0_k < self.total_weight / 2.0)): + self.k += 1 + self.sum_w_0_k += self.samples.get_weight_from_index(self.k-1) + return 0 + + if data >= original_median: + # removing above the median + # minimize k such that sum(W[0:k]) >= total_weight / 2 + while(self.k > 1 and ((self.sum_w_0_k - + self.samples.get_weight_from_index(self.k-1)) + >= self.total_weight / 2.0)): + self.k -= 1 + self.sum_w_0_k -= self.samples.get_weight_from_index(self.k) + return 0 + + cdef DOUBLE_t get_median(self) noexcept nogil: + """Write the median to a pointer, taking into account + sample weights.""" + if self.sum_w_0_k == (self.total_weight / 2.0): + # split median + return (self.samples.get_value_from_index(self.k) + + self.samples.get_value_from_index(self.k-1)) / 2.0 + if self.sum_w_0_k > (self.total_weight / 2.0): + # whole median + return self.samples.get_value_from_index(self.k-1) + + +def _any_isnan_axis0(const DTYPE_t[:, :] X): + """Same as np.any(np.isnan(X), axis=0)""" + cdef: + int i, j + int n_samples = X.shape[0] + int n_features = X.shape[1] + unsigned char[::1] isnan_out = np.zeros(X.shape[1], dtype=np.bool_) + + with nogil: + for i in range(n_samples): + for j in range(n_features): + if isnan_out[j]: + continue + if isnan(X[i, j]): + isnan_out[j] = True + break + return np.asarray(isnan_out) \ No newline at end of file diff --git a/ivy/functional/frontends/sklearn/tree/splitter.py b/ivy/functional/frontends/sklearn/tree/splitter.py index 311d90f02b2c8..ea089cf3328d2 100644 --- a/ivy/functional/frontends/sklearn/tree/splitter.py +++ b/ivy/functional/frontends/sklearn/tree/splitter.py @@ -1,31 +1,32 @@ -#splitter.init -#splitter.n_samples -#splitter.node_reset -#splitter.node_impurity -#splitter.node_split -#splitter.node_value +# splitter.init +# splitter.n_samples +# splitter.node_reset +# splitter.node_impurity +# splitter.node_split +# splitter.node_value import ivy +import random -def init_split(split_record, start_pos): - split_record.impurity_left = float('inf') - split_record.impurity_right = float('inf') - split_record.pos = start_pos - split_record.feature = 0 - split_record.threshold = 0.0 - split_record.improvement = float('-inf') - split_record.missing_go_to_left = False - split_record.n_missing = 0 +class SplitRecord: + def __init__(): + raise NotImplementedError( + "No idea what SplitRecord is, just created to remove error!" + ) class Splitter: - """Abstract splitter class. + """ + Abstract splitter class. - Splitters are called by tree builders to find the best splits on both - sparse and dense data, one split at a time. + Splitters are called by tree builders to find the best splits on + both sparse and dense data, one split at a time. """ - def __init__(self, criterion, max_features, min_samples_leaf, min_weight_leaf, random_state): + + def __init__( + self, criterion, max_features, min_samples_leaf, min_weight_leaf, random_state + ): self.criterion = criterion self.random_state = random_state @@ -37,7 +38,7 @@ def __init__(self, criterion, max_features, min_samples_leaf, min_weight_leaf, r self.min_weight_leaf = min_weight_leaf self.rand_r_state = random_state.randint(0, 0x7FFFFFFF) - + self.samples = None self.weighted_n_samples = 0.0 @@ -50,7 +51,7 @@ def __init__(self, criterion, max_features, min_samples_leaf, min_weight_leaf, r def init(self, X, y, sample_weight, missing_values_in_feature_mask=None): n_samples = X.shape[0] - + # Create a list to store indices of positively weighted samples samples = [] @@ -83,12 +84,21 @@ def init(self, X, y, sample_weight, missing_values_in_feature_mask=None): if missing_values_in_feature_mask is not None: self.criterion.init_sum_missing() - + return 0 - + def __reduce__(self): - return (type(self), (self.criterion, self.max_features, self.min_samples_leaf, - self.min_weight_leaf, self.random_state), self.__getstate__()) + return ( + type(self), + ( + self.criterion, + self.max_features, + self.min_samples_leaf, + self.min_weight_leaf, + self.random_state, + ), + self.__getstate__(), + ) def node_reset(self, start, end, weighted_n_node_samples): self.start = start @@ -100,12 +110,12 @@ def node_reset(self, start, end, weighted_n_node_samples): self.weighted_n_samples, self.samples, start, - end + end, ) weighted_n_node_samples[0] = self.criterion.weighted_n_node_samples return 0 - + def node_split(self, impurity, split, n_constant_features): # This is a placeholder method; actual implementation required. # You should add your computation logic here to find the best split. @@ -118,30 +128,14 @@ def node_value(self, dest): def node_impurity(self): """Return the impurity of the current node.""" return self.criterion.node_impurity() - -class BestSplitter(Splitter): - """Splitter for finding the best split on dense data.""" - - def __init__(self, X, y, sample_weight, missing_values_in_feature_mask): - super().__init__(X, y, sample_weight, missing_values_in_feature_mask) - self.partitioner = DensePartitioner( - X, self.samples, self.feature_values, missing_values_in_feature_mask - ) - def node_split(self, impurity, split, n_constant_features): - return node_split_best( - self, - self.partitioner, - self.criterion, - impurity, - split, - n_constant_features, - ) class DensePartitioner: - """Partitioner specialized for dense data. + """ + Partitioner specialized for dense data. - Note that this partitioner is agnostic to the splitting strategy (best vs. random). + Note that this partitioner is agnostic to the splitting strategy + (best vs. random). """ def __init__(self, X, samples, feature_values, missing_values_in_feature_mask): @@ -165,15 +159,18 @@ def sort_samples_and_feature_values(self, current_feature): n_missing = 0 missing_values_in_feature_mask = self.missing_values_in_feature_mask - if missing_values_in_feature_mask is not None and missing_values_in_feature_mask[current_feature]: + if ( + missing_values_in_feature_mask is not None + and missing_values_in_feature_mask[current_feature] + ): i, current_end = self.start, self.end - 1 while i <= current_end: - if isnan(X[samples[current_end], current_feature]): + if ivy.isnan(X[samples[current_end], current_feature]): n_missing += 1 current_end -= 1 continue - if isnan(X[samples[i], current_feature]): + if ivy.isnan(X[samples[i], current_feature]): samples[i], samples[current_end] = samples[current_end], samples[i] n_missing += 1 current_end -= 1 @@ -189,12 +186,217 @@ def sort_samples_and_feature_values(self, current_feature): self.n_missing = n_missing +class SparsePartitioner: + def __init__( + self, + X, + samples, + n_samples, + feature_values, + missing_values_in_feature_mask, + ): + if not self.isspmatrix_csc(X): + raise ValueError("X should be in csc format") + + self.samples = samples + self.feature_values = feature_values + + # Initialize X + n_total_samples = X.shape[0] + + self.X_data = X.data + self.X_indices = X.indices + self.X_indptr = X.indptr + self.n_total_samples = n_total_samples + + # Initialize auxiliary arrays used for partitioning + self.index_to_samples = ivy.full(n_total_samples, fill_value=-1, dtype=ivy.intp) + self.sorted_samples = ivy.empty(n_samples, dtype=ivy.intp) + self.n_missing = 0 # Placeholder for missing values (not supported yet) + self.missing_values_in_feature_mask = missing_values_in_feature_mask + + self.start_positive = 0 + self.end_negative = 0 + self.is_samples_sorted = False + + def init_node_split(self, start, end): + self.start = start + self.end = end + self.is_samples_sorted = False + + def sort_samples_and_feature_values(self, current_feature): + # Simultaneously sort based on feature_values + self.extract_nnz(current_feature) + # Rest of the code for sorting... + + def find_min_max( + self, current_feature, min_feature_value_out, max_feature_value_out + ): + # Find the minimum and maximum value for current_feature + self.extract_nnz(current_feature) + # Rest of the code for finding min and max... + + def next_p(self, p_prev, p): + # Compute the next p_prev and p for iterating over feature values + # Rest of the code... + pass + + def partition_samples(self, current_threshold): + # Partition samples for feature_values at the current_threshold + return self._partition(current_threshold, self.start_positive) + + def partition_samples_final( + self, best_pos, best_threshold, best_feature, n_missing + ): + # Partition samples for X at the best_threshold and best_feature + self.extract_nnz(best_feature) + self._partition(best_threshold, best_pos) + + def _partition(self, threshold, zero_pos): + # Partition samples based on threshold + # Rest of the code... + pass + + def extract_nnz(self, feature): + # Extract and partition values for a given feature + # Rest of the code... + pass + + def isspmatrix_csc(self, X): + # Check if X is in CSC format + # You may need to implement this function according to your specific requirements + # For the example, we assume it's already in CSC format + return True + + +class BestSplitter(Splitter): + """Splitter for finding the best split on dense data.""" + + def __init__(self, X, y, sample_weight, missing_values_in_feature_mask): + super().__init__(X, y, sample_weight, missing_values_in_feature_mask) + self.partitioner = DensePartitioner( + X, self.samples, self.feature_values, missing_values_in_feature_mask + ) + + def node_split(self, impurity, split, n_constant_features): + return node_split_best( + self, + self.partitioner, + self.criterion, + impurity, + split, + n_constant_features, + ) + + +class BestSparseSplitter(Splitter): + """Splitter for finding the best split, using sparse data.""" + + def __init__(self, X, y, sample_weight, missing_values_in_feature_mask): + super().__init__(X, y, sample_weight, missing_values_in_feature_mask) + self.partitioner = SparsePartitioner( + X, + self.samples, + self.n_samples, + self.feature_values, + missing_values_in_feature_mask, + ) + + def node_split(self, impurity, split, n_constant_features): + return node_split_best( + self, + self.partitioner, + self.criterion, + impurity, + split, + n_constant_features, + ) + + +class RandomSplitter(Splitter): + """Splitter for finding the best random split on dense data.""" + + def __init__( + self, + X, + y, + sample_weight, + missing_values_in_feature_mask, + ): + Splitter.__init__(self, X, y, sample_weight, missing_values_in_feature_mask) + self.partitioner = DensePartitioner( + X, self.samples, self.feature_values, missing_values_in_feature_mask + ) + + def node_split(self, impurity, split, n_constant_features): + return node_split_random( + self, + self.partitioner, + self.criterion, + impurity, + split, + n_constant_features, + ) + + +class RandomSparseSplitter(Splitter): + """Splitter for finding the best random split, using sparse data.""" + + def __init__( + self, + X, + y, + sample_weight, + missing_values_in_feature_mask, + ): + Splitter.__init__(self, X, y, sample_weight, missing_values_in_feature_mask) + self.partitioner = SparsePartitioner( + X, + self.samples, + self.n_samples, + self.feature_values, + missing_values_in_feature_mask, + ) + + def node_split(self, impurity, split, n_constant_features): + return node_split_random( + self, + self.partitioner, + self.criterion, + impurity, + split, + n_constant_features, + ) + + +def heapsort(feature_values, samples, n): + # Heapify + start = (n - 2) // 2 + end = n + while True: + sift_down(feature_values, samples, start, end) + if start == 0: + break + start -= 1 + + # Sort by shrinking the heap, putting the max element immediately after it + end = n - 1 + while end > 0: + swap(feature_values, samples, 0, end) + sift_down(feature_values, samples, 0, end) + end -= 1 + + +def init_split(split_record, start_pos): + split_record.impurity_left = float("inf") + split_record.impurity_right = float("inf") + split_record.pos = start_pos + split_record.feature = 0 + split_record.threshold = 0.0 + split_record.improvement = float("-inf") + split_record.missing_go_to_left = False + split_record.n_missing = 0 -def sort(feature_values, samples, n): - if n == 0: - return - maxd = 2 * int(math.log(n)) - introsort(feature_values, samples, n, maxd) def introsort(feature_values, samples, n, maxd): while n > 1: @@ -225,48 +427,6 @@ def introsort(feature_values, samples, n, maxd): samples = samples[r:] n -= r -def heapsort(feature_values, samples, n): - # Heapify - start = (n - 2) // 2 - end = n - while True: - sift_down(feature_values, samples, start, end) - if start == 0: - break - start -= 1 - - # Sort by shrinking the heap, putting the max element immediately after it - end = n - 1 - while end > 0: - swap(feature_values, samples, 0, end) - sift_down(feature_values, samples, 0, end) - end -= 1 - -def sift_down(feature_values, samples, start, end): - # Restore heap order in feature_values[start:end] by moving the max element to start. - root = start - while True: - child = root * 2 + 1 - - # Find the max of root, left child, right child - maxind = root - if child < end and feature_values[samples[maxind]] < feature_values[samples[child]]: - maxind = child - if child + 1 < end and feature_values[samples[maxind]] < feature_values[samples[child + 1]]: - maxind = child + 1 - - if maxind == root: - break - else: - swap(feature_values, samples, root, maxind) - root = maxind - - -# Define the swap function here -def swap(feature_values, samples, i, j): - feature_values[samples[i]], feature_values[samples[j]] = feature_values[samples[j]], feature_values[samples[i]] - samples[i], samples[j] = samples[j], samples[i] - def median3(feature_values, n): # Median of three pivot selection, after Bentley and McIlroy (1993). @@ -287,9 +447,10 @@ def median3(feature_values, n): else: return b -import random -def node_split_best(splitter, partitioner, criterion, impurity, split, n_constant_features): +def node_split_best( + splitter, partitioner, criterion, impurity, split, n_constant_features +): start = splitter.start end = splitter.end end_non_missing = end @@ -301,12 +462,12 @@ def node_split_best(splitter, partitioner, criterion, impurity, split, n_constan features = list(splitter.features) constant_features = list(splitter.constant_features) - n_features = splitter.n_features + splitter.n_features feature_values = list(splitter.feature_values) max_features = splitter.max_features min_samples_leaf = splitter.min_samples_leaf min_weight_leaf = splitter.min_weight_leaf - random_state = random.Random(splitter.rand_r_state) + random.Random(splitter.rand_r_state) n_visited_features = 0 n_found_constants = 0 @@ -314,12 +475,18 @@ def node_split_best(splitter, partitioner, criterion, impurity, split, n_constan n_known_constants = n_constant_features[0] n_total_constants = n_known_constants - while f_i > n_total_constants and (n_visited_features < max_features or n_visited_features <= n_found_constants + n_drawn_constants): + while f_i > n_total_constants and ( + n_visited_features < max_features + or n_visited_features <= n_found_constants + n_drawn_constants + ): n_visited_features += 1 f_j = random.randint(n_drawn_constants, f_i - n_found_constants - 1) if f_j < n_known_constants: - features[n_drawn_constants], features[f_j] = features[f_j], features[n_drawn_constants] + features[n_drawn_constants], features[f_j] = ( + features[f_j], + features[n_drawn_constants], + ) n_drawn_constants += 1 continue @@ -329,8 +496,15 @@ def node_split_best(splitter, partitioner, criterion, impurity, split, n_constan n_missing = partitioner.n_missing end_non_missing = end - n_missing - if end_non_missing == start or feature_values[end_non_missing - 1] <= feature_values[start] + FEATURE_THRESHOLD: - features[f_j], features[n_total_constants] = features[n_total_constants], features[f_j] + if ( + end_non_missing == start + or feature_values[end_non_missing - 1] + <= feature_values[start] + FEATURE_THRESHOLD + ): + features[f_j], features[n_total_constants] = ( + features[n_total_constants], + features[f_j], + ) n_found_constants += 1 n_total_constants += 1 continue @@ -369,16 +543,25 @@ def node_split_best(splitter, partitioner, criterion, impurity, split, n_constan current_split.pos = p criterion.update(current_split.pos) - if criterion.weighted_n_left < min_weight_leaf or criterion.weighted_n_right < min_weight_leaf: + if ( + criterion.weighted_n_left < min_weight_leaf + or criterion.weighted_n_right < min_weight_leaf + ): continue current_proxy_improvement = criterion.proxy_impurity_improvement() if current_proxy_improvement > best_proxy_improvement: best_proxy_improvement = current_proxy_improvement - current_split.threshold = (feature_values[p_prev] / 2.0 + feature_values[p] / 2.0) - - if current_split.threshold == feature_values[p] or current_split.threshold == float("inf") or current_split.threshold == -float("inf"): + current_split.threshold = ( + feature_values[p_prev] / 2.0 + feature_values[p] / 2.0 + ) + + if ( + current_split.threshold == feature_values[p] + or current_split.threshold == float("inf") + or current_split.threshold == -float("inf") + ): current_split.threshold = feature_values[p_prev] current_split.n_missing = n_missing @@ -398,7 +581,10 @@ def node_split_best(splitter, partitioner, criterion, impurity, split, n_constan criterion.missing_go_to_left = missing_go_to_left criterion.update(p) - if not (criterion.weighted_n_left < min_weight_leaf or criterion.weighted_n_right < min_weight_leaf): + if not ( + criterion.weighted_n_left < min_weight_leaf + or criterion.weighted_n_right < min_weight_leaf + ): current_proxy_improvement = criterion.proxy_impurity_improvement() if current_proxy_improvement > best_proxy_improvement: @@ -414,7 +600,7 @@ def node_split_best(splitter, partitioner, criterion, impurity, split, n_constan best_split.pos, best_split.threshold, best_split.feature, - best_split.n_missing + best_split.n_missing, ) if best_split.n_missing != 0: @@ -423,31 +609,31 @@ def node_split_best(splitter, partitioner, criterion, impurity, split, n_constan criterion.reset() criterion.update(best_split.pos) criterion.children_impurity( - best_split.impurity_left, - best_split.impurity_right + best_split.impurity_left, best_split.impurity_right ) best_split.improvement = criterion.impurity_improvement( - impurity, - best_split.impurity_left, - best_split.impurity_right - ) - shift_missing_values_to_left_if_required( - best_split, - samples, - end + impurity, best_split.impurity_left, best_split.impurity_right ) + shift_missing_values_to_left_if_required(best_split, samples, end) memcpy(features, constant_features, sizeof(SIZE_t) * n_known_constants) memcpy( constant_features[n_known_constants:], - features[n_known_constants:n_known_constants + n_found_constants], - sizeof(SIZE_t) * n_found_constants + features[n_known_constants : n_known_constants + n_found_constants], + sizeof(SIZE_t) * n_found_constants, ) split[0] = best_split n_constant_features[0] = n_total_constants return 0 + +def node_split_random( + splitter, partitioner, criterion, impurity, split, n_constant_features +): + raise NotImplementedError("Function not implemented yet!") + + def shift_missing_values_to_left_if_required(best, samples, end): # The partitioner partitions the data such that the missing values are in # samples[-n_missing:] for the criterion to consume. If the missing values @@ -461,3 +647,44 @@ def shift_missing_values_to_left_if_required(best, samples, end): samples[i], samples[current_end] = samples[current_end], samples[i] best.pos += best.n_missing + +def sift_down(feature_values, samples, start, end): + # Restore heap order in feature_values[start:end] by moving the max element to start. + root = start + while True: + child = root * 2 + 1 + + # Find the max of root, left child, right child + maxind = root + if ( + child < end + and feature_values[samples[maxind]] < feature_values[samples[child]] + ): + maxind = child + if ( + child + 1 < end + and feature_values[samples[maxind]] < feature_values[samples[child + 1]] + ): + maxind = child + 1 + + if maxind == root: + break + else: + swap(feature_values, samples, root, maxind) + root = maxind + + +def sort(feature_values, samples, n): + if n == 0: + return + maxd = 2 * int(math.log(n)) + introsort(feature_values, samples, n, maxd) + + +# Define the swap function here +def swap(feature_values, samples, i, j): + feature_values[samples[i]], feature_values[samples[j]] = ( + feature_values[samples[j]], + feature_values[samples[i]], + ) + samples[i], samples[j] = samples[j], samples[i] diff --git a/ivy/functional/frontends/sklearn/tree/tree ivy.py b/ivy/functional/frontends/sklearn/tree/tree ivy.py index fa547997eb761..89c408ec7816c 100644 --- a/ivy/functional/frontends/sklearn/tree/tree ivy.py +++ b/ivy/functional/frontends/sklearn/tree/tree ivy.py @@ -1,58 +1,11 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - import numpy as np - from scipy.sparse import issparse from scipy.sparse import csr_matrix from scipy.sparse import isspmatrix_csr - - - - - - - - - - - - - - - - - - # ============================================================================= # Types and constants # ============================================================================= @@ -60,21 +13,17 @@ from numpy import float32 as DTYPE from numpy import float64 as DOUBLE - +EPSILON = np.finfo(np.double).eps # Define constants INFINITY = np.inf -EPSILON = np.finfo(np.double).eps - # Some handy constants (BestFirstTreeBuilder) IS_FIRST = 1 -IS_NOT_FIRST = 0 IS_LEFT = 1 +IS_NOT_FIRST = 0 IS_NOT_LEFT = 0 - TREE_LEAF = -1 TREE_UNDEFINED = -2 -_TREE_LEAF = TREE_LEAF -_TREE_UNDEFINED = TREE_UNDEFINED + # Since you're dealing with Cython-specific types and features, # it's important to provide a dummy definition for Node. @@ -89,15 +38,15 @@ def __init__(self): self.weighted_n_node_samples = None self.missing_go_to_left = None -dummy = Node() -# Create a numpy dtype for Node using the dummy object -NODE_DTYPE = np.asarray([dummy], dtype=object).dtype + # ============================================================================= # TreeBuilder # ============================================================================= + class TreeBuilder: """Interface for different tree building strategies.""" + def __init__(self): self.splitter = None self.min_samples_split = None @@ -105,7 +54,7 @@ def __init__(self): self.min_weight_leaf = None self.max_depth = None self.min_impurity_decrease = None - + def build( self, tree, @@ -123,10 +72,12 @@ def _check_input( y, sample_weight, ): - """Check input dtype, layout, and format""" + """Check input dtype, layout, and format.""" if issparse(X): - X = X.tocsc() #tocsc() is a method provided by the scipy.sparse module in the SciPy library. It's used to convert a sparse matrix to the Compressed Sparse Column (CSC) format. - X.sort_indices() #This is done to ensure that the indices of non-zero elements within the matrix are sorted in ascending order. + X = ( + X.tocsc() + ) # tocsc() is a method provided by the scipy.sparse module in the SciPy library. It's used to convert a sparse matrix to the Compressed Sparse Column (CSC) format. + X.sort_indices() # This is done to ensure that the indices of non-zero elements within the matrix are sorted in ascending order. if X.data.dtype != DTYPE: X.data = np.ascontiguousarray(X.data, dtype=DTYPE) @@ -141,24 +92,21 @@ def _check_input( if y.base.dtype != DTYPE or not y.base.flags.contiguous: y = np.ascontiguousarray(y, dtype=DTYPE) - if ( - sample_weight is not None and - ( - sample_weight.base.dtype != DOUBLE or - not sample_weight.base.flags.contiguous - ) + if sample_weight is not None and ( + sample_weight.base.dtype != DOUBLE + or not sample_weight.base.flags.contiguous ): sample_weight = np.asarray(sample_weight, dtype=DOUBLE, order="C") return X, y, sample_weight - - # Depth first builder --------------------------------------------------------- # A record on the stack for depth-first tree growing class StackRecord: - def __init__(self, start, end, depth, parent, is_left, impurity, n_constant_features): + def __init__( + self, start, end, depth, parent, is_left, impurity, n_constant_features + ): self.start = start self.end = end self.depth = depth @@ -169,9 +117,17 @@ def __init__(self, start, end, depth, parent, is_left, impurity, n_constant_feat class SplitRecord: - def __init__(self, feature, pos, threshold, improvement, - impurity_left, impurity_right, missing_go_to_left, - n_missing): + def __init__( + self, + feature, + pos, + threshold, + improvement, + impurity_left, + impurity_right, + missing_go_to_left, + n_missing, + ): self.feature = feature self.pos = pos self.threshold = threshold @@ -182,147 +138,6 @@ def __init__(self, feature, pos, threshold, improvement, self.n_missing = n_missing - - - -class DepthFirstTreeBuilder(TreeBuilder): - """Build a decision tree in depth-first fashion.""" - - def __init__( - self, splitter, min_samples_split, - min_samples_leaf, min_weight_leaf, - max_depth, min_impurity_decrease - ): - self.splitter = splitter - self.min_samples_split = min_samples_split - self.min_samples_leaf = min_samples_leaf - self.min_weight_leaf = min_weight_leaf - self.max_depth = max_depth - self.min_impurity_decrease = min_impurity_decrease - - def build( - self, - tree, - X, - y, - sample_weight=None, - missing_values_in_feature_mask=None - ): - """Build a decision tree from the training set (X, y).""" - - # Check input - X, y, sample_weight = self._check_input(X, y, sample_weight) - - # Initial capacity - init_capacity = (2 ** (tree.max_depth + 1)) - 1 if tree.max_depth <= 10 else 2047 - - tree._resize(init_capacity) - - # Parameters - splitter = self.splitter - max_depth = self.max_depth - min_samples_leaf = self.min_samples_leaf - min_weight_leaf = self.min_weight_leaf - min_samples_split = self.min_samples_split - min_impurity_decrease = self.min_impurity_decrease - - # Recursive partition (without actual recursion) - splitter.init(X, y, sample_weight, missing_values_in_feature_mask) - - stack = [] - - # Push root node onto stack - stack.append( - StackRecord( - start=0, - end=splitter.n_samples, - depth=0, - parent=_TREE_UNDEFINED, - is_left=False, - impurity=INFINITY, - n_constant_features=0 - ) - ) - weighted_n_node_samples = np.zeros(1, dtype=np.double) - while stack: - stack_record = stack.pop() - - start = stack_record.start - end = stack_record.end - depth = stack_record.depth - parent = stack_record.parent - is_left = stack_record.is_left - impurity = stack_record.impurity - n_constant_features = stack_record.n_constant_features - - n_node_samples = end - start - splitter.node_reset(start, end, weighted_n_node_samples) - - is_leaf = ( - depth >= max_depth - or n_node_samples < min_samples_split - or n_node_samples < 2 * min_samples_leaf - or np.sum(sample_weight[start:end]) < 2 * min_weight_leaf - ) - - if is_left: - impurity = splitter.node_impurity() - - is_leaf = is_leaf or impurity <= EPSILON - - if not is_leaf: - split = SplitRecord() # No idea what is SplitRecord in original code. Maybe this never gets called, not sure - splitter.node_split(impurity, split, n_constant_features) - is_leaf = ( - is_leaf - or split.pos >= end - or (split.improvement + EPSILON < min_impurity_decrease) - ) - - node_id = tree._add_node( - parent, - is_left, - is_leaf, - split.feature if not is_leaf else 0, - split.threshold if not is_leaf else 0, - impurity, - n_node_samples, - np.sum(sample_weight[start:end]), - split.missing_go_to_left, - ) - - if node_id == np.iinfo(np.intp).max: - raise MemoryError() - - splitter.node_value(tree.value + node_id * tree.value_stride) - - if not is_leaf: - # Push right child on stack - stack.append( - StackRecord( - start=split.pos, - end=end, - depth=depth + 1, - parent=node_id, - is_left=False, - impurity=split.impurity_right, - n_constant_features=n_constant_features, - ) - ) - # Push left child on stack - stack.append( - StackRecord( - start=start, - end=split.pos, - depth=depth + 1, - parent=node_id, - is_left=True, - impurity=split.impurity_left, - n_constant_features=n_constant_features, - ) - ) - - class Tree: def __init__(self, n_features, n_classes, n_outputs): """Constructor.""" @@ -367,11 +182,12 @@ def __del__(self): self.value = None self.nodes = None - #NOT CONSIDERING PICKINLING FOR NOW + # NOT CONSIDERING PICKINLING FOR NOW def __reduce__(self): """Reduce re-implementation, for pickling.""" raise NotImplementedError - #NOT CONSIDERING PICKINLING FOR NOW + + # NOT CONSIDERING PICKINLING FOR NOW def __getstate__(self): """Getstate re-implementation, for pickling.""" d = {} @@ -381,30 +197,30 @@ def __getstate__(self): d["nodes"] = self._get_node_ndarray() d["values"] = self._get_value_ndarray() return d - #NOT CONSIDERING PICKINLING FOR NOW + + # NOT CONSIDERING PICKINLING FOR NOW def __setstate__(self, d): """Setstate re-implementation, for unpickling.""" raise NotImplementedError def _resize(self, capacity): """ - Resize all inner arrays to `capacity`. If `capacity` is -1, then double the size of the inner arrays. + Resize all inner arrays to `capacity`. + + If `capacity` is -1, then double the size of the inner arrays. Returns -1 in case of failure to allocate memory (and raise MemoryError), or 0 otherwise. """ if self._resize_c(capacity) != 0: # Raise MemoryError if resizing fails raise MemoryError() - def _resize_c(self, capacity=float('inf')): - """ - Guts of _resize - Returns -1 in case of failure to allocate memory (and raise MemoryError), - or 0 otherwise. - """ + def _resize_c(self, capacity=float("inf")): + """Guts of _resize Returns -1 in case of failure to allocate memory (and raise + MemoryError), or 0 otherwise.""" if capacity == self.capacity and self.nodes is not None: return 0 - if capacity == float('inf'): + if capacity == float("inf"): if self.capacity == 0: capacity = 3 # default initial value else: @@ -413,7 +229,7 @@ def _resize_c(self, capacity=float('inf')): # This section is relevant if the code is dealing with C arrays. # In Python, resizing arrays is handled automatically by lists or numpy arrays. # You won't need to explicitly reallocate memory or initialize values like this. - + # replaced safe_realloc(&self.nodes, capacity) with the following new_nodes = np.empty(capacity, dtype=object) for i in range(len(self.nodes)): @@ -433,9 +249,18 @@ def _resize_c(self, capacity=float('inf')): self.capacity = capacity return 0 - - def _add_node(self, parent, is_left, is_leaf, feature, threshold, impurity, - n_node_samples, weighted_n_node_samples, missing_go_to_left): + def _add_node( + self, + parent, + is_left, + is_leaf, + feature, + threshold, + impurity, + n_node_samples, + weighted_n_node_samples, + missing_go_to_left, + ): """ Add a node to the tree. @@ -479,19 +304,19 @@ def _add_node(self, parent, is_left, is_leaf, feature, threshold, impurity, def predict(self, X): """Predict target for X.""" out = self._get_value_ndarray()[self.apply(X), :, :] - + if self.n_outputs == 1: out = out.reshape(X.shape[0], self.max_n_classes) - + return out - + def apply(self, X): """Finds the terminal region (=leaf node) for each sample in X.""" if issparse(X): return self._apply_sparse_csr(X) else: return self._apply_dense(X) - + def _apply_dense(self, X): """Finds the terminal region (=leaf node) for each sample in X.""" @@ -508,7 +333,9 @@ def _apply_dense(self, X): # Initialize output out = np.zeros(n_samples, dtype=np.intp) - with np.nditer(X, flags=['c_index', 'multi_index'], op_flags=['readonly'], order='C') as it: + with np.nditer( + X, flags=["c_index", "multi_index"], op_flags=["readonly"], order="C" + ) as it: for x_value, index in it: node = self.nodes # While node not a leaf @@ -528,7 +355,7 @@ def _apply_dense(self, X): out[index[0]] = node - self.nodes # node offset return out - + def _apply_sparse_csr(self, X): """Finds the terminal region (=leaf node) for each sample in sparse X.""" if not isinstance(X, csr_matrix): @@ -567,7 +394,7 @@ def _apply_sparse_csr(self, X): out[i] = node - self.nodes # node offset return out - + def decision_path(self, X): """Finds the decision path (=node) for each sample in X.""" if issparse(X): @@ -616,12 +443,12 @@ def _decision_path_dense(self, X): indices[indptr[i + 1]] = node - self.nodes indptr[i + 1] += 1 - indices = indices[:indptr[n_samples]] + indices = indices[: indptr[n_samples]] data = np.ones(shape=len(indices), dtype=np.intp) out = csr_matrix((data, indices, indptr), shape=(n_samples, self.node_count)) return out - + def _decision_path_sparse_csr(self, X): """Finds the decision path (=node) for each sample in X.""" @@ -677,14 +504,15 @@ def _decision_path_sparse_csr(self, X): indices[indptr[i + 1]] = node - self.nodes indptr[i + 1] += 1 - indices = indices[:indptr[n_samples]] + indices = indices[: indptr[n_samples]] data = np.ones(shape=len(indices), dtype=np.intp) out = csr_matrix((data, indices, indptr), shape=(n_samples, self.node_count)) return out def compute_node_depths(self): - """Compute the depth of each node in a tree. + """ + Compute the depth of each node in a tree. .. versionadded:: 1.3 @@ -721,9 +549,10 @@ def compute_feature_importances(self, normalize=True): right = nodes[node.right_child] importances[node.feature] += ( - node.weighted_n_node_samples * node.impurity - - left.weighted_n_node_samples * left.impurity - - right.weighted_n_node_samples * right.impurity) + node.weighted_n_node_samples * node.impurity + - left.weighted_n_node_samples * left.impurity + - right.weighted_n_node_samples * right.impurity + ) node += 1 for i in range(self.n_features): @@ -737,40 +566,44 @@ def compute_feature_importances(self, normalize=True): importances /= normalizer return importances - + def _get_value_ndarray(self): - """Wraps value as a 3-d NumPy array. + """ + Wraps value as a 3-d NumPy array. - The array keeps a reference to this Tree, which manages the underlying - memory. + The array keeps a reference to this Tree, which manages the + underlying memory. """ shape = (self.node_count, self.n_outputs, self.max_n_classes) arr = np.ndarray(shape, dtype=np.float64, buffer=self.value) arr.base = self return arr - + def _get_node_ndarray(self): - """Wraps nodes as a NumPy struct array. + """ + Wraps nodes as a NumPy struct array. - The array keeps a reference to this Tree, which manages the underlying - memory. Individual fields are publicly accessible as properties of the - Tree. + The array keeps a reference to this Tree, which manages the + underlying memory. Individual fields are publicly accessible as + properties of the Tree. """ shape = (self.node_count,) - dtype = np.dtype([ - ('left_child', np.intp), - ('right_child', np.intp), - ('feature', np.intp), - ('threshold', np.float64), - ('impurity', np.float64), - ('n_node_samples', np.intp), - ('weighted_n_node_samples', np.float64), - ('missing_go_to_left', np.uint8) - ]) + dtype = np.dtype( + [ + ("left_child", np.intp), + ("right_child", np.intp), + ("feature", np.intp), + ("threshold", np.float64), + ("impurity", np.float64), + ("n_node_samples", np.intp), + ("weighted_n_node_samples", np.float64), + ("missing_go_to_left", np.uint8), + ] + ) arr = np.ndarray(shape, dtype=dtype, buffer=self.nodes) arr.base = self return arr - + def compute_partial_dependence(self, X, target_features, out): out.fill(0.0) # Initialize the output array @@ -789,12 +622,20 @@ def compute_partial_dependence(self, X, target_features, out): if current_node.left_child == _TREE_LEAF: # Leaf node - out[sample_idx] += weight_stack[stack_size] * self.value[current_node_idx] + out[sample_idx] += ( + weight_stack[stack_size] * self.value[current_node_idx] + ) total_weight += weight_stack[stack_size] else: - is_target_feature = any(target_feature == current_node.feature for target_feature in target_features) + is_target_feature = any( + target_feature == current_node.feature + for target_feature in target_features + ) if is_target_feature: - if X[sample_idx, current_node.feature] <= current_node.threshold: + if ( + X[sample_idx, current_node.feature] + <= current_node.threshold + ): node_idx_stack.append(current_node.left_child) weight_stack.append(weight_stack[stack_size]) stack_size += 1 @@ -803,20 +644,177 @@ def compute_partial_dependence(self, X, target_features, out): weight_stack.append(weight_stack[stack_size]) stack_size += 1 else: - left_sample_frac = self.nodes[current_node.left_child].weighted_n_node_samples / current_node.weighted_n_node_samples + left_sample_frac = ( + self.nodes[current_node.left_child].weighted_n_node_samples + / current_node.weighted_n_node_samples + ) current_weight = weight_stack[stack_size] - node_idx_stack.extend([current_node.left_child, current_node.right_child]) - weight_stack.extend([current_weight * left_sample_frac, current_weight * (1 - left_sample_frac)]) + node_idx_stack.extend( + [current_node.left_child, current_node.right_child] + ) + weight_stack.extend( + [ + current_weight * left_sample_frac, + current_weight * (1 - left_sample_frac), + ] + ) stack_size += 2 if not (0.999 < total_weight < 1.001): - raise ValueError(f"Total weight should be 1.0 but was {total_weight:.9f}") + raise ValueError( + f"Total weight should be 1.0 but was {total_weight:.9f}" + ) + + +class DepthFirstTreeBuilder(TreeBuilder): + """Build a decision tree in depth-first fashion.""" + + def __init__( + self, + splitter, + min_samples_split, + min_samples_leaf, + min_weight_leaf, + max_depth, + min_impurity_decrease, + ): + self.splitter = splitter + self.min_samples_split = min_samples_split + self.min_samples_leaf = min_samples_leaf + self.min_weight_leaf = min_weight_leaf + self.max_depth = max_depth + self.min_impurity_decrease = min_impurity_decrease + + def build( + self, tree, X, y, sample_weight=None, missing_values_in_feature_mask=None + ): + """Build a decision tree from the training set (X, y).""" + + # Check input + X, y, sample_weight = self._check_input(X, y, sample_weight) + + # Initial capacity + init_capacity = ( + (2 ** (tree.max_depth + 1)) - 1 if tree.max_depth <= 10 else 2047 + ) + + tree._resize(init_capacity) + + # Parameters + splitter = self.splitter + max_depth = self.max_depth + min_samples_leaf = self.min_samples_leaf + min_weight_leaf = self.min_weight_leaf + min_samples_split = self.min_samples_split + min_impurity_decrease = self.min_impurity_decrease + + # Recursive partition (without actual recursion) + splitter.init(X, y, sample_weight, missing_values_in_feature_mask) + + stack = [] + + # Push root node onto stack + stack.append( + StackRecord( + start=0, + end=splitter.n_samples, + depth=0, + parent=_TREE_UNDEFINED, + is_left=False, + impurity=INFINITY, + n_constant_features=0, + ) + ) + weighted_n_node_samples = np.zeros(1, dtype=np.double) + while stack: + stack_record = stack.pop() + + start = stack_record.start + end = stack_record.end + depth = stack_record.depth + parent = stack_record.parent + is_left = stack_record.is_left + impurity = stack_record.impurity + n_constant_features = stack_record.n_constant_features + + n_node_samples = end - start + splitter.node_reset(start, end, weighted_n_node_samples) + + is_leaf = ( + depth >= max_depth + or n_node_samples < min_samples_split + or n_node_samples < 2 * min_samples_leaf + or np.sum(sample_weight[start:end]) < 2 * min_weight_leaf + ) + + if is_left: + impurity = splitter.node_impurity() + + is_leaf = is_leaf or impurity <= EPSILON + + if not is_leaf: + split = ( + SplitRecord() + ) # No idea what is SplitRecord in original code. Maybe this never gets called, not sure + splitter.node_split(impurity, split, n_constant_features) + is_leaf = ( + is_leaf + or split.pos >= end + or (split.improvement + EPSILON < min_impurity_decrease) + ) + + node_id = tree._add_node( + parent, + is_left, + is_leaf, + split.feature if not is_leaf else 0, + split.threshold if not is_leaf else 0, + impurity, + n_node_samples, + np.sum(sample_weight[start:end]), + split.missing_go_to_left, + ) + + if node_id == np.iinfo(np.intp).max: + raise MemoryError() + + splitter.node_value(tree.value + node_id * tree.value_stride) + + if not is_leaf: + # Push right child on stack + stack.append( + StackRecord( + start=split.pos, + end=end, + depth=depth + 1, + parent=node_id, + is_left=False, + impurity=split.impurity_right, + n_constant_features=n_constant_features, + ) + ) + # Push left child on stack + stack.append( + StackRecord( + start=start, + end=split.pos, + depth=depth + 1, + parent=node_id, + is_left=True, + impurity=split.impurity_left, + n_constant_features=n_constant_features, + ) + ) + + +# --- Helpers --- # +# --------------- # def _check_n_classes(n_classes, expected_dtype): if n_classes.ndim != 1: raise ValueError( - f"Wrong dimensions for n_classes from the pickle: " + "Wrong dimensions for n_classes from the pickle: " f"expected 1, got {n_classes.ndim}" ) @@ -834,7 +832,8 @@ def _check_n_classes(n_classes, expected_dtype): ) - - - - +dummy = Node() +# Create a numpy dtype for Node using the dummy object +NODE_DTYPE = np.asarray([dummy], dtype=object).dtype +_TREE_LEAF = TREE_LEAF +_TREE_UNDEFINED = TREE_UNDEFINED diff --git a/ivy/functional/frontends/sklearn/tree/tree.py b/ivy/functional/frontends/sklearn/tree/tree.py index 41d325a55e5a3..c15f2722d46d0 100644 --- a/ivy/functional/frontends/sklearn/tree/tree.py +++ b/ivy/functional/frontends/sklearn/tree/tree.py @@ -6,21 +6,16 @@ from ._splitter import SplitRecord - +EPSILON = ivy.finfo(ivy.double).eps # Define constants INFINITY = ivy.inf -EPSILON = ivy.finfo(ivy.double).eps - # Some handy constants (BestFirstTreeBuilder) IS_FIRST = 1 -IS_NOT_FIRST = 0 IS_LEFT = 1 +IS_NOT_FIRST = 0 IS_NOT_LEFT = 0 - TREE_LEAF = -1 TREE_UNDEFINED = -2 -_TREE_LEAF = TREE_LEAF -_TREE_UNDEFINED = TREE_UNDEFINED class Node: @@ -39,8 +34,10 @@ def __init__(self): # TreeBuilder # ============================================================================= + class TreeBuilder: """Interface for different tree building strategies.""" + def __init__(self): self.splitter = None self.min_samples_split = None @@ -48,7 +45,7 @@ def __init__(self): self.min_weight_leaf = None self.max_depth = None self.min_impurity_decrease = None - + def build( self, tree, @@ -66,10 +63,12 @@ def _check_input( y, sample_weight, ): - """Check input dtype, layout, and format""" + """Check input dtype, layout, and format.""" if issparse(X): - X = X.tocsc() #tocsc() is a method provided by the scipy.sparse module in the SciPy library. It's used to convert a sparse matrix to the Compressed Sparse Column (CSC) format. - X.sort_indices() #This is done to ensure that the indices of non-zero elements within the matrix are sorted in ascending order. + X = ( + X.tocsc() + ) # tocsc() is a method provided by the scipy.sparse module in the SciPy library. It's used to convert a sparse matrix to the Compressed Sparse Column (CSC) format. + X.sort_indices() # This is done to ensure that the indices of non-zero elements within the matrix are sorted in ascending order. if X.data.dtype != "float32": X.data = ivy.ascontiguousarray(X.data, dtype="float32") @@ -84,24 +83,23 @@ def _check_input( if y.base.dtype != "float32" or not y.base.flags.contiguous: y = ivy.ascontiguousarray(y, dtype="float32") - if ( - sample_weight is not None and - ( - sample_weight.base.dtype != "float32" or - not sample_weight.base.flags.contiguous - ) + if sample_weight is not None and ( + sample_weight.base.dtype != "float32" + or not sample_weight.base.flags.contiguous ): sample_weight = ivy.asarray(sample_weight, dtype="float32", order="C") return X, y, sample_weight - # Depth first builder --------------------------------------------------------- # A record on the stack for depth-first tree growing + class StackRecord: - def __init__(self, start, end, depth, parent, is_left, impurity, n_constant_features): + def __init__( + self, start, end, depth, parent, is_left, impurity, n_constant_features + ): self.start = start self.end = end self.depth = depth @@ -111,136 +109,6 @@ def __init__(self, start, end, depth, parent, is_left, impurity, n_constant_feat self.n_constant_features = n_constant_features -class DepthFirstTreeBuilder(TreeBuilder): - """Build a decision tree in depth-first fashion.""" - - def __init__( - self, splitter, min_samples_split, - min_samples_leaf, min_weight_leaf, - max_depth, min_impurity_decrease - ): - self.splitter = splitter - self.min_samples_split = min_samples_split - self.min_samples_leaf = min_samples_leaf - self.min_weight_leaf = min_weight_leaf - self.max_depth = max_depth - self.min_impurity_decrease = min_impurity_decrease - - def build( - self, - tree, - X, - y, - sample_weight=None, - missing_values_in_feature_mask=None - ): - """Build a decision tree from the training set (X, y).""" - - # Check input - X, y, sample_weight = self._check_input(X, y, sample_weight) - - # Parameters - splitter = self.splitter - max_depth = self.max_depth - min_samples_leaf = self.min_samples_leaf - min_weight_leaf = self.min_weight_leaf - min_samples_split = self.min_samples_split - min_impurity_decrease = self.min_impurity_decrease - - # Recursive partition (without actual recursion) - splitter.init(X, y, sample_weight, missing_values_in_feature_mask) - - stack = [] - - # Push root node onto stack - stack.append( - StackRecord( - start=0, - end=splitter.n_samples, - depth=0, - parent=_TREE_UNDEFINED, - is_left=False, - impurity=INFINITY, - n_constant_features=0 - ) - ) - weighted_n_node_samples = ivy.zeros(1, dtype="float32") - while stack: - stack_record = stack.pop() - - start = stack_record.start - end = stack_record.end - depth = stack_record.depth - parent = stack_record.parent - is_left = stack_record.is_left - impurity = stack_record.impurity - n_constant_features = stack_record.n_constant_features - - n_node_samples = end - start - splitter.node_reset(start, end, weighted_n_node_samples) - - is_leaf = ( - depth >= max_depth - or n_node_samples < min_samples_split - or n_node_samples < 2 * min_samples_leaf - or ivy.sum(sample_weight[start:end]) < 2 * min_weight_leaf - ) - - if is_left: - impurity = splitter.node_impurity() - - is_leaf = is_leaf or impurity <= EPSILON - - if not is_leaf: - split = SplitRecord() # No idea what is SplitRecord in original code. Maybe this never gets called, not sure - splitter.node_split(impurity, split, n_constant_features) - is_leaf = ( - is_leaf - or split.pos >= end - or (split.improvement + EPSILON < min_impurity_decrease) - ) - - node_id = tree._add_node( - parent, - is_left, - is_leaf, - split.feature if not is_leaf else 0, - split.threshold if not is_leaf else 0, - impurity, - n_node_samples, - ivy.sum(sample_weight[start:end]), - split.missing_go_to_left, - ) - - splitter.node_value(tree.value + node_id * tree.value_stride) - - if not is_leaf: - # Push right child on stack - stack.append( - StackRecord( - start=split.pos, - end=end, - depth=depth + 1, - parent=node_id, - is_left=False, - impurity=split.impurity_right, - n_constant_features=n_constant_features, - ) - ) - # Push left child on stack - stack.append( - StackRecord( - start=start, - end=split.pos, - depth=depth + 1, - parent=node_id, - is_left=True, - impurity=split.impurity_left, - n_constant_features=n_constant_features, - ) - ) - - class Tree: def __init__(self, n_features, n_classes, n_outputs): """Constructor.""" @@ -251,10 +119,10 @@ def __init__(self, n_features, n_classes, n_outputs): self.max_depth = None self.node_count = None self.capacity = None - self.nodes = [] #replaced it with array since this array will contain nodes + self.nodes = [] # replaced it with array since this array will contain nodes self.value = None self.value_stride = None - + size_t_dtype = "float32" n_classes = _check_n_classes(n_classes, size_t_dtype) @@ -305,10 +173,21 @@ def __setstate__(self, d): def _resize(self, capacity): raise NotImplementedError - def _resize_c(self, capacity=float('inf')): + def _resize_c(self, capacity=float("inf")): raise NotImplementedError - def _add_node(self, parent, is_left, is_leaf, feature, threshold, impurity, n_node_samples, weighted_n_node_samples, missing_go_to_left): + def _add_node( + self, + parent, + is_left, + is_leaf, + feature, + threshold, + impurity, + n_node_samples, + weighted_n_node_samples, + missing_go_to_left, + ): """ Add a node to the tree. @@ -318,7 +197,9 @@ def _add_node(self, parent, is_left, is_leaf, feature, threshold, impurity, n_no """ node_id = self.node_count - node = Node() #self.nodes contains a list of nodes, it returns the node at node_id location + node = ( + Node() + ) # self.nodes contains a list of nodes, it returns the node at node_id location self.nodes.append(node) node.impurity = impurity node.n_node_samples = n_node_samples @@ -352,22 +233,26 @@ def predict(self, X): # Get the internal data as a NumPy array internal_data = self._get_value_ndarray() # Use the predictions to index the internal data - out = internal_data[predictions] # not sure if this accurately translates to .take(self.apply(X), axis=0, mode='clip') + out = internal_data[ + predictions + ] # not sure if this accurately translates to .take(self.apply(X), axis=0, mode='clip') # Reshape the output if the model is single-output if self.n_outputs == 1: out = out.reshape(X.shape[0], self.max_n_classes) return out - + def apply(self, X): """Finds the terminal region (=leaf node) for each sample in X.""" if issparse(X): return self._apply_sparse_csr(X) else: return self._apply_dense(X) - + def _apply_dense(self, X): if not isinstance(X, ivy.data_classes.array.array.Array): - raise ValueError("X should be a ivy.data_classes.array.array.Array, got %s" % type(X)) + raise ValueError( + "X should be a ivy.data_classes.array.array.Array, got %s" % type(X) + ) if X.dtype != "float32": raise ValueError("X.dtype should be float32, got %s" % X.dtype) @@ -395,12 +280,14 @@ def _apply_dense(self, X): out[i] = self.nodes.index(node) # Get the index of the terminal node return out - - #not so sure about sparse implimentation yet + + # not so sure about sparse implimentation yet def _apply_sparse_csr(self, X): """Finds the terminal region (=leaf node) for each sample in sparse X.""" if not isinstance(X, ivy.data_classes.array.array.Array): - raise ValueError("X should be a ivy.data_classes.array.array.Array, got %s" % type(X)) + raise ValueError( + "X should be a ivy.data_classes.array.array.Array, got %s" % type(X) + ) if X.dtype != "float32": raise ValueError("X.dtype should be float32, got %s" % X.dtype) @@ -425,7 +312,9 @@ def _apply_sparse_csr(self, X): if feature_to_sample[node.feature] == i: feature_value = X_sample[node.feature] else: - feature_value = ivy.array(0, dtype="float32") # feature value is computed during training + feature_value = ivy.array( + 0, dtype="float32" + ) # feature value is computed during training threshold = ivy.array(node.threshold, dtype="float32") if feature_value <= threshold: @@ -437,7 +326,7 @@ def _apply_sparse_csr(self, X): out[i] = self.nodes.index(node) return out - + def decision_path(self, X): if issparse(X): return self._decision_path_sparse_csr(X) @@ -445,10 +334,11 @@ def decision_path(self, X): return self._decision_path_dense(X) def _decision_path_dense(self, X): - # Check input if not isinstance(X, ivy.data_classes.array.array.Array): - raise ValueError("X should be a ivy.data_classes.array.array.Array, got %s" % type(X)) + raise ValueError( + "X should be a ivy.data_classes.array.array.Array, got %s" % type(X) + ) if X.dtype != "float32": raise ValueError("X.dtype should be float32, got %s" % X.dtype) @@ -469,7 +359,7 @@ def _decision_path_dense(self, X): # Add all external nodes while node.left_child != _TREE_LEAF: - indices[indptr[i + 1]] = node + indices[indptr[i + 1]] = node indptr[i + 1] += 1 if X[i, node.feature] <= node.threshold: @@ -481,16 +371,15 @@ def _decision_path_dense(self, X): indices[indptr[i + 1]] = node indptr[i + 1] += 1 - indices = indices[:indptr[n_samples]] + indices = indices[: indptr[n_samples]] data = ivy.ones(indices.shape, dtype="float32") - #csr_matrix is not implemented + # csr_matrix is not implemented out = csr_matrix((data, indices, indptr), shape=(n_samples, self.node_count)) return out - - #not tested - def _decision_path_sparse_csr(self, X): + # not tested + def _decision_path_sparse_csr(self, X): # Check input if not isspmatrix_csr(X): raise ValueError("X should be in csr_matrix format, got %s" % type(X)) @@ -543,7 +432,7 @@ def _decision_path_sparse_csr(self, X): indices[indptr[i + 1]] = node - self.nodes indptr[i + 1] += 1 - indices = indices[:indptr[n_samples]] + indices = indices[: indptr[n_samples]] data = ivy.ones(shape=len(indices), dtype="int32") out = csr_matrix((data, indices, indptr), shape=(n_samples, self.node_count)) @@ -564,11 +453,11 @@ def compute_node_depths(self): return depths.base - - ''' + """ This code is typically used after fitting a decision tree model to assess the importance of each feature in making predictions. The feature importances indicate the contribution of each feature to the overall decision-making process of the tree. - ''' + """ + def compute_feature_importances(self, normalize=True): # Compute the importance of each feature (variable). @@ -582,9 +471,9 @@ def compute_feature_importances(self, normalize=True): # Calculate the importance for the feature associated with this node. importance = ( - node.weighted_n_node_samples * node.impurity - - left.weighted_n_node_samples * left.impurity - - right.weighted_n_node_samples * right.impurity + node.weighted_n_node_samples * node.impurity + - left.weighted_n_node_samples * left.impurity + - right.weighted_n_node_samples * right.impurity ) importances[node.feature] += importance @@ -597,46 +486,47 @@ def compute_feature_importances(self, normalize=True): importances /= total_importance return importances - + def _get_value_ndarray(self): - shape = ( - int(self.node_count), - int(self.n_outputs), - int(self.max_n_classes) - ) + shape = (int(self.node_count), int(self.n_outputs), int(self.max_n_classes)) arr = ivy.array(self.value, dtype="float32").reshape(shape) return arr - - ''' + + """ this code creates a NumPy structured array that wraps the internal nodes of a decision tree. This array can be used to access and manipulate the tree's nodes efficiently in Python. - ''' + """ + def _get_node_tensor(self): # Create a tensor with a custom data type for the tree nodes nodes_tensor = ivy.zeros(self.node_count, dtype="float32") # Fill the tensor with node data for i, node in enumerate(self.nodes): - nodes_tensor[i] = ivy.array(( - node.impurity, - node.n_node_samples, - node.weighted_n_node_samples, - node.left_child, - node.right_child, - node.feature, - node.threshold, - node.missing_go_to_left, - ), dtype="float32") + nodes_tensor[i] = ivy.array( + ( + node.impurity, + node.n_node_samples, + node.weighted_n_node_samples, + node.left_child, + node.right_child, + node.feature, + node.threshold, + node.missing_go_to_left, + ), + dtype="float32", + ) # Attach a reference to the tree nodes_tensor.tree_reference = self return nodes_tensor - - ''' + + """ This code effectively computes the partial dependence values for a set of grid points based on a given decision tree. Partial dependence helps understand how the model's predictions change with variations in specific features while keeping other features constant. - ''' + """ + def compute_partial_dependence(self, X, target_features, out): weight_stack = ivy.zeros(self.node_count, dtype="float32") node_idx_stack = ivy.zeros(self.node_count, dtype="int32") @@ -655,12 +545,17 @@ def compute_partial_dependence(self, X, target_features, out): current_node = self.nodes[current_node_idx] if current_node.left_child == -1: # Leaf node - out[sample_idx] += weight_stack[stack_size] * self.value[current_node_idx] + out[sample_idx] += ( + weight_stack[stack_size] * self.value[current_node_idx] + ) else: # Non-leaf node is_target_feature = current_node.feature in target_features if is_target_feature: # Push left or right child on the stack - if X[sample_idx, current_node.feature] <= current_node.threshold: + if ( + X[sample_idx, current_node.feature] + <= current_node.threshold + ): node_idx_stack[stack_size] = current_node.left_child else: node_idx_stack[stack_size] = current_node.right_child @@ -669,24 +564,166 @@ def compute_partial_dependence(self, X, target_features, out): # Push both children onto the stack and give a weight proportional to the number of samples going through each branch left_child = current_node.left_child right_child = current_node.right_child - left_sample_frac = self.nodes[left_child].weighted_n_node_samples / current_node.weighted_n_node_samples + left_sample_frac = ( + self.nodes[left_child].weighted_n_node_samples + / current_node.weighted_n_node_samples + ) current_weight = weight_stack[stack_size] weight_stack[stack_size] = current_weight * left_sample_frac stack_size += 1 node_idx_stack[stack_size] = right_child - weight_stack[stack_size] = current_weight * (1 - left_sample_frac) + weight_stack[stack_size] = current_weight * ( + 1 - left_sample_frac + ) stack_size += 1 # Sanity check. Should never happen. if not (0.999 < ivy.sum(weight_stack) < 1.001): - raise ValueError("Total weight should be 1.0 but was %.9f" % ivy.sum(weight_stack)) + raise ValueError( + "Total weight should be 1.0 but was %.9f" % ivy.sum(weight_stack) + ) + + +class DepthFirstTreeBuilder(TreeBuilder): + """Build a decision tree in depth-first fashion.""" + + def __init__( + self, + splitter, + min_samples_split, + min_samples_leaf, + min_weight_leaf, + max_depth, + min_impurity_decrease, + ): + self.splitter = splitter + self.min_samples_split = min_samples_split + self.min_samples_leaf = min_samples_leaf + self.min_weight_leaf = min_weight_leaf + self.max_depth = max_depth + self.min_impurity_decrease = min_impurity_decrease + + def build( + self, tree, X, y, sample_weight=None, missing_values_in_feature_mask=None + ): + """Build a decision tree from the training set (X, y).""" + + # Check input + X, y, sample_weight = self._check_input(X, y, sample_weight) + + # Parameters + splitter = self.splitter + max_depth = self.max_depth + min_samples_leaf = self.min_samples_leaf + min_weight_leaf = self.min_weight_leaf + min_samples_split = self.min_samples_split + min_impurity_decrease = self.min_impurity_decrease + + # Recursive partition (without actual recursion) + splitter.init(X, y, sample_weight, missing_values_in_feature_mask) + + stack = [] + + # Push root node onto stack + stack.append( + StackRecord( + start=0, + end=splitter.n_samples, + depth=0, + parent=_TREE_UNDEFINED, + is_left=False, + impurity=INFINITY, + n_constant_features=0, + ) + ) + weighted_n_node_samples = ivy.zeros(1, dtype="float32") + while stack: + stack_record = stack.pop() + + start = stack_record.start + end = stack_record.end + depth = stack_record.depth + parent = stack_record.parent + is_left = stack_record.is_left + impurity = stack_record.impurity + n_constant_features = stack_record.n_constant_features + + n_node_samples = end - start + splitter.node_reset(start, end, weighted_n_node_samples) + + is_leaf = ( + depth >= max_depth + or n_node_samples < min_samples_split + or n_node_samples < 2 * min_samples_leaf + or ivy.sum(sample_weight[start:end]) < 2 * min_weight_leaf + ) + + if is_left: + impurity = splitter.node_impurity() + + is_leaf = is_leaf or impurity <= EPSILON + + if not is_leaf: + split = ( + SplitRecord() + ) # No idea what is SplitRecord in original code. Maybe this never gets called, not sure + splitter.node_split(impurity, split, n_constant_features) + is_leaf = ( + is_leaf + or split.pos >= end + or (split.improvement + EPSILON < min_impurity_decrease) + ) + + node_id = tree._add_node( + parent, + is_left, + is_leaf, + split.feature if not is_leaf else 0, + split.threshold if not is_leaf else 0, + impurity, + n_node_samples, + ivy.sum(sample_weight[start:end]), + split.missing_go_to_left, + ) + + splitter.node_value(tree.value + node_id * tree.value_stride) + + if not is_leaf: + # Push right child on stack + stack.append( + StackRecord( + start=split.pos, + end=end, + depth=depth + 1, + parent=node_id, + is_left=False, + impurity=split.impurity_right, + n_constant_features=n_constant_features, + ) + ) + # Push left child on stack + stack.append( + StackRecord( + start=start, + end=split.pos, + depth=depth + 1, + parent=node_id, + is_left=True, + impurity=split.impurity_left, + n_constant_features=n_constant_features, + ) + ) + + +# --- Helpers --- # +# --------------- # def _check_n_classes(n_classes, expected_dtype): if n_classes.ndim != 1: raise ValueError( - f"Wrong dimensions for n_classes from the pickle: " + "Wrong dimensions for n_classes from the pickle: " f"expected 1, got {n_classes.ndim}" ) @@ -704,7 +741,5 @@ def _check_n_classes(n_classes, expected_dtype): ) - - - - +_TREE_LEAF = TREE_LEAF +_TREE_UNDEFINED = TREE_UNDEFINED diff --git a/ivy/functional/frontends/sklearn/utils/_array_api.py b/ivy/functional/frontends/sklearn/utils/_array_api.py new file mode 100644 index 0000000000000..b4168f3990a10 --- /dev/null +++ b/ivy/functional/frontends/sklearn/utils/_array_api.py @@ -0,0 +1,247 @@ +"""Tools to support array_api.""" +import numpy +from .._config import get_config +import scipy.special as special + + +class _ArrayAPIWrapper: + """ + Sklearn specific Array API compatibility wrapper. + + This wrapper makes it possible for scikit-learn maintainers to + deal with discrepancies between different implementations of the + Python array API standard and its evolution over time. + + The Python array API standard specification: + https://data-apis.org/array-api/latest/ + + Documentation of the NumPy implementation: + https://numpy.org/neps/nep-0047-array-api-standard.html + """ + + def __init__(self, array_namespace): + self._namespace = array_namespace + + def __getattr__(self, name): + return getattr(self._namespace, name) + + def take(self, X, indices, *, axis): + # When array_api supports `take` we can use this directly + # https://github.com/data-apis/array-api/issues/177 + if self._namespace.__name__ == "numpy.array_api": + X_np = numpy.take(X, indices, axis=axis) + return self._namespace.asarray(X_np) + + # We only support axis in (0, 1) and ndim in (1, 2) because that is all we need + # in scikit-learn + if axis not in {0, 1}: + raise ValueError(f"Only axis in (0, 1) is supported. Got {axis}") + + if X.ndim not in {1, 2}: + raise ValueError(f"Only X.ndim in (1, 2) is supported. Got {X.ndim}") + + if axis == 0: + if X.ndim == 1: + selected = [X[i] for i in indices] + else: # X.ndim == 2 + selected = [X[i, :] for i in indices] + else: # axis == 1 + selected = [X[:, i] for i in indices] + return self._namespace.stack(selected, axis=axis) + + +class _NumPyApiWrapper: + """ + Array API compat wrapper for any numpy version. + + NumPy < 1.22 does not expose the numpy.array_api namespace. This + wrapper makes it possible to write code that uses the standard Array + API while working with any version of NumPy supported by scikit- + learn. + + See the `get_namespace()` public function for more details. + """ + + def __getattr__(self, name): + return getattr(numpy, name) + + def astype(self, x, dtype, *, copy=True, casting="unsafe"): + # astype is not defined in the top level NumPy namespace + return x.astype(dtype, copy=copy, casting=casting) + + def asarray(self, x, *, dtype=None, device=None, copy=None): + # Support copy in NumPy namespace + if copy is True: + return numpy.array(x, copy=True, dtype=dtype) + else: + return numpy.asarray(x, dtype=dtype) + + def unique_inverse(self, x): + return numpy.unique(x, return_inverse=True) + + def unique_counts(self, x): + return numpy.unique(x, return_counts=True) + + def unique_values(self, x): + return numpy.unique(x) + + def concat(self, arrays, *, axis=None): + return numpy.concatenate(arrays, axis=axis) + + +# --- Helpers --- # +# --------------- # + + +def _asarray_with_order(array, dtype=None, order=None, copy=None, xp=None): + """ + Helper to support the order kwarg only for NumPy-backed arrays. + + Memory layout parameter `order` is not exposed in the Array API standard, + however some input validation code in scikit-learn needs to work both + for classes and functions that will leverage Array API only operations + and for code that inherently relies on NumPy backed data containers with + specific memory layout constraints (e.g. our own Cython code). The + purpose of this helper is to make it possible to share code for data + container validation without memory copies for both downstream use cases: + the `order` parameter is only enforced if the input array implementation + is NumPy based, otherwise `order` is just silently ignored. + """ + if xp is None: + xp, _ = get_namespace(array) + if xp.__name__ in {"numpy", "numpy.array_api"}: + # Use NumPy API to support order + array = numpy.asarray(array, order=order, dtype=dtype) + return xp.asarray(array, copy=copy) + else: + return xp.asarray(array, dtype=dtype, copy=copy) + + +def _convert_to_numpy(array, xp): + """ + Convert X into a NumPy ndarray. + + Only works on cupy.array_api and numpy.array_api and is used for + testing. + """ + supported_array_api = ["numpy.array_api", "cupy.array_api"] + if xp.__name__ not in supported_array_api: + support_array_api_str = ", ".join(supported_array_api) + raise ValueError(f"Supported namespaces are: {support_array_api_str}") + + if xp.__name__ == "cupy.array_api": + return array._array.get() + else: + return numpy.asarray(array) + + +def _estimator_with_converted_arrays(estimator, converter): + """ + Create new estimator which converting all attributes that are arrays. + + Parameters + ---------- + estimator : Estimator + Estimator to convert + + converter : callable + Callable that takes an array attribute and returns the converted array. + + Returns + ------- + new_estimator : Estimator + Convert estimator + """ + from sklearn.base import clone + + new_estimator = clone(estimator) + for key, attribute in vars(estimator).items(): + if hasattr(attribute, "__array_namespace__") or isinstance( + attribute, numpy.ndarray + ): + attribute = converter(attribute) + setattr(new_estimator, key, attribute) + return new_estimator + + +def _expit(X): + xp, _ = get_namespace(X) + if xp.__name__ in {"numpy", "numpy.array_api"}: + return xp.asarray(special.expit(numpy.asarray(X))) + + return 1.0 / (1.0 + xp.exp(-X)) + + +# --- Main --- # +# ------------ # + + +def get_namespace(*arrays): + """ + Get namespace of arrays. + + Introspect `arrays` arguments and return their common Array API + compatible namespace object, if any. NumPy 1.22 and later can + construct such containers using the `numpy.array_api` namespace + for instance. + + See: https://numpy.org/neps/nep-0047-array-api-standard.html + + If `arrays` are regular numpy arrays, an instance of the + `_NumPyApiWrapper` compatibility wrapper is returned instead. + + Namespace support is not enabled by default. To enabled it + call: + + sklearn.set_config(array_api_dispatch=True) + + or: + + with sklearn.config_context(array_api_dispatch=True): + # your code here + + Otherwise an instance of the `_NumPyApiWrapper` + compatibility wrapper is always returned irrespective of + the fact that arrays implement the `__array_namespace__` + protocol or not. + + Parameters + ---------- + *arrays : array objects + Array objects. + + Returns + ------- + namespace : module + Namespace shared by array objects. + + is_array_api : bool + True of the arrays are containers that implement the Array API spec. + """ + # `arrays` contains one or more arrays, or possibly Python scalars (accepting + # those is a matter of taste, but doesn't seem unreasonable). + # Returns a tuple: (array_namespace, is_array_api) + + if not get_config()["array_api_dispatch"]: + return _NumPyApiWrapper(), False + + namespaces = { + x.__array_namespace__() if hasattr(x, "__array_namespace__") else None + for x in arrays + if not isinstance(x, (bool, int, float, complex)) + } + + if not namespaces: + # one could special-case np.ndarray above or use np.asarray here if + # older numpy versions need to be supported. + raise ValueError("Unrecognized array input") + + if len(namespaces) != 1: + raise ValueError(f"Multiple namespaces for array inputs: {namespaces}") + + (xp,) = namespaces + if xp is None: + # Use numpy as default + return _NumPyApiWrapper(), False + + return _ArrayAPIWrapper(xp), True diff --git a/ivy/functional/frontends/sklearn/utils/_isfinite.py b/ivy/functional/frontends/sklearn/utils/_isfinite.py new file mode 100644 index 0000000000000..86aada9f966c9 --- /dev/null +++ b/ivy/functional/frontends/sklearn/utils/_isfinite.py @@ -0,0 +1,40 @@ +import math + + +class FiniteStatus: + all_finite = 0 + has_nan = 1 + has_infinite = 2 + + +# --- Helpers --- # +# --------------- # + + +def _isfinite_allow_nan(a): + for v in a: + if math.isinf(v): + return FiniteStatus.has_infinite + return FiniteStatus.all_finite + + +def _isfinite_disable_nan(a): + for v in a: + if math.isnan(v): + return FiniteStatus.has_nan + elif math.isinf(v): + return FiniteStatus.has_infinite + return FiniteStatus.all_finite + + +# --- Main --- # +# ------------ # + + +def cy_isfinite(a, allow_nan=False): + result = FiniteStatus.all_finite + if allow_nan: + result = _isfinite_allow_nan(a) + else: + result = _isfinite_disable_nan(a) + return result diff --git a/ivy/functional/frontends/sklearn/utils/_isfinite.pyx b/ivy/functional/frontends/sklearn/utils/_isfinite.pyx new file mode 100644 index 0000000000000..20fdba0230d10 --- /dev/null +++ b/ivy/functional/frontends/sklearn/utils/_isfinite.pyx @@ -0,0 +1,50 @@ +# Author: John Kirkham, Meekail Zain, Thomas Fan + +from libc.math cimport isnan, isinf +from cython cimport floating + + +cpdef enum FiniteStatus: + all_finite = 0 + has_nan = 1 + has_infinite = 2 + + +def cy_isfinite(floating[::1] a, bint allow_nan=False): + cdef FiniteStatus result + with nogil: + result = _isfinite(a, allow_nan) + return result + + +cdef inline FiniteStatus _isfinite(floating[::1] a, bint allow_nan) nogil: + cdef floating* a_ptr = &a[0] + cdef Py_ssize_t length = len(a) + if allow_nan: + return _isfinite_allow_nan(a_ptr, length) + else: + return _isfinite_disable_nan(a_ptr, length) + + +cdef inline FiniteStatus _isfinite_allow_nan(floating* a_ptr, + Py_ssize_t length) nogil: + cdef Py_ssize_t i + cdef floating v + for i in range(length): + v = a_ptr[i] + if isinf(v): + return FiniteStatus.has_infinite + return FiniteStatus.all_finite + + +cdef inline FiniteStatus _isfinite_disable_nan(floating* a_ptr, + Py_ssize_t length) nogil: + cdef Py_ssize_t i + cdef floating v + for i in range(length): + v = a_ptr[i] + if isnan(v): + return FiniteStatus.has_nan + elif isinf(v): + return FiniteStatus.has_infinite + return FiniteStatus.all_finite \ No newline at end of file diff --git a/ivy/functional/frontends/sklearn/utils/class_weight.py b/ivy/functional/frontends/sklearn/utils/class_weight.py new file mode 100644 index 0000000000000..b3bea77e65e43 --- /dev/null +++ b/ivy/functional/frontends/sklearn/utils/class_weight.py @@ -0,0 +1,188 @@ +import ivy +from scipy import sparse + + +def compute_class_weight(class_weight, *, classes, y): + """ + Estimate class weights for unbalanced datasets. + + Parameters + ---------- + class_weight : dict, "balanced" or None + If "balanced", class weights will be given by + `n_samples / (n_classes * ivy.bincount(y))`. + If a dictionary is given, keys are classes and values are corresponding class + weights. + If `None` is given, the class weights will be uniform. + + classes : ndarray + Array of the classes occurring in the data, as given by + `ivy.unique_all(y_org)` with `y_org` the original class labels. + + y : array-like of shape (n_samples,) + Array of original class labels per sample. + + Returns + ------- + class_weight_vect : ndarray of shape (n_classes,) + Array with `class_weight_vect[i]` the weight for i-th class. + + References + ---------- + The "balanced" heuristic is inspired by + Logistic Regression in Rare Events Data, King, Zen, 2001. + """ + # Import error caused by circular imports. + from ..preprocessing import LabelEncoder + + if set(y) - set(classes): + raise ValueError("classes should include all valid labels that can be in y") + if class_weight is None or len(class_weight) == 0: + # uniform class weights + weight = ivy.ones(classes.shape[0], dtype=ivy.float64, order="C") + elif class_weight == "balanced": + # Find the weight of each class as present in y. + le = LabelEncoder() + y_ind = le.fit_transform(y) + if not all(ivy.isin(classes, le.classes_)): + raise ValueError("classes should have valid labels that are in y") + + recip_freq = len(y) / ( + len(le.classes_) * ivy.bincount(y_ind).astype(ivy.float64) + ) + weight = recip_freq[le.transform(classes)] + else: + # user-defined dictionary + weight = ivy.ones(classes.shape[0], dtype=ivy.float64, order="C") + unweighted_classes = [] + for i, c in enumerate(classes): + if c in class_weight: + weight[i] = class_weight[c] + else: + unweighted_classes.append(c) + + n_weighted_classes = len(classes) - len(unweighted_classes) + if unweighted_classes and n_weighted_classes != len(class_weight): + unweighted_classes_user_friendly_str = ivy.array( + unweighted_classes + ).tolist() + raise ValueError( + f"The classes, {unweighted_classes_user_friendly_str}, are not in" + " class_weight" + ) + + return weight + + +def compute_sample_weight(class_weight, y, *, indices=None): + """ + Estimate sample weights by class for unbalanced datasets. + + Parameters + ---------- + class_weight : dict, list of dicts, "balanced", or None + Weights associated with classes in the form `{class_label: weight}`. + If not given, all classes are supposed to have weight one. For + multi-output problems, a list of dicts can be provided in the same + order as the columns of y. + + Note that for multioutput (including multilabel) weights should be + defined for each class of every column in its own dict. For example, + for four-class multilabel classification weights should be + `[{0: 1, 1: 1}, {0: 1, 1: 5}, {0: 1, 1: 1}, {0: 1, 1: 1}]` instead of + `[{1:1}, {2:5}, {3:1}, {4:1}]`. + + The `"balanced"` mode uses the values of y to automatically adjust + weights inversely proportional to class frequencies in the input data: + `n_samples / (n_classes * ivy.bincount(y))`. + + For multi-output, the weights of each column of y will be multiplied. + + y : {array-like, sparse matrix} of shape (n_samples,) or (n_samples, n_outputs) + Array of original class labels per sample. + + indices : array-like of shape (n_subsample,), default=None + Array of indices to be used in a subsample. Can be of length less than + `n_samples` in the case of a subsample, or equal to `n_samples` in the + case of a bootstrap subsample with repeated indices. If `None`, the + sample weight will be calculated over the full sample. Only `"balanced"` + is supported for `class_weight` if this is provided. + + Returns + ------- + sample_weight_vect : ndarray of shape (n_samples,) + Array with sample weights as applied to the original `y`. + """ + + # Ensure y is 2D. Sparse matrices are already 2D. + if not sparse.issparse(y): + y = ivy.atleast_1d(y) + if y.ndim == 1: + y = ivy.reshape(y, (-1, 1)) + n_outputs = y.shape[1] + + if indices is not None and class_weight != "balanced": + raise ValueError( + "The only valid class_weight for subsampling is 'balanced'. " + f"Given {class_weight}." + ) + elif n_outputs > 1: + if class_weight is None or isinstance(class_weight, dict): + raise ValueError( + "For multi-output, class_weight should be a list of dicts, or the " + "string 'balanced'." + ) + elif isinstance(class_weight, list) and len(class_weight) != n_outputs: + raise ValueError( + "For multi-output, number of elements in class_weight should match " + f"number of outputs. Got {len(class_weight)} element(s) while having " + f"{n_outputs} outputs." + ) + + expanded_class_weight = [] + for k in range(n_outputs): + if sparse.issparse(y): + # Ok to densify a single column at a time + y_full = y[:, [k]].toarray().flatten() + else: + y_full = y[:, k] + classes_full = ivy.unique_all(y_full) + classes_missing = None + + if class_weight == "balanced" or n_outputs == 1: + class_weight_k = class_weight + else: + class_weight_k = class_weight[k] + + if indices is not None: + # Get class weights for the subsample, covering all classes in + # case some labels that were present in the original data are + # missing from the sample. + y_subsample = y_full[indices] + classes_subsample = ivy.unique_all(y_subsample) + + weight_k = ivy.take( + compute_class_weight( + class_weight_k, classes=classes_subsample, y=y_subsample + ), + ivy.searchsorted(classes_subsample, classes_full), + mode="clip", + ) + + classes_missing = set(classes_full) - set(classes_subsample) + else: + weight_k = compute_class_weight( + class_weight_k, classes=classes_full, y=y_full + ) + + weight_k = weight_k[ivy.searchsorted(classes_full, y_full)] + + if classes_missing: + # Make missing classes' weight zero + weight_k[ivy.isin(y_full, list(classes_missing))] = 0.0 + + expanded_class_weight.append(weight_k) + + expanded_class_weight = ivy.prod(expanded_class_weight, axis=0, dtype=ivy.float64) + + return expanded_class_weight diff --git a/ivy/functional/frontends/sklearn/utils/fixes.py b/ivy/functional/frontends/sklearn/utils/fixes.py new file mode 100644 index 0000000000000..003edad8a8233 --- /dev/null +++ b/ivy/functional/frontends/sklearn/utils/fixes.py @@ -0,0 +1,4 @@ +# --- Helpers --- # +# --------------- # +def _object_dtype_isnan(X): + return X != X diff --git a/ivy/functional/frontends/sklearn/utils/multiclass.py b/ivy/functional/frontends/sklearn/utils/multiclass.py index 16e83ba2f8459..45c072fe7c08f 100644 --- a/ivy/functional/frontends/sklearn/utils/multiclass.py +++ b/ivy/functional/frontends/sklearn/utils/multiclass.py @@ -1,6 +1,34 @@ import ivy +def check_classification_targets(y): + """ + Ensure that target y is of a non-regression type. + + Only the following target types (as defined in type_of_target) are allowed: + 'binary', 'multiclass', 'multiclass-multioutput', + 'multilabel-indicator', 'multilabel-sequences' + + Parameters + ---------- + y : array-like + Target values. + """ + y_type = type_of_target(y, input_name="y") + if y_type not in [ + "binary", + "multiclass", + "multiclass-multioutput", + "multilabel-indicator", + "multilabel-sequences", + ]: + raise ValueError( + f"Unknown label type: {y_type}. Maybe you are trying to fit a " + "classifier, which expects discrete classes on a " + "regression target with continuous values." + ) + + # reapeated utility function def type_of_target(y, input_name="y"): # purely utility function diff --git a/ivy/functional/frontends/sklearn/utils/validation.py b/ivy/functional/frontends/sklearn/utils/validation.py index b7cf8f30d5491..982ab8b49d83a 100644 --- a/ivy/functional/frontends/sklearn/utils/validation.py +++ b/ivy/functional/frontends/sklearn/utils/validation.py @@ -1,6 +1,407 @@ +import numpy as np import ivy from ivy.functional.frontends.numpy.func_wrapper import to_ivy_arrays_and_back from ivy.func_wrapper import with_unsupported_dtypes +import numbers +from _array_api import get_namespace, _asarray_with_order +from contextlib import suppress +import warnings +import scipy.sparse as sp +from _isfinite import cy_isfinite, FiniteStatus +from .._config import get_config as _get_config +from fixes import _object_dtype_isnan +from numpy.core.numeric import ComplexWarning + + +# --- Helpers --- # +# --------------- # + + +@to_ivy_arrays_and_back +def _assert_all_finite( + X, allow_nan=False, msg_dtype=None, estimator_name=None, input_name="" +): + """Like assert_all_finite, but only for ndarray.""" + + xp, _ = get_namespace(X) + + if _get_config()["assume_finite"]: + return + + X = xp.asarray(X) + + # for object dtype data, we only check for NaNs (GH-13254) + if X.dtype == ivy.dtype("object") and not allow_nan: + if _object_dtype_isnan(X).any(): + raise ValueError("Input contains NaN") + + # We need only consider float arrays, hence can early return for all else. + if X.dtype.kind not in "fc": + return + + # First try an O(n) time, O(1) space solution for the common case that + # everything is finite; fall back to O(n) space `ivy.isinf/isnan` or custom + # Cython implementation to prevent false positives and provide a detailed + # error message. + with np.errstate(over="ignore"): + first_pass_isfinite = xp.isfinite(xp.sum(X)) + if first_pass_isfinite: + return + # Cython implementation doesn't support FP16 or complex numbers + use_cython = ( + xp is np and X.data.contiguous and X.dtype.type in {ivy.float32, ivy.float64} + ) + if use_cython: + out = cy_isfinite(X.reshape(-1), allow_nan=allow_nan) + has_nan_error = False if allow_nan else out == FiniteStatus.has_nan + has_inf = out == FiniteStatus.has_infinite + else: + has_inf = ivy.isinf(X).any() + has_nan_error = False if allow_nan else xp.isnan(X).any() + if has_inf or has_nan_error: + if has_nan_error: + type_err = "NaN" + else: + msg_dtype = msg_dtype if msg_dtype is not None else X.dtype + type_err = f"infinity or a value too large for {msg_dtype!r}" + padded_input_name = input_name + " " if input_name else "" + msg_err = f"Input {padded_input_name}contains {type_err}." + if estimator_name and input_name == "X" and has_nan_error: + # Improve the error message on how to handle missing values in + # scikit-learn. + msg_err += ( + f"\n{estimator_name} does not accept missing values" + " encoded as NaN natively. For supervised learning, you might want" + " to consider sklearn.ensemble.HistGradientBoostingClassifier and" + " Regressor which accept missing values encoded as NaNs natively." + " Alternatively, it is possible to preprocess the data, for" + " instance by using an imputer transformer in a pipeline or drop" + " samples with missing values. See" + " https://scikit-learn.org/stable/modules/impute.html" + " You can find a list of all estimators that handle NaN values" + " at the following page:" + " https://scikit-learn.org/stable/modules/impute.html" + "#estimators-that-handle-nan-values" + ) + raise ValueError(msg_err) + + +@to_ivy_arrays_and_back +def _check_estimator_name(estimator): + if estimator is not None: + if isinstance(estimator, str): + return estimator + else: + return estimator.__class__.__name__ + return None + + +@to_ivy_arrays_and_back +def _check_large_sparse(X, accept_large_sparse=False): + """Raise a ValueError if X has 64bit indices and accept_large_sparse=False.""" + if not accept_large_sparse: + supported_indices = ["int32"] + if X.getformat() == "coo": + index_keys = ["col", "row"] + elif X.getformat() in ["csr", "csc", "bsr"]: + index_keys = ["indices", "indptr"] + else: + return + for key in index_keys: + indices_datatype = getattr(X, key).dtype + if indices_datatype not in supported_indices: + raise ValueError( + "Only sparse matrices with 32-bit integer" + " indices are accepted. Got %s indices." % indices_datatype + ) + + +@to_ivy_arrays_and_back +def _check_sample_weight( + sample_weight, X, dtype=None, copy=False, only_non_negative=False +): + """ + Validate sample weights. + + Note that passing sample_weight=None will output an array of ones. + Therefore, in some cases, you may want to protect the call with: + if sample_weight is not None: + sample_weight = _check_sample_weight(...) + + Parameters + ---------- + sample_weight : {ndarray, Number or None}, shape (n_samples,) + Input sample weights. + + X : {ndarray, list, sparse matrix} + Input data. + + only_non_negative : bool, default=False, + Whether or not the weights are expected to be non-negative. + + .. versionadded:: 1.0 + + dtype : dtype, default=None + dtype of the validated `sample_weight`. + If None, and the input `sample_weight` is an array, the dtype of the + input is preserved; otherwise an array with the default numpy dtype + is be allocated. If `dtype` is not one of `float32`, `float64`, + `None`, the output will be of dtype `float64`. + + copy : bool, default=False + If True, a copy of sample_weight will be created. + + Returns + ------- + sample_weight : ndarray of shape (n_samples,) + Validated sample weight. It is guaranteed to be "C" contiguous. + """ + n_samples = _num_samples(X) + + if dtype is not None and dtype not in [ivy.float32, ivy.float64]: + dtype = ivy.float64 + + if sample_weight is None: + sample_weight = ivy.ones(n_samples, dtype=dtype) + elif isinstance(sample_weight, numbers.Number): + sample_weight = ivy.full(n_samples, sample_weight, dtype=dtype) + else: + if dtype is None: + dtype = [ivy.float64, ivy.float32] + sample_weight = check_array( + sample_weight, + accept_sparse=False, + ensure_2d=False, + dtype=dtype, + order="C", + copy=copy, + input_name="sample_weight", + ) + if sample_weight.ndim != 1: + raise ValueError("Sample weights must be 1D array or scalar") + + if sample_weight.shape != (n_samples,): + raise ValueError( + "sample_weight.shape == {}, expected {}!".format( + sample_weight.shape, (n_samples,) + ) + ) + + if only_non_negative: + check_non_negative(sample_weight, "`sample_weight`") + + return sample_weight + + +@to_ivy_arrays_and_back +def _ensure_no_complex_data(array): + if ( + hasattr(array, "dtype") + and array.dtype is not None + and hasattr(array.dtype, "kind") + and array.dtype.kind == "c" + ): + raise ValueError("Complex data not supported\n{}\n".format(array)) + + +@to_ivy_arrays_and_back +def _ensure_sparse_format( + spmatrix, + accept_sparse, + dtype, + copy, + force_all_finite, + accept_large_sparse, + estimator_name=None, + input_name="", +): + """ + Convert a sparse matrix to a given format. + + Checks the sparse format of spmatrix and converts if necessary. + + Parameters + ---------- + spmatrix : sparse matrix + Input to validate and convert. + + accept_sparse : str, bool or list/tuple of str + String[s] representing allowed sparse matrix formats ('csc', + 'csr', 'coo', 'dok', 'bsr', 'lil', 'dia'). If the input is sparse but + not in the allowed format, it will be converted to the first listed + format. True allows the input to be any format. False means + that a sparse matrix input will raise an error. + + dtype : str, type or None + Data type of result. If None, the dtype of the input is preserved. + + copy : bool + Whether a forced copy will be triggered. If copy=False, a copy might + be triggered by a conversion. + + force_all_finite : bool or 'allow-nan' + Whether to raise an error on ivy.inf, ivy.nan, pd.NA in X. The + possibilities are: + + - True: Force all values of X to be finite. + - False: accepts ivy.inf, ivy.nan, pd.NA in X. + - 'allow-nan': accepts only ivy.nan and pd.NA values in X. Values cannot + be infinite. + + .. versionadded:: 0.20 + ``force_all_finite`` accepts the string ``'allow-nan'``. + + .. versionchanged:: 0.23 + Accepts `pd.NA` and converts it into `ivy.nan` + + + estimator_name : str, default=None + The estimator name, used to construct the error message. + + input_name : str, default="" + The data name used to construct the error message. In particular + if `input_name` is "X" and the data has NaN values and + allow_nan is False, the error message will link to the imputer + documentation. + + Returns + ------- + spmatrix_converted : sparse matrix. + Matrix that is ensured to have an allowed type. + """ + if dtype is None: + dtype = spmatrix.dtype + + changed_format = False + + if isinstance(accept_sparse, str): + accept_sparse = [accept_sparse] + + # Indices dtype validation + _check_large_sparse(spmatrix, accept_large_sparse) + + if accept_sparse is False: + raise TypeError( + "A sparse matrix was passed, but dense " + "data is required. Use X.toarray() to " + "convert to a dense numpy array." + ) + elif isinstance(accept_sparse, (list, tuple)): + if len(accept_sparse) == 0: + raise ValueError( + "When providing 'accept_sparse' " + "as a tuple or list, it must contain at " + "least one string value." + ) + # ensure correct sparse format + if spmatrix.format not in accept_sparse: + # create new with correct sparse + spmatrix = spmatrix.asformat(accept_sparse[0]) + changed_format = True + elif accept_sparse is not True: + # any other type + raise ValueError( + "Parameter 'accept_sparse' should be a string, " + "boolean or list of strings. You provided " + "'accept_sparse={}'.".format(accept_sparse) + ) + + if dtype != spmatrix.dtype: + # convert dtype + spmatrix = spmatrix.astype(dtype) + elif copy and not changed_format: + # force copy + spmatrix = spmatrix.copy() + + if force_all_finite: + if not hasattr(spmatrix, "data"): + warnings.warn( + "Can't check %s sparse matrix for nan or inf." % spmatrix.format, + stacklevel=2, + ) + else: + _assert_all_finite( + spmatrix.data, + allow_nan=force_all_finite == "allow-nan", + estimator_name=estimator_name, + input_name=input_name, + ) + + return spmatrix + + +@to_ivy_arrays_and_back +def _num_samples(x): + """Return number of samples in array-like x.""" + message = "Expected sequence or array-like, got %s" % type(x) + if hasattr(x, "fit") and callable(x.fit): + # Don't get num_samples from an ensembles length! + raise TypeError(message) + + if not hasattr(x, "__len__") and not hasattr(x, "shape"): + if hasattr(x, "__array__"): + x = ivy.asarray(x) + else: + raise TypeError(message) + + if hasattr(x, "shape") and x.shape is not None: + if len(x.shape) == 0: + raise TypeError( + "Singleton array %r cannot be considered a valid collection." % x + ) + # Check that shape is returning an integer or default to len + # Dask dataframes may not return numeric shape[0] value + if isinstance(x.shape[0], numbers.Integral): + return x.shape[0] + + try: + return len(x) + except TypeError as type_error: + raise TypeError(message) from type_error + + +@to_ivy_arrays_and_back +def _pandas_dtype_needs_early_conversion(pd_dtype): + """Return True if pandas extension pd_dtype need to be converted early.""" + # Check these early for pandas versions without extension dtypes + from pandas.api.types import ( + is_bool_dtype, + is_sparse, + is_float_dtype, + is_integer_dtype, + ) + + if is_bool_dtype(pd_dtype): + # bool and extension booleans need early converstion because __array__ + # converts mixed dtype dataframes into object dtypes + return True + + if is_sparse(pd_dtype): + # Sparse arrays will be converted later in `check_array` + return False + + try: + from pandas.api.types import is_extension_array_dtype + except ImportError: + return False + + if is_sparse(pd_dtype) or not is_extension_array_dtype(pd_dtype): + # Sparse arrays will be converted later in `check_array` + # Only handle extension arrays for integer and floats + return False + elif is_float_dtype(pd_dtype): + # Float ndarrays can normally support nans. They need to be converted + # first to map pd.NA to ivy.nan + return True + elif is_integer_dtype(pd_dtype): + # XXX: Warn when converting from a high integer to a float + return True + + return False + + +# --- Main --- # +# ------------ # @to_ivy_arrays_and_back @@ -16,6 +417,398 @@ def as_float_array(X, *, copy=True, force_all_finite=True): return ivy.asarray(X, dtype=return_dtype) +@to_ivy_arrays_and_back +def assert_all_finite( + X, + *, + allow_nan=False, + estimator_name=None, + input_name="", +): + """ + Throw a ValueError if X contains NaN or infinity. + + Parameters + ---------- + X : {ndarray, sparse matrix} + The input data. + + allow_nan : bool, default=False + If True, do not throw error when `X` contains NaN. + + estimator_name : str, default=None + The estimator name, used to construct the error message. + + input_name : str, default="" + The data name used to construct the error message. In particular + if `input_name` is "X" and the data has NaN values and + allow_nan is False, the error message will link to the imputer + documentation. + """ + _assert_all_finite( + X.data if sp.issparse(X) else X, + allow_nan=allow_nan, + estimator_name=estimator_name, + input_name=input_name, + ) + + +@to_ivy_arrays_and_back +def check_array( + array, + accept_sparse=False, + *, + accept_large_sparse=True, + dtype="numeric", + order=None, + copy=False, + force_all_finite=True, + ensure_2d=True, + allow_nd=False, + ensure_min_samples=1, + ensure_min_features=1, + estimator=None, + input_name="", +): + """ + Input validation on an array, list, sparse matrix or similar. + + By default, the input is checked to be a non-empty 2D array containing + only finite values. If the dtype of the array is object, attempt + converting to float, raising on failure. + + Parameters + ---------- + array : object + Input object to check / convert. + + accept_sparse : str, bool or list/tuple of str, default=False + String[s] representing allowed sparse matrix formats, such as 'csc', + 'csr', etc. If the input is sparse but not in the allowed format, + it will be converted to the first listed format. True allows the input + to be any format. False means that a sparse matrix input will + raise an error. + + accept_large_sparse : bool, default=True + If a CSR, CSC, COO or BSR sparse matrix is supplied and accepted by + accept_sparse, accept_large_sparse=False will cause it to be accepted + only if its indices are stored with a 32-bit dtype. + + .. versionadded:: 0.20 + + dtype : 'numeric', type, list of type or None, default='numeric' + Data type of result. If None, the dtype of the input is preserved. + If "numeric", dtype is preserved unless array.dtype is object. + If dtype is a list of types, conversion on the first type is only + performed if the dtype of the input is not in the list. + + order : {'F', 'C'} or None, default=None + Whether an array will be forced to be fortran or c-style. + When order is None (default), then if copy=False, nothing is ensured + about the memory layout of the output array; otherwise (copy=True) + the memory layout of the returned array is kept as close as possible + to the original array. + + copy : bool, default=False + Whether a forced copy will be triggered. If copy=False, a copy might + be triggered by a conversion. + + force_all_finite : bool or 'allow-nan', default=True + Whether to raise an error on ivy.inf, ivy.nan, pd.NA in array. The + possibilities are: + + - True: Force all values of array to be finite. + - False: accepts ivy.inf, ivy.nan, pd.NA in array. + - 'allow-nan': accepts only ivy.nan and pd.NA values in array. Values + cannot be infinite. + + .. versionadded:: 0.20 + ``force_all_finite`` accepts the string ``'allow-nan'``. + + .. versionchanged:: 0.23 + Accepts `pd.NA` and converts it into `ivy.nan` + + ensure_2d : bool, default=True + Whether to raise a value error if array is not 2D. + + allow_nd : bool, default=False + Whether to allow array.ndim > 2. + + ensure_min_samples : int, default=1 + Make sure that the array has a minimum number of samples in its first + axis (rows for a 2D array). Setting to 0 disables this check. + + ensure_min_features : int, default=1 + Make sure that the 2D array has some minimum number of features + (columns). The default value of 1 rejects empty datasets. + This check is only enforced when the input data has effectively 2 + dimensions or is originally 1D and ``ensure_2d`` is True. Setting to 0 + disables this check. + + estimator : str or estimator instance, default=None + If passed, include the name of the estimator in warning messages. + + input_name : str, default="" + The data name used to construct the error message. In particular + if `input_name` is "X" and the data has NaN values and + allow_nan is False, the error message will link to the imputer + documentation. + + .. versionadded:: 1.1.0 + + Returns + ------- + array_converted : object + The converted and validated array. + """ + xp, is_array_api = get_namespace(array) + + # store reference to original array to check if copy is needed when + # function returns + array_orig = array + + # store whether originally we wanted numeric dtype + dtype_numeric = isinstance(dtype, str) and dtype == "numeric" + + dtype_orig = getattr(array, "dtype", None) + if not hasattr(dtype_orig, "kind"): + # not a data type (e.g. a column named dtype in a pandas DataFrame) + dtype_orig = None + + # check if the object contains several dtypes (typically a pandas + # DataFrame), and store them. If not, store None. + dtypes_orig = None + pandas_requires_conversion = False + if hasattr(array, "dtypes") and hasattr(array.dtypes, "__array__"): + # throw warning if columns are sparse. If all columns are sparse, then + # array.sparse exists and sparsity will be preserved (later). + with suppress(ImportError): + from pandas.api.types import is_sparse + + if not hasattr(array, "sparse") and array.dtypes.apply(is_sparse).any(): + warnings.warn( + "pandas.DataFrame with sparse columns found." + "It will be converted to a dense numpy array." + ) + + dtypes_orig = list(array.dtypes) + pandas_requires_conversion = any( + _pandas_dtype_needs_early_conversion(i) for i in dtypes_orig + ) + if all(isinstance(dtype_iter, ivy.dtype) for dtype_iter in dtypes_orig): + dtype_orig = ivy.result_type(*dtypes_orig) + + elif hasattr(array, "iloc") and hasattr(array, "dtype"): + # array is a pandas series + pandas_requires_conversion = _pandas_dtype_needs_early_conversion(array.dtype) + if isinstance(array.dtype, ivy.dtype): + dtype_orig = array.dtype + else: + # Set to None to let array.astype work out the best dtype + dtype_orig = None + + if dtype_numeric: + if dtype_orig is not None and dtype_orig.kind == "O": + # if input is object, convert to float. + dtype = xp.float64 + else: + dtype = None + + if isinstance(dtype, (list, tuple)): + if dtype_orig is not None and dtype_orig in dtype: + # no dtype conversion required + dtype = None + else: + # dtype conversion required. Let's select the first element of the + # list of accepted types. + dtype = dtype[0] + + if pandas_requires_conversion: + # pandas dataframe requires conversion earlier to handle extension dtypes with + # nans + # Use the original dtype for conversion if dtype is None + new_dtype = dtype_orig if dtype is None else dtype + array = array.astype(new_dtype) + # Since we converted here, we do not need to convert again later + dtype = None + + if force_all_finite not in (True, False, "allow-nan"): + raise ValueError( + 'force_all_finite should be a bool or "allow-nan". Got {!r} instead'.format( + force_all_finite + ) + ) + + estimator_name = _check_estimator_name(estimator) + context = " by %s" % estimator_name if estimator is not None else "" + + # When all dataframe columns are sparse, convert to a sparse array + if hasattr(array, "sparse") and array.ndim > 1: + with suppress(ImportError): + from pandas.api.types import is_sparse + + if array.dtypes.apply(is_sparse).all(): + # DataFrame.sparse only supports `to_coo` + array = array.sparse.to_coo() + if array.dtype == ivy.dtype("object"): + unique_dtypes = set([dt.subtype.name for dt in array_orig.dtypes]) + if len(unique_dtypes) > 1: + raise ValueError( + "Pandas DataFrame with mixed sparse extension arrays " + "generated a sparse matrix with object dtype which " + "can not be converted to a scipy sparse matrix." + "Sparse extension arrays should all have the same " + "numeric type." + ) + + if sp.issparse(array): + _ensure_no_complex_data(array) + array = _ensure_sparse_format( + array, + accept_sparse=accept_sparse, + dtype=dtype, + copy=copy, + force_all_finite=force_all_finite, + accept_large_sparse=accept_large_sparse, + estimator_name=estimator_name, + input_name=input_name, + ) + else: + # If ivy.array(..) gives ComplexWarning, then we convert the warning + # to an error. This is needed because specifying a non complex + # dtype to the function converts complex to real dtype, + # thereby passing the test made in the lines following the scope + # of warnings context manager. + with warnings.catch_warnings(): + try: + warnings.simplefilter("error", ComplexWarning) + if dtype is not None and ivy.dtype(dtype).kind in "iu": + # Conversion float -> int should not contain NaN or + # inf (numpy#14412). We cannot use casting='safe' because + # then conversion float -> int would be disallowed. + array = _asarray_with_order(array, order=order, xp=xp) + if array.dtype.kind == "f": + _assert_all_finite( + array, + allow_nan=False, + msg_dtype=dtype, + estimator_name=estimator_name, + input_name=input_name, + ) + array = xp.astype(array, dtype, copy=False) + else: + array = _asarray_with_order(array, order=order, dtype=dtype, xp=xp) + except ComplexWarning as complex_warning: + raise ValueError( + "Complex data not supported\n{}\n".format(array) + ) from complex_warning + + # It is possible that the ivy.array(..) gave no warning. This happens + # when no dtype conversion happened, for example dtype = None. The + # result is that ivy.array(..) produces an array of complex dtype + # and we need to catch and raise exception for such cases. + _ensure_no_complex_data(array) + + if ensure_2d: + # If input is scalar raise error + if array.ndim == 0: + raise ValueError( + "Expected 2D array, got scalar array instead:\narray={}.\n" + "Reshape your data either using array.reshape(-1, 1) if " + "your data has a single feature or array.reshape(1, -1) " + "if it contains a single sample.".format(array) + ) + # If input is 1D raise error + if array.ndim == 1: + raise ValueError( + "Expected 2D array, got 1D array instead:\narray={}.\n" + "Reshape your data either using array.reshape(-1, 1) if " + "your data has a single feature or array.reshape(1, -1) " + "if it contains a single sample.".format(array) + ) + + if dtype_numeric and array.dtype.kind in "USV": + raise ValueError( + "dtype='numeric' is not compatible with arrays of bytes/strings." + "Convert your data to numeric values explicitly instead." + ) + if not allow_nd and array.ndim >= 3: + raise ValueError( + "Found array with dim %d. %s expected <= 2." + % (array.ndim, estimator_name) + ) + + if force_all_finite: + _assert_all_finite( + array, + input_name=input_name, + estimator_name=estimator_name, + allow_nan=force_all_finite == "allow-nan", + ) + + if ensure_min_samples > 0: + n_samples = _num_samples(array) + if n_samples < ensure_min_samples: + raise ValueError( + "Found array with %d sample(s) (shape=%s) while a" + " minimum of %d is required%s." + % (n_samples, array.shape, ensure_min_samples, context) + ) + + if ensure_min_features > 0 and array.ndim == 2: + n_features = array.shape[1] + if n_features < ensure_min_features: + raise ValueError( + "Found array with %d feature(s) (shape=%s) while" + " a minimum of %d is required%s." + % (n_features, array.shape, ensure_min_features, context) + ) + + if copy: + if xp.__name__ in {"numpy", "numpy.array_api"}: + # only make a copy if `array` and `array_orig` may share memory` + if np.may_share_memory(array, array_orig): + array = _asarray_with_order( + array, dtype=dtype, order=order, copy=True, xp=xp + ) + else: + # always make a copy for non-numpy arrays + array = _asarray_with_order( + array, dtype=dtype, order=order, copy=True, xp=xp + ) + + return array + + +@to_ivy_arrays_and_back +def check_non_negative(X, whom): + """ + Check if there is any negative value in an array. + + Parameters + ---------- + X : {array-like, sparse matrix} + Input data. + + whom : str + Who passed X to this function. + """ + xp, _ = get_namespace(X) + # avoid X.min() on sparse matrix since it also sorts the indices + if sp.issparse(X): + if X.format in ["lil", "dok"]: + X = X.tocsr() + if X.data.size == 0: + X_min = 0 + else: + X_min = X.data.min() + else: + X_min = xp.min(X) + + if X_min < 0: + raise ValueError("Negative values in data passed to %s" % whom) + + @with_unsupported_dtypes({"1.3.0 and below": ("complex",)}, "sklearn") @to_ivy_arrays_and_back def column_or_1d(y, *, warn=False): From ddfd42a8ba4a6e592fabd71e65c0f2118a695b0f Mon Sep 17 00:00:00 2001 From: Gadri Ebenezer Date: Sat, 9 Sep 2023 20:46:50 +0000 Subject: [PATCH 013/117] Frontend Paddle: pixel_unshuffle (#23289) Co-authored-by: nathzi1505 <41519676+nathzi1505@users.noreply.github.com> --- .../frontends/paddle/nn/functional/vision.py | 42 ++++++++++++++++++ .../test_nn/test_functional/test_vision.py | 44 +++++++++++++++++++ 2 files changed, 86 insertions(+) diff --git a/ivy/functional/frontends/paddle/nn/functional/vision.py b/ivy/functional/frontends/paddle/nn/functional/vision.py index 653ed76189da5..4e98183b4ce4d 100644 --- a/ivy/functional/frontends/paddle/nn/functional/vision.py +++ b/ivy/functional/frontends/paddle/nn/functional/vision.py @@ -170,3 +170,45 @@ def pixel_shuffle(x, upscale_factor, data_format="NCHW"): return ivy.reshape( ivy.permute_dims(input_reshaped, (0, 1, 4, 2, 5, 3)), (b, oh, ow, oc) ) + + +@to_ivy_arrays_and_back +def pixel_unshuffle(x, downscale_factor, data_format="NCHW"): + if len(ivy.shape(x)) != 4: + raise ValueError( + "Input x should be 4D tensor, but received x with the shape of {}".format( + ivy.shape(x) + ) + ) + + if not isinstance(downscale_factor, int): + raise ValueError("Downscale factor must be int type") + + if downscale_factor <= 0: + raise ValueError("Downscale factor must be positive") + + if data_format not in ["NCHW", "NHWC"]: + raise ValueError( + "Attr(data_format) should be 'NCHW' or 'NHWC'." + "But recevie Attr(data_format): {} ".format(data_format) + ) + + if data_format == "NCHW": + b, c, h, w = ivy.shape(x) + oc = c * downscale_factor**2 + oh = h // downscale_factor + ow = w // downscale_factor + + x = ivy.reshape(x, (b, c, oh, downscale_factor, ow, downscale_factor)) + x = ivy.permute_dims(x, (0, 1, 3, 5, 2, 4)) + x = ivy.reshape(x, (b, oc, oh, ow)) + else: + b, h, w, c = ivy.shape(x) + oc = c * downscale_factor**2 + oh = h // downscale_factor + ow = w // downscale_factor + + x = ivy.reshape(x, (b, downscale_factor, oh, downscale_factor, ow, c)) + x = ivy.permute_dims(x, (0, 1, 3, 5, 2, 4)) + x = ivy.reshape(x, (b, oh, ow, oc)) + return x diff --git a/ivy_tests/test_ivy/test_frontends/test_paddle/test_nn/test_functional/test_vision.py b/ivy_tests/test_ivy/test_frontends/test_paddle/test_nn/test_functional/test_vision.py index 0659e33a95bf2..a0841ee2e9a82 100644 --- a/ivy_tests/test_ivy/test_frontends/test_paddle/test_nn/test_functional/test_vision.py +++ b/ivy_tests/test_ivy/test_frontends/test_paddle/test_nn/test_functional/test_vision.py @@ -174,3 +174,47 @@ def test_paddle_pixel_shuffle( data_format=data_format, backend_to_test=backend_fw, ) + + +# pixel_unshuffle +@handle_frontend_test( + fn_tree="paddle.nn.functional.pixel_unshuffle", + dtype_and_x=helpers.dtype_and_values( + available_dtypes=["float32", "float64"], + min_value=0, + min_num_dims=4, + max_num_dims=4, + min_dim_size=3, + ), + factor=helpers.ints(min_value=1), + data_format=st.sampled_from(["NCHW", "NHWC"]), +) +def test_paddle_pixel_unshuffle( + *, + dtype_and_x, + factor, + data_format, + on_device, + fn_tree, + frontend, + test_flags, + backend_fw, +): + input_dtype, x = dtype_and_x + if data_format == "NCHW": + assume(ivy.shape(x[0])[2] % factor == 0) + assume(ivy.shape(x[0])[3] % factor == 0) + else: + assume(ivy.shape(x[0])[1] % factor == 0) + assume(ivy.shape(x[0])[2] % factor == 0) + helpers.test_frontend_function( + input_dtypes=input_dtype, + frontend=frontend, + test_flags=test_flags, + fn_tree=fn_tree, + on_device=on_device, + x=x[0], + downscale_factor=factor, + data_format=data_format, + backend_to_test=backend_fw, + ) From c98b5a35066e3bfe0c651fa8d95e9344a572a2e0 Mon Sep 17 00:00:00 2001 From: muzakkirhussain011 <138936198+muzakkirhussain011@users.noreply.github.com> Date: Sun, 10 Sep 2023 02:17:19 +0530 Subject: [PATCH 014/117] =?UTF-8?q?Implemented=20precision=5Fscore=20funct?= =?UTF-8?q?ion=20in=20=5Fclassification.py=20and=20Added=20=E2=80=A6=20(#2?= =?UTF-8?q?3277)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: nathzi1505 <41519676+nathzi1505@users.noreply.github.com> --- .../sklearn/metrics/_classification.py | 26 ++++++++++++ .../test_metrics/test_classification.py | 41 +++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/ivy/functional/frontends/sklearn/metrics/_classification.py b/ivy/functional/frontends/sklearn/metrics/_classification.py index e6679505631b5..b5fece615691b 100644 --- a/ivy/functional/frontends/sklearn/metrics/_classification.py +++ b/ivy/functional/frontends/sklearn/metrics/_classification.py @@ -1,6 +1,7 @@ import ivy from ivy.functional.frontends.numpy.func_wrapper import to_ivy_arrays_and_back from sklearn.utils.multiclass import type_of_target +from ivy.utils.exceptions import IvyValueError @to_ivy_arrays_and_back @@ -17,3 +18,28 @@ def accuracy_score(y_true, y_pred, *, normalize=True, sample_weight=None): ret = ret / y_true.shape[0] ret = ret.astype("float64") return ret + + +@to_ivy_arrays_and_back +def precision_score(y_true, y_pred, *, average="binary", sample_weight=None): + # TODO: implement sample_weight + y_type = type_of_target(y_true) + if y_type.startswith("multilabel"): + true_positives = ivy.count_nonzero( + ivy.equal(y_true, y_pred).astype("int64"), axis=0 + ) + all_positives = ivy.count_nonzero(y_pred, axis=0) + else: + true_positives = ivy.count_nonzero( + ivy.equal(y_true, y_pred).astype("int64"), axis=1 + ) + all_positives = ivy.count_nonzero(y_pred) + if average == "binary": + precision = true_positives / all_positives + elif average == "micro": + precision = ivy.sum(true_positives) / ivy.sum(all_positives) + elif average == "macro": + precision = ivy.mean(true_positives / all_positives) + else: + raise IvyValueError("Invalid value for 'average'.") + return precision diff --git a/ivy_tests/test_ivy/test_frontends/test_sklearn/test_metrics/test_classification.py b/ivy_tests/test_ivy/test_frontends/test_sklearn/test_metrics/test_classification.py index 7977c77df3d03..d94f489a169b0 100644 --- a/ivy_tests/test_ivy/test_frontends/test_sklearn/test_metrics/test_classification.py +++ b/ivy_tests/test_ivy/test_frontends/test_sklearn/test_metrics/test_classification.py @@ -43,3 +43,44 @@ def test_sklearn_accuracy_score( normalize=normalize, sample_weight=None, ) + + +@handle_frontend_test( + fn_tree="sklearn.metrics.precision_score", + arrays_and_dtypes=helpers.dtype_and_values( + available_dtypes=helpers.get_dtypes("float_and_integer"), + num_arrays=2, + min_value=0, + max_value=1, # Precision score typically works with binary classification + shared_dtype=True, + shape=(helpers.ints(min_value=2, max_value=5)), + ), + average=st.sampled_from(["micro", "macro", "weighted"]), +) +def test_sklearn_precision_score( + arrays_and_dtypes, + on_device, + fn_tree, + frontend, + test_flags, + backend_fw, + average, +): + dtypes, values = arrays_and_dtypes + + # Ensure binary classification labels (0 and 1) + for i in range(2): + values[i] = np.round(values[i]) + + helpers.test_frontend_function( + input_dtypes=dtypes, + backend_to_test=backend_fw, + test_flags=test_flags, + fn_tree=fn_tree, + frontend=frontend, + on_device=on_device, + y_true=values[0], + y_pred=values[1], + average=average, + sample_weight=None, + ) From a6307a874077e0b88b6ef9d6795d91c7d248e223 Mon Sep 17 00:00:00 2001 From: AliTarekk Date: Sat, 9 Sep 2023 23:48:44 +0300 Subject: [PATCH 015/117] Feature/add frontend function pytorch eigh (#22637) Co-authored-by: nathzi1505 <41519676+nathzi1505@users.noreply.github.com> --- ivy/functional/frontends/torch/linalg.py | 6 +++ .../test_frontends/test_torch/test_linalg.py | 45 +++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/ivy/functional/frontends/torch/linalg.py b/ivy/functional/frontends/torch/linalg.py index 0b66b968144f7..f5020bcf19593 100644 --- a/ivy/functional/frontends/torch/linalg.py +++ b/ivy/functional/frontends/torch/linalg.py @@ -390,3 +390,9 @@ def vector_norm(input, ord=2, dim=None, keepdim=False, *, dtype=None, out=None): return ivy.vector_norm( input, axis=dim, keepdims=keepdim, ord=ord, out=out, dtype=dtype ) + +@with_supported_dtypes( + {"2.0.1 and below": ("float32", "float64", "complex32", "complex64", "complex128")}, "torch", +) +def eigh(A, UPLO="L", *, out=None): + return ivy.eigh(A, UPLO=UPLO, out=out) \ No newline at end of file diff --git a/ivy_tests/test_ivy/test_frontends/test_torch/test_linalg.py b/ivy_tests/test_ivy/test_frontends/test_torch/test_linalg.py index 51a4c7c6add48..fc0b29ba83dec 100644 --- a/ivy_tests/test_ivy/test_frontends/test_torch/test_linalg.py +++ b/ivy_tests/test_ivy/test_frontends/test_torch/test_linalg.py @@ -1364,3 +1364,48 @@ def test_torch_vector_norm( keepdim=kd, dtype=dtype[0], ) + + +@handle_frontend_test( + fn_tree="torch.linalg.eigh", + dtype_and_x=_get_dtype_and_matrix(dtype="valid", square=True, invertible=True), + UPLO=st.sampled_from(("L", "U")), +) +def test_torch_eigh( + *, + dtype_and_x, + UPLO, + on_device, + fn_tree, + frontend, + test_flags, + backend_fw, +): + dtype, x = dtype_and_x + x = np.array(x[0], dtype=dtype[0]) + # make symmetric positive-definite beforehand + x = np.matmul(x.T, x) + np.identity(x.shape[0]) * 1e-3 + + ret, frontend_ret = helpers.test_frontend_function( + input_dtypes=dtype, + backend_to_test=backend_fw, + frontend=frontend, + test_flags=test_flags, + fn_tree=fn_tree, + on_device=on_device, + test_values=False, + a=x, + UPLO=UPLO, + ) + ret = [ivy.to_numpy(x) for x in ret] + frontend_ret = [np.asarray(x) for x in frontend_ret] + + L, Q = ret + frontend_L, frontend_Q = frontend_ret + + assert_all_close( + ret_np=Q @ np.diag(L) @ Q.T, + ret_from_gt_np=frontend_Q @ np.diag(frontend_L) @ frontend_Q.T, + atol=1e-02, + + ) From 70ec79a2e3aa5b9c5a16cb89662d1a87e20779c6 Mon Sep 17 00:00:00 2001 From: ivy-branch Date: Sun, 10 Sep 2023 08:06:26 +0000 Subject: [PATCH 016/117] =?UTF-8?q?=F0=9F=A4=96=20Lint=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ivy/functional/frontends/torch/linalg.py | 14 +-- .../test_frontends/test_torch/test_linalg.py | 89 +++++++++---------- 2 files changed, 52 insertions(+), 51 deletions(-) diff --git a/ivy/functional/frontends/torch/linalg.py b/ivy/functional/frontends/torch/linalg.py index f5020bcf19593..dcfc92da0a9a9 100644 --- a/ivy/functional/frontends/torch/linalg.py +++ b/ivy/functional/frontends/torch/linalg.py @@ -76,6 +76,14 @@ def eigh(a, /, UPLO="L", out=None): return ivy.eigh(a, UPLO=UPLO, out=out) +@with_supported_dtypes( + {"2.0.1 and below": ("float32", "float64", "complex32", "complex64", "complex128")}, + "torch", +) +def eigh(A, UPLO="L", *, out=None): + return ivy.eigh(A, UPLO=UPLO, out=out) + + @to_ivy_arrays_and_back @with_supported_dtypes( {"2.0.1 and below": ("float32", "float64", "complex32", "complex64")}, "torch" @@ -390,9 +398,3 @@ def vector_norm(input, ord=2, dim=None, keepdim=False, *, dtype=None, out=None): return ivy.vector_norm( input, axis=dim, keepdims=keepdim, ord=ord, out=out, dtype=dtype ) - -@with_supported_dtypes( - {"2.0.1 and below": ("float32", "float64", "complex32", "complex64", "complex128")}, "torch", -) -def eigh(A, UPLO="L", *, out=None): - return ivy.eigh(A, UPLO=UPLO, out=out) \ No newline at end of file diff --git a/ivy_tests/test_ivy/test_frontends/test_torch/test_linalg.py b/ivy_tests/test_ivy/test_frontends/test_torch/test_linalg.py index fc0b29ba83dec..7d11bfa7b5531 100644 --- a/ivy_tests/test_ivy/test_frontends/test_torch/test_linalg.py +++ b/ivy_tests/test_ivy/test_frontends/test_torch/test_linalg.py @@ -516,6 +516,50 @@ def test_torch_eigh( ) +@handle_frontend_test( + fn_tree="torch.linalg.eigh", + dtype_and_x=_get_dtype_and_matrix(dtype="valid", square=True, invertible=True), + UPLO=st.sampled_from(("L", "U")), +) +def test_torch_eigh( + *, + dtype_and_x, + UPLO, + on_device, + fn_tree, + frontend, + test_flags, + backend_fw, +): + dtype, x = dtype_and_x + x = np.array(x[0], dtype=dtype[0]) + # make symmetric positive-definite beforehand + x = np.matmul(x.T, x) + np.identity(x.shape[0]) * 1e-3 + + ret, frontend_ret = helpers.test_frontend_function( + input_dtypes=dtype, + backend_to_test=backend_fw, + frontend=frontend, + test_flags=test_flags, + fn_tree=fn_tree, + on_device=on_device, + test_values=False, + a=x, + UPLO=UPLO, + ) + ret = [ivy.to_numpy(x) for x in ret] + frontend_ret = [np.asarray(x) for x in frontend_ret] + + L, Q = ret + frontend_L, frontend_Q = frontend_ret + + assert_all_close( + ret_np=Q @ np.diag(L) @ Q.T, + ret_from_gt_np=frontend_Q @ np.diag(frontend_L) @ frontend_Q.T, + atol=1e-02, + ) + + # eigvals @handle_frontend_test( fn_tree="torch.linalg.eigvals", @@ -1364,48 +1408,3 @@ def test_torch_vector_norm( keepdim=kd, dtype=dtype[0], ) - - -@handle_frontend_test( - fn_tree="torch.linalg.eigh", - dtype_and_x=_get_dtype_and_matrix(dtype="valid", square=True, invertible=True), - UPLO=st.sampled_from(("L", "U")), -) -def test_torch_eigh( - *, - dtype_and_x, - UPLO, - on_device, - fn_tree, - frontend, - test_flags, - backend_fw, -): - dtype, x = dtype_and_x - x = np.array(x[0], dtype=dtype[0]) - # make symmetric positive-definite beforehand - x = np.matmul(x.T, x) + np.identity(x.shape[0]) * 1e-3 - - ret, frontend_ret = helpers.test_frontend_function( - input_dtypes=dtype, - backend_to_test=backend_fw, - frontend=frontend, - test_flags=test_flags, - fn_tree=fn_tree, - on_device=on_device, - test_values=False, - a=x, - UPLO=UPLO, - ) - ret = [ivy.to_numpy(x) for x in ret] - frontend_ret = [np.asarray(x) for x in frontend_ret] - - L, Q = ret - frontend_L, frontend_Q = frontend_ret - - assert_all_close( - ret_np=Q @ np.diag(L) @ Q.T, - ret_from_gt_np=frontend_Q @ np.diag(frontend_L) @ frontend_Q.T, - atol=1e-02, - - ) From 33d00426ec7f482cda651507a917e41eaf2a282d Mon Sep 17 00:00:00 2001 From: Shreyansh Bardia <104841983+ShreyanshBardia@users.noreply.github.com> Date: Sun, 10 Sep 2023 21:02:46 +0530 Subject: [PATCH 017/117] fixed numpy.prod failing tests (#21858) Co-authored-by: ivy-branch --- ivy/functional/backends/tensorflow/creation.py | 3 +++ .../numpy/mathematical_functions/sums_products_differences.py | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/ivy/functional/backends/tensorflow/creation.py b/ivy/functional/backends/tensorflow/creation.py index 4ee6f22cced5c..05f3487191575 100644 --- a/ivy/functional/backends/tensorflow/creation.py +++ b/ivy/functional/backends/tensorflow/creation.py @@ -93,6 +93,9 @@ def asarray( try: ret = tf.convert_to_tensor(obj, dtype) except (TypeError, ValueError): + obj = ( + obj if isinstance(obj, tf.Tensor) else tf.convert_to_tensor(obj, tf.float64) + ) ret = tf.cast(obj, dtype) return tf.identity(ret) if copy else ret diff --git a/ivy/functional/frontends/numpy/mathematical_functions/sums_products_differences.py b/ivy/functional/frontends/numpy/mathematical_functions/sums_products_differences.py index 361a55c5c0dd0..a863284396fd9 100644 --- a/ivy/functional/frontends/numpy/mathematical_functions/sums_products_differences.py +++ b/ivy/functional/frontends/numpy/mathematical_functions/sums_products_differences.py @@ -8,6 +8,7 @@ from_zero_dim_arrays_to_scalar, handle_numpy_out, ) +import ivy.functional.frontends.numpy as np_frontend @handle_numpy_out @@ -114,9 +115,10 @@ def prod( initial=None, where=True, ): - if ivy.is_array(where): + if where is not True: x = ivy.where(where, x, ivy.default(out, ivy.ones_like(x)), out=out) if initial is not None: + initial = np_frontend.array(initial, dtype=dtype).tolist() if axis is not None: s = ivy.to_list(ivy.shape(x, as_array=True)) s[axis] = 1 From 09163ecb57f222ce3041f92802c456d426b0b1ea Mon Sep 17 00:00:00 2001 From: Vaatsalya <96314403+vaatsalya123@users.noreply.github.com> Date: Sun, 10 Sep 2023 12:15:05 -0400 Subject: [PATCH 018/117] Add patchelf for binaries handling. --- requirements/requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements/requirements.txt b/requirements/requirements.txt index e14c24869e34d..ade25f709cc43 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -11,3 +11,4 @@ requests pyvis dill astunparse +patchelf From c6e6167f3a1414f2f06b9c603e2477943859b8ed Mon Sep 17 00:00:00 2001 From: ivy-branch Date: Sun, 10 Sep 2023 16:43:49 +0000 Subject: [PATCH 019/117] =?UTF-8?q?Update=20demos=20=F0=9F=A4=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/demos | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/demos b/docs/demos index 0491d7886cb34..1ba2b6ee7fe97 160000 --- a/docs/demos +++ b/docs/demos @@ -1 +1 @@ -Subproject commit 0491d7886cb34dedc0416f0acc09891ca4cee70e +Subproject commit 1ba2b6ee7fe97fff704f103c90b2ec8ca19a703e From c010d1723df4a605a6834db67c758c79c46bda46 Mon Sep 17 00:00:00 2001 From: Akshay <59740725+Killua7362@users.noreply.github.com> Date: Sun, 10 Sep 2023 22:29:04 +0530 Subject: [PATCH 020/117] changed key and value to pos argument in multiheadattention (#23375) --- ivy/data_classes/array/layers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ivy/data_classes/array/layers.py b/ivy/data_classes/array/layers.py index 550a225b7e798..1bfbb3cc805db 100644 --- a/ivy/data_classes/array/layers.py +++ b/ivy/data_classes/array/layers.py @@ -394,10 +394,10 @@ def scaled_dot_product_attention( def multi_head_attention( self: ivy.Array, - /, - *, key: Optional[Union[ivy.Array, ivy.NativeArray]] = None, value: Optional[Union[ivy.Array, ivy.NativeArray]] = None, + /, + *, num_heads: int = 8, scale: Optional[float] = None, attention_mask: Optional[Union[ivy.Array, ivy.NativeArray]] = None, From 0d17da0a589a0c44916b5e6c083279287e36780e Mon Sep 17 00:00:00 2001 From: Akshay <59740725+Killua7362@users.noreply.github.com> Date: Sun, 10 Sep 2023 22:47:57 +0530 Subject: [PATCH 021/117] move from keyword arg to pos arg multihead (#23376) --- ivy/data_classes/container/layers.py | 4 ++-- ivy/functional/ivy/layers.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ivy/data_classes/container/layers.py b/ivy/data_classes/container/layers.py index b56ed5c84a9d6..8e28923ad204a 100644 --- a/ivy/data_classes/container/layers.py +++ b/ivy/data_classes/container/layers.py @@ -1094,10 +1094,10 @@ def _static_multi_head_attention( def multi_head_attention( self: ivy.Container, - /, - *, key: Optional[Union[ivy.Array, ivy.NativeArray, ivy.Container]] = None, value: Optional[Union[ivy.Array, ivy.NativeArray, ivy.Container]] = None, + /, + *, num_heads: Union[int, ivy.Container] = 8, scale: Optional[Union[float, ivy.Container]] = None, attention_mask: Optional[ diff --git a/ivy/functional/ivy/layers.py b/ivy/functional/ivy/layers.py index a471d62d08a78..a378c626db5da 100644 --- a/ivy/functional/ivy/layers.py +++ b/ivy/functional/ivy/layers.py @@ -710,10 +710,10 @@ def scaled_dot_product_attention( @handle_array_function def multi_head_attention( query: Union[ivy.Array, ivy.NativeArray], - /, - *, key: Optional[Union[ivy.Array, ivy.NativeArray]] = None, value: Optional[Union[ivy.Array, ivy.NativeArray]] = None, + /, + *, num_heads: int = 8, scale: Optional[float] = None, attention_mask: Optional[Union[ivy.Array, ivy.NativeArray]] = None, From 637bbd98f70783c3db180475dfde4e628db43128 Mon Sep 17 00:00:00 2001 From: Vaatsalya <96314403+vaatsalya123@users.noreply.github.com> Date: Sun, 10 Sep 2023 15:01:08 -0400 Subject: [PATCH 022/117] Update requirements.txt --- requirements/requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements/requirements.txt b/requirements/requirements.txt index ade25f709cc43..e14c24869e34d 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -11,4 +11,3 @@ requests pyvis dill astunparse -patchelf From 7695f21fbcc14f489ddccc28ec4d478722e370a9 Mon Sep 17 00:00:00 2001 From: ReneFabricius <65137969+ReneFabricius@users.noreply.github.com> Date: Mon, 11 Sep 2023 07:12:47 +0200 Subject: [PATCH 023/117] Ivy Functional API column_stack (#23110) --- .../array/experimental/manipulation.py | 36 ++++++ .../container/experimental/manipulation.py | 107 ++++++++++++++++++ .../backends/jax/experimental/manipulation.py | 6 + .../numpy/experimental/manipulation.py | 6 + .../paddle/experimental/manipulation.py | 4 + .../torch/experimental/manipulation.py | 6 + .../ivy/experimental/manipulation.py | 67 +++++++++++ .../hypothesis_helpers/array_helpers.py | 75 ++++++++++++ .../test_core/test_manipulation.py | 74 +++++++++++- 9 files changed, 380 insertions(+), 1 deletion(-) diff --git a/ivy/data_classes/array/experimental/manipulation.py b/ivy/data_classes/array/experimental/manipulation.py index 48d9586c24dba..3acf71a02d305 100644 --- a/ivy/data_classes/array/experimental/manipulation.py +++ b/ivy/data_classes/array/experimental/manipulation.py @@ -1334,3 +1334,39 @@ def soft_thresholding( thresholded tensor on which the operator has been applied """ return ivy.soft_thresholding(self._data, threshold, out=out) + + def column_stack( + self: ivy.Array, + arrays: Sequence[Union[ivy.Array, ivy.NativeArray]], + /, + *, + out: Optional[ivy.Array] = None, + ) -> ivy.Array: + """ + ivy.Array instance method variant of ivy.column_stack. + + This method simply wraps the function, and so the docstring for + ivy.column_stack also applies to this method with minimal + changes. + + Parameters + ---------- + self + Array that will be stacked at the begining of the provided array iterable. + arrays + Arrays to be stacked. + out + Output array. + + Returns + ------- + ret + Stacked input. + """ + if not isinstance(arrays, (list, tuple)): + arrays = [arrays] + if isinstance(arrays, tuple): + x = (self._data) + arrays + else: + x = [self._data] + arrays + return ivy.column_stack(x, out=out) diff --git a/ivy/data_classes/container/experimental/manipulation.py b/ivy/data_classes/container/experimental/manipulation.py index c0e37f7b1f81d..a8a43a3f75322 100644 --- a/ivy/data_classes/container/experimental/manipulation.py +++ b/ivy/data_classes/container/experimental/manipulation.py @@ -3678,3 +3678,110 @@ def soft_thresholding( thresholded tensor on which the operator has been applied """ return self.static_soft_thresholding(self, threshold, out=out) + + @staticmethod + def static_column_stack( + xs: Sequence[Union[ivy.Array, ivy.NativeArray, ivy.Container]], + /, + *, + key_chains: Optional[Union[List[str], Dict[str, str], ivy.Container]] = None, + to_apply: Union[bool, ivy.Container] = True, + prune_unapplied: Union[bool, ivy.Container] = False, + map_sequences: Union[bool, ivy.Container] = False, + out: Optional[ivy.Container] = None, + ) -> ivy.Container: + """ + ivy.Container static method variant of ivy.column_stack. + + This method simply wraps the function, and so the docstring for + ivy.column_stack also applies to this method with minimal + changes. + + Parameters + ---------- + xs + Container with leaves to stack. + + key_chains + The key-chains to apply or not apply the method to. Default is ``None``. + to_apply + If True, the method will be applied to key_chains, otherwise key_chains + will be skipped. Default is ``True``. + prune_unapplied + Whether to prune key_chains for which the function was not applied. + Default is ``False``. + map_sequences + Whether to also map method to sequences (lists, tuples). + Default is ``False``. + out + Optional output array, for writing the result to. + + Returns + ------- + ret + An output container with the results. + """ + return ContainerBase.cont_multi_map_in_function( + "column_stack", + xs, + key_chains=key_chains, + to_apply=to_apply, + prune_unapplied=prune_unapplied, + map_sequences=map_sequences, + out=out, + ) + + def column_stack( + self: ivy.Container, + /, + xs: Sequence[Union[ivy.Array, ivy.NativeArray, ivy.Container]], + *, + key_chains: Optional[Union[List[str], Dict[str, str], ivy.Container]] = None, + to_apply: Union[bool, ivy.Container] = True, + prune_unapplied: Union[bool, ivy.Container] = False, + map_sequences: Union[bool, ivy.Container] = False, + out: Optional[ivy.Container] = None, + ) -> ivy.Container: + """ + ivy.Container instance method variant of ivy.column_stack. + + This method simply wraps the function, and so the docstring for + ivy.column_stack also applies to this method with minimal + changes. + + Parameters + ---------- + self + Container with leaves to stack with leaves of other arrays/containers. + xs + Container with other leaves to join. + + key_chains + The key-chains to apply or not apply the method to. Default is ``None``. + to_apply + If True, the method will be applied to key_chains, otherwise key_chains + will be skipped. Default is ``True``. + prune_unapplied + Whether to prune key_chains for which the function was not applied. + Default is ``False``. + map_sequences + Whether to also map method to sequences (lists, tuples). + Default is ``False``. + out + Optional output array, for writing the result to. + + Returns + ------- + ret + An output container with the results. + """ + new_xs = xs.cont_copy() if ivy.is_ivy_container(xs) else list(xs).copy() + new_xs.insert(0, self.cont_copy()) + return self.static_column_stack( + new_xs, + key_chains=key_chains, + to_apply=to_apply, + prune_unapplied=prune_unapplied, + map_sequences=map_sequences, + out=out, + ) diff --git a/ivy/functional/backends/jax/experimental/manipulation.py b/ivy/functional/backends/jax/experimental/manipulation.py index 7258a7ae7ebe7..bd7575f2532f4 100644 --- a/ivy/functional/backends/jax/experimental/manipulation.py +++ b/ivy/functional/backends/jax/experimental/manipulation.py @@ -412,3 +412,9 @@ def fill_diagonal( a = a.at[:end:step].set(jnp.array(v).astype(a.dtype)) a = jnp.reshape(a, shape) return a + + +def column_stack( + arrays: Sequence[JaxArray], /, *, out: Optional[JaxArray] = None +) -> JaxArray: + return jnp.column_stack(arrays) diff --git a/ivy/functional/backends/numpy/experimental/manipulation.py b/ivy/functional/backends/numpy/experimental/manipulation.py index 53436987e2187..fe6971128e20c 100644 --- a/ivy/functional/backends/numpy/experimental/manipulation.py +++ b/ivy/functional/backends/numpy/experimental/manipulation.py @@ -477,3 +477,9 @@ def fill_diagonal( ) -> np.ndarray: np.fill_diagonal(a, v, wrap=wrap) return a + + +def column_stack( + arrays: Sequence[np.ndarray], /, *, out: Optional[np.ndarray] = None +) -> np.ndarray: + return np.column_stack(arrays) diff --git a/ivy/functional/backends/paddle/experimental/manipulation.py b/ivy/functional/backends/paddle/experimental/manipulation.py index 8b2a81dcb4d01..88099753ec83b 100644 --- a/ivy/functional/backends/paddle/experimental/manipulation.py +++ b/ivy/functional/backends/paddle/experimental/manipulation.py @@ -215,6 +215,10 @@ def vstack( return ivy.stack(arrays, axis=0) +@with_unsupported_device_and_dtypes( + {"2.5.1 and below": {"cpu": ("int16", "bfloat16")}}, + backend_version, +) def hstack( arrays: Sequence[paddle.Tensor], /, diff --git a/ivy/functional/backends/torch/experimental/manipulation.py b/ivy/functional/backends/torch/experimental/manipulation.py index 16c7cc3e5df76..2b6d0aa525429 100644 --- a/ivy/functional/backends/torch/experimental/manipulation.py +++ b/ivy/functional/backends/torch/experimental/manipulation.py @@ -462,3 +462,9 @@ def fill_diagonal( a = torch.where(w, v, a) a = torch.reshape(a, shape) return a + + +def column_stack( + arrays: Sequence[torch.Tensor], /, *, out: Optional[torch.Tensor] = None +) -> torch.Tensor: + return torch.column_stack(arrays) diff --git a/ivy/functional/ivy/experimental/manipulation.py b/ivy/functional/ivy/experimental/manipulation.py index 347e0d5497ab1..438b90ad4c195 100644 --- a/ivy/functional/ivy/experimental/manipulation.py +++ b/ivy/functional/ivy/experimental/manipulation.py @@ -2632,3 +2632,70 @@ def choose( ivy.array([20, 1, 12, 3]) """ return ivy.current_backend(arr).choose(arr, choices, out=out, mode=mode) + + +@handle_array_function +@inputs_to_ivy_arrays +@handle_nestable +@handle_exceptions +@handle_device_shifting +def column_stack( + arrays: Sequence[Union[ivy.Array, ivy.NativeArray]], + /, + *, + out: Optional[ivy.Array] = None, +) -> ivy.Array: + """ + Create a new array by horizontally stacking the arrays in arrays. + + Equivalent to `ivy.hstack(arrays)`, except each zero or one dimensional + array `x` in arrays is first reshaped into a `(x.size(), 1)` column + before being stacked horizontally. + + Parameters + ---------- + arrays + Arrays to be stacked. + out + Output array. + + Returns + ------- + ret + Stacked input. + + Examples + -------- + Arrays of different dtypes up to dimension 2. + >>> a0 = ivy.array(True) + >>> a1 = ivy.array([7]) + >>> a2 = ivy.array([[11.3, 13.7]]) + >>> ivy.column_stack((a0, a1, a2)) + ivy.array([[ 1. , 7. , 11.30000019, 13.69999981]]) + + Arrays of dimension 3. + >>> a = ivy.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]]) + >>> b = ivy.array([[[11, 12]], [[13, 14]]]) + >>> ivy.column_stack((a, b)) + ivy.array([[[ 1, 2], + [ 3, 4], + [11, 12]], + + [[ 5, 6], + [ 7, 8], + [13, 14]]]) + """ + arrays = [ivy.reshape(x, shape=(-1, 1)) if x.ndim < 2 else x for x in arrays] + + return ivy.hstack(arrays, out=out) + + +column_stack.mixed_backend_wrappers = { + "to_add": ( + "handle_backend_invalid", + "inputs_to_native_arrays", + "outputs_to_ivy_arrays", + "handle_out_argument", + ), + "to_skip": ("inputs_to_ivy_arrays",), +} diff --git a/ivy_tests/test_ivy/helpers/hypothesis_helpers/array_helpers.py b/ivy_tests/test_ivy/helpers/hypothesis_helpers/array_helpers.py index ce163e7d8a072..ecab2b63b01c0 100644 --- a/ivy_tests/test_ivy/helpers/hypothesis_helpers/array_helpers.py +++ b/ivy_tests/test_ivy/helpers/hypothesis_helpers/array_helpers.py @@ -2162,3 +2162,78 @@ def einsum_helper(draw): eq = "".join(eq_1) + "," + "".join(eq_2) + "->" + output_eq return eq, (value_1[0], value_2[0]), [dtype_1[0], dtype_2[0]] + + +@st.composite +def create_concatenable_arrays_dtypes( + draw, + min_num_dims, + max_num_dims, + min_num_arrays, + max_num_arrays, + concat_dim, + dtypes, + common_shape=None, +): + """ + Draws a random number of arrays with concatenable or stackable dimensions. Arrays + have same number of dimensions, but their shape can differ along a specified + dimension (concat_dim). If concat_dim is None, arrays have the same shape. Dtypes of + arrays can differ. + + Parameters + ---------- + min_num_dims + minimum number of dimensions + max_num_dims + maximum number of dimensions + min_num_arrays + minimum number of arrays + max_num_arrays + maximum number of arrays + concat_dim + dimension along which the shape of arrays can differ, + if None all the arrays will have the same shape + dtypes + list of dtypes from which array dtypes will be draws, + each array can have different dtype + given_common_shape + if not None, specifies the shape of the arrays + (dimension concat_dim can still be modified) + """ + num_arrays = draw(helpers.ints(min_value=min_num_arrays, max_value=max_num_arrays)) + if common_shape is None: + num_dims = draw(helpers.ints(min_value=min_num_dims, max_value=max_num_dims)) + common_shape = draw( + helpers.list_of_size( + x=helpers.ints(min_value=1, max_value=5), + size=num_dims, + ) + ) + else: + num_dims = len(common_shape) + input_dtypes = draw( + helpers.array_dtypes(num_arrays=num_arrays, available_dtypes=dtypes) + ) + array_shapes = [common_shape.copy() for i in range(num_arrays)] + if num_dims > 0 and concat_dim is not None: + unique_dims = draw( + helpers.list_of_size( + x=helpers.ints(min_value=1, max_value=5), + size=num_arrays, + ) + ) + for i in range(num_arrays): + array_shapes[i][concat_dim] = unique_dims[i] + + xs = list() + + for sh, dt in zip(array_shapes, input_dtypes): + x = draw( + helpers.array_values( + shape=sh, + dtype=dt, + ) + ) + xs.append(x) + return xs, input_dtypes diff --git a/ivy_tests/test_ivy/test_functional/test_experimental/test_core/test_manipulation.py b/ivy_tests/test_ivy/test_functional/test_experimental/test_core/test_manipulation.py index 234c75451f748..2b06ac879c412 100644 --- a/ivy_tests/test_ivy/test_functional/test_experimental/test_core/test_manipulation.py +++ b/ivy_tests/test_ivy/test_functional/test_experimental/test_core/test_manipulation.py @@ -6,7 +6,7 @@ # local import ivy import ivy_tests.test_ivy.helpers as helpers -from ivy_tests.test_ivy.helpers import handle_test +from ivy_tests.test_ivy.helpers import handle_test, create_concatenable_arrays_dtypes from ivy.functional.ivy.experimental.manipulation import _check_bounds from ivy_tests.test_ivy.test_functional.test_core.test_manipulation import _get_splits @@ -375,6 +375,60 @@ def _soft_thresholding_data(draw): return x_dtype + t_dtype, x, threshold +@st.composite +def _st_col_row_stack_arrays(draw, stack_dim): + ndim = draw(st.integers(min_value=2, max_value=5)) + dtype = draw(st.sampled_from(draw(helpers.get_dtypes("valid")))) + arrays, dtypes = draw( + create_concatenable_arrays_dtypes( + min_num_dims=ndim, + max_num_dims=ndim, + min_num_arrays=1, + max_num_arrays=3, + concat_dim=stack_dim, + dtypes=[dtype], + ) + ) + if ndim == 2: + non_stack_dim_len = arrays[0].shape[1 - stack_dim] + add_1D = draw(st.booleans()) + if add_1D: + arrays_1D, dtypes_1D = draw( + create_concatenable_arrays_dtypes( + min_num_dims=None, + max_num_dims=None, + min_num_arrays=1, + max_num_arrays=2, + concat_dim=None, + dtypes=[dtype], + common_shape=[non_stack_dim_len], + ) + ) + arrays += arrays_1D + dtypes += dtypes_1D + + if non_stack_dim_len == 1: + add_0D = draw(st.booleans()) + if add_0D: + arrays_0D, dtypes_0D = draw( + create_concatenable_arrays_dtypes( + min_num_dims=0, + max_num_dims=0, + min_num_arrays=1, + max_num_arrays=2, + concat_dim=None, + dtypes=[dtype], + ) + ) + arrays += arrays_0D + dtypes += dtypes_0D + + arrays_dtypes = draw(st.permutations(list(zip(arrays, dtypes)))) + arrays, dtypes = list(zip(*arrays_dtypes)) + + return list(arrays), list(dtypes) + + def _st_tuples_or_int(n_pairs, min_val=0): return st.one_of( st_tuples( @@ -552,6 +606,24 @@ def test_broadcast_shapes(*, shapes, test_flags, backend_fw, fn_name, on_device) ) +# column_stack +@handle_test( + fn_tree="functional.ivy.experimental.column_stack", + arrays_dtypes=_st_col_row_stack_arrays(stack_dim=1), + test_gradients=st.just(False), +) +def test_column_stack(*, arrays_dtypes, test_flags, backend_fw, fn_name, on_device): + arrays, dtypes = arrays_dtypes + helpers.test_function( + input_dtypes=dtypes, + test_flags=test_flags, + on_device=on_device, + backend_to_test=backend_fw, + fn_name=fn_name, + arrays=arrays, + ) + + # concat_from_sequence @handle_test( fn_tree="functional.ivy.experimental.concat_from_sequence", From d667dc92a8f03a70d4aba1e3e485bb960edd07d6 Mon Sep 17 00:00:00 2001 From: Vaibhav Deshpande <64426988+dash96@users.noreply.github.com> Date: Mon, 11 Sep 2023 13:05:08 +0530 Subject: [PATCH 024/117] fixing maxwell() for jax frontend (#23358) --- ivy/functional/frontends/jax/random.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/ivy/functional/frontends/jax/random.py b/ivy/functional/frontends/jax/random.py index c861bdade17f7..021fafc242aa7 100644 --- a/ivy/functional/frontends/jax/random.py +++ b/ivy/functional/frontends/jax/random.py @@ -270,13 +270,11 @@ def logistic(key, shape=(), dtype="float64"): }, "jax", ) -def maxwell(key, shape=None, dtype="float64"): +def maxwell(key, shape, dtype="float64"): seed = _get_seed(key) - # generate uniform random numbers between 0 and 1 - z = ivy.random_uniform(seed=seed, shape=shape, dtype=dtype) - # applying inverse transform sampling - x = (z**2) * ivy.exp(-(z**2) / 2) - return x + shape = shape + (3,) + random_normal = ivy.random_normal(seed=seed, shape=shape, dtype=dtype) + return ivy.vector_norm(random_normal, axis=-1) @handle_jax_dtype From b0d6f60fa3aa20598d2b3c595c7886764c8c5b28 Mon Sep 17 00:00:00 2001 From: Vaibhav Deshpande <137694306+stalemate1@users.noreply.github.com> Date: Mon, 11 Sep 2023 13:32:31 +0530 Subject: [PATCH 025/117] For double sided maxwell (#21264) --- ivy/functional/frontends/jax/random.py | 26 +++++++- .../test_frontends/test_jax/test_random.py | 64 ++++++++++++++++++- 2 files changed, 87 insertions(+), 3 deletions(-) diff --git a/ivy/functional/frontends/jax/random.py b/ivy/functional/frontends/jax/random.py index 021fafc242aa7..1afaf7a3b03af 100644 --- a/ivy/functional/frontends/jax/random.py +++ b/ivy/functional/frontends/jax/random.py @@ -277,6 +277,28 @@ def maxwell(key, shape, dtype="float64"): return ivy.vector_norm(random_normal, axis=-1) +@handle_jax_dtype +@to_ivy_arrays_and_back +@with_unsupported_dtypes( + { + "0.4.14 and below": ( + "uint32" + ) + }, + "jax", +) +def double_sided_maxwell(key, loc, scale, shape=(), dtype="float64"): + params_shapes = ivy.broadcast_shapes(ivy.shape(loc), ivy.shape(scale)) + if not shape: + shape = params_shapes + + shape = shape + params_shapes + maxwell_rvs = maxwell(key, shape=shape, dtype=dtype) + random_sign = rademacher(key, shape=shape, dtype=dtype) + + return random_sign * maxwell_rvs * scale+loc + + @handle_jax_dtype @to_ivy_arrays_and_back @with_unsupported_dtypes( @@ -387,11 +409,11 @@ def poisson(key, lam, shape=None, dtype=None): ) def rademacher(key, shape, dtype="int64"): seed = _get_seed(key) - b = ivy.bernoulli(ivy.array([0.5]), shape=shape, dtype="float32", seed=seed) + prob = ivy.full(shape, 0.5, dtype="float32") + b = ivy.bernoulli(prob, shape=shape, dtype="float32", seed=seed) b = ivy.astype(b, dtype) return 2 * b - 1 - @handle_jax_dtype @to_ivy_arrays_and_back @with_unsupported_dtypes( diff --git a/ivy_tests/test_ivy/test_frontends/test_jax/test_random.py b/ivy_tests/test_ivy/test_frontends/test_jax/test_random.py index 692dbccc62c62..43b8c726b2ac3 100644 --- a/ivy_tests/test_ivy/test_frontends/test_jax/test_random.py +++ b/ivy_tests/test_ivy/test_frontends/test_jax/test_random.py @@ -931,6 +931,68 @@ def call(): assert u.shape == v.shape +@pytest.mark.xfail +@handle_frontend_test( + fn_tree="jax.random.double_sided_maxwell", + dtype_key=helpers.dtype_and_values( + available_dtypes=["uint32"], + min_value=1, + max_value=2000, + min_num_dims=1, + max_num_dims=1, + min_dim_size=2, + max_dim_size=2, + ), + shape=helpers.get_shape(), + dtype=helpers.get_dtypes("float", full=False), + loc=st.integers(min_value=10, max_value=100), + scale=st.floats(min_value=0, max_value=100, exclude_min=True), + test_with_out=st.just(False), +) +def test_jax_double_sided_maxwell( + *, + dtype_key, + loc, + scale, + shape, + dtype, + on_device, + fn_tree, + frontend, + test_flags, + backend_fw, +): + input_dtype, key = dtype_key + + def call(): + return helpers.test_frontend_function( + input_dtypes=input_dtype, + frontend=frontend, + test_flags=test_flags, + fn_tree=fn_tree, + on_device=on_device, + test_values=False, + backend_to_test=backend_fw, + key=key[0], + loc=loc, + scale=scale, + shape=shape, + dtype=dtype[0], + ) + + ret = call() + + if not ivy.exists(ret): + return + + ret_np, ret_from_np = ret + ret_np = helpers.flatten_and_to_np(backend=backend_fw, ret=ret_np) + ret_from_np = helpers.flatten_and_to_np(backend=backend_fw, ret=ret_from_np) + for u, v in zip(ret_np, ret_from_np): + assert u.dtype == v.dtype + assert u.shape == v.shape + + @pytest.mark.xfail @handle_frontend_test( fn_tree="jax.random.multivariate_normal", @@ -1522,7 +1584,7 @@ def call(): @pytest.mark.xfail @handle_frontend_test( - fn_tree="jax.random.uniform", + fn_tree="jax.random.ball", dtype_key=helpers.dtype_and_values( available_dtypes=["uint32"], min_value=0, From af64f7d3474a8e7661d409e57d9a89f529abad4d Mon Sep 17 00:00:00 2001 From: ivy-branch Date: Mon, 11 Sep 2023 08:08:12 +0000 Subject: [PATCH 026/117] =?UTF-8?q?=F0=9F=A4=96=20Lint=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ivy/functional/frontends/jax/random.py | 41 +++--- .../test_frontends/test_jax/test_random.py | 124 +++++++++--------- 2 files changed, 81 insertions(+), 84 deletions(-) diff --git a/ivy/functional/frontends/jax/random.py b/ivy/functional/frontends/jax/random.py index 1afaf7a3b03af..694b5f1004ff3 100644 --- a/ivy/functional/frontends/jax/random.py +++ b/ivy/functional/frontends/jax/random.py @@ -145,6 +145,24 @@ def dirichlet(key, alpha, shape=None, dtype="float32"): return ivy.dirichlet(alpha, size=shape, dtype=dtype, seed=seed) +@handle_jax_dtype +@to_ivy_arrays_and_back +@with_unsupported_dtypes( + {"0.4.14 and below": "uint32"}, + "jax", +) +def double_sided_maxwell(key, loc, scale, shape=(), dtype="float64"): + params_shapes = ivy.broadcast_shapes(ivy.shape(loc), ivy.shape(scale)) + if not shape: + shape = params_shapes + + shape = shape + params_shapes + maxwell_rvs = maxwell(key, shape=shape, dtype=dtype) + random_sign = rademacher(key, shape=shape, dtype=dtype) + + return random_sign * maxwell_rvs * scale + loc + + @handle_jax_dtype @to_ivy_arrays_and_back @with_unsupported_dtypes( @@ -277,28 +295,6 @@ def maxwell(key, shape, dtype="float64"): return ivy.vector_norm(random_normal, axis=-1) -@handle_jax_dtype -@to_ivy_arrays_and_back -@with_unsupported_dtypes( - { - "0.4.14 and below": ( - "uint32" - ) - }, - "jax", -) -def double_sided_maxwell(key, loc, scale, shape=(), dtype="float64"): - params_shapes = ivy.broadcast_shapes(ivy.shape(loc), ivy.shape(scale)) - if not shape: - shape = params_shapes - - shape = shape + params_shapes - maxwell_rvs = maxwell(key, shape=shape, dtype=dtype) - random_sign = rademacher(key, shape=shape, dtype=dtype) - - return random_sign * maxwell_rvs * scale+loc - - @handle_jax_dtype @to_ivy_arrays_and_back @with_unsupported_dtypes( @@ -414,6 +410,7 @@ def rademacher(key, shape, dtype="int64"): b = ivy.astype(b, dtype) return 2 * b - 1 + @handle_jax_dtype @to_ivy_arrays_and_back @with_unsupported_dtypes( diff --git a/ivy_tests/test_ivy/test_frontends/test_jax/test_random.py b/ivy_tests/test_ivy/test_frontends/test_jax/test_random.py index 43b8c726b2ac3..7f9778edacea6 100644 --- a/ivy_tests/test_ivy/test_frontends/test_jax/test_random.py +++ b/ivy_tests/test_ivy/test_frontends/test_jax/test_random.py @@ -478,6 +478,68 @@ def call(): assert u.shape == v.shape +@pytest.mark.xfail +@handle_frontend_test( + fn_tree="jax.random.double_sided_maxwell", + dtype_key=helpers.dtype_and_values( + available_dtypes=["uint32"], + min_value=1, + max_value=2000, + min_num_dims=1, + max_num_dims=1, + min_dim_size=2, + max_dim_size=2, + ), + shape=helpers.get_shape(), + dtype=helpers.get_dtypes("float", full=False), + loc=st.integers(min_value=10, max_value=100), + scale=st.floats(min_value=0, max_value=100, exclude_min=True), + test_with_out=st.just(False), +) +def test_jax_double_sided_maxwell( + *, + dtype_key, + loc, + scale, + shape, + dtype, + on_device, + fn_tree, + frontend, + test_flags, + backend_fw, +): + input_dtype, key = dtype_key + + def call(): + return helpers.test_frontend_function( + input_dtypes=input_dtype, + frontend=frontend, + test_flags=test_flags, + fn_tree=fn_tree, + on_device=on_device, + test_values=False, + backend_to_test=backend_fw, + key=key[0], + loc=loc, + scale=scale, + shape=shape, + dtype=dtype[0], + ) + + ret = call() + + if not ivy.exists(ret): + return + + ret_np, ret_from_np = ret + ret_np = helpers.flatten_and_to_np(backend=backend_fw, ret=ret_np) + ret_from_np = helpers.flatten_and_to_np(backend=backend_fw, ret=ret_from_np) + for u, v in zip(ret_np, ret_from_np): + assert u.dtype == v.dtype + assert u.shape == v.shape + + @pytest.mark.xfail @handle_frontend_test( fn_tree="jax.random.exponential", @@ -931,68 +993,6 @@ def call(): assert u.shape == v.shape -@pytest.mark.xfail -@handle_frontend_test( - fn_tree="jax.random.double_sided_maxwell", - dtype_key=helpers.dtype_and_values( - available_dtypes=["uint32"], - min_value=1, - max_value=2000, - min_num_dims=1, - max_num_dims=1, - min_dim_size=2, - max_dim_size=2, - ), - shape=helpers.get_shape(), - dtype=helpers.get_dtypes("float", full=False), - loc=st.integers(min_value=10, max_value=100), - scale=st.floats(min_value=0, max_value=100, exclude_min=True), - test_with_out=st.just(False), -) -def test_jax_double_sided_maxwell( - *, - dtype_key, - loc, - scale, - shape, - dtype, - on_device, - fn_tree, - frontend, - test_flags, - backend_fw, -): - input_dtype, key = dtype_key - - def call(): - return helpers.test_frontend_function( - input_dtypes=input_dtype, - frontend=frontend, - test_flags=test_flags, - fn_tree=fn_tree, - on_device=on_device, - test_values=False, - backend_to_test=backend_fw, - key=key[0], - loc=loc, - scale=scale, - shape=shape, - dtype=dtype[0], - ) - - ret = call() - - if not ivy.exists(ret): - return - - ret_np, ret_from_np = ret - ret_np = helpers.flatten_and_to_np(backend=backend_fw, ret=ret_np) - ret_from_np = helpers.flatten_and_to_np(backend=backend_fw, ret=ret_from_np) - for u, v in zip(ret_np, ret_from_np): - assert u.dtype == v.dtype - assert u.shape == v.shape - - @pytest.mark.xfail @handle_frontend_test( fn_tree="jax.random.multivariate_normal", From e37911b91cdfa42def298e524f4974117a5d6e2a Mon Sep 17 00:00:00 2001 From: Jomer Barcenilla <89145244+JomBarce@users.noreply.github.com> Date: Mon, 11 Sep 2023 16:19:57 +0800 Subject: [PATCH 027/117] Reformatted array_equal (#23058) --- ivy/data_classes/array/general.py | 6 ++++++ ivy/data_classes/container/general.py | 17 ++++++++++++++--- ivy/functional/backends/tensorflow/general.py | 2 +- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/ivy/data_classes/array/general.py b/ivy/data_classes/array/general.py index 0cbc69521ade3..6320790c8b13d 100644 --- a/ivy/data_classes/array/general.py +++ b/ivy/data_classes/array/general.py @@ -807,6 +807,12 @@ def array_equal(self: ivy.Array, x: Union[ivy.Array, ivy.NativeArray], /) -> boo >>> c = a.array_equal(b) >>> print(c) True + + >>> i = ivy.array([1, 2]) + >>> j = ivy.array([1, 2, 3]) + >>> k = i.array_equal(j) + >>> print(k) + False """ return ivy.array_equal(self, x) diff --git a/ivy/data_classes/container/general.py b/ivy/data_classes/container/general.py index 6b7dc87e08a66..32c021edc72a4 100644 --- a/ivy/data_classes/container/general.py +++ b/ivy/data_classes/container/general.py @@ -4031,14 +4031,25 @@ def array_equal( >>> b = ivy.array([[-2., 1.], [1. ,2.]]) >>> c = ivy.array([[0., 1.], [1. ,0.]]) >>> d = ivy.array([[2., 1.], [1. ,2.]]) - >>> a0 = ivy.Container(a = a, b = b) - >>> a1 = ivy.Container(a = c, b = d) - >>> y = a0.array_equal(a1) + >>> a1 = ivy.Container(a = a, b = b) + >>> a2 = ivy.Container(a = c, b = d) + >>> y = a1.array_equal(a2) >>> print(y) { a: True, b: False } + + >>> x1 = ivy.Container(a=ivy.native_array([1, 0, 0]), + b=ivy.array([1, 2, 3])) + >>> x2 = ivy.Container(a=ivy.native_array([1, 0, 1]), + b=ivy.array([1, 2, 3])) + >>> y = x1.array_equal(x2) + >>> print(y) + { + a: False, + b: True + } """ return _ContainerWithGeneral._static_array_equal( self, diff --git a/ivy/functional/backends/tensorflow/general.py b/ivy/functional/backends/tensorflow/general.py index ba239549e3fdd..cb24872314909 100644 --- a/ivy/functional/backends/tensorflow/general.py +++ b/ivy/functional/backends/tensorflow/general.py @@ -37,7 +37,7 @@ def array_equal( /, ) -> bool: x0, x1 = ivy.promote_types_of_inputs(x0, x1) - return bool((tf.experimental.numpy.array_equal(x0, x1))) + return bool(tf.experimental.numpy.array_equal(x0, x1)) def container_types(): From f5531f4d1925b6c9e7b4989ee7c0af48ed61c112 Mon Sep 17 00:00:00 2001 From: Nripesh Niketan <86844847+NripeshN@users.noreply.github.com> Date: Mon, 11 Sep 2023 12:27:59 +0400 Subject: [PATCH 028/117] docs: ivy lint (#22991) Co-authored-by: Kareem Morsy --- docs/overview/deep_dive.rst | 28 +++++++------ docs/overview/deep_dive/formatting.rst | 4 +- docs/overview/deep_dive/ivy_lint.rst | 58 ++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 13 deletions(-) create mode 100644 docs/overview/deep_dive/ivy_lint.rst diff --git a/docs/overview/deep_dive.rst b/docs/overview/deep_dive.rst index 1fe0354d93881..024dfd599ad43 100644 --- a/docs/overview/deep_dive.rst +++ b/docs/overview/deep_dive.rst @@ -46,40 +46,43 @@ We're excited for you to get involved! 🦾 | (k) `Formatting `_ 📋 | How the code is automatically formatted | -| (l) `Function Arguments `_ 📑 +| (l) `Ivy Lint `_ 🧹 +| Ivy's Custom Code Formatters +| +| (m) `Function Arguments `_ 📑 | How to add the correct function arguments | -| (m) `Docstrings `_ 📄 +| (n) `Docstrings `_ 📄 | How to properly write docstrings | -| (n) `Docstring Examples `_ 💯 +| (o) `Docstring Examples `_ 💯 | How to add useful examples to the docstrings | -| (o) `Array API Tests `_ 🤝 +| (p) `Array API Tests `_ 🤝 | How we're borrowing the test suite from the Array API Standard | -| (p) `Ivy Tests `_ 🧪 +| (q) `Ivy Tests `_ 🧪 | How to add new tests for each Ivy function | -| (q) `Ivy Frontends `_ ➡ +| (r) `Ivy Frontends `_ ➡ | How to implement frontend functions | -| (r) `Ivy Frontend Tests `_ 🧪 +| (s) `Ivy Frontend Tests `_ 🧪 | How to add new tests for each frontend function | -| (s) `Exception Handling `_ ⚠ +| (t) `Exception Handling `_ ⚠ | How to handle exceptions and assertions in a function | -| (t) `Continuous Integration `_ 🔁 +| (u) `Continuous Integration `_ 🔁 | Ivy Tests running on the Repository | -| (u) `Gradients `_ 🔁 +| (v) `Gradients `_ 🔁 | Everything about our Gradients API | -| (v) `Operating Modes `_ 🧮 +| (w) `Operating Modes `_ 🧮 | Everything about modes Ivy can operate in, along with their purposes | -| (w) `Building the Docs Pipeline `_ 📚 +| (x) `Building the Docs Pipeline `_ 📚 | How are we building our docs @@ -99,6 +102,7 @@ We're excited for you to get involved! 🦾 deep_dive/inplace_updates.rst deep_dive/function_wrapping.rst deep_dive/formatting.rst + deep_dive/ivy_lint.rst deep_dive/function_arguments.rst deep_dive/docstrings.rst deep_dive/docstring_examples.rst diff --git a/docs/overview/deep_dive/formatting.rst b/docs/overview/deep_dive/formatting.rst index fa52cd490f053..c561f1fe04b97 100644 --- a/docs/overview/deep_dive/formatting.rst +++ b/docs/overview/deep_dive/formatting.rst @@ -27,7 +27,7 @@ We use the following linters: * `autoflake `_ * `docformatter `_ * `pydocstyle `_ -* `ivy-lint `_ (WIP 🚧) +* `ivy-lint `_ You can also take a look at our configuration for linting in `setup.cfg `_ file. @@ -76,6 +76,7 @@ You should expect to see something similar to the following output when you run flake8...................................................................Passed docformatter.............................................................Passed pydocstyle...............................................................Passed + ivy-lint.................................................................Passed [INFO] Restored changes from ~/.cache/pre-commit/patch1687898304-8072. [formatting-docs 3516aed563] Test commit 1 file changed, 1 insertion(+) @@ -98,6 +99,7 @@ If something goes wrong, you will see the following output: flake8...................................................................Passed docformatter.............................................................Passed pydocstyle...............................................................Passed + ivy-lint.................................................................Passed [INFO] Restored changes from ~/.cache/pre-commit/patch1687898304-8072. You will notice that some files have changed if you checked ``git status``, you'll need to add them and commit again. diff --git a/docs/overview/deep_dive/ivy_lint.rst b/docs/overview/deep_dive/ivy_lint.rst new file mode 100644 index 0000000000000..388e67bfd58b2 --- /dev/null +++ b/docs/overview/deep_dive/ivy_lint.rst @@ -0,0 +1,58 @@ +Ivy-Lint: Ivy's Custom Code Formatters +====================================== + +Overview +-------- + +``ivy-lint`` is a specialized suite of formatters crafted for the Ivy codebase. It addresses unique formatting requirements not catered to by standard Python formatters. While the suite currently highlights the ``FunctionOrderingFormatter``, we're continually expanding to include more formatters tailored to Ivy's needs. + +Existing Formatters +------------------- + +FunctionOrderingFormatter +~~~~~~~~~~~~~~~~~~~~~~~~~ + +This formatter ensures a standardized order of declarations within Python files, organizing functions, classes, and assignments based on a hierarchy designed for the Ivy codebase. + +**Purpose**: To bring a sense of uniformity and structure to the code files by sorting various Python declarations. + +**Target Files**: Specifically designed for frontends and tests. + +How the Formatter Works: +~~~~~~~~~~~~~~~~~~~~~~~~ + +1. **Header Management**: + - Removes pre-existing headers in the source code based on specific patterns. + +2. **Comments Handling**: + - Extracts code components along with their leading comments, ensuring that relevant comments are retained during the reordering process. + +3. **Dependency Handling**: + - Constructs dependency graphs to understand and maintain the relationships between classes and assignments. + +4. **Sorting Logic**: + - Prioritizes imports, followed by assignments based on certain dependencies, then classes, and finally functions. + - Preserves module-level docstrings at the top of the file. + - Organizes helper functions and primary functions into separate sections for clarity. + +5. **File Processing**: + - Processes files that align with certain patterns, rearranging their content as needed. + +Integration and Usage +--------------------- + +To get the best out of ``ivy-lint``, integrate it within a pre-commit hook. This ensures that whenever code changes are about to be committed, the suite checks and, if needed, formats the files to align with Ivy's standards. + +For comprehensive details on weaving ``ivy-lint`` into your development practices, kindly refer to our `formatting guide `_. + +Contribution +------------ + +We’re always thrilled to welcome contributions to ``ivy-lint``. If you're brimming with ideas for a new formatter or can enhance our existing ones, please connect with us either on our GitHub repository or our `discord `_ channel. + +Round Up +-------- + +``ivy-lint`` stands as a testament to Ivy's commitment to code clarity and uniformity. As the landscape of our needs shifts, we foresee further refining and expanding our suite of formatters. + +For all discussions or inquiries, you're always welcome on `discord `_ in the `formatting channel `_. From c6dc5974dc9ec59357bc075cba5299bfb6059334 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Alava=20Pe=C3=B1a?= Date: Mon, 11 Sep 2023 10:37:20 +0100 Subject: [PATCH 029/117] fix(docs): renamed ivy.get_backend to ivy.current_backend in backend setting deep dive (#23403) --- docs/overview/deep_dive/backend_setting.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/overview/deep_dive/backend_setting.rst b/docs/overview/deep_dive/backend_setting.rst index 3777089cfd13b..b2be3a2f0a7a5 100644 --- a/docs/overview/deep_dive/backend_setting.rst +++ b/docs/overview/deep_dive/backend_setting.rst @@ -30,23 +30,23 @@ It's helpful to look at an example: .. code-block:: python x = ivy.array([[2., 3.]]) - ivy.get_backend() + ivy.current_backend() .. code-block:: python y = ivy.multiply(torch.Tensor([3.]), torch.Tensor([4.])) - ivy.get_backend() + ivy.current_backend() .. code-block:: python ivy.set_backend('jax') z = ivy.matmul(jax.numpy.array([[2.,3.]]), jax.numpy.array([[5.],[6.]])) - ivy.get_backend() + ivy.current_backend() ivy.previous_backend() - ivy.get_backend() + ivy.current_backend() In the last example above, the moment any backend is set, it will be used over the `implicit_backend`_. From 1723568b3cd1e76c2efd2ba9eaebccd932cefc73 Mon Sep 17 00:00:00 2001 From: Sai-Suraj-27 Date: Mon, 11 Sep 2023 15:26:41 +0530 Subject: [PATCH 030/117] refactor: Reformatted and Refactored few files to make the code better and clean (#23357) --- .../dashboard_automation/update_db.py | 9 ++-- determine_test_coverage.py | 5 +- determine_tests.py | 11 ++-- duplicate.py | 2 +- run_tests.py | 36 +++++-------- run_tests_CLI/array_api_determine_tests.py | 6 +-- run_tests_CLI/array_api_run_tests.py | 17 ++++--- run_tests_CLI/cron_tests_multi_version.py | 12 ++--- run_tests_CLI/get_all_tests.py | 5 +- run_tests_CLI/setup_priority_tests.py | 17 +++---- run_tests_CLI/synchronize_db.py | 13 +++-- run_tests_CLI/test_dependencies.py | 27 +++++----- run_tests_pr.py | 51 +++++++++---------- scripts/backend_generation/generate.py | 36 ++++++------- scripts/eager_mode_benchmark/benchmark.py | 24 ++++----- setup_tests.py | 7 ++- 16 files changed, 130 insertions(+), 148 deletions(-) diff --git a/automation_tools/dashboard_automation/update_db.py b/automation_tools/dashboard_automation/update_db.py index 42c3f19cefa84..092e313725f91 100644 --- a/automation_tools/dashboard_automation/update_db.py +++ b/automation_tools/dashboard_automation/update_db.py @@ -22,9 +22,10 @@ def make_clickable(url, name): - return ''.format(name) + return ( + f'' + ) def update_test_results(): @@ -45,7 +46,7 @@ def update_test_results(): res = make_clickable(action_url + run_id, result_config[result]) collection.update_one( {"_id": test_configs[workflow][1]}, - {"$set": {backend + "." + submodule: res}}, + {"$set": {f"{backend}.{submodule}": res}}, upsert=True, ) return diff --git a/determine_test_coverage.py b/determine_test_coverage.py index e2fcbf0ab6691..588399a9ab8e5 100644 --- a/determine_test_coverage.py +++ b/determine_test_coverage.py @@ -17,8 +17,7 @@ test_names = get_all_tests() # Create a Dictionary of Test Names to Index -tests["index_mapping"] = test_names -tests["tests_mapping"] = {} +tests = {"index_mapping": test_names, "tests_mapping": {}} for i in range(len(test_names)): tests["tests_mapping"][test_names[i]] = i @@ -47,7 +46,7 @@ for directory in directories: for file_name in os.listdir(directory): if file_name.endswith("cover"): - file_name = directory + "/" + file_name + file_name = f"{directory}/{file_name}" if file_name not in tests: tests[file_name] = [] with open(file_name) as f: diff --git a/determine_tests.py b/determine_tests.py index d3a94ddc91fab..c422d2bee1e2e 100644 --- a/determine_tests.py +++ b/determine_tests.py @@ -44,15 +44,15 @@ def main(): modified_files = commit._parse_diff(diff_index) for file in modified_files: try: - file_name = file.new_path + ",cover" + file_name = f"{file.new_path},cover" except: # noqa continue if file_name not in tests.keys(): continue tests_file = tests[file_name] change = file.diff_parsed - added = set([x - 1 for (x, _) in change["added"]]) - deleted = set([x - 1 for (x, _) in change["deleted"]]) + added = {x - 1 for (x, _) in change["added"]} + deleted = {x - 1 for (x, _) in change["deleted"]} updated = added.intersection(deleted) added = added.difference(updated) deleted = deleted.difference(updated) @@ -121,9 +121,8 @@ def main(): relevant_added_tests.append(test) break added_tests = relevant_added_tests - else: - if len(added_tests) > 50: - added_tests = added_tests[:50] + elif len(added_tests) > 50: + added_tests = added_tests[:50] # Add these new_tests in the Mapping old_num_tests = len(old_tests) tests["index_mapping"] += added_tests diff --git a/duplicate.py b/duplicate.py index e7ac5767d8b2c..bd084f4ca411b 100644 --- a/duplicate.py +++ b/duplicate.py @@ -8,7 +8,7 @@ def get_all_functions_from_directory(root_dir, startswith="test"): print("Invalid directory") exit(1) functions_names = [] - for filename in glob.iglob(root_dir + "/**/*.py", recursive=True): + for filename in glob.iglob(f"{root_dir}/**/*.py", recursive=True): if len(filename) >= 2 and filename[:2] == "./": filename = filename[2:] filename = filename.replace(".py", "") diff --git a/run_tests.py b/run_tests.py index 8013eaf7a9cab..0832fd9cdbfe2 100644 --- a/run_tests.py +++ b/run_tests.py @@ -46,9 +46,10 @@ def make_clickable(url, name): - return ''.format(name) + return ( + f'' + ) def get_submodule(test_path): @@ -57,9 +58,9 @@ def get_submodule(test_path): if name in test_path: if name == "test_functional": if len(test_path) > 3 and test_path[3] == "test_experimental": - coll = db_dict["test_experimental/" + test_path[4]] + coll = db_dict[f"test_experimental/{test_path[4]}"] else: - coll = db_dict["test_functional/" + test_path[-2]] + coll = db_dict[f"test_functional/{test_path[-2]}"] else: coll = db_dict[name] break @@ -80,16 +81,16 @@ def update_individual_test_results( frontend_version=None, device=None, ): - key = submod + "." + backend + key = f"{submod}.{backend}" if backend_version is not None: backend_version = backend_version.replace(".", "_") - key += "." + backend_version + key += f".{backend_version}" if frontend_version is not None: frontend_version = frontend_version.replace(".", "_") - key += "." + frontend_version - key += "." + test + key += f".{frontend_version}" + key += f".{test}" if device: - key += "." + device + key += f".{device}" collection.update_one( {"_id": id}, {"$set": {key: result}}, @@ -99,10 +100,7 @@ def update_individual_test_results( def remove_from_db(collection, id, submod, backend, test): - collection.update_one( - {"_id": id}, - {"$unset": {submod + "." + backend + ".": test}}, - ) + collection.update_one({"_id": id}, {"$unset": {f"{submod}.{backend}.": test}}) return @@ -169,7 +167,7 @@ def run_multiversion_testing(): if len(sys.argv) > 8 and sys.argv[8] != "null": run_id = sys.argv[8] else: - run_id = "https://github.com/unifyai/ivy/actions/runs/" + workflow_id + run_id = f"https://github.com/unifyai/ivy/actions/runs/{workflow_id}" failed = False # GPU Testing with_gpu = False @@ -221,13 +219,7 @@ def run_multiversion_testing(): else: res = make_clickable(run_id, result_config["success"]) frontend_version = None - if ( - coll[0] == "numpy" - or coll[0] == "jax" - or coll[0] == "tensorflow" - or coll[0] == "torch" - or coll[0] == "paddle" - ): + if coll[0] in ["numpy", "jax", "tensorflow", "torch", "paddle"]: frontend_version = "latest-stable" if priority_flag: print("Updating Priority DB") diff --git a/run_tests_CLI/array_api_determine_tests.py b/run_tests_CLI/array_api_determine_tests.py index d15665b13330d..fade279139968 100644 --- a/run_tests_CLI/array_api_determine_tests.py +++ b/run_tests_CLI/array_api_determine_tests.py @@ -41,15 +41,15 @@ def determine_tests_line(_tests_file, _line, _tests_to_run): modified_files = commit._parse_diff(diff_index) for file in modified_files: try: - file_name = file.new_path + ",cover" + file_name = f"{file.new_path},cover" except: # noqa continue if file_name not in tests.keys(): continue tests_file = tests[file_name] change = file.diff_parsed - added = set([x - 1 for (x, _) in change["added"]]) - deleted = set([x - 1 for (x, _) in change["deleted"]]) + added = {x - 1 for (x, _) in change["added"]} + deleted = {x - 1 for (x, _) in change["deleted"]} updated = added.intersection(deleted) added = added.difference(updated) deleted = deleted.difference(updated) diff --git a/run_tests_CLI/array_api_run_tests.py b/run_tests_CLI/array_api_run_tests.py index d17ae3c4528aa..bf960761b1a64 100644 --- a/run_tests_CLI/array_api_run_tests.py +++ b/run_tests_CLI/array_api_run_tests.py @@ -11,9 +11,10 @@ def make_clickable(url, name): - return ''.format(name) + return ( + f'' + ) def get_submodule(test_path): @@ -34,14 +35,14 @@ def update_individual_test_results( backend_version=None, frontend_version=None, ): - key = submod + "." + backend + key = f"{submod}.{backend}" if backend_version is not None: backend_version = backend_version.replace(".", "_") - key += "." + backend_version + key += f".{backend_version}" if frontend_version is not None: frontend_version = frontend_version.replace(".", "_") - key += "." + frontend_version - key += "." + test + key += f".{frontend_version}" + key += f".{test}" collection.update_one( {"_id": id}, {"$set": {key: result}}, @@ -60,7 +61,7 @@ def main(): if len(sys.argv) > 5: run_id = sys.argv[5] else: - run_id = "https://github.com/unifyai/ivy/actions/runs/" + workflow_id + run_id = f"https://github.com/unifyai/ivy/actions/runs/{workflow_id}" failed = False cluster = MongoClient( f"mongodb+srv://deep-ivy:{mongo_key}@cluster0.qdvf8q3.mongodb.net/?retryWrites=true&w=majority" # noqa diff --git a/run_tests_CLI/cron_tests_multi_version.py b/run_tests_CLI/cron_tests_multi_version.py index 8efbadb0ef9e4..a3ad33eaa3f74 100644 --- a/run_tests_CLI/cron_tests_multi_version.py +++ b/run_tests_CLI/cron_tests_multi_version.py @@ -52,7 +52,7 @@ "numpy/1.24.2", ] jax_req = [ - jax_ver + "/" + jaxlib_ver for jax_ver in jax_only_req for jaxlib_ver in jaxlib_req + f"{jax_ver}/{jaxlib_ver}" for jax_ver in jax_only_req for jaxlib_ver in jaxlib_req ] framework_versions = { @@ -81,21 +81,19 @@ test_names_without_backend.append(test_name) for test_name in test_names_without_backend: - for backend, backend_versions in framework_versions.items(): + for backend_versions in framework_versions.values(): for backend_version in backend_versions: - test_backend = test_name + "," + backend_version + test_backend = f"{test_name},{backend_version}" if "test_frontends" in test_name: frontend = test_name[39:] frontend = frontend[: frontend.find("/")] frontend_versions = framework_versions.get(frontend, []) for frontend_version in frontend_versions: - test_names.append(test_backend + ";" + frontend_version) + test_names.append(f"{test_backend};{frontend_version}") else: test_names.append(test_backend) -test_names = list(set(test_names)) -test_names.sort() - +test_names = sorted(set(test_names)) # Run 150 tests in each iteration of the cron job num_tests = len(test_names) print(num_tests) diff --git a/run_tests_CLI/get_all_tests.py b/run_tests_CLI/get_all_tests.py index 213eb0cb0d8ba..3568424441b94 100644 --- a/run_tests_CLI/get_all_tests.py +++ b/run_tests_CLI/get_all_tests.py @@ -37,14 +37,13 @@ def extract_tests_from_dir(directory): def get_all_tests(): test_names_without_backend = extract_tests_from_dir("ivy_tests/test_ivy") - test_names_without_backend = list(set(test_names_without_backend)) - test_names_without_backend.sort() + test_names_without_backend = sorted(set(test_names_without_backend)) random.Random(4).shuffle(test_names_without_backend) test_names = [] for test_name in test_names_without_backend: for backend in BACKENDS: - test_backend = test_name + "," + backend + test_backend = f"{test_name},{backend}" test_names.append(test_backend) return test_names diff --git a/run_tests_CLI/setup_priority_tests.py b/run_tests_CLI/setup_priority_tests.py index 3fbff574d2a48..c0bb3f1442304 100644 --- a/run_tests_CLI/setup_priority_tests.py +++ b/run_tests_CLI/setup_priority_tests.py @@ -3,15 +3,14 @@ def main(): - write_file = open("tests_to_run", "w") - with open(sys.argv[1], "r") as f: - for test in f: - test = test.strip() - if test.startswith("ivy/"): - test = test[4:] - for backend in BACKENDS: - write_file.write(f"{test},{backend}\n") - write_file.close() + with open("tests_to_run", "w") as write_file: + with open(sys.argv[1], "r") as f: + for test in f: + test = test.strip() + if test.startswith("ivy/"): + test = test[4:] + for backend in BACKENDS: + write_file.write(f"{test},{backend}\n") if __name__ == "__main__": diff --git a/run_tests_CLI/synchronize_db.py b/run_tests_CLI/synchronize_db.py index fc309e961c998..93f80a2f35986 100644 --- a/run_tests_CLI/synchronize_db.py +++ b/run_tests_CLI/synchronize_db.py @@ -24,17 +24,16 @@ def keys_to_delete_from_db(all_tests, module, data, current_key=""): keys_for_deletion = [] for key, value in data.items(): - new_key = current_key + "." + key if current_key else key + new_key = f"{current_key}.{key}" if current_key else key # If this is a dictionary, recurse deeper if isinstance(value, dict): keys_for_deletion.extend(keys_to_delete_from_db(all_tests, value, new_key)) - # If the new_key is not in keys_to_keep, mark it for deletion elif key != "_id": components = new_key.split(".") submodule = components[0] function = components[-2] - test = module + "/" + submodule + "::" + function + test = f"{module}/{submodule}::{function}" if test not in all_tests: keys_for_deletion.append(".".join(components[:-1])) @@ -85,9 +84,9 @@ def get_submodule(test_path): if name in test_path: if name == "test_functional": if test_path[3] == "test_experimental": - coll = db_dict["test_experimental/" + test_path[4]] + coll = db_dict[f"test_experimental/{test_path[4]}"] else: - coll = db_dict["test_functional/" + test_path[-2]] + coll = db_dict[f"test_functional/{test_path[-2]}"] else: coll = db_dict[name] break @@ -99,12 +98,12 @@ def get_submodule(test_path): def process_test(test): coll, submod, test_fn = get_submodule(test) - return coll[0] + "/" + submod + "::" + test_fn + return f"{coll[0]}/{submod}::{test_fn}" def main(): all_tests = get_all_tests() - all_tests = set([process_test(test.split(",")[0].strip()) for test in all_tests]) + all_tests = {process_test(test.split(",")[0].strip()) for test in all_tests} mongo_key = sys.argv[1] cluster = MongoClient( f"mongodb+srv://deep-ivy:{mongo_key}@cluster0.qdvf8q3.mongodb.net/?retryWrites=true&w=majority" # noqa diff --git a/run_tests_CLI/test_dependencies.py b/run_tests_CLI/test_dependencies.py index 52ba1c061ab4d..348a63be15e6c 100644 --- a/run_tests_CLI/test_dependencies.py +++ b/run_tests_CLI/test_dependencies.py @@ -34,8 +34,8 @@ def test_imports(fname, assert_version, update_versions): global WARN global WARN_MSG global PRINT_MSG - versions_to_update = dict() - msg = "\nasserting imports work for: {}\n\n".format(fname) + versions_to_update = {} + msg = f"\nasserting imports work for: {fname}\n\n" PRINT_MSG += msg ERROR_MSG += msg WARN_MSG += msg @@ -48,7 +48,7 @@ def test_imports(fname, assert_version, update_versions): mod = importlib.import_module(mod_name) except Exception as e: ERROR = True - msg = "{} could not be imported: {}\n".format(mod_name, e) + msg = f"{mod_name} could not be imported: {e}\n" ERROR_MSG += msg PRINT_MSG += msg continue @@ -65,13 +65,11 @@ def test_imports(fname, assert_version, update_versions): detected_version = None if detected_version and expected_version: if detected_version == expected_version: - msg = "{} detected correct version: {}\n".format( - mod_name, detected_version - ) + msg = f"{mod_name} detected correct version: {detected_version}\n" else: msg = ( - "expected version {} for module {}, but detected version " - "{}\n".format(expected_version, mod_name, detected_version) + f"expected version {expected_version} for module {mod_name}, but" + f" detected version {detected_version}\n" ) versions_to_update[line_num] = { "expected": expected_version, @@ -87,17 +85,18 @@ def test_imports(fname, assert_version, update_versions): else: if detected_version: msg = ( - "{} detected version: {}, but no expected version " - "provided\n".format(mod_name, detected_version) + f"{mod_name} detected version: {detected_version}, but no expected" + " version provided\n" ) elif expected_version: - msg = "{} expected version: {}, but unable to detect version\n".format( - mod_name, expected_version + msg = ( + f"{mod_name} expected version: {expected_version}, but unable to" + " detect version\n" ) else: msg = ( - "no expected version provided, and unable to detect " - "version for {}\n".format(mod_name) + "no expected version provided, and unable to detect version for" + f" {mod_name}\n" ) WARN = True PRINT_MSG += msg diff --git a/run_tests_pr.py b/run_tests_pr.py index 167e33a26d8c5..3c5ee3391454a 100644 --- a/run_tests_pr.py +++ b/run_tests_pr.py @@ -40,9 +40,9 @@ def get_mod_submod_test(test_path): for name in modules: if name in test_path: if name == "test_functional": - module = module_map["test_functional/" + test_path[-2]] + module = module_map[f"test_functional/{test_path[-2]}"] elif name == "test_experimental": - module = module_map["test_experimental/" + test_path[-2]] + module = module_map[f"test_experimental/{test_path[-2]}"] else: module = module_map[name] break @@ -54,32 +54,29 @@ def get_mod_submod_test(test_path): if __name__ == "__main__": failed = False - f_write = open(sys.argv[1], "w") - with open("tests_to_run", "r") as f: - for line in f: - test, backend = line.split(",") - print(f"\n{'*' * 100}") - print(f"{line[:-1]}") - print(f"{'*' * 100}\n") - sys.stdout.flush() - ret = os.system( - f'docker run --rm -v "$(pwd)":/ivy -v "$(pwd)"/.hypothesis:/.hypothesis unifyai/ivy:latest python3 -m pytest --tb=short {test} --skip-compile-testing --backend {backend}' # noqa - ) - if ret != 0: - failed = True - module, submodule, test = get_mod_submod_test(test) - params = { - "module": module, - "submodule": submodule, - "backend": backend[:-1], - "test": test, - } - response = requests.get(url, params=params) - if response.status_code == 200: - if response.json(): - # The test passes on main but fails in this fork/branch + with open(sys.argv[1], "w") as f_write: + with open("tests_to_run", "r") as f: + for line in f: + test, backend = line.split(",") + print(f"\n{'*' * 100}") + print(f"{line[:-1]}") + print(f"{'*' * 100}\n") + sys.stdout.flush() + ret = os.system( + f'docker run --rm -v "$(pwd)":/ivy -v "$(pwd)"/.hypothesis:/.hypothesis unifyai/ivy:latest python3 -m pytest --tb=short {test} --skip-compile-testing --backend {backend}' # noqa + ) + if ret != 0: + failed = True + module, submodule, test = get_mod_submod_test(test) + params = { + "module": module, + "submodule": submodule, + "backend": backend[:-1], + "test": test, + } + response = requests.get(url, params=params) + if response.status_code == 200 and response.json(): f_write.write(line) - f_write.close() if failed: exit(1) diff --git a/scripts/backend_generation/generate.py b/scripts/backend_generation/generate.py index 812bae002b91d..c68757a53b8d4 100644 --- a/scripts/backend_generation/generate.py +++ b/scripts/backend_generation/generate.py @@ -113,23 +113,23 @@ def _update_native_config_value(key): try: obj = __builtins__.__dict__[parsed[-1]] except KeyError: - print(Fore.RED + f"{parsed[-1]} is not a primitive object.") + print(f"{Fore.RED}{parsed[-1]} is not a primitive object.") return False else: try: mod = import_module(parsed[0]) except ModuleNotFoundError: - print(Fore.RED + f"failed to import {parsed[0]}") + print(f"{Fore.RED}failed to import {parsed[0]}") return False try: obj = getattr(mod, parsed[-1]) except AttributeError: - print(Fore.RED + f"{parsed[-1]} is not found in module.") + print(f"{Fore.RED}{parsed[-1]} is not found in module.") return False if not inspect.isclass(obj): - print(Fore.RED + f"{obj} is not a class.") + print(f"{Fore.RED}{obj} is not a class.") return False - print(Fore.GREEN + f"Found class: {obj}") + print(f"{Fore.GREEN}Found class: {obj}") # Use alias if exists if backend["alias"] is not None: modified_namespace = parsed[0].replace( @@ -140,7 +140,7 @@ def _update_native_config_value(key): ) return True except KeyError: - print(Fore.RED + f"Couldn't find {ret}") + print(f"{Fore.RED}Couldn't find {ret}") return False return True @@ -163,7 +163,7 @@ def _should_install_backend(package_name): reqr_file.write("\n" + package_name + "\n") except subprocess.CalledProcessError as e: raise RuntimeError( - Fore.RED + f"Installing {package_name} failed. {e}" + f"{Fore.RED}Installing {package_name} failed. {e}" ) from e elif ret.lower() == "n": print( @@ -172,7 +172,7 @@ def _should_install_backend(package_name): "type checking won't be available.\n" ) else: - print(Fore.RED + f"{ret} not understood.") + print(f"{Fore.RED}{ret} not understood.") return False return True @@ -221,12 +221,12 @@ def _import_name(): _get_user_input(_import_name) global _imported_backend - print(Style.BRIGHT + f"Importing {backend['name']} for type checking...") + print(f"{Style.BRIGHT}Importing {backend['name']} for type checking...") try: _imported_backend = import_module(backend["name"]) return True except Exception as e: - print(Fore.RED + f"Failed to import {backend['name']}:{e}") + print(f"{Fore.RED}Failed to import {backend['name']}:{e}") return False return True @@ -253,9 +253,9 @@ def _update_flag_config_value(key): if ret == "y": config_flags[key] = not config_flags[key] return True - elif ret == "n" or ret == "": + elif ret in ["n", ""]: return True - print(Fore.RED + f"{ret} not understood.") + print(f"{Fore.RED}{ret} not understood.") return False @@ -327,15 +327,15 @@ def _call_generate_tree(config_name: str): for key, value in config_valids.copy().items(): all_items = fullset_mapping[key] invalid_items = list(set(all_items).difference(value)) - config_valids["in" + key] = invalid_items + config_valids[f"in{key}"] = invalid_items for key in config_valids["valid_dtypes"]: - new_key = "native_" + key + new_key = f"native_{key}" config_natives[new_key] = asdict(BackendNativeObject(name="None", namespace="")) _get_user_input(_update_native_config_value, new_key) for key in config_valids["invalid_dtypes"]: - new_key = "native_" + key + new_key = f"native_{key}" config_natives[new_key] = asdict(BackendNativeObject(name="None", namespace="")) print("\n:: Backend\n") @@ -348,10 +348,10 @@ def _call_generate_tree(config_name: str): if key.startswith("in"): continue valid_items = config_valids[key] - invalid_items = config_valids["in" + key] + invalid_items = config_valids[f"in{key}"] print("\n:: " + key.partition("_")[-1]) - print(Fore.GREEN + "valid > " + valid_items.__str__()) - print(Fore.RED + "invalid > " + invalid_items.__str__()) + print(f"{Fore.GREEN}valid > {valid_items.__str__()}") + print(f"{Fore.RED}invalid > {invalid_items.__str__()}") # Print flags for key, value in config_flags.items(): diff --git a/scripts/eager_mode_benchmark/benchmark.py b/scripts/eager_mode_benchmark/benchmark.py index f55f7fb2f484e..c2c8fe22cfaad 100644 --- a/scripts/eager_mode_benchmark/benchmark.py +++ b/scripts/eager_mode_benchmark/benchmark.py @@ -84,7 +84,7 @@ def _read_or_create_csv(output_path="./report.csv"): def _write_to_csv(df, row_list, output_path="./report.csv"): - row = {k: v for k, v in zip(COLUMNS, row_list)} + row = dict(zip(COLUMNS, row_list)) df = df.append(row, ignore_index=True) df.to_csv(output_path, index=False) @@ -206,16 +206,16 @@ def eager_benchmark( devices = ivy.default(devices, []) output_path = ivy.default(output_path, "./report.csv") print("\nBenchmarking backends : " + " ".join(backends)) - print("Number of experiments : {}".format(num_experiments) + "\n") + print(f"Number of experiments : {num_experiments}" + "\n") for i in range(num_experiments): if num_experiments > 1: print("====================") - print("Experiment {}".format(i + 1)) + print(f"Experiment {i + 1}") print("====================\n") for backend in backends: with _AvoidGPUPreallocation(backend) as _: print("------------------------------------------------\n") - print("backend : {}".format(backend)) + print(f"backend : {backend}") ivy.set_backend(backend, dynamic=True) valid_devices = [ device @@ -223,7 +223,7 @@ def eager_benchmark( if device.split(":")[0] not in ivy.invalid_devices ] for device in valid_devices: - print("device : {}".format(device)) + print(f"device : {device}") obj_call = obj if functional_api: obj_call = ivy.__dict__[obj] @@ -264,9 +264,9 @@ def eager_benchmark( ) ivy.clear_cached_mem_on_dev(device) print(LINE_UP * (len(valid_devices) - i), end=LINE_CLEAR) - print("device : {}\t --> done\n".format(device)) + print(f"device : {device}\t --> done\n") ivy.unset_backend() - print("Results written to {} ...".format(output_path)) + print(f"Results written to {output_path} ...") def visualize_speed_up( @@ -325,7 +325,7 @@ def visualize_speed_up( fig.set_figwidth(30) fig.set_figheight(12) fig.tight_layout(pad=10.0) - axes = np.asarray([axes]) if not isinstance(axes, np.ndarray) else axes + axes = axes if isinstance(axes, np.ndarray) else np.asarray([axes]) while len(axes.shape) < 2: if len(devices) > len(backends): axes = np.expand_dims(axes, len(axes.shape)) @@ -333,7 +333,7 @@ def visualize_speed_up( axes = np.expand_dims(axes, 0) for device, axis in zip(devices, axes): for backend, ax in zip(backends, axis): - ax.set_title("{} : {}".format(backend, device), {"fontsize": 18}) + ax.set_title(f"{backend} : {device}", {"fontsize": 18}) ax.set_ylabel("Percent Speed up on compiling", {"fontsize": 18}) ax.tick_params(axis="both", labelsize=15) query = df.query("backend == @backend and device == @device") @@ -341,8 +341,8 @@ def visualize_speed_up( ax.violinplot(query["percent_speed_up"]) else: warnings.warn( - "No records matching the filters passed" - "backend={} and device={}".format(backend, device) + f"No records matching the filters passedbackend={backend} and" + f" device={device}" ) plt.savefig(output_path) - print("plot saved to {} ...".format(output_path)) + print(f"plot saved to {output_path} ...") diff --git a/setup_tests.py b/setup_tests.py index 10c9f5cabb102..9f2a357d9a829 100644 --- a/setup_tests.py +++ b/setup_tests.py @@ -6,11 +6,10 @@ def main(): if len(sys.argv) < 2: return test = sys.argv[1] - if "," in test: - with open("tests_to_run", "w") as f: + with open("tests_to_run", "w") as f: + if "," in test: f.write(test + "\n") - else: - with open("tests_to_run", "w") as f: + else: for backend in BACKENDS: f.write(f"{test},{backend}\n") From 70d8127b484fa570c6431b95fd4bae19e86ce059 Mon Sep 17 00:00:00 2001 From: Sulaiman Mutawalli <64346270+sabre-code@users.noreply.github.com> Date: Mon, 11 Sep 2023 15:35:43 +0530 Subject: [PATCH 031/117] added paddle.nanmean function frontend and test for it (#23278) --- ivy/functional/frontends/paddle/math.py | 6 ++++ .../test_frontends/test_paddle/test_math.py | 31 +++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/ivy/functional/frontends/paddle/math.py b/ivy/functional/frontends/paddle/math.py index 242ea42e02d60..c4d3a0130f79b 100644 --- a/ivy/functional/frontends/paddle/math.py +++ b/ivy/functional/frontends/paddle/math.py @@ -376,6 +376,12 @@ def multiply(x, y, name=None): return ivy.multiply(x, y) +@with_supported_dtypes({"2.5.1 and below": ("float32", "float64")}, "paddle") +@to_ivy_arrays_and_back +def nanmean(x, axis=None, keepdims=False): + return ivy.nanmean(x, axis=axis, keepdims=keepdims) + + @with_supported_dtypes( {"2.5.1 and below": ("float32", "float64", "int32", "int64")}, "paddle" ) diff --git a/ivy_tests/test_ivy/test_frontends/test_paddle/test_math.py b/ivy_tests/test_ivy/test_frontends/test_paddle/test_math.py index eb1dbccffaba7..926836080a9b3 100644 --- a/ivy_tests/test_ivy/test_frontends/test_paddle/test_math.py +++ b/ivy_tests/test_ivy/test_frontends/test_paddle/test_math.py @@ -1622,6 +1622,37 @@ def test_paddle_multiply( ) +@handle_frontend_test( + fn_tree="paddle.nanmean", + dtype_and_x=helpers.dtype_and_values( + available_dtypes=helpers.get_dtypes("valid"), + num_arrays=1, + allow_nan=True, + ), +) +def test_paddle_nanmean( + *, + dtype_and_x, + on_device, + fn_tree, + frontend, + backend_fw, + test_flags, +): + input_dtype, x = dtype_and_x + helpers.test_frontend_function( + input_dtypes=input_dtype, + backend_to_test=backend_fw, + frontend=frontend, + fn_tree=fn_tree, + test_flags=test_flags, + on_device=on_device, + x=x[0], + rtol=1e-04, + atol=1e-04, + ) + + # nansum @handle_frontend_test( fn_tree="paddle.nansum", From 6b545c2b7eab8bfc53806f3ba16c1e8bdd1629b9 Mon Sep 17 00:00:00 2001 From: RashulChutani Date: Mon, 11 Sep 2023 15:52:45 +0530 Subject: [PATCH 032/117] Resolve all the issues with the synchronize_db.py script and add support to remove empty containers MongoDB collections recursively [skip ci] --- run_tests_CLI/synchronize_db.py | 84 ++++++++++++++++++++++++--------- 1 file changed, 61 insertions(+), 23 deletions(-) diff --git a/run_tests_CLI/synchronize_db.py b/run_tests_CLI/synchronize_db.py index 93f80a2f35986..54518733fdae9 100644 --- a/run_tests_CLI/synchronize_db.py +++ b/run_tests_CLI/synchronize_db.py @@ -1,3 +1,4 @@ +import json import sys from pymongo import MongoClient from get_all_tests import get_all_tests @@ -5,8 +6,8 @@ module_map = { "core": "test_functional/test_core", - "exp_core": "test_experimental/test_core", - "nn": "test_functional/test_nn", + "exp_core": "test_functional/test_experimental/test_core", + "nn": "test_functional/test_experimental/test_nn", "exp_nn": "test_experimental/test_nn", "stateful": "test_stateful", "torch": "test_frontends/test_torch", @@ -20,20 +21,23 @@ def keys_to_delete_from_db(all_tests, module, data, current_key=""): - """Recursively navigate and identify keys not in the list.""" + """ + Recursively navigate and identify keys not in the list. + """ keys_for_deletion = [] for key, value in data.items(): - new_key = f"{current_key}.{key}" if current_key else key + new_key = current_key + "." + key if current_key else key # If this is a dictionary, recurse deeper if isinstance(value, dict): - keys_for_deletion.extend(keys_to_delete_from_db(all_tests, value, new_key)) + keys_for_deletion.extend(keys_to_delete_from_db(all_tests, module, value, new_key)) + # If the new_key is not in keys_to_keep, mark it for deletion elif key != "_id": components = new_key.split(".") submodule = components[0] function = components[-2] - test = f"{module}/{submodule}::{function}" + test = module + "/" + submodule + "::" + function if test not in all_tests: keys_for_deletion.append(".".join(components[:-1])) @@ -55,7 +59,7 @@ def keys_to_delete_from_db(all_tests, module, data, current_key=""): "test_mindspore", "test_onnx", "test_sklearn", - "test_xgboost", + "test_xgboost" ) db_dict = { "test_functional/test_core": ["core", 10], @@ -74,19 +78,17 @@ def keys_to_delete_from_db(all_tests, module, data, current_key=""): "test_mindspore": ["mindspore", 23], "test_onnx": ["onnx", 24], "test_sklearn": ["sklearn", 25], - "test_xgboost": ["xgboost", 26], + "test_xgboost": ["xgboost", 26] } - - def get_submodule(test_path): test_path = test_path.split("/") for name in submodules: if name in test_path: if name == "test_functional": if test_path[3] == "test_experimental": - coll = db_dict[f"test_experimental/{test_path[4]}"] + coll = db_dict["test_experimental/" + test_path[4]] else: - coll = db_dict[f"test_functional/{test_path[-2]}"] + coll = db_dict["test_functional/" + test_path[-2]] else: coll = db_dict[name] break @@ -98,28 +100,64 @@ def get_submodule(test_path): def process_test(test): coll, submod, test_fn = get_submodule(test) - return f"{coll[0]}/{submod}::{test_fn}" + return coll[0] + "/" + submod + "::" + test_fn + + +def remove_empty_objects(document, key_prefix=''): + # Base case: if the document is not a dictionary, return an empty list + if not isinstance(document, dict): + return [] + + # List to store keys associated with empty objects + empty_keys = [] + + for key, value in document.items(): + # Generate the full key path + full_key = key_prefix + '.' + key if key_prefix else key + + # If the value is a dictionary, recursively check for empty objects + if isinstance(value, dict): + # If the dictionary is empty, store its key + if not value: + empty_keys.append(full_key) + else: + empty_keys.extend(remove_empty_objects(value, full_key)) + + return empty_keys def main(): all_tests = get_all_tests() - all_tests = {process_test(test.split(",")[0].strip()) for test in all_tests} - mongo_key = sys.argv[1] + all_tests = set([process_test(test.split(",")[0].strip()) for test in all_tests]) cluster = MongoClient( - f"mongodb+srv://deep-ivy:{mongo_key}@cluster0.qdvf8q3.mongodb.net/?retryWrites=true&w=majority" # noqa + f"mongodb+srv://rashul:rashulivy@cluster0.qdvf8q3.mongodb.net/?retryWrites=true&w=majority" # noqa ) db = cluster["Ivy_tests_multi_gpu"] for collection_name in db.list_collection_names(): collection = db[collection_name] for document in collection.find({}): - print(document) - undesired_keys = keys_to_delete_from_db( - all_tests, module_map[collection_name], document - ) + undesired_keys = keys_to_delete_from_db(all_tests, collection_name, document) for key in undesired_keys: - print(key) - # collection.update_one({"_id": document["_id"]}, {"$unset": {key: 1}}) + collection.update_one({"_id": document["_id"]}, {"$unset": {key: 1}}) + + for collection_name in db.list_collection_names(): + collection = db[collection_name] + break_flag = False + while True: + for document in collection.find({}): + keys_to_remove = remove_empty_objects(document) + if keys_to_remove: + update_operation = {'$unset': {key: 1 for key in keys_to_remove}} + collection.update_one({'_id': document['_id']}, update_operation) + else: + break_flag = True + break + if break_flag: + break_flag = False + break + + if __name__ == "__main__": - main() + main() \ No newline at end of file From c05be235f21f60b6e9e671beb3c5d4d5f914648c Mon Sep 17 00:00:00 2001 From: RashulChutani Date: Mon, 11 Sep 2023 16:01:41 +0530 Subject: [PATCH 033/117] Reformat synchronize_db.py script and update the MongoDB user account [skip ci] --- run_tests_CLI/synchronize_db.py | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/run_tests_CLI/synchronize_db.py b/run_tests_CLI/synchronize_db.py index 54518733fdae9..a21ee7a88337f 100644 --- a/run_tests_CLI/synchronize_db.py +++ b/run_tests_CLI/synchronize_db.py @@ -31,7 +31,9 @@ def keys_to_delete_from_db(all_tests, module, data, current_key=""): # If this is a dictionary, recurse deeper if isinstance(value, dict): - keys_for_deletion.extend(keys_to_delete_from_db(all_tests, module, value, new_key)) + keys_for_deletion.extend( + keys_to_delete_from_db(all_tests, module, value, new_key) + ) # If the new_key is not in keys_to_keep, mark it for deletion elif key != "_id": components = new_key.split(".") @@ -59,7 +61,7 @@ def keys_to_delete_from_db(all_tests, module, data, current_key=""): "test_mindspore", "test_onnx", "test_sklearn", - "test_xgboost" + "test_xgboost", ) db_dict = { "test_functional/test_core": ["core", 10], @@ -78,8 +80,10 @@ def keys_to_delete_from_db(all_tests, module, data, current_key=""): "test_mindspore": ["mindspore", 23], "test_onnx": ["onnx", 24], "test_sklearn": ["sklearn", 25], - "test_xgboost": ["xgboost", 26] + "test_xgboost": ["xgboost", 26], } + + def get_submodule(test_path): test_path = test_path.split("/") for name in submodules: @@ -103,7 +107,7 @@ def process_test(test): return coll[0] + "/" + submod + "::" + test_fn -def remove_empty_objects(document, key_prefix=''): +def remove_empty_objects(document, key_prefix=""): # Base case: if the document is not a dictionary, return an empty list if not isinstance(document, dict): return [] @@ -113,7 +117,7 @@ def remove_empty_objects(document, key_prefix=''): for key, value in document.items(): # Generate the full key path - full_key = key_prefix + '.' + key if key_prefix else key + full_key = key_prefix + "." + key if key_prefix else key # If the value is a dictionary, recursively check for empty objects if isinstance(value, dict): @@ -129,14 +133,17 @@ def remove_empty_objects(document, key_prefix=''): def main(): all_tests = get_all_tests() all_tests = set([process_test(test.split(",")[0].strip()) for test in all_tests]) + mongo_key = sys.argv[1] cluster = MongoClient( - f"mongodb+srv://rashul:rashulivy@cluster0.qdvf8q3.mongodb.net/?retryWrites=true&w=majority" # noqa + f"mongodb+srv://deep-ivy:{mongo_key}@cluster0.qdvf8q3.mongodb.net/?retryWrites=true&w=majority" # noqa ) db = cluster["Ivy_tests_multi_gpu"] for collection_name in db.list_collection_names(): collection = db[collection_name] for document in collection.find({}): - undesired_keys = keys_to_delete_from_db(all_tests, collection_name, document) + undesired_keys = keys_to_delete_from_db( + all_tests, collection_name, document + ) for key in undesired_keys: collection.update_one({"_id": document["_id"]}, {"$unset": {key: 1}}) @@ -147,8 +154,8 @@ def main(): for document in collection.find({}): keys_to_remove = remove_empty_objects(document) if keys_to_remove: - update_operation = {'$unset': {key: 1 for key in keys_to_remove}} - collection.update_one({'_id': document['_id']}, update_operation) + update_operation = {"$unset": {key: 1 for key in keys_to_remove}} + collection.update_one({"_id": document["_id"]}, update_operation) else: break_flag = True break @@ -157,7 +164,5 @@ def main(): break - - if __name__ == "__main__": - main() \ No newline at end of file + main() From dfa2526b82b3c5d32df74e01bd4b968c52164951 Mon Sep 17 00:00:00 2001 From: Moses Daudu Date: Mon, 11 Sep 2023 11:43:12 +0100 Subject: [PATCH 034/117] feat: add complex type decorator and arguments for logsigmoid (#23273) --- .../array/experimental/activations.py | 6 +++++- .../container/experimental/activations.py | 10 ++++++++++ .../backends/jax/experimental/activations.py | 4 +++- .../backends/numpy/experimental/activations.py | 4 +++- .../backends/paddle/experimental/activations.py | 7 +++++-- .../tensorflow/experimental/activations.py | 6 +++++- .../backends/torch/experimental/activations.py | 4 +++- .../frontends/jax/nn/non_linear_activations.py | 2 +- ivy/functional/ivy/experimental/activations.py | 10 +++++++++- ivy/stateful/activations.py | 15 ++++++++++++--- .../test_nn/test_non_linear_activations.py | 2 +- 11 files changed, 57 insertions(+), 13 deletions(-) diff --git a/ivy/data_classes/array/experimental/activations.py b/ivy/data_classes/array/experimental/activations.py index 92f997bf66ec9..f27ef89516bbb 100644 --- a/ivy/data_classes/array/experimental/activations.py +++ b/ivy/data_classes/array/experimental/activations.py @@ -160,6 +160,7 @@ def relu6(self, /, *, out: Optional[ivy.Array] = None) -> ivy.Array: def logsigmoid( self: ivy.Array, + complex_mode: Literal["split", "magnitude", "jax"] = "jax", ) -> ivy.Array: """ ivy.Array instance method variant of ivy.logsigmoid. This method simply wraps @@ -170,6 +171,9 @@ def logsigmoid( ---------- self Input array. + complex_mode + optional specifier for how to handle complex data types. See + ``ivy.func_wrapper.handle_complex_input`` for more detail. Returns ------- @@ -187,7 +191,7 @@ def logsigmoid( >>> print(z) ivy.array([-2.57888985, -0.31326169, -0.69314718, -0.01104775]) """ - return ivy.logsigmoid(self._data) + return ivy.logsigmoid(self._data, complex_mode=complex_mode) def selu(self, /, *, out: Optional[ivy.Array] = None) -> ivy.Array: """ diff --git a/ivy/data_classes/container/experimental/activations.py b/ivy/data_classes/container/experimental/activations.py index 29c29a519761e..082fb5e062b40 100644 --- a/ivy/data_classes/container/experimental/activations.py +++ b/ivy/data_classes/container/experimental/activations.py @@ -451,6 +451,7 @@ def static_logsigmoid( to_apply: Union[bool, ivy.Container] = True, prune_unapplied: Union[bool, ivy.Container] = False, map_sequences: Union[bool, ivy.Container] = False, + complex_mode: Literal["split", "magnitude", "jax"] = "jax", ) -> ivy.Container: """ ivy.Container static method variant of ivy.logsigmoid. This method simply wraps @@ -472,6 +473,9 @@ def static_logsigmoid( map_sequences Whether to also map method to sequences (lists, tuples). Default is ``False``. + complex_mode + optional specifier for how to handle complex data types. See + ``ivy.func_wrapper.handle_complex_input`` for more detail. Returns ------- @@ -506,6 +510,7 @@ def static_logsigmoid( to_apply=to_apply, prune_unapplied=prune_unapplied, map_sequences=map_sequences, + complex_mode=complex_mode, ) def logsigmoid( @@ -516,6 +521,7 @@ def logsigmoid( to_apply: Union[bool, ivy.Container] = True, prune_unapplied: Union[bool, ivy.Container] = False, map_sequences: Union[bool, ivy.Container] = False, + complex_mode: Literal["split", "magnitude", "jax"] = "jax", ) -> ivy.Container: """ Apply element-wise Log-sigmoid of x i.e. log(1 / (1 + exp(-x)). @@ -524,6 +530,9 @@ def logsigmoid( ---------- self Input container. + complex_mode + optional specifier for how to handle complex data types. See + ``ivy.func_wrapper.handle_complex_input`` for more detail. Returns ------- @@ -546,6 +555,7 @@ def logsigmoid( to_apply=to_apply, prune_unapplied=prune_unapplied, map_sequences=map_sequences, + complex_mode=complex_mode, ) @staticmethod diff --git a/ivy/functional/backends/jax/experimental/activations.py b/ivy/functional/backends/jax/experimental/activations.py index a18f2b240b61c..ccb239dcbc430 100644 --- a/ivy/functional/backends/jax/experimental/activations.py +++ b/ivy/functional/backends/jax/experimental/activations.py @@ -49,7 +49,9 @@ def thresholded_relu( return jnp.where(x > threshold, x, 0).astype(x.dtype) -def logsigmoid(input: JaxArray, /, *, out: Optional[JaxArray] = None) -> JaxArray: +def logsigmoid( + input: JaxArray, /, *, complex_mode="jax", out: Optional[JaxArray] = None +) -> JaxArray: return jax.nn.log_sigmoid(input) diff --git a/ivy/functional/backends/numpy/experimental/activations.py b/ivy/functional/backends/numpy/experimental/activations.py index 1ac8c43285a96..707ecd8a4f4df 100644 --- a/ivy/functional/backends/numpy/experimental/activations.py +++ b/ivy/functional/backends/numpy/experimental/activations.py @@ -53,7 +53,9 @@ def relu6(x: np.ndarray, /, *, out: Optional[np.ndarray] = None) -> np.ndarray: @with_unsupported_dtypes({"1.25.2 and below": ("bool",)}, backend_version) @_scalar_output_to_0d_array -def logsigmoid(input: np.ndarray, /, *, out: Optional[np.ndarray] = None) -> np.ndarray: +def logsigmoid( + input: np.ndarray, /, *, complex_mode="jax", out: Optional[np.ndarray] = None +) -> np.ndarray: return -(np.log1p(np.exp(-(input)))) diff --git a/ivy/functional/backends/paddle/experimental/activations.py b/ivy/functional/backends/paddle/experimental/activations.py index d4fd80403a331..33f1d80bbf8ef 100644 --- a/ivy/functional/backends/paddle/experimental/activations.py +++ b/ivy/functional/backends/paddle/experimental/activations.py @@ -62,15 +62,18 @@ def relu6(x: paddle.Tensor, /, *, out: Optional[paddle.Tensor] = None) -> paddle return F.relu6(x.cast("float32")).cast(x.dtype) +@with_unsupported_device_and_dtypes( + {"2.5.1 and below": {"cpu": ("bfloat16",)}}, backend_version +) def logsigmoid( - input: paddle.Tensor, /, *, out: Optional[paddle.Tensor] = None + input: paddle.Tensor, /, *, complex_mode="jax", out: Optional[paddle.Tensor] = None ) -> paddle.Tensor: if input.dtype in [paddle.float32, paddle.float64]: return F.log_sigmoid(input) if paddle.is_complex(input): return paddle_backend.log( paddle_backend.divide( - 1.0, (paddle_backend.add(1.0, paddle_backend.exp(input))) + 1.0, (paddle_backend.add(1.0, paddle_backend.exp(-input))) ) ) return F.log_sigmoid(input.cast("float32")).cast(input.dtype) diff --git a/ivy/functional/backends/tensorflow/experimental/activations.py b/ivy/functional/backends/tensorflow/experimental/activations.py index 5ba64d523be10..709ae09314cd1 100644 --- a/ivy/functional/backends/tensorflow/experimental/activations.py +++ b/ivy/functional/backends/tensorflow/experimental/activations.py @@ -44,7 +44,11 @@ def relu6(x: Tensor, /, *, out: Optional[Tensor] = None) -> Tensor: @with_supported_dtypes({"2.13.0 and below": ("float",)}, backend_version) -def logsigmoid(input: Tensor, /, *, out: Optional[Tensor] = None) -> Tensor: +def logsigmoid( + input: Tensor, /, *, complex_mode="jax", out: Optional[Tensor] = None +) -> Tensor: + if input.dtype in [tf.complex64, tf.complex128]: + return tf.math.log(tf.nn.sigmoid(input)) return tf.math.log_sigmoid(input) diff --git a/ivy/functional/backends/torch/experimental/activations.py b/ivy/functional/backends/torch/experimental/activations.py index 4331ac7b57fed..5969804298c59 100644 --- a/ivy/functional/backends/torch/experimental/activations.py +++ b/ivy/functional/backends/torch/experimental/activations.py @@ -40,8 +40,10 @@ def relu6(x: torch.Tensor, /, *, out: Optional[torch.Tensor] = None) -> torch.Te @with_unsupported_dtypes({"2.0.1 and below": ("float16",)}, backend_version) def logsigmoid( - input: torch.Tensor, /, *, out: Optional[torch.Tensor] = None + input: torch.Tensor, /, *, complex_mode="jax", out: Optional[torch.Tensor] = None ) -> torch.Tensor: + if torch.is_complex(input): + return torch.log(torch.sigmoid(input)) return torch.nn.functional.logsigmoid(input) diff --git a/ivy/functional/frontends/jax/nn/non_linear_activations.py b/ivy/functional/frontends/jax/nn/non_linear_activations.py index 2f6ad3a036c69..8d10397702cec 100644 --- a/ivy/functional/frontends/jax/nn/non_linear_activations.py +++ b/ivy/functional/frontends/jax/nn/non_linear_activations.py @@ -186,7 +186,7 @@ def leaky_relu(x, negative_slope=0.01): @to_ivy_arrays_and_back def log_sigmoid(x): x = _type_conversion(x) - return ivy.negative(ivy.softplus(ivy.negative(x))).astype(x.dtype) + return ivy.logsigmoid(x, complex_mode="jax").astype(x.dtype) @to_ivy_arrays_and_back diff --git a/ivy/functional/ivy/experimental/activations.py b/ivy/functional/ivy/experimental/activations.py index c4519ec90fdb9..d4f701cf45b0d 100644 --- a/ivy/functional/ivy/experimental/activations.py +++ b/ivy/functional/ivy/experimental/activations.py @@ -267,8 +267,13 @@ def relu6( @handle_out_argument @to_native_arrays_and_back @handle_device_shifting +@handle_complex_input def logsigmoid( - input: Union[ivy.NativeArray, ivy.Array], /, *, out: Optional[ivy.Array] = None + input: Union[ivy.NativeArray, ivy.Array], + /, + *, + complex_mode: Literal["split", "magnitude", "jax"] = "jax", + out: Optional[ivy.Array] = None, ) -> ivy.Array: """ Apply element-wise Log-sigmoid of x. @@ -279,6 +284,9 @@ def logsigmoid( ---------- input Input array. + complex_mode + optional specifier for how to handle complex data types. See + ``ivy.func_wrapper.handle_complex_input`` for more detail. Returns ------- diff --git a/ivy/stateful/activations.py b/ivy/stateful/activations.py index b83bac4fe826b..7b9c67035e117 100644 --- a/ivy/stateful/activations.py +++ b/ivy/stateful/activations.py @@ -482,8 +482,17 @@ def _forward(self, x): class LogSigmoid(Module): - def __init__(self): - """Apply the LogSigmoid activation function.""" + def __init__(self, complex_mode: Literal["split", "magnitude", "jax"] = "jax"): + """ + Apply the LogSigmoid activation function. + + Parameter + ---------- + complex_mode + Specifies how to handle complex input. See + ``ivy.func_wrapper.handle_complex_input`` for more detail. + """ + self._complex_mode = complex_mode Module.__init__(self) def _forward(self, x): @@ -499,4 +508,4 @@ def _forward(self, x): ret The outputs following the LogSigmoid activation *[batch_shape, d]* """ - return ivy.logsigmoid(x) + return ivy.logsigmoid(x, complex_mode=self._complex_mode) diff --git a/ivy_tests/test_ivy/test_frontends/test_jax/test_nn/test_non_linear_activations.py b/ivy_tests/test_ivy/test_frontends/test_jax/test_nn/test_non_linear_activations.py index ef904c7854dde..98660c17328ed 100644 --- a/ivy_tests/test_ivy/test_frontends/test_jax/test_nn/test_non_linear_activations.py +++ b/ivy_tests/test_ivy/test_frontends/test_jax/test_nn/test_non_linear_activations.py @@ -348,7 +348,7 @@ def test_jax_leaky_relu( @handle_frontend_test( fn_tree="jax.nn.log_sigmoid", dtype_and_x=helpers.dtype_and_values( - available_dtypes=helpers.get_dtypes("float"), + available_dtypes=helpers.get_dtypes("float_and_complex"), min_value=-100, max_value=100, large_abs_safety_factor=8, From 389434f920326346e3680539d07adc319c1d7169 Mon Sep 17 00:00:00 2001 From: Ahmed Hisham <62694574+Mghrabi@users.noreply.github.com> Date: Mon, 11 Sep 2023 14:35:24 +0300 Subject: [PATCH 035/117] fixed wrong returned shape of paddle.mean function (#23389) --- ivy/functional/frontends/paddle/stat.py | 1 - 1 file changed, 1 deletion(-) diff --git a/ivy/functional/frontends/paddle/stat.py b/ivy/functional/frontends/paddle/stat.py index bfee9e9d65d2c..91b69d7441091 100644 --- a/ivy/functional/frontends/paddle/stat.py +++ b/ivy/functional/frontends/paddle/stat.py @@ -10,7 +10,6 @@ @to_ivy_arrays_and_back def mean(input, axis=None, keepdim=False, out=None): ret = ivy.mean(input, axis=axis, keepdims=keepdim, out=out) - ret = ivy.expand_dims(ret, axis=-1) if ret.ndim == 0 else ret return ret From e2277370ac36c58bae1d4745931ad360b000d7ba Mon Sep 17 00:00:00 2001 From: Sai-Suraj-27 Date: Mon, 11 Sep 2023 18:03:58 +0530 Subject: [PATCH 036/117] refactor: Simplified conditional logic using `De Morgan's law` at few places. (#23310) --- .../frontends/numpy/linalg/norms_and_other_numbers.py | 2 +- ivy/functional/frontends/torch/linalg.py | 2 +- .../frontends/torch/nn/functional/vision_functions.py | 4 ++-- ivy/functional/ivy/experimental/sparse_array.py | 2 +- .../array_api_testing/write_array_api_tests_k_flag.py | 2 +- .../test_ivy/test_functional/test_core/test_device.py | 2 +- .../test_functional/test_core/test_elementwise.py | 10 +++++----- ivy_tests/test_ivy/test_misc/test_array.py | 6 +++--- ivy_tests/test_ivy/test_misc/test_shape.py | 2 +- run_tests_CLI/array_api_det_coverage.py | 2 +- 10 files changed, 17 insertions(+), 17 deletions(-) diff --git a/ivy/functional/frontends/numpy/linalg/norms_and_other_numbers.py b/ivy/functional/frontends/numpy/linalg/norms_and_other_numbers.py index 505fee51f9ee6..b02b6fe921046 100644 --- a/ivy/functional/frontends/numpy/linalg/norms_and_other_numbers.py +++ b/ivy/functional/frontends/numpy/linalg/norms_and_other_numbers.py @@ -27,7 +27,7 @@ def matrix_rank(A, tol=None, hermitian=False): @to_ivy_arrays_and_back @from_zero_dim_arrays_to_scalar def norm(x, ord=None, axis=None, keepdims=False): - if axis is None and not (ord is None): + if axis is None and (ord is not None): if x.ndim not in (1, 2): raise ValueError("Improper number of dimensions to norm.") else: diff --git a/ivy/functional/frontends/torch/linalg.py b/ivy/functional/frontends/torch/linalg.py index dcfc92da0a9a9..1055938e53a4d 100644 --- a/ivy/functional/frontends/torch/linalg.py +++ b/ivy/functional/frontends/torch/linalg.py @@ -198,7 +198,7 @@ def multi_dot(tensors, *, out=None): {"2.0.1 and below": ("float32", "float64", "complex64", "complex128")}, "torch" ) def norm(input, ord=None, dim=None, keepdim=False, *, dtype=None, out=None): - if dim is None and not (ord is None): + if dim is None and (ord is not None): if input.ndim == 1: ret = ivy.vector_norm(input, axis=dim, keepdims=keepdim, ord=ord) else: diff --git a/ivy/functional/frontends/torch/nn/functional/vision_functions.py b/ivy/functional/frontends/torch/nn/functional/vision_functions.py index 56aeb000e4f84..9d0aa88c30d1a 100644 --- a/ivy/functional/frontends/torch/nn/functional/vision_functions.py +++ b/ivy/functional/frontends/torch/nn/functional/vision_functions.py @@ -309,7 +309,7 @@ def grid_sample( ) elif mode == "bicubic": - raise ivy.exceptions.IvyError(f"Bicubic is not support in 3D grid sampling") + raise ivy.exceptions.IvyError("Bicubic is not support in 3D grid sampling") else: raise ivy.exceptions.IvyError(f"Not supported input shape {input_clone.shape}") @@ -451,7 +451,7 @@ def interpolate( if ( bool(antialias) - and not (mode in ["bilinear", "bicubic"]) + and (mode not in ["bilinear", "bicubic"]) and ivy.get_num_dims(input) == 4 ): raise ivy.utils.exceptions.IvyException( diff --git a/ivy/functional/ivy/experimental/sparse_array.py b/ivy/functional/ivy/experimental/sparse_array.py index 5c67cc823e7c1..49a13248921c8 100644 --- a/ivy/functional/ivy/experimental/sparse_array.py +++ b/ivy/functional/ivy/experimental/sparse_array.py @@ -316,7 +316,7 @@ def _is_valid_format( ): valid_formats = ["coo", "csr", "csc", "csc", "bsc", "bsr"] - if not isinstance(format, str) or not format.lower() in valid_formats: + if not isinstance(format, str) or format.lower() not in valid_formats: return False if format.endswith("o"): diff --git a/ivy_tests/array_api_testing/write_array_api_tests_k_flag.py b/ivy_tests/array_api_testing/write_array_api_tests_k_flag.py index 61b678fda7f08..a41b8243ce608 100644 --- a/ivy_tests/array_api_testing/write_array_api_tests_k_flag.py +++ b/ivy_tests/array_api_testing/write_array_api_tests_k_flag.py @@ -40,7 +40,7 @@ continue if ("#" not in s) or ( "#" in s - and not (framework in s.lower()) + and (framework not in s.lower()) and any(f in s.lower() for f in framework_tests_to_run) ): tests_to_run += ( diff --git a/ivy_tests/test_ivy/test_functional/test_core/test_device.py b/ivy_tests/test_ivy/test_functional/test_core/test_device.py index b4bf40ba7fe16..ec6c8b6a84351 100644 --- a/ivy_tests/test_ivy/test_functional/test_core/test_device.py +++ b/ivy_tests/test_ivy/test_functional/test_core/test_device.py @@ -691,7 +691,7 @@ def test_to_device( # check if native arrays are the same # these backends do not support native inplace updates - assume(not (backend_fw in ["tensorflow", "jax"])) + assume(backend_fw not in ["tensorflow", "jax"]) assert x_on_dev.data is out.data diff --git a/ivy_tests/test_ivy/test_functional/test_core/test_elementwise.py b/ivy_tests/test_ivy/test_functional/test_core/test_elementwise.py index dddf7963e4f8d..953b621b1d70f 100644 --- a/ivy_tests/test_ivy/test_functional/test_core/test_elementwise.py +++ b/ivy_tests/test_ivy/test_functional/test_core/test_elementwise.py @@ -936,7 +936,7 @@ def test_gcd(*, dtype_and_x, test_flags, backend_fw, fn_name, on_device): def test_greater(*, dtype_and_x, test_flags, backend_fw, fn_name, on_device): input_dtype, x = dtype_and_x # bfloat16 is not supported - assume(not ("bfloat16" in input_dtype)) + assume("bfloat16" not in input_dtype) helpers.test_function( input_dtypes=input_dtype, test_flags=test_flags, @@ -959,7 +959,7 @@ def test_greater(*, dtype_and_x, test_flags, backend_fw, fn_name, on_device): def test_greater_equal(*, dtype_and_x, test_flags, backend_fw, fn_name, on_device): input_dtype, x = dtype_and_x # bfloat16 is not supported by numpy - assume(not ("bfloat16" in input_dtype)) + assume("bfloat16" not in input_dtype) # make sure they're not too close together assume(not (np.any(np.isclose(x[0], x[1])) or np.any(np.isclose(x[1], x[0])))) helpers.test_function( @@ -1142,7 +1142,7 @@ def test_lcm(dtype_and_x, test_flags, backend_fw, fn_name, on_device): def test_less(*, dtype_and_x, test_flags, backend_fw, fn_name, on_device): input_dtype, x = dtype_and_x # bfloat16 is not supported by numpy - assume(not ("bfloat16" in input_dtype)) + assume("bfloat16" not in input_dtype) # make sure they're not too close together assume(not (np.any(np.isclose(x[0], x[1])) or np.any(np.isclose(x[1], x[0])))) helpers.test_function( @@ -1167,7 +1167,7 @@ def test_less(*, dtype_and_x, test_flags, backend_fw, fn_name, on_device): def test_less_equal(*, dtype_and_x, test_flags, backend_fw, fn_name, on_device): input_dtype, x = dtype_and_x # bfloat16 is not supported by numpy - assume(not ("bfloat16" in input_dtype)) + assume("bfloat16" not in input_dtype) # make sure they're not too close together assume(not (np.any(np.isclose(x[0], x[1])) or np.any(np.isclose(x[1], x[0])))) helpers.test_function( @@ -1595,7 +1595,7 @@ def test_pow(*, dtype_and_x, test_flags, backend_fw, fn_name, on_device): input_dtype, x = dtype_and_x # bfloat16 is not supported by numpy - assume(not ("bfloat16" in input_dtype)) + assume("bfloat16" not in input_dtype) # Make sure x2 isn't a float when x1 is integer assume( diff --git a/ivy_tests/test_ivy/test_misc/test_array.py b/ivy_tests/test_ivy/test_misc/test_array.py index 93b15c77a456e..853bb85708e63 100644 --- a/ivy_tests/test_ivy/test_misc/test_array.py +++ b/ivy_tests/test_ivy/test_misc/test_array.py @@ -873,7 +873,7 @@ def test_array__ipow__( input_dtype, x = dtype_and_x # bfloat16 is not supported by numpy - assume(not ("bfloat16" in input_dtype)) + assume("bfloat16" not in input_dtype) # Make sure x2 isn't a float when x1 is integer assume( @@ -1502,7 +1502,7 @@ def test_array__pow__( input_dtype, x = dtype_and_x # bfloat16 is not supported by numpy - assume(not ("bfloat16" in input_dtype)) + assume("bfloat16" not in input_dtype) # Make sure x2 isn't a float when x1 is integer assume( @@ -1881,7 +1881,7 @@ def test_array__rpow__( input_dtype, x = dtype_and_x # bfloat16 is not supported by numpy - assume(not ("bfloat16" in input_dtype)) + assume("bfloat16" not in input_dtype) # Make sure x2 isn't a float when x1 is integer assume( diff --git a/ivy_tests/test_ivy/test_misc/test_shape.py b/ivy_tests/test_ivy/test_misc/test_shape.py index 266292f7e2500..ec84ce3ac3bfd 100644 --- a/ivy_tests/test_ivy/test_misc/test_shape.py +++ b/ivy_tests/test_ivy/test_misc/test_shape.py @@ -545,7 +545,7 @@ def test_shape__pow__( input_dtype, x = dtype_and_x # bfloat16 is not supported by numpy - assume(not ("bfloat16" in input_dtype)) + assume("bfloat16" not in input_dtype) # Make sure x2 isn't a float when x1 is integer with BackendHandler.update_backend(backend_fw) as ivy_backend: diff --git a/run_tests_CLI/array_api_det_coverage.py b/run_tests_CLI/array_api_det_coverage.py index 463f4f5944281..712ce6c8dbee1 100644 --- a/run_tests_CLI/array_api_det_coverage.py +++ b/run_tests_CLI/array_api_det_coverage.py @@ -33,7 +33,7 @@ def main(): continue if ("#" not in s) or ( "#" in s - and not (framework in s.lower()) + and (framework not in s.lower()) and any(f in s.lower() for f in framework_tests_to_run) ): submod = f"ivy_tests/array_api_testing/test_array_api/array_api_tests/test_{fname.replace('.txt', '.py')}" # noqa From f713068bd05ec2ec5ae573bff2d622ac58ef568b Mon Sep 17 00:00:00 2001 From: AnnaTz <111577222+AnnaTz@users.noreply.github.com> Date: Mon, 11 Sep 2023 13:54:14 +0100 Subject: [PATCH 037/117] fix(ivy): Extends ivy.pow to work for all input and exponent cases --- ivy/functional/backends/numpy/elementwise.py | 4 +++- .../backends/tensorflow/elementwise.py | 9 ++++++++- ivy/functional/ivy/elementwise.py | 7 ------- .../test_core/test_elementwise.py | 17 +---------------- 4 files changed, 12 insertions(+), 25 deletions(-) diff --git a/ivy/functional/backends/numpy/elementwise.py b/ivy/functional/backends/numpy/elementwise.py index 6c4459bbaaf4f..654c37a673245 100644 --- a/ivy/functional/backends/numpy/elementwise.py +++ b/ivy/functional/backends/numpy/elementwise.py @@ -611,7 +611,9 @@ def pow( out: Optional[np.ndarray] = None, ) -> np.ndarray: x1, x2 = ivy.promote_types_of_inputs(x1, x2) - return np.power(x1, x2, out=out) + if ivy.is_int_dtype(x1) and ivy.any(x2 < 0): + return np.float_power(x1, x2, casting='unsafe').astype(x1.dtype) + return np.power(x1, x2) pow.support_native_out = True diff --git a/ivy/functional/backends/tensorflow/elementwise.py b/ivy/functional/backends/tensorflow/elementwise.py index 1e060e8e8cae3..a8dc01ba58d3f 100644 --- a/ivy/functional/backends/tensorflow/elementwise.py +++ b/ivy/functional/backends/tensorflow/elementwise.py @@ -620,7 +620,14 @@ def pow( if x2.dtype.is_unsigned: x2 = tf.cast(x2, tf.float64) return tf.cast(tf.experimental.numpy.power(x1, x2), promoted_type) - return tf.experimental.numpy.power(x1, x2) + orig_x1_dtype = None + if ivy.is_int_dtype(x1) and ivy.any(x2 < 0): + orig_x1_dtype = x1.dtype + x1 = tf.cast(x1, tf.float32) + ret = tf.experimental.numpy.power(x1, x2) + if orig_x1_dtype is not None: + return tf.cast(ret, orig_x1_dtype) + return ret @with_unsupported_dtypes({"2.13.0 and below": ("bfloat16", "complex")}, backend_version) diff --git a/ivy/functional/ivy/elementwise.py b/ivy/functional/ivy/elementwise.py index 3d9eebc0aa71e..7909bb56cecd3 100644 --- a/ivy/functional/ivy/elementwise.py +++ b/ivy/functional/ivy/elementwise.py @@ -5201,13 +5201,6 @@ def pow( (the exponent), where ``x2_i`` is the corresponding element of the input array ``x2``. - .. note:: - If both ``x1`` and ``x2`` have integer data types, the result of ``pow`` when - ``x2_i`` is negative (i.e., less than zero) is unspecified and thus - implementation-dependent. If ``x1`` has an integer data type and ``x2`` has a - floating-point data type, behavior is implementation-dependent (type promotion - between data type "kinds" (integer versus floating-point) is unspecified). - **Special cases** For floating-point operands, diff --git a/ivy_tests/test_ivy/test_functional/test_core/test_elementwise.py b/ivy_tests/test_ivy/test_functional/test_core/test_elementwise.py index 953b621b1d70f..447e5d0a34448 100644 --- a/ivy_tests/test_ivy/test_functional/test_core/test_elementwise.py +++ b/ivy_tests/test_ivy/test_functional/test_core/test_elementwise.py @@ -136,10 +136,6 @@ def cast_filter(dtype1_x1_dtype2): ) ) dtype2 = dtype2[0] - if "int" in dtype2: - x2 = ivy.nested_map( - x2[0], lambda x: abs(x), include_derived={"list": True}, shallow=False - ) return [dtype1, dtype2], [x1, x2] @@ -1593,19 +1589,8 @@ def test_positive(*, dtype_and_x, test_flags, backend_fw, fn_name, on_device): ) def test_pow(*, dtype_and_x, test_flags, backend_fw, fn_name, on_device): input_dtype, x = dtype_and_x - # bfloat16 is not supported by numpy - assume("bfloat16" not in input_dtype) - - # Make sure x2 isn't a float when x1 is integer - assume( - not (ivy.is_int_dtype(input_dtype[0] and ivy.is_float_dtype(input_dtype[1]))) - ) - - # Make sure x2 is non-negative when both is integer - if ivy.is_int_dtype(input_dtype[1]) and ivy.is_int_dtype(input_dtype[0]): - x[1] = np.abs(x[1]) - + assume(not ("bfloat16" in input_dtype)) x[0] = not_too_close_to_zero(x[0]) x[1] = not_too_close_to_zero(x[1]) helpers.test_function( From 9c2dc947e00e78be19d4e3716f19e6c402951dd0 Mon Sep 17 00:00:00 2001 From: Indraneel kumar <45780459+Indraneel99@users.noreply.github.com> Date: Mon, 11 Sep 2023 20:20:36 +0530 Subject: [PATCH 038/117] feat(Paddle-frontend): added multilabel_soft_margin_loss to the paddle frontend (#20909) Co-authored-by: hirwa-nshuti --- .../frontends/paddle/nn/functional/loss.py | 18 +++++++ .../test_nn/test_functional/test_loss.py | 52 +++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/ivy/functional/frontends/paddle/nn/functional/loss.py b/ivy/functional/frontends/paddle/nn/functional/loss.py index 774e3d8e43cf4..04377a35403df 100644 --- a/ivy/functional/frontends/paddle/nn/functional/loss.py +++ b/ivy/functional/frontends/paddle/nn/functional/loss.py @@ -472,3 +472,21 @@ def triplet_margin_loss( loss = reduction(loss).astype(input.dtype) return loss + + +@with_supported_dtypes({"2.5.1 and below": ("float32", "float64")}, "paddle") +@to_ivy_arrays_and_back +def multi_label_soft_margin_loss( + input, label, weight=None, reduction="mean", name=None +): + reduction = _get_reduction_func(reduction) + loss = -( + label * ivy.log(ivy.sigmoid(input)) + + (1 - label) * ivy.log(1 - ivy.sigmoid(input)) + ) + + if weight is not None: + loss = ivy.multiply(weight, loss) + loss = ivy.mean(loss, axis=-1) + ret = reduction(loss).astype(input.dtype) + return ret diff --git a/ivy_tests/test_ivy/test_frontends/test_paddle/test_nn/test_functional/test_loss.py b/ivy_tests/test_ivy/test_frontends/test_paddle/test_nn/test_functional/test_loss.py index c6792a06158c3..2a8a9486576d2 100644 --- a/ivy_tests/test_ivy/test_frontends/test_paddle/test_nn/test_functional/test_loss.py +++ b/ivy_tests/test_ivy/test_frontends/test_paddle/test_nn/test_functional/test_loss.py @@ -720,3 +720,55 @@ def test_paddle_triplet_margin_loss( swap=swap, reduction=reduction, ) + + +@handle_frontend_test( + fn_tree="paddle.nn.functional.multi_label_soft_margin_loss", + dtype_and_x=helpers.dtype_and_values( + available_dtypes=helpers.get_dtypes("float"), + num_arrays=2, + min_value=-2, + max_value=2, + shared_dtype=True, + allow_inf=False, + min_num_dims=2, + max_num_dims=5, + min_dim_size=1, + max_dim_size=10, + ), + dtype_and_weight=helpers.dtype_and_values( + available_dtypes=helpers.get_dtypes("float"), + min_num_dims=2, + min_value=-2, + max_value=2, + ), + reduction=st.sampled_from(["mean", "none", "sum"]), +) +def test_paddle_multi_label_soft_margin_loss( + dtype_and_x, + dtype_and_weight, + reduction, + on_device, + fn_tree, + backend_fw, + frontend, + test_flags, +): + x_dtype, x = dtype_and_x + weight_dtype, weight = dtype_and_weight + helpers.test_frontend_function( + input_dtypes=[ + x_dtype[0], + x_dtype[1], + weight_dtype[0], + ], + frontend=frontend, + backend_to_test=backend_fw, + test_flags=test_flags, + fn_tree=fn_tree, + on_device=on_device, + input=x[0], + label=x[1], + weight=weight[0], + reduction=reduction, + ) From b6cdb1f58d0283456c75b89df30c714137c121c8 Mon Sep 17 00:00:00 2001 From: Javeria-Siddique <92924644+Javeria-Siddique@users.noreply.github.com> Date: Mon, 11 Sep 2023 20:03:30 +0500 Subject: [PATCH 039/117] feat(Numpy-frontend): Added eigvals function to numpy frontend (#22920) Co-authored-by: hirwa-nshuti --- .../numpy/linalg/matrix_eigenvalues.py | 5 ++ .../test_linalg/test_matrix_eigenvalues.py | 49 +++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/ivy/functional/frontends/numpy/linalg/matrix_eigenvalues.py b/ivy/functional/frontends/numpy/linalg/matrix_eigenvalues.py index d0b98b00264c7..6c3a6e61f1265 100644 --- a/ivy/functional/frontends/numpy/linalg/matrix_eigenvalues.py +++ b/ivy/functional/frontends/numpy/linalg/matrix_eigenvalues.py @@ -17,6 +17,11 @@ def eigh(a, /, UPLO="L"): return ivy.eigh(a, UPLO=UPLO) +@to_ivy_arrays_and_back +def eigvals(a): + return ivy.eig(a)[0] + + @to_ivy_arrays_and_back @from_zero_dim_arrays_to_scalar def eigvalsh(a, /, UPLO="L"): diff --git a/ivy_tests/test_ivy/test_frontends/test_numpy/test_linalg/test_matrix_eigenvalues.py b/ivy_tests/test_ivy/test_frontends/test_numpy/test_linalg/test_matrix_eigenvalues.py index d240cef1dc48b..b76655cf0897d 100644 --- a/ivy_tests/test_ivy/test_frontends/test_numpy/test_linalg/test_matrix_eigenvalues.py +++ b/ivy_tests/test_ivy/test_frontends/test_numpy/test_linalg/test_matrix_eigenvalues.py @@ -130,6 +130,55 @@ def test_numpy_eigh( ) +@handle_frontend_test( + fn_tree="numpy.linalg.eigvals", + dtype_and_x=helpers.dtype_and_values( + available_dtypes=helpers.get_dtypes("float"), + min_value=0, + max_value=10, + shape=helpers.ints(min_value=2, max_value=4).map(lambda x: tuple([x, x])), + ).filter( + lambda x: "float16" not in x[0] + and "bfloat16" not in x[0] + and np.linalg.cond(x[1][0]) < 1 / sys.float_info.epsilon + and np.linalg.det(np.asarray(x[1][0])) != 0 + ), + test_with_out=st.just(False), +) +def test_numpy_eigvals( + dtype_and_x, + on_device, + fn_tree, + frontend, + test_flags, + backend_fw, +): + dtype, x = dtype_and_x + ret, frontend_ret = helpers.test_frontend_function( + input_dtypes=dtype, + backend_to_test=backend_fw, + frontend=frontend, + test_flags=test_flags, + fn_tree=fn_tree, + on_device=on_device, + test_values=False, + a=x, + ) + with BackendHandler.update_backend(backend_fw) as ivy_backend: + ret = np.sort( + np.array([ivy_backend.to_numpy(x).astype(np.float128) for x in ret]) + ) + frontend_ret = np.sort(np.array([x.astype(np.float128) for x in frontend_ret])) + assert_all_close( + ret_np=ret, + ret_from_gt_np=frontend_ret, + backend=backend_fw, + ground_truth_backend=frontend, + atol=1e-2, + rtol=1e-2, + ) + + # eigvalsh @handle_frontend_test( fn_tree="numpy.linalg.eigvalsh", From 20440c560330196f5be6f9a7499bb64dde4ce7f9 Mon Sep 17 00:00:00 2001 From: AnnaTz <111577222+AnnaTz@users.noreply.github.com> Date: Mon, 11 Sep 2023 16:30:48 +0100 Subject: [PATCH 040/117] fix(ivy): Tests and fixes ivy.pow for int/float exponents --- ivy/data_classes/array/elementwise.py | 2 +- ivy/data_classes/container/elementwise.py | 4 +-- ivy/functional/backends/jax/elementwise.py | 12 +++++-- ivy/functional/backends/mxnet/elementwise.py | 4 +-- ivy/functional/backends/numpy/elementwise.py | 4 +-- ivy/functional/backends/paddle/elementwise.py | 10 ++++-- .../backends/tensorflow/elementwise.py | 4 +-- ivy/functional/backends/torch/elementwise.py | 4 +-- ivy/functional/ivy/elementwise.py | 11 +++++-- .../test_core/test_elementwise.py | 31 ++++++++++++------- 10 files changed, 58 insertions(+), 28 deletions(-) diff --git a/ivy/data_classes/array/elementwise.py b/ivy/data_classes/array/elementwise.py index 794da705e123c..c9ce05589e45b 100644 --- a/ivy/data_classes/array/elementwise.py +++ b/ivy/data_classes/array/elementwise.py @@ -2055,7 +2055,7 @@ def positive(self: ivy.Array, *, out: Optional[ivy.Array] = None) -> ivy.Array: def pow( self: ivy.Array, - x2: Union[ivy.Array, ivy.NativeArray], + x2: Union[int, float, ivy.Array, ivy.NativeArray], /, *, out: Optional[ivy.Array] = None, diff --git a/ivy/data_classes/container/elementwise.py b/ivy/data_classes/container/elementwise.py index 4de059cf06057..2c4432952b979 100644 --- a/ivy/data_classes/container/elementwise.py +++ b/ivy/data_classes/container/elementwise.py @@ -6755,7 +6755,7 @@ def positive( @staticmethod def _static_pow( x1: Union[ivy.Array, ivy.NativeArray, ivy.Container], - x2: Union[ivy.Array, ivy.NativeArray, ivy.Container], + x2: Union[int, float, ivy.Array, ivy.NativeArray, ivy.Container], /, *, key_chains: Optional[Union[List[str], Dict[str, str], ivy.Container]] = None, @@ -6823,7 +6823,7 @@ def _static_pow( def pow( self: ivy.Container, - x2: Union[ivy.Container, ivy.Array, ivy.NativeArray], + x2: Union[int, float, ivy.Container, ivy.Array, ivy.NativeArray], /, *, key_chains: Optional[Union[List[str], Dict[str, str], ivy.Container]] = None, diff --git a/ivy/functional/backends/jax/elementwise.py b/ivy/functional/backends/jax/elementwise.py index 6de81f65a16eb..84f0f7d0b6518 100644 --- a/ivy/functional/backends/jax/elementwise.py +++ b/ivy/functional/backends/jax/elementwise.py @@ -14,6 +14,7 @@ from ivy.functional.backends.jax import JaxArray from ivy.func_wrapper import with_unsupported_dtypes from . import backend_version +from ...ivy.elementwise import _complex_to_inf def abs( @@ -401,13 +402,20 @@ def positive( def pow( - x1: Union[float, JaxArray], - x2: Union[float, JaxArray], + x1: JaxArray, + x2: Union[int, float, JaxArray], /, *, out: Optional[JaxArray] = None, ) -> JaxArray: x1, x2 = ivy.promote_types_of_inputs(x1, x2) + if ivy.is_complex_dtype(x1) and ivy.any(ivy.isinf(x2)): + inf_indices = jnp.nonzero(jnp.isinf(x2)) + ret = jnp.power(x1, x2) + ret[inf_indices] = _complex_to_inf(ret[inf_indices]) + return ret + if ivy.is_int_dtype(x1) and ivy.any(x2 < 0): + return jnp.float_power(x1, x2).astype(x1.dtype) return jnp.power(x1, x2) diff --git a/ivy/functional/backends/mxnet/elementwise.py b/ivy/functional/backends/mxnet/elementwise.py index 42a8a2788b605..5be5e5d59cf5c 100644 --- a/ivy/functional/backends/mxnet/elementwise.py +++ b/ivy/functional/backends/mxnet/elementwise.py @@ -463,8 +463,8 @@ def positive( def pow( - x1: Union[(float, None, mx.ndarray.NDArray)], - x2: Union[(float, None, mx.ndarray.NDArray)], + x1: Union[(None, mx.ndarray.NDArray)], + x2: Union[(int, float, None, mx.ndarray.NDArray)], /, *, out: Optional[Union[(None, mx.ndarray.NDArray)]] = None, diff --git a/ivy/functional/backends/numpy/elementwise.py b/ivy/functional/backends/numpy/elementwise.py index 654c37a673245..72060aae2fed1 100644 --- a/ivy/functional/backends/numpy/elementwise.py +++ b/ivy/functional/backends/numpy/elementwise.py @@ -604,8 +604,8 @@ def positive( @_scalar_output_to_0d_array def pow( - x1: Union[float, np.ndarray], - x2: Union[float, np.ndarray], + x1: np.ndarray, + x2: Union[int, float, np.ndarray], /, *, out: Optional[np.ndarray] = None, diff --git a/ivy/functional/backends/paddle/elementwise.py b/ivy/functional/backends/paddle/elementwise.py index d5dda72b4a901..09210b97a29f0 100644 --- a/ivy/functional/backends/paddle/elementwise.py +++ b/ivy/functional/backends/paddle/elementwise.py @@ -10,6 +10,7 @@ # local from . import backend_version +from ...ivy.elementwise import _complex_to_inf def _elementwise_helper(x1, x2): @@ -800,13 +801,18 @@ def square( {"2.5.1 and below": {"cpu": ("bfloat16",)}}, backend_version ) def pow( - x1: Union[float, paddle.Tensor], - x2: Union[float, paddle.Tensor], + x1: paddle.Tensor, + x2: Union[int, float, paddle.Tensor], /, *, out: Optional[paddle.Tensor] = None, ) -> paddle.Tensor: x1, x2, ret_dtype = _elementwise_helper(x1, x2) + if ivy.is_complex_dtype(x1) and ivy.any(ivy.isinf(x2)): + inf_indices = paddle.nonzero(paddle.isinf(x2)) + ret = paddle.pow(x1, x2) + ret[inf_indices] = _complex_to_inf(ret[inf_indices]) + return ret if x1.dtype in [ paddle.int8, paddle.int16, diff --git a/ivy/functional/backends/tensorflow/elementwise.py b/ivy/functional/backends/tensorflow/elementwise.py index a8dc01ba58d3f..751a42d4a24e6 100644 --- a/ivy/functional/backends/tensorflow/elementwise.py +++ b/ivy/functional/backends/tensorflow/elementwise.py @@ -605,8 +605,8 @@ def positive( backend_version, ) def pow( - x1: Union[float, tf.Tensor, tf.Variable], - x2: Union[float, tf.Tensor, tf.Variable], + x1: Union[tf.Tensor, tf.Variable], + x2: Union[int, float, tf.Tensor, tf.Variable], /, *, out: Optional[Union[tf.Tensor, tf.Variable]] = None, diff --git a/ivy/functional/backends/torch/elementwise.py b/ivy/functional/backends/torch/elementwise.py index a0a3f5c11e053..4d7c767fd5b8c 100644 --- a/ivy/functional/backends/torch/elementwise.py +++ b/ivy/functional/backends/torch/elementwise.py @@ -583,8 +583,8 @@ def square(x: torch.Tensor, /, *, out: Optional[torch.Tensor] = None) -> torch.T @handle_numpy_arrays_in_specific_backend def pow( - x1: Union[float, torch.Tensor], - x2: Union[float, torch.Tensor], + x1: torch.Tensor, + x2: Union[int, float, torch.Tensor], /, *, out: Optional[torch.Tensor] = None, diff --git a/ivy/functional/ivy/elementwise.py b/ivy/functional/ivy/elementwise.py index 7909bb56cecd3..461b1adacc179 100644 --- a/ivy/functional/ivy/elementwise.py +++ b/ivy/functional/ivy/elementwise.py @@ -5189,8 +5189,8 @@ def positive( @handle_array_function @handle_device_shifting def pow( - x1: Union[float, ivy.Array, ivy.NativeArray], - x2: Union[float, ivy.Array, ivy.NativeArray], + x1: Union[ivy.Array, ivy.NativeArray], + x2: Union[int, float, ivy.Array, ivy.NativeArray], /, *, out: Optional[ivy.Array] = None, @@ -5321,6 +5321,13 @@ def pow( pow.unsupported_gradients = {"torch": ["float16"]} +def _complex_to_inf(exponent): + if exponent < 0: + return float('inf') + ivy.nan * 1j + else: + return -0 * 1j + + @handle_exceptions @handle_backend_invalid @handle_nestable diff --git a/ivy_tests/test_ivy/test_functional/test_core/test_elementwise.py b/ivy_tests/test_ivy/test_functional/test_core/test_elementwise.py index 447e5d0a34448..5fd064506ed80 100644 --- a/ivy_tests/test_ivy/test_functional/test_core/test_elementwise.py +++ b/ivy_tests/test_ivy/test_functional/test_core/test_elementwise.py @@ -126,17 +126,26 @@ def cast_filter(dtype1_x1_dtype2): max_value = int(math.log(max_val) / math.log(max_x1)) if abs(max_value) > abs(max_val) / 40 or max_value < 0: max_value = None - dtype2, x2 = draw( - helpers.dtype_and_values( - small_abs_safety_factor=16, - large_abs_safety_factor=16, - safety_factor_scale="log", - max_value=max_value, - dtype=[dtype2], + dtype_and_x2 = draw( + st.one_of( + helpers.dtype_and_values( + small_abs_safety_factor=16, + large_abs_safety_factor=16, + safety_factor_scale="log", + max_value=max_value, + dtype=[dtype2], + ), + st.floats(max_value=max_value), + st.integers(max_value=max_value), ) ) - dtype2 = dtype2[0] - return [dtype1, dtype2], [x1, x2] + input_dtypes = [dtype1] + if isinstance(dtype_and_x2, tuple): + input_dtypes += dtype_and_x2[0] + x2 = dtype_and_x2[1][0] + else: + x2 = dtype_and_x2 + return input_dtypes, [x1[0], x2] # --- Main --- # @@ -144,8 +153,7 @@ def cast_filter(dtype1_x1_dtype2): def not_too_close_to_zero(x): - f = np.vectorize(lambda item: item + (_one if np.isclose(item, 0) else _zero)) - return f(x) + return np.where(np.isclose(x, 0), x + 1, x) # abs @@ -1586,6 +1594,7 @@ def test_positive(*, dtype_and_x, test_flags, backend_fw, fn_name, on_device): @handle_test( fn_tree="functional.ivy.pow", dtype_and_x=pow_helper(), + test_gradients=st.just(False), ) def test_pow(*, dtype_and_x, test_flags, backend_fw, fn_name, on_device): input_dtype, x = dtype_and_x From 1faa24ce8d0d8d8e1ac7146e7c9eef9a600e70d2 Mon Sep 17 00:00:00 2001 From: Peter Kiprop <71701881+Kiprop2020@users.noreply.github.com> Date: Mon, 11 Sep 2023 18:54:07 +0300 Subject: [PATCH 041/117] rfftfreq (#23306) --- ivy/functional/frontends/paddle/fft.py | 9 ++++++++ .../test_frontends/test_paddle/test_fft.py | 22 +++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/ivy/functional/frontends/paddle/fft.py b/ivy/functional/frontends/paddle/fft.py index bd40f59060eb0..4355c8d954eef 100644 --- a/ivy/functional/frontends/paddle/fft.py +++ b/ivy/functional/frontends/paddle/fft.py @@ -122,3 +122,12 @@ def irfft(x, n=None, axis=-1.0, norm="backward", name=None): if ivy.isreal(x): time_domain = ivy.real(time_domain) return time_domain + + +@to_ivy_arrays_and_back +def rfftfreq(n, d=1.0, dtype=None, name=None): + dtype = ivy.default_dtype() + val = 1.0 / (n * d) + pos_max = n // 2 + 1 + indices = ivy.arange(0, pos_max, dtype=dtype) + return indices * val diff --git a/ivy_tests/test_ivy/test_frontends/test_paddle/test_fft.py b/ivy_tests/test_ivy/test_frontends/test_paddle/test_fft.py index e136ea66d7209..a03816f2f7151 100644 --- a/ivy_tests/test_ivy/test_frontends/test_paddle/test_fft.py +++ b/ivy_tests/test_ivy/test_frontends/test_paddle/test_fft.py @@ -220,3 +220,25 @@ def test_paddle_irfft( valid_axis=True, force_int_axis=True, ) + + +@handle_frontend_test( + fn_tree="paddle.fft.rfftfreq", + n=st.integers(min_value=1, max_value=1000), + sample_rate=st.integers(min_value=1, max_value=20), +) +def test_paddle_rfftfreq( + n, sample_rate, backend_fw, frontend, test_flags, fn_tree, on_device +): + d = 1 / sample_rate + helpers.test_frontend_function( + input_dtypes=[int], + frontend=frontend, + backend_to_test=backend_fw, + test_flags=test_flags, + fn_tree=fn_tree, + on_device=on_device, + test_values=True, + n=n, + d=d, + ) From b1a4da38cf956ea76e99b6ac280a5824da1957c6 Mon Sep 17 00:00:00 2001 From: Humza Tareen Date: Mon, 11 Sep 2023 21:00:27 +0500 Subject: [PATCH 042/117] updated scipy.linalg.svdvals test (#22716) --- .../test_scipy/test_linalg/test_linalg.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/ivy_tests/test_ivy/test_frontends/test_scipy/test_linalg/test_linalg.py b/ivy_tests/test_ivy/test_frontends/test_scipy/test_linalg/test_linalg.py index bb2f61a1c45b0..d73718550615a 100644 --- a/ivy_tests/test_ivy/test_frontends/test_scipy/test_linalg/test_linalg.py +++ b/ivy_tests/test_ivy/test_frontends/test_scipy/test_linalg/test_linalg.py @@ -362,31 +362,36 @@ def test_scipy_svd( # svdvals @handle_frontend_test( fn_tree="scipy.linalg.svdvals", - dtype_x=helpers.dtype_and_values( + dtype_and_x=helpers.dtype_and_values( available_dtypes=helpers.get_dtypes("float"), - min_value=0, + min_value=0.1, max_value=50, - min_num_dims=2, + shape=helpers.ints(min_value=2, max_value=5).map(lambda x: tuple([x, x])), ), + check_finite=st.booleans(), test_with_out=st.just(False), ) def test_scipy_svdvals( - dtype_x, + dtype_and_x, + check_finite, frontend, test_flags, fn_tree, - on_device, backend_fw, + on_device, ): - dtype, x = dtype_x + dtype, x = dtype_and_x + x = x[0] helpers.test_frontend_function( input_dtypes=dtype, backend_to_test=backend_fw, frontend=frontend, test_flags=test_flags, + test_values=False, fn_tree=fn_tree, on_device=on_device, - a=x[0], + a=x, + check_finite=check_finite, ) From b26bdac3f8dcb2f740be178a223e84f6eb78302d Mon Sep 17 00:00:00 2001 From: Nitesh Kesharwani <141853536+NiteshK84@users.noreply.github.com> Date: Mon, 11 Sep 2023 21:35:54 +0530 Subject: [PATCH 043/117] lcm_ (#22396) --- ivy/functional/frontends/torch/tensor.py | 21 +++++++++ .../test_frontends/test_torch/test_tensor.py | 45 +++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/ivy/functional/frontends/torch/tensor.py b/ivy/functional/frontends/torch/tensor.py index 0caec65281ba1..20c0c5326a8df 100644 --- a/ivy/functional/frontends/torch/tensor.py +++ b/ivy/functional/frontends/torch/tensor.py @@ -1833,6 +1833,27 @@ def char(self): def lcm(self, other, *, out=None): return torch_frontend.lcm(self, other, out=out) + @with_unsupported_dtypes( + { + "2.0.1 and below": ( + "float16", + "bfloat16", + "float32", + "float64", + "complex", + "uint8", + "uint16", + "uint32", + "uint64", + "int8", + ) + }, + "torch", + ) + def lcm_(self, other, *, out=None): + self.ivy_array = self.lcm(other, out=out).ivy_array + return self + @with_unsupported_dtypes( { "2.0.1 and below": ( diff --git a/ivy_tests/test_ivy/test_frontends/test_torch/test_tensor.py b/ivy_tests/test_ivy/test_frontends/test_torch/test_tensor.py index 53c056b9ae711..cdd54b5cf2ccd 100644 --- a/ivy_tests/test_ivy/test_frontends/test_torch/test_tensor.py +++ b/ivy_tests/test_ivy/test_frontends/test_torch/test_tensor.py @@ -7851,6 +7851,51 @@ def test_torch_tensor_lcm( ) +# lcm_ +@handle_frontend_method( + class_tree=CLASS_TREE, + init_tree="torch.tensor", + method_name="lcm_", + dtype_and_x=helpers.dtype_and_values( + available_dtypes=helpers.get_dtypes("integer"), + num_arrays=2, + min_value=-100, + max_value=100, + min_num_dims=1, + max_num_dims=3, + min_dim_size=1, + max_dim_size=3, + shared_dtype=True, + ), +) +def test_torch_tensor_lcm_( + dtype_and_x, + frontend, + frontend_method_data, + init_flags, + method_flags, + on_device, + backend_fw, +): + input_dtype, x = dtype_and_x + helpers.test_frontend_method( + init_input_dtypes=input_dtype, + backend_to_test=backend_fw, + init_all_as_kwargs_np={ + "data": x[0], + }, + method_input_dtypes=input_dtype, + method_all_as_kwargs_np={ + "other": x[1], + }, + frontend=frontend, + frontend_method_data=frontend_method_data, + init_flags=init_flags, + method_flags=method_flags, + on_device=on_device, + ) + + # less @handle_frontend_method( class_tree=CLASS_TREE, From f3ef8253e4c2682740d9ea0ba1ff1cf910d2a913 Mon Sep 17 00:00:00 2001 From: Bhushan Srivastava <59949692+he11owthere@users.noreply.github.com> Date: Mon, 11 Sep 2023 23:39:03 +0530 Subject: [PATCH 044/117] feat(frontend): Added lerp method to the Paddle frontend (#22571) Co-authored-by: hmahmood24 --- .../frontends/paddle/tensor/tensor.py | 4 ++ .../test_paddle/test_tensor/test_tensor.py | 58 +++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/ivy/functional/frontends/paddle/tensor/tensor.py b/ivy/functional/frontends/paddle/tensor/tensor.py index 7a4f5dc308c88..ad68778a1c0fe 100644 --- a/ivy/functional/frontends/paddle/tensor/tensor.py +++ b/ivy/functional/frontends/paddle/tensor/tensor.py @@ -141,6 +141,10 @@ def sin(self, name=None): def sinh(self, name=None): return paddle_frontend.Tensor(ivy.sinh(self._ivy_array)) + @with_supported_dtypes({"2.5.1 and below": ("float32", "float64")}, "paddle") + def lerp(self, y, weight, name=None): + return paddle_frontend.lerp(self, y, weight) + @with_supported_dtypes({"2.5.1 and below": ("float32", "float64")}, "paddle") def lerp_(self, y, weight, name=None): self.ivy_array = paddle_frontend.lerp(self, y, weight).ivy_array diff --git a/ivy_tests/test_ivy/test_frontends/test_paddle/test_tensor/test_tensor.py b/ivy_tests/test_ivy/test_frontends/test_paddle/test_tensor/test_tensor.py index 4c9762080387f..7ceeba34f4bff 100644 --- a/ivy_tests/test_ivy/test_frontends/test_paddle/test_tensor/test_tensor.py +++ b/ivy_tests/test_ivy/test_frontends/test_paddle/test_tensor/test_tensor.py @@ -127,6 +127,30 @@ def _get_dtype_and_square_matrix(draw): return dtype, mat +@st.composite +def _get_dtype_and_values_for_lerp(draw): + is_tensor = draw(st.booleans()) + if is_tensor: + input_dtype, x = draw( + helpers.dtype_and_values( + num_arrays=3, + available_dtypes=helpers.get_dtypes("valid"), + shared_dtype=True, + ) + ) + return input_dtype, x[0], x[1], x[2] + else: + input_dtype, x = draw( + helpers.dtype_and_values( + num_arrays=2, + available_dtypes=helpers.get_dtypes("valid"), + shared_dtype=True, + ) + ) + weight = draw(st.floats()) + return input_dtype, x[0], x[1], weight + + @st.composite def _reshape_helper(draw): # generate a shape s.t len(shape) > 0 @@ -2154,6 +2178,40 @@ def test_paddle_tensor_isnan( ) +# lerp +@handle_frontend_method( + class_tree=CLASS_TREE, + init_tree="paddle.to_tensor", + method_name="lerp", + dtypes_and_x=_get_dtype_and_values_for_lerp(), +) +def test_paddle_tensor_lerp( + dtypes_and_x, + frontend_method_data, + init_flags, + method_flags, + frontend, + on_device, + backend_fw, +): + input_dtype, x, y, weight = dtypes_and_x + helpers.test_frontend_method( + init_input_dtypes=input_dtype, + backend_to_test=backend_fw, + init_all_as_kwargs_np={"data": x}, + method_input_dtypes=input_dtype, + method_all_as_kwargs_np={ + "y": y, + "weight": weight, + }, + frontend_method_data=frontend_method_data, + init_flags=init_flags, + method_flags=method_flags, + frontend=frontend, + on_device=on_device, + ) + + # lerp_ @handle_frontend_method( class_tree=CLASS_TREE, From 832e3eec40b32492dde9586548c6f8edf2effe69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Haider=20Sultan=20=20=E2=9A=A1?= Date: Mon, 11 Sep 2023 21:03:06 +0000 Subject: [PATCH 045/117] fix --- ivy/functional/frontends/sklearn/tree/_criterion.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ivy/functional/frontends/sklearn/tree/_criterion.py b/ivy/functional/frontends/sklearn/tree/_criterion.py index 737fa9b27f3e2..6b3c14b60a002 100644 --- a/ivy/functional/frontends/sklearn/tree/_criterion.py +++ b/ivy/functional/frontends/sklearn/tree/_criterion.py @@ -49,7 +49,12 @@ def proxy_impurity_improvement(self): The absolute impurity improvement is only computed by the impurity_improvement method once the best split has been found. """ - impurity_left, impurity_right = self.children_impurity() + impurity_left = 0.0 + impurity_right = 0.0 + impurity_left, impurity_right = self.children_impurity( + impurity_left, impurity_right + ) + return ( -self.weighted_n_right * impurity_right - self.weighted_n_left * impurity_left From 510ce004da64dc896cfd2c62b85ec10645d38ded Mon Sep 17 00:00:00 2001 From: Daniel4078 <45633544+Daniel4078@users.noreply.github.com> Date: Tue, 12 Sep 2023 08:35:14 +0800 Subject: [PATCH 046/117] chore: remove test for not fullly implemented max_unpool1d --- .../test_experimental/test_nn/test_layers.py | 33 ------------------- 1 file changed, 33 deletions(-) diff --git a/ivy_tests/test_ivy/test_functional/test_experimental/test_nn/test_layers.py b/ivy_tests/test_ivy/test_functional/test_experimental/test_nn/test_layers.py index 4c963d7bdd711..2e131e2c7ff48 100644 --- a/ivy_tests/test_ivy/test_functional/test_experimental/test_nn/test_layers.py +++ b/ivy_tests/test_ivy/test_functional/test_experimental/test_nn/test_layers.py @@ -1190,39 +1190,6 @@ def test_max_pool3d( ) -@handle_test( - fn_tree="functional.ivy.experimental.layers.max_unpool1d", - x_k_s_p=helpers.arrays_for_pooling(min_dims=3, max_dims=3, min_side=1, max_side=4), - indices=st.lists(st.integers(0, 1), min_size=1, max_size=4), - ground_truth_backend="jax", - test_gradients=st.just(False), -) -def test_max_unpool1d( - *, - x_k_s_p, - indices, - test_flags, - backend_fw, - fn_name, - on_device, -): - dtype, x, kernel, stride, pad = x_k_s_p - helpers.test_function( - input_dtypes=dtype, - test_flags=test_flags, - backend_to_test=backend_fw, - on_device=on_device, - fn_name=fn_name, - rtol_=1e-2, - atol_=1e-2, - x=x[0], - kernel=kernel, - strides=stride, - padding=pad, - indices=indices, - ) - - @handle_test( fn_tree="functional.ivy.experimental.reduce_window", all_args=_reduce_window_helper(_get_reduce_func), From c541422ec523528dd7781777c8ece6618e0a94e1 Mon Sep 17 00:00:00 2001 From: Daniel4078 <45633544+Daniel4078@users.noreply.github.com> Date: Tue, 12 Sep 2023 08:35:23 +0800 Subject: [PATCH 047/117] chore: remove frontend for not fullly implemented max_unpool1d --- ivy/functional/ivy/experimental/layers.py | 71 ----------------------- 1 file changed, 71 deletions(-) diff --git a/ivy/functional/ivy/experimental/layers.py b/ivy/functional/ivy/experimental/layers.py index 120ebe0279784..5003ef0472ddf 100644 --- a/ivy/functional/ivy/experimental/layers.py +++ b/ivy/functional/ivy/experimental/layers.py @@ -114,77 +114,6 @@ def max_pool1d( ) -@handle_backend_invalid -@handle_nestable -@handle_out_argument -@to_native_arrays_and_back -@handle_device_shifting -def max_unpool1d( - x: ivy.Union[ivy.Array, ivy.NativeArray], - indices: Union[ivy.Array, ivy.NativeArray], - kernel: Union[int, Tuple[int]], - strides: Union[int, Tuple[int]], - padding: str, - /, - *, - data_format: str = "NWC", - out: Optional[ivy.Array] = None, -) -> ivy.Array: - """ - Compute a 1-D max unpooling given the 1-D pooled input x and its indices. - - Parameters - ---------- - x - Pooled input image *[batch_size, w, d_in]*. - indices - Indices obtained from the corresponding max pooling operation. - kernel - Size of the kernel i.e., the sliding window for each - dimension of input. *[w]*. - strides - The stride of the sliding window for each dimension of input. - padding - SAME" or "VALID" indicating the algorithm, or list - indicating the per-dimension paddings. - data_format - NWC" or "NCW". Defaults to "NWC". - out - optional output array, for writing the result to. - - Returns - ------- - ret - The result of the unpooling operation. - - Both the description and the type hints above assume an array input - for simplicity, but this function is *nestable*, and therefore - also accepts :class:`ivy.Container` instances in place of any of - the arguments. - - Examples - -------- - >>> x = ivy.arange(0, 24.).reshape((2, 3, 4)) - >>> pool_result = ivy.max_pool1d(x, 2, 2, 'SAME') - >>> print(pool_result) - ivy.array([[[ 4., 5., 6., 7.], - [ 8., 9., 10., 11.]], - - [[16., 17., 18., 19.], - [20., 21., 22., 23.]]]) - >>> unpool_result = ivy.max_unpool1d(pool_result, indices, 2, 2, 'SAME') - >>> print(unpool_result) - ivy.array([[[ 0., 4., 0., 5., 0., 6., 0., 7., 0., 0., 0., 0.], - [ 0., 0., 0., 0., 8., 0., 9., 0., 10., 0., 11., 0.]], - - [[ 0., 0., 0., 0., 0., 0., 0., 0., 16., 0., 17., 0.], - [ 0., 18., 0., 19., 0., 0., 0., 0., 20., 0., 21., 0.]]]) - """ - return ivy.current_backend(x).max_unpool1d( - x, indices, kernel, strides, padding, data_format=data_format, out=out - ) - - @handle_backend_invalid @handle_nestable @handle_out_argument From 7bccf71d7deb7fa1a2c9a3302ea377402eba37b8 Mon Sep 17 00:00:00 2001 From: Mostafa Hani <71686115+CatB1t@users.noreply.github.com> Date: Tue, 12 Sep 2023 04:51:03 +0300 Subject: [PATCH 048/117] fix(testing): update CLI flags to be dynamically retrieved (#22788) --- .../test_ivy/helpers/test_parameter_flags.py | 32 +++++--- ivy_tests/test_ivy/helpers/testing_helpers.py | 73 ++++++++++--------- 2 files changed, 59 insertions(+), 46 deletions(-) diff --git a/ivy_tests/test_ivy/helpers/test_parameter_flags.py b/ivy_tests/test_ivy/helpers/test_parameter_flags.py index 2ab790f34b83f..8e00df11582c4 100644 --- a/ivy_tests/test_ivy/helpers/test_parameter_flags.py +++ b/ivy_tests/test_ivy/helpers/test_parameter_flags.py @@ -3,6 +3,14 @@ from . import globals as test_globals from .pipeline_helper import BackendHandler +from dataclasses import dataclass +from hypothesis.strategies import SearchStrategy + + +@dataclass +class DynamicFlag: + strategy: SearchStrategy + @st.composite def _gradient_strategy(draw): @@ -27,17 +35,17 @@ def _as_varaible_strategy(draw): return draw(st.lists(st.booleans(), min_size=1, max_size=1)) -BuiltNativeArrayStrategy = st.lists(st.booleans(), min_size=1, max_size=1) -BuiltAsVariableStrategy = _as_varaible_strategy() -BuiltContainerStrategy = st.lists(st.booleans(), min_size=1, max_size=1) -BuiltInstanceStrategy = st.booleans() -BuiltInplaceStrategy = st.just(False) -BuiltGradientStrategy = _gradient_strategy() -BuiltWithOutStrategy = st.booleans() -BuiltCompileStrategy = st.just(False) -BuiltFrontendArrayStrategy = st.booleans() -BuiltTranspileStrategy = st.just(False) -BuiltPrecisionModeStrategy = st.booleans() +BuiltNativeArrayStrategy = DynamicFlag(st.lists(st.booleans(), min_size=1, max_size=1)) +BuiltAsVariableStrategy = DynamicFlag(_as_varaible_strategy()) +BuiltContainerStrategy = DynamicFlag(st.lists(st.booleans(), min_size=1, max_size=1)) +BuiltInstanceStrategy = DynamicFlag(st.booleans()) +BuiltInplaceStrategy = DynamicFlag(st.just(False)) +BuiltGradientStrategy = DynamicFlag(_gradient_strategy()) +BuiltWithOutStrategy = DynamicFlag(st.booleans()) +BuiltCompileStrategy = DynamicFlag(st.booleans()) +BuiltFrontendArrayStrategy = DynamicFlag(st.booleans()) +BuiltTranspileStrategy = DynamicFlag(st.just(False)) +BuiltPrecisionModeStrategy = DynamicFlag(st.booleans()) flags_mapping = { @@ -61,7 +69,7 @@ def build_flag(key: str, value: bool): assert ( flags_mapping[key] in globals().keys() ), f"{flags_mapping[key]} is not a valid flag variable." - globals()[flags_mapping[key]] = value + globals()[flags_mapping[key]].strategy = value # Strategy Helpers # diff --git a/ivy_tests/test_ivy/helpers/testing_helpers.py b/ivy_tests/test_ivy/helpers/testing_helpers.py index 43de1243a3e8f..0bbd9d3ab21e0 100644 --- a/ivy_tests/test_ivy/helpers/testing_helpers.py +++ b/ivy_tests/test_ivy/helpers/testing_helpers.py @@ -17,6 +17,7 @@ from . import test_globals as t_globals from .pipeline_helper import BackendHandler from ivy_tests.test_ivy.helpers.test_parameter_flags import ( + DynamicFlag, BuiltInstanceStrategy, BuiltAsVariableStrategy, BuiltNativeArrayStrategy, @@ -51,6 +52,10 @@ ) +def _get_runtime_flag_value(flag): + return flag.strategy if isinstance(flag, DynamicFlag) else flag + + @st.composite def num_positional_args_method(draw, *, method): """ @@ -395,14 +400,14 @@ def handle_test( possible_arguments["test_flags"] = pf.function_flags( ground_truth_backend=st.just(ground_truth_backend), num_positional_args=number_positional_args, - instance_method=test_instance_method, - with_out=test_with_out, - test_gradients=test_gradients, - test_compile=test_compile, - as_variable=as_variable_flags, - native_arrays=native_array_flags, - container_flags=container_flags, - precision_mode=precision_mode, + instance_method=_get_runtime_flag_value(test_instance_method), + with_out=_get_runtime_flag_value(test_with_out), + test_gradients=_get_runtime_flag_value(test_gradients), + test_compile=_get_runtime_flag_value(test_compile), + as_variable=_get_runtime_flag_value(as_variable_flags), + native_arrays=_get_runtime_flag_value(native_array_flags), + container_flags=_get_runtime_flag_value(container_flags), + precision_mode=_get_runtime_flag_value(precision_mode), ) def test_wrapper(test_fn): @@ -526,14 +531,14 @@ def handle_frontend_test( # Generate the test flags strategy test_flags = pf.frontend_function_flags( num_positional_args=number_positional_args, - with_out=test_with_out, - inplace=test_inplace, - as_variable=as_variable_flags, - native_arrays=native_array_flags, - test_compile=test_compile, - generate_frontend_arrays=generate_frontend_arrays, - transpile=transpile, - precision_mode=precision_mode, + with_out=_get_runtime_flag_value(test_with_out), + inplace=_get_runtime_flag_value(test_inplace), + as_variable=_get_runtime_flag_value(as_variable_flags), + native_arrays=_get_runtime_flag_value(native_array_flags), + test_compile=_get_runtime_flag_value(test_compile), + generate_frontend_arrays=_get_runtime_flag_value(generate_frontend_arrays), + transpile=_get_runtime_flag_value(transpile), + precision_mode=_get_runtime_flag_value(precision_mode), ) def test_wrapper(test_fn): @@ -635,9 +640,9 @@ def handle_method( is_hypothesis_test = len(_given_kwargs) != 0 possible_arguments = { "ground_truth_backend": st.just(ground_truth_backend), - "test_gradients": test_gradients, - "test_compile": test_compile, - "precision_mode": precision_mode, + "test_gradients": _get_runtime_flag_value(test_gradients), + "test_compile": _get_runtime_flag_value(test_compile), + "precision_mode": _get_runtime_flag_value(precision_mode), } if is_hypothesis_test and is_method_tree_provided: @@ -650,9 +655,9 @@ def handle_method( possible_arguments["init_flags"] = pf.init_method_flags( num_positional_args=init_num_positional_args, - as_variable=init_as_variable_flags, - native_arrays=init_native_arrays, - precision_mode=precision_mode, + as_variable=_get_runtime_flag_value(init_as_variable_flags), + native_arrays=_get_runtime_flag_value(init_native_arrays), + precision_mode=_get_runtime_flag_value(precision_mode), ) if method_num_positional_args is None: @@ -662,10 +667,10 @@ def handle_method( possible_arguments["method_flags"] = pf.method_flags( num_positional_args=method_num_positional_args, - as_variable=method_as_variable_flags, - native_arrays=method_native_arrays, - container_flags=method_container_flags, - precision_mode=precision_mode, + as_variable=_get_runtime_flag_value(method_as_variable_flags), + native_arrays=_get_runtime_flag_value(method_native_arrays), + container_flags=_get_runtime_flag_value(method_container_flags), + precision_mode=_get_runtime_flag_value(precision_mode), ) def test_wrapper(test_fn): @@ -783,18 +788,18 @@ def test_wrapper(test_fn): param_names = inspect.signature(test_fn).parameters.keys() init_flags = pf.frontend_method_flags( num_positional_args=init_num_positional_args, - as_variable=init_as_variable_flags, - native_arrays=init_native_arrays, - test_compile=test_compile, - precision_mode=precision_mode, + as_variable=_get_runtime_flag_value(init_as_variable_flags), + native_arrays=_get_runtime_flag_value(init_native_arrays), + test_compile=_get_runtime_flag_value(test_compile), + precision_mode=_get_runtime_flag_value(precision_mode), ) method_flags = pf.frontend_method_flags( num_positional_args=method_num_positional_args, - as_variable=method_as_variable_flags, - native_arrays=method_native_arrays, - test_compile=test_compile, - precision_mode=precision_mode, + as_variable=_get_runtime_flag_value(method_as_variable_flags), + native_arrays=_get_runtime_flag_value(method_native_arrays), + test_compile=_get_runtime_flag_value(test_compile), + precision_mode=_get_runtime_flag_value(precision_mode), ) ivy_init_modules = str(ivy_init_module) framework_init_modules = str(framework_init_module) From 0f4a62e6c0d8026e84c7f887dc70e3f546c86daf Mon Sep 17 00:00:00 2001 From: Mostafa Hany Date: Tue, 12 Sep 2023 05:07:24 +0300 Subject: [PATCH 049/117] fix(testing): update compile flag to be False by default --- ivy_tests/test_ivy/helpers/test_parameter_flags.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ivy_tests/test_ivy/helpers/test_parameter_flags.py b/ivy_tests/test_ivy/helpers/test_parameter_flags.py index 8e00df11582c4..32dfbdb8aacf5 100644 --- a/ivy_tests/test_ivy/helpers/test_parameter_flags.py +++ b/ivy_tests/test_ivy/helpers/test_parameter_flags.py @@ -42,7 +42,7 @@ def _as_varaible_strategy(draw): BuiltInplaceStrategy = DynamicFlag(st.just(False)) BuiltGradientStrategy = DynamicFlag(_gradient_strategy()) BuiltWithOutStrategy = DynamicFlag(st.booleans()) -BuiltCompileStrategy = DynamicFlag(st.booleans()) +BuiltCompileStrategy = DynamicFlag(st.just(False)) BuiltFrontendArrayStrategy = DynamicFlag(st.booleans()) BuiltTranspileStrategy = DynamicFlag(st.just(False)) BuiltPrecisionModeStrategy = DynamicFlag(st.booleans()) From 6fad2fa1f11fbf1423cfbe87b0025d76046c07d8 Mon Sep 17 00:00:00 2001 From: Mostafa Hani <71686115+CatB1t@users.noreply.github.com> Date: Tue, 12 Sep 2023 05:32:56 +0300 Subject: [PATCH 050/117] feat(github): Add Sherry as a testing CODEOWNER --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index a2175bcbfaf5f..f375d2cf90da1 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -5,7 +5,7 @@ ivy/utils/backend @VedPatwardhan @CatB1t ivy/utils/backend/ast_helpers.py @CatB1t # Ivy Testing -ivy_tests/test_ivy/helpers/ @CatB1t +ivy_tests/test_ivy/helpers/ @sherry30 @CatB1t ivy_tests/array_api_testing/ @aarsh2001 @hirwa-nshuti # Docs builder From 61ddd4c4d9a8cc833e85560cf70282a1fa403303 Mon Sep 17 00:00:00 2001 From: Vismay Suramwar <83938053+Vismay-dev@users.noreply.github.com> Date: Mon, 11 Sep 2023 22:48:48 -0500 Subject: [PATCH 051/117] Added kl_div loss to ivy experimental api (#23054) Co-authored-by: Eddy Oyieko <67474838+mobley-trent@users.noreply.github.com> --- ivy/data_classes/array/experimental/losses.py | 44 +++++++ .../container/experimental/losses.py | 114 ++++++++++++++++++ .../backends/jax/experimental/losses.py | 20 +++ .../backends/numpy/experimental/losses.py | 23 ++++ .../backends/paddle/experimental/losses.py | 26 ++++ .../tensorflow/experimental/losses.py | 22 ++++ .../backends/torch/experimental/losses.py | 29 +++++ ivy/functional/ivy/experimental/losses.py | 69 +++++++++++ .../test_experimental/test_nn/test_losses.py | 49 ++++++++ 9 files changed, 396 insertions(+) diff --git a/ivy/data_classes/array/experimental/losses.py b/ivy/data_classes/array/experimental/losses.py index 8913cf936cc85..68265a85a16e7 100644 --- a/ivy/data_classes/array/experimental/losses.py +++ b/ivy/data_classes/array/experimental/losses.py @@ -251,3 +251,47 @@ def soft_margin_loss( ivy.array([0.35667497, 0.22314353, 1.60943791]) """ return ivy.soft_margin_loss(self._data, target, reduction=reduction, out=out) + + def kl_div( + self: ivy.Array, + target: Union[ivy.Array, ivy.NativeArray], + /, + *, + reduction: Optional[str] = "mean", + out: Optional[ivy.Array] = None, + ) -> ivy.Array: + """ + ivy.Array instance method variant of ivy.kl_div. This method simply wraps the + function, and so the docstring for ivy.kl_div also applies to this method with + minimal changes. + + Parameters + ---------- + self + Array containing input probability distribution. + target + Array contaiing target probability distribution. + reduction + 'none': No reduction will be applied to the output. + 'mean': The output will be averaged. + 'batchmean': The output will be divided by batch size. + 'sum': The output will be summed. + Default: 'mean'. + out + Optional output array, for writing the result to. + It must have a shape that the inputs broadcast to. + + Returns + ------- + ret + The Kullback-Leibler divergence loss between the two input arrays. + + Examples + -------- + >>> input = ivy.array([0.2, 0.8], [0.5, 0.5]) + >>> target = ivy.array([0.6, 0.4], [0.3, 0.7]) + >>> output_array = input.kl_div(target) + >>> print(output_array) + ivy.array(0.0916) + """ + return ivy.kl_div(self._data, target, reduction=reduction, out=out) diff --git a/ivy/data_classes/container/experimental/losses.py b/ivy/data_classes/container/experimental/losses.py index 482d9e82198f7..e4ee3c40b45d7 100644 --- a/ivy/data_classes/container/experimental/losses.py +++ b/ivy/data_classes/container/experimental/losses.py @@ -787,3 +787,117 @@ def soft_margin_loss( map_sequences=map_sequences, out=out, ) + + @staticmethod + def _static_kl_div( + input: Union[ivy.Container, ivy.Array, ivy.NativeArray], + target: Union[ivy.Container, ivy.Array, ivy.NativeArray], + /, + *, + reduction: Optional[Union[str, ivy.Container]] = "mean", + key_chains: Optional[Union[List[str], Dict[str, str], ivy.Container]] = None, + to_apply: Union[bool, ivy.Container] = True, + prune_unapplied: Union[bool, ivy.Container] = False, + map_sequences: Union[bool, ivy.Container] = False, + out: Optional[ivy.Container] = None, + ) -> ivy.Container: + """ + ivy.Container static method variant of ivy.kl_div. This method simply wraps the + function, and so the docstring for ivy.kl_div also applies to this method with + minimal changes. + + Parameters + ---------- + input + input array or container containing input distribution. + target + input array or container containing target distribution. + reduction + the reduction method. Default: "mean". + key_chains + The key-chains to apply or not apply the method to. Default is None. + to_apply + If input, the method will be applied to key_chains, otherwise key_chains + will be skipped. Default is input. + prune_unapplied + Whether to prune key_chains for which the function was not applied. + Default is False. + map_sequences + Whether to also map method to sequences (lists, tuples). + Default is False. + out + optional output container, for writing the result to. It must have a shape + that the inputs broadcast to. + + Returns + ------- + ret + The Kullback-Leibler divergence loss between the given distributions. + """ + return ContainerBase.cont_multi_map_in_function( + "kl_div", + input, + target, + reduction=reduction, + key_chains=key_chains, + to_apply=to_apply, + prune_unapplied=prune_unapplied, + map_sequences=map_sequences, + out=out, + ) + + def kl_div( + self: ivy.Container, + target: Union[ivy.Container, ivy.Array, ivy.NativeArray], + /, + *, + reduction: Optional[Union[str, ivy.Container]] = "mean", + key_chains: Optional[Union[List[str], Dict[str, str], ivy.Container]] = None, + to_apply: Union[bool, ivy.Container] = True, + prune_unapplied: Union[bool, ivy.Container] = False, + map_sequences: Union[bool, ivy.Container] = False, + out: Optional[ivy.Container] = None, + ) -> ivy.Container: + """ + ivy.Container instance method variant of ivy.kl_div. This method simply wraps + the function, and so the docstring for ivy.kl_div also applies to this method + with minimal changes. + + Parameters + ---------- + self + input container containing input distribution. + target + input array or container containing target distribution. + reduction + the reduction method. Default: "mean". + key_chains + The key-chains to apply or not apply the method to. Default is None. + to_apply + If input, the method will be applied to key_chains, otherwise key_chains + will be skipped. Default is input. + prune_unapplied + Whether to prune key_chains for which the function was not applied. + Default is False. + map_sequences + Whether to also map method to sequences (lists, tuples). + Default is False. + out + optional output container, for writing the result to. It must have a shape + that the inputs broadcast to. + + Returns + ------- + ret + The Kullback-Leibler divergence loss between the given distributions. + """ + return self._static_kl_div( + self, + target, + reduction=reduction, + key_chains=key_chains, + to_apply=to_apply, + prune_unapplied=prune_unapplied, + map_sequences=map_sequences, + out=out, + ) diff --git a/ivy/functional/backends/jax/experimental/losses.py b/ivy/functional/backends/jax/experimental/losses.py index a5470e0afbc39..3fa778c0dbb9a 100644 --- a/ivy/functional/backends/jax/experimental/losses.py +++ b/ivy/functional/backends/jax/experimental/losses.py @@ -56,3 +56,23 @@ def soft_margin_loss( return jnp.sum(loss) else: return loss + + +def kl_div( + input: JaxArray, + target: JaxArray, + /, + *, + reduction: Optional[str] = "mean", +) -> JaxArray: + size = jnp.shape(input) + loss = jnp.sum(input * jnp.log(input / target), axis=-1) + + if reduction == "mean": + loss = jnp.mean(loss) + elif reduction == "sum": + loss = jnp.sum(loss) + elif reduction == "batchmean": + loss = jnp.divide(jnp.sum(loss), size[0]) + + return loss diff --git a/ivy/functional/backends/numpy/experimental/losses.py b/ivy/functional/backends/numpy/experimental/losses.py index 0a33b6bcf1dad..76a266109e785 100644 --- a/ivy/functional/backends/numpy/experimental/losses.py +++ b/ivy/functional/backends/numpy/experimental/losses.py @@ -70,3 +70,26 @@ def soft_margin_loss( return np.sum(loss) else: return loss + + +@with_unsupported_dtypes({"1.25.2 and below": ("bool", "bfloat16")}, backend_version) +@_scalar_output_to_0d_array +def kl_div( + input: np.ndarray, + target: np.ndarray, + /, + *, + reduction: Optional[str] = "mean", +) -> np.ndarray: + size = np.shape(input) + + loss = np.sum(input * np.log(input / target), axis=-1) + + if reduction == "mean": + loss = np.mean(loss) + elif reduction == "sum": + loss = np.sum(loss) + elif reduction == "batchmean": + loss = np.divide(np.sum(loss), size[0]) + + return loss diff --git a/ivy/functional/backends/paddle/experimental/losses.py b/ivy/functional/backends/paddle/experimental/losses.py index b0be8bda8d502..a582b43e15fb2 100644 --- a/ivy/functional/backends/paddle/experimental/losses.py +++ b/ivy/functional/backends/paddle/experimental/losses.py @@ -120,3 +120,29 @@ def soft_margin_loss( reduction: Optional[str] = "mean", ) -> paddle.Tensor: return paddle.nn.functional.soft_margin_loss(input, label, reduction=reduction) + + +@with_unsupported_device_and_dtypes( + { + "2.5.1 and below": { + "cpu": ( + "bfloat16", + "float16", + "int8", + "int16", + "int32", + "int64", + "uint8", + "complex64", + "complex128", + "bool", + ) + } + }, + backend_version, +) +def kl_div( + input: paddle.Tensor, target: paddle.Tensor, /, *, reduction: Optional[str] = "mean" +) -> paddle.Tensor: + loss = F.kl_div(input, target, reduction=reduction) + return loss diff --git a/ivy/functional/backends/tensorflow/experimental/losses.py b/ivy/functional/backends/tensorflow/experimental/losses.py index fdd493e40b8e8..e0c2da76b9958 100644 --- a/ivy/functional/backends/tensorflow/experimental/losses.py +++ b/ivy/functional/backends/tensorflow/experimental/losses.py @@ -62,3 +62,25 @@ def soft_margin_loss( return tf.reduce_mean(loss) else: return loss + + +@with_unsupported_dtypes({"2.13.0 and below": ("bool", "bfloat16")}, backend_version) +def kl_div( + input: tf.Tensor, + target: tf.Tensor, + /, + *, + reduction: Optional[str] = "mean", +) -> tf.Tensor: + size = tf.shape(input) + + loss = tf.reduce_sum(input * tf.math.log(input / target), axis=-1) + + if reduction == "mean": + loss = tf.math.reduce_mean(loss) + elif reduction == "sum": + loss = tf.math.reduce_sum(loss) + elif reduction == "batchmean": + loss = tf.math.reduce_sum(loss) / tf.cast(size[0], dtype=tf.float32) + + return loss diff --git a/ivy/functional/backends/torch/experimental/losses.py b/ivy/functional/backends/torch/experimental/losses.py index 2c24c6afd01e2..a37365922a1b6 100644 --- a/ivy/functional/backends/torch/experimental/losses.py +++ b/ivy/functional/backends/torch/experimental/losses.py @@ -97,3 +97,32 @@ def soft_margin_loss( target, reduction=reduction, ) + + +@with_unsupported_dtypes( + { + "2.0.1 and below": ( + "float16", + "uint8", + "int8", + "int16", + "int32", + "int64", + "bool", + ) + }, + backend_version, +) +def kl_div( + input: torch.Tensor, + target: torch.Tensor, + /, + *, + reduction: Optional[str] = "mean", +) -> torch.Tensor: + loss = torch.nn.functional.kl_div( + input, + target, + reduction=reduction, + ) + return loss diff --git a/ivy/functional/ivy/experimental/losses.py b/ivy/functional/ivy/experimental/losses.py index 8d2ef5d3e8f6a..f2950d682bb0d 100644 --- a/ivy/functional/ivy/experimental/losses.py +++ b/ivy/functional/ivy/experimental/losses.py @@ -408,3 +408,72 @@ def soft_margin_loss( return ivy.mean(loss, out=out) else: return ivy.inplace_update(out, loss) if out is not None else loss + + +@handle_exceptions +@handle_nestable +@inputs_to_ivy_arrays +@handle_array_function +def kl_div( + input: Union[ivy.Array, ivy.NativeArray], + target: Union[ivy.Array, ivy.NativeArray], + /, + *, + reduction: Optional[str] = "mean", + out: Optional[ivy.Array] = None, +) -> ivy.Array: + """ + Compute the Kullback-Leibler divergence loss between two input tensors + (conventionally, probability distributions). + + Parameters + ---------- + input : array_like + Input probability distribution (first tensor). + target : array_like + Target probability distribution (second tensor). + reduction : {'mean', 'sum', 'batchmean', 'none'}, optional + Type of reduction to apply to the output. Default is 'mean'. + out : array_like, optional + Optional output array, for writing the result to. + It must have a shape that the inputs broadcast to. + + Returns + ------- + ret : array + The Kullback-Leibler divergence loss between the two input tensors. + + Examples + -------- + >>> input = ivy.array([0.2, 0.8], [0.5, 0.5]) + >>> target = ivy.array([0.6, 0.4], [0.3, 0.7]) + >>> ivy.kl_div(input, target) + ivy.array(0.0916) + + >>> input = ivy.array([0.2, 0.8], [0.5, 0.5]) + >>> target = ivy.array([0.6, 0.4], [0.3, 0.7]) + >>> ivy.kl_div(input, target, reduction='sum') + ivy.array(0.1832) + + >>> input = ivy.array([0.2, 0.8], [0.5, 0.5]) + >>> target = ivy.array([0.6, 0.4], [0.3, 0.7]) + >>> ivy.kl_div(input, target, reduction='batchmean') + ivy.array(0.0916) + + >>> input = ivy.array([0.2, 0.8], [0.5, 0.5]) + >>> target = ivy.array([0.6, 0.4], [0.3, 0.7]) + >>> ivy.kl_div(input, target, reduction='none') + ivy.array([0.0378], [0.1453]) + """ + size = ivy.shape(input) + + loss = ivy.sum(input * ivy.log(input / target), axis=-1) + + if reduction == "sum": + loss = ivy.sum(loss, out=out) + elif reduction == "mean": + loss = ivy.mean(loss, out=out) + elif reduction == "batchmean": + loss = ivy.sum(loss, out=out) / size[0] + + return ivy.inplace_update(out, loss) if out is not None else loss diff --git a/ivy_tests/test_ivy/test_functional/test_experimental/test_nn/test_losses.py b/ivy_tests/test_ivy/test_functional/test_experimental/test_nn/test_losses.py index c41b3f0d2d63c..cbddd092c511e 100644 --- a/ivy_tests/test_ivy/test_functional/test_experimental/test_nn/test_losses.py +++ b/ivy_tests/test_ivy/test_functional/test_experimental/test_nn/test_losses.py @@ -55,6 +55,55 @@ def test_huber_loss( ) +# kl_div +@handle_test( + fn_tree="functional.ivy.experimental.kl_div", + dtype_and_input=helpers.dtype_and_values( + available_dtypes=helpers.get_dtypes("float"), + min_value=1e-04, + max_value=1, + allow_inf=False, + min_num_dims=1, + max_num_dims=3, + min_dim_size=3, + ), + dtype_and_target=helpers.dtype_and_values( + available_dtypes=helpers.get_dtypes("float"), + min_value=1e-04, + max_value=1, + allow_inf=False, + min_num_dims=1, + max_num_dims=3, + min_dim_size=3, + ), + reduction=st.sampled_from(["none", "sum", "batchmean", "mean"]), + test_with_out=st.just(False), +) +def test_kl_div( + dtype_and_input, + dtype_and_target, + reduction, + test_flags, + backend_fw, + fn_name, + on_device, +): + input_dtype, input = dtype_and_input + target_dtype, target = dtype_and_target + + helpers.test_function( + input_dtypes=input_dtype + target_dtype, + test_flags=test_flags, + backend_to_test=backend_fw, + fn_name=fn_name, + on_device=on_device, + atol_=1e-02, + input=input[0], + target=target[0], + reduction=reduction, + ) + + @handle_test( fn_tree="functional.ivy.experimental.l1_loss", dtype_input=helpers.dtype_and_values( From 57a3a50423771c561fa1fbbec7077155a6b4dd2e Mon Sep 17 00:00:00 2001 From: ivy-branch Date: Tue, 12 Sep 2023 08:08:09 +0000 Subject: [PATCH 052/117] =?UTF-8?q?=F0=9F=A4=96=20Lint=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/overview/deep_dive/formatting.rst | 2 +- docs/overview/deep_dive/ivy_lint.rst | 6 +- ivy/functional/backends/numpy/elementwise.py | 2 +- .../frontends/paddle/nn/functional/loss.py | 36 +++--- ivy/functional/ivy/elementwise.py | 2 +- .../test_nn/test_functional/test_loss.py | 104 +++++++++--------- run_tests_CLI/synchronize_db.py | 5 +- 7 files changed, 77 insertions(+), 80 deletions(-) diff --git a/docs/overview/deep_dive/formatting.rst b/docs/overview/deep_dive/formatting.rst index c561f1fe04b97..a5d51ffcedbd9 100644 --- a/docs/overview/deep_dive/formatting.rst +++ b/docs/overview/deep_dive/formatting.rst @@ -27,7 +27,7 @@ We use the following linters: * `autoflake `_ * `docformatter `_ * `pydocstyle `_ -* `ivy-lint `_ +* `ivy-lint `_ You can also take a look at our configuration for linting in `setup.cfg `_ file. diff --git a/docs/overview/deep_dive/ivy_lint.rst b/docs/overview/deep_dive/ivy_lint.rst index 388e67bfd58b2..a777f110a7470 100644 --- a/docs/overview/deep_dive/ivy_lint.rst +++ b/docs/overview/deep_dive/ivy_lint.rst @@ -21,13 +21,13 @@ This formatter ensures a standardized order of declarations within Python files, How the Formatter Works: ~~~~~~~~~~~~~~~~~~~~~~~~ -1. **Header Management**: +1. **Header Management**: - Removes pre-existing headers in the source code based on specific patterns. -2. **Comments Handling**: +2. **Comments Handling**: - Extracts code components along with their leading comments, ensuring that relevant comments are retained during the reordering process. -3. **Dependency Handling**: +3. **Dependency Handling**: - Constructs dependency graphs to understand and maintain the relationships between classes and assignments. 4. **Sorting Logic**: diff --git a/ivy/functional/backends/numpy/elementwise.py b/ivy/functional/backends/numpy/elementwise.py index 72060aae2fed1..7a360eb14e5c7 100644 --- a/ivy/functional/backends/numpy/elementwise.py +++ b/ivy/functional/backends/numpy/elementwise.py @@ -612,7 +612,7 @@ def pow( ) -> np.ndarray: x1, x2 = ivy.promote_types_of_inputs(x1, x2) if ivy.is_int_dtype(x1) and ivy.any(x2 < 0): - return np.float_power(x1, x2, casting='unsafe').astype(x1.dtype) + return np.float_power(x1, x2, casting="unsafe").astype(x1.dtype) return np.power(x1, x2) diff --git a/ivy/functional/frontends/paddle/nn/functional/loss.py b/ivy/functional/frontends/paddle/nn/functional/loss.py index 04377a35403df..19f5a4e67559b 100644 --- a/ivy/functional/frontends/paddle/nn/functional/loss.py +++ b/ivy/functional/frontends/paddle/nn/functional/loss.py @@ -262,6 +262,24 @@ def mse_loss(input, label, reduction="mean", name=None): return paddle.to_tensor(ret) +@with_supported_dtypes({"2.5.1 and below": ("float32", "float64")}, "paddle") +@to_ivy_arrays_and_back +def multi_label_soft_margin_loss( + input, label, weight=None, reduction="mean", name=None +): + reduction = _get_reduction_func(reduction) + loss = -( + label * ivy.log(ivy.sigmoid(input)) + + (1 - label) * ivy.log(1 - ivy.sigmoid(input)) + ) + + if weight is not None: + loss = ivy.multiply(weight, loss) + loss = ivy.mean(loss, axis=-1) + ret = reduction(loss).astype(input.dtype) + return ret + + @with_supported_dtypes({"2.5.1 and below": ("float32", "float64")}, "paddle") @to_ivy_arrays_and_back def nll_loss( @@ -472,21 +490,3 @@ def triplet_margin_loss( loss = reduction(loss).astype(input.dtype) return loss - - -@with_supported_dtypes({"2.5.1 and below": ("float32", "float64")}, "paddle") -@to_ivy_arrays_and_back -def multi_label_soft_margin_loss( - input, label, weight=None, reduction="mean", name=None -): - reduction = _get_reduction_func(reduction) - loss = -( - label * ivy.log(ivy.sigmoid(input)) - + (1 - label) * ivy.log(1 - ivy.sigmoid(input)) - ) - - if weight is not None: - loss = ivy.multiply(weight, loss) - loss = ivy.mean(loss, axis=-1) - ret = reduction(loss).astype(input.dtype) - return ret diff --git a/ivy/functional/ivy/elementwise.py b/ivy/functional/ivy/elementwise.py index 461b1adacc179..cdc3776ebc357 100644 --- a/ivy/functional/ivy/elementwise.py +++ b/ivy/functional/ivy/elementwise.py @@ -5323,7 +5323,7 @@ def pow( def _complex_to_inf(exponent): if exponent < 0: - return float('inf') + ivy.nan * 1j + return float("inf") + ivy.nan * 1j else: return -0 * 1j diff --git a/ivy_tests/test_ivy/test_frontends/test_paddle/test_nn/test_functional/test_loss.py b/ivy_tests/test_ivy/test_frontends/test_paddle/test_nn/test_functional/test_loss.py index 2a8a9486576d2..58ac9148a2ad5 100644 --- a/ivy_tests/test_ivy/test_frontends/test_paddle/test_nn/test_functional/test_loss.py +++ b/ivy_tests/test_ivy/test_frontends/test_paddle/test_nn/test_functional/test_loss.py @@ -417,6 +417,58 @@ def test_paddle_mse_loss( ) +@handle_frontend_test( + fn_tree="paddle.nn.functional.multi_label_soft_margin_loss", + dtype_and_x=helpers.dtype_and_values( + available_dtypes=helpers.get_dtypes("float"), + num_arrays=2, + min_value=-2, + max_value=2, + shared_dtype=True, + allow_inf=False, + min_num_dims=2, + max_num_dims=5, + min_dim_size=1, + max_dim_size=10, + ), + dtype_and_weight=helpers.dtype_and_values( + available_dtypes=helpers.get_dtypes("float"), + min_num_dims=2, + min_value=-2, + max_value=2, + ), + reduction=st.sampled_from(["mean", "none", "sum"]), +) +def test_paddle_multi_label_soft_margin_loss( + dtype_and_x, + dtype_and_weight, + reduction, + on_device, + fn_tree, + backend_fw, + frontend, + test_flags, +): + x_dtype, x = dtype_and_x + weight_dtype, weight = dtype_and_weight + helpers.test_frontend_function( + input_dtypes=[ + x_dtype[0], + x_dtype[1], + weight_dtype[0], + ], + frontend=frontend, + backend_to_test=backend_fw, + test_flags=test_flags, + fn_tree=fn_tree, + on_device=on_device, + input=x[0], + label=x[1], + weight=weight[0], + reduction=reduction, + ) + + @handle_frontend_test( fn_tree="paddle.nn.functional.nll_loss", dtype_and_x=helpers.dtype_and_values( @@ -720,55 +772,3 @@ def test_paddle_triplet_margin_loss( swap=swap, reduction=reduction, ) - - -@handle_frontend_test( - fn_tree="paddle.nn.functional.multi_label_soft_margin_loss", - dtype_and_x=helpers.dtype_and_values( - available_dtypes=helpers.get_dtypes("float"), - num_arrays=2, - min_value=-2, - max_value=2, - shared_dtype=True, - allow_inf=False, - min_num_dims=2, - max_num_dims=5, - min_dim_size=1, - max_dim_size=10, - ), - dtype_and_weight=helpers.dtype_and_values( - available_dtypes=helpers.get_dtypes("float"), - min_num_dims=2, - min_value=-2, - max_value=2, - ), - reduction=st.sampled_from(["mean", "none", "sum"]), -) -def test_paddle_multi_label_soft_margin_loss( - dtype_and_x, - dtype_and_weight, - reduction, - on_device, - fn_tree, - backend_fw, - frontend, - test_flags, -): - x_dtype, x = dtype_and_x - weight_dtype, weight = dtype_and_weight - helpers.test_frontend_function( - input_dtypes=[ - x_dtype[0], - x_dtype[1], - weight_dtype[0], - ], - frontend=frontend, - backend_to_test=backend_fw, - test_flags=test_flags, - fn_tree=fn_tree, - on_device=on_device, - input=x[0], - label=x[1], - weight=weight[0], - reduction=reduction, - ) diff --git a/run_tests_CLI/synchronize_db.py b/run_tests_CLI/synchronize_db.py index a21ee7a88337f..b133bb0854434 100644 --- a/run_tests_CLI/synchronize_db.py +++ b/run_tests_CLI/synchronize_db.py @@ -1,4 +1,3 @@ -import json import sys from pymongo import MongoClient from get_all_tests import get_all_tests @@ -21,9 +20,7 @@ def keys_to_delete_from_db(all_tests, module, data, current_key=""): - """ - Recursively navigate and identify keys not in the list. - """ + """Recursively navigate and identify keys not in the list.""" keys_for_deletion = [] for key, value in data.items(): From f296f4b658264ba32c6602089ebcf26cda51abc6 Mon Sep 17 00:00:00 2001 From: Gadri Ebenezer Date: Tue, 12 Sep 2023 08:28:10 +0000 Subject: [PATCH 053/117] Jax Frontend: from_dlpack (#23445) --- .../frontends/jax/numpy/creation.py | 5 ++++ .../test_jax/test_numpy/test_creation.py | 28 +++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/ivy/functional/frontends/jax/numpy/creation.py b/ivy/functional/frontends/jax/numpy/creation.py index cc61ef7c143d2..0a22d72ea11db 100644 --- a/ivy/functional/frontends/jax/numpy/creation.py +++ b/ivy/functional/frontends/jax/numpy/creation.py @@ -112,6 +112,11 @@ def eye(N, M=None, k=0, dtype=None): return Array(ivy.eye(N, M, k=k, dtype=dtype)) +@to_ivy_arrays_and_back +def from_dlpack(x): + return ivy.from_dlpack(x) + + @to_ivy_arrays_and_back def frombuffer(buffer, dtype="float", count=-1, offset=0): return ivy.frombuffer(buffer, dtype, count, offset) diff --git a/ivy_tests/test_ivy/test_frontends/test_jax/test_numpy/test_creation.py b/ivy_tests/test_ivy/test_frontends/test_jax/test_numpy/test_creation.py index ccf37438b1bf0..34514e70d4c4c 100644 --- a/ivy_tests/test_ivy/test_frontends/test_jax/test_numpy/test_creation.py +++ b/ivy_tests/test_ivy/test_frontends/test_jax/test_numpy/test_creation.py @@ -772,6 +772,34 @@ def test_jax_ndim( ) +# from_dlpack +@handle_frontend_test( + fn_tree="jax.numpy.from_dlpack", + dtype_and_x=helpers.dtype_and_values( + available_dtypes=helpers.get_dtypes("numeric") + ), +) +def test_jax_numpy_from_dlpack( + *, + dtype_and_x, + on_device, + fn_tree, + frontend, + test_flags, + backend_fw, +): + input_dtype, x = dtype_and_x + helpers.test_frontend_function( + x=x[0], + backend_to_test=backend_fw, + input_dtypes=input_dtype, + frontend=frontend, + test_flags=test_flags, + fn_tree=fn_tree, + on_device=on_device, + ) + + @handle_frontend_test( fn_tree="jax.numpy.frombuffer", dtype_buffer_count_offset=_get_dtype_buffer_count_offset(), From f201cff3c627e934d4c082f82d3e50e2b24618db Mon Sep 17 00:00:00 2001 From: Madjid Chergui <100947451+Madjid-CH@users.noreply.github.com> Date: Tue, 12 Sep 2023 09:34:40 +0100 Subject: [PATCH 054/117] refactor: handle_exceptions decorator (#23383) handle_exceptions decorator is starting to get very long so I took the liberty in adding some tests that covers all the decorator and refactoring it. --- ivy/func_wrapper.py | 2 +- ivy/utils/exceptions.py | 78 +++++------------ .../test_misc/test_handle_exceptions.py | 84 +++++++++++++++++++ 3 files changed, 108 insertions(+), 56 deletions(-) create mode 100644 ivy_tests/test_ivy/test_misc/test_handle_exceptions.py diff --git a/ivy/func_wrapper.py b/ivy/func_wrapper.py index e7fa2ca9acce7..a246541af1ee4 100644 --- a/ivy/func_wrapper.py +++ b/ivy/func_wrapper.py @@ -1580,7 +1580,7 @@ def func(x): and ivy.backend != "" and ivy.current_backend_str() != target_backend.backend ): - raise ivy.utils.exceptions.InvalidBackendException( + raise ivy.utils.exceptions.IvyInvalidBackendException( "Operation not allowed. Array was instantiated with backend" f" {target_backend.backend}. But current backend is" f" {ivy.backend}. Please set dynamic=True" diff --git a/ivy/utils/exceptions.py b/ivy/utils/exceptions.py index 886c41400d36d..483149ce1d9a0 100644 --- a/ivy/utils/exceptions.py +++ b/ivy/utils/exceptions.py @@ -279,14 +279,14 @@ def __init__(self, *messages, include_backend=False): super().__init__(*messages, include_backend=include_backend) -class InvalidBackendException(IvyException): +class IvyInvalidBackendException(IvyException): def __init__(self, *messages, include_backend=False): super().__init__(*messages, include_backend=include_backend) -class IvyNotImplementedException(NotImplementedError): - def __init__(self, message=""): - super().__init__(message) +class IvyNotImplementedException(IvyException, NotImplementedError): + def __init__(self, *messages, include_backend=False): + super().__init__(*messages, include_backend=include_backend) class IvyError(IvyException): @@ -329,6 +329,15 @@ def __init__(self, *messages, include_backend=False): super().__init__(*messages, include_backend=include_backend) +_non_ivy_exceptions_mapping = { + IndexError: IvyIndexError, + AttributeError: IvyAttributeError, + ValueError: IvyValueError, + Exception: IvyBackendException, + NotImplementedError: IvyNotImplementedException, +} + + def handle_exceptions(fn: Callable) -> Callable: @functools.wraps(fn) def _handle_exceptions(*args, **kwargs): @@ -349,58 +358,17 @@ def _handle_exceptions(*args, **kwargs): """ try: return fn(*args, **kwargs) - # Not to rethrow as IvyBackendException - except IvyNotImplementedException as e: - _configure_stack_trace(e.__traceback__) - raise e - except IvyError as e: - _configure_stack_trace(e.__traceback__) - raise ivy.utils.exceptions.IvyError( - fn.__name__, str(e), include_backend=True - ) - except IvyBroadcastShapeError as e: - _configure_stack_trace(e.__traceback__) - raise ivy.utils.exceptions.IvyBroadcastShapeError( - fn.__name__, str(e), include_backend=True - ) - except IvyDtypePromotionError as e: - _configure_stack_trace(e.__traceback__) - raise ivy.utils.exceptions.IvyDtypePromotionError( - fn.__name__, str(e), include_backend=True - ) - except (IndexError, IvyIndexError) as e: - _configure_stack_trace(e.__traceback__) - raise ivy.utils.exceptions.IvyIndexError( - fn.__name__, str(e), include_backend=True - ) - except (AttributeError, IvyAttributeError) as e: - _configure_stack_trace(e.__traceback__) - raise ivy.utils.exceptions.IvyAttributeError( - fn.__name__, str(e), include_backend=True - ) - except (ValueError, IvyValueError) as e: - _configure_stack_trace(e.__traceback__) - raise ivy.utils.exceptions.IvyValueError( - fn.__name__, str(e), include_backend=True - ) - except IvyDeviceError as e: - _configure_stack_trace(e.__traceback__) - raise ivy.utils.exceptions.IvyDeviceError( - fn.__name__, str(e), include_backend=True - ) - except InvalidBackendException as e: - _configure_stack_trace(e.__traceback__) - raise e - except InplaceUpdateException as e: - _configure_stack_trace(e.__traceback__) - raise ivy.utils.exceptions.InplaceUpdateException( - fn.__name__, str(e), include_backend=True - ) - except (Exception, IvyBackendException) as e: - _configure_stack_trace(e.__traceback__) - raise ivy.utils.exceptions.IvyBackendException( - fn.__name__, str(e), include_backend=True + except IvyException as e: + _handle_exceptions_helper(e, type(e)) + except Exception as e: + ivy_exception = _non_ivy_exceptions_mapping.get( + type(e), IvyBackendException ) + _handle_exceptions_helper(e, ivy_exception) + + def _handle_exceptions_helper(e, cls): + _configure_stack_trace(e.__traceback__) + raise cls(fn.__name__, str(e), include_backend=True) _handle_exceptions.handle_exceptions = True return _handle_exceptions diff --git a/ivy_tests/test_ivy/test_misc/test_handle_exceptions.py b/ivy_tests/test_ivy/test_misc/test_handle_exceptions.py new file mode 100644 index 0000000000000..79cfe53b1a547 --- /dev/null +++ b/ivy_tests/test_ivy/test_misc/test_handle_exceptions.py @@ -0,0 +1,84 @@ +import pytest +from hypothesis import given +import hypothesis.strategies as st + +from ivy import handle_exceptions +from ivy.utils.exceptions import ( + IvyError, + IvyNotImplementedException, + IvyBroadcastShapeError, + IvyValueError, + InplaceUpdateException, + IvyException, + IvyIndexError, + IvyAttributeError, + IvyBackendException, + IvyDeviceError, + IvyInvalidBackendException, + IvyDtypePromotionError, + _non_ivy_exceptions_mapping, +) + + +@handle_exceptions +def func(e): + if e is None: + return + + raise e() + + +@pytest.mark.parametrize( + "e", + ( + IvyError, + IvyNotImplementedException, + IvyBroadcastShapeError, + IvyValueError, + InplaceUpdateException, + IvyException, + IvyIndexError, + IvyAttributeError, + IvyBackendException, + IvyDeviceError, + IvyInvalidBackendException, + IvyDtypePromotionError, + ), +) +def test_ivy_errors_raising(e): + with pytest.raises(e): + func(e) + + +def test_no_exception(): + func(None) + + +@pytest.mark.parametrize( + "e, to_be_raised", + _non_ivy_exceptions_mapping.items(), +) +def test_non_ivy_errors_mapping(e, to_be_raised): + with pytest.raises( + to_be_raised, + ) as raised: + func(e) + assert issubclass(raised.type, to_be_raised) + + +@given( + e=st.sampled_from( + [ + Exception, + ZeroDivisionError, + BufferError, + AssertionError, + ImportError, + KeyError, + LookupError, + ] + ) +) +def test_non_ivy_errors_raising(e): + with pytest.raises(IvyBackendException): + func(e) From 06bbb9edd371726b53e58562b8087b5dc8c0ebde Mon Sep 17 00:00:00 2001 From: Kareem Morsy Date: Tue, 12 Sep 2023 09:21:35 +0000 Subject: [PATCH 055/117] refactor: Manual lint fixes --- .../frontends/torch/comparison_ops.py | 2 +- ivy/functional/frontends/torch/linalg.py | 8 ---- ivy/functional/ivy/general.py | 6 ++- .../test_frontends/test_torch/test_linalg.py | 46 ------------------- .../test_core/test_manipulation.py | 22 --------- 5 files changed, 5 insertions(+), 79 deletions(-) diff --git a/ivy/functional/frontends/torch/comparison_ops.py b/ivy/functional/frontends/torch/comparison_ops.py index b743b38d135f0..eeaadadfbf864 100644 --- a/ivy/functional/frontends/torch/comparison_ops.py +++ b/ivy/functional/frontends/torch/comparison_ops.py @@ -290,7 +290,7 @@ def topk(input, k, dim=None, largest=True, sorted=True, *, out=None): gt = greater +ne = not_equal ge = greater_equal le = less_equal lt = less -ne = not_equal diff --git a/ivy/functional/frontends/torch/linalg.py b/ivy/functional/frontends/torch/linalg.py index 1055938e53a4d..b8f2360bc8854 100644 --- a/ivy/functional/frontends/torch/linalg.py +++ b/ivy/functional/frontends/torch/linalg.py @@ -68,14 +68,6 @@ def eig(input, *, out=None): return ivy.eig(input, out=out) -@to_ivy_arrays_and_back -@with_supported_dtypes( - {"2.0.1 and below": ("float32", "float64", "complex32", "complex64")}, "torch" -) -def eigh(a, /, UPLO="L", out=None): - return ivy.eigh(a, UPLO=UPLO, out=out) - - @with_supported_dtypes( {"2.0.1 and below": ("float32", "float64", "complex32", "complex64", "complex128")}, "torch", diff --git a/ivy/functional/ivy/general.py b/ivy/functional/ivy/general.py index 5b63f9e020d66..9b11f59bb7bf7 100644 --- a/ivy/functional/ivy/general.py +++ b/ivy/functional/ivy/general.py @@ -189,7 +189,8 @@ def get_referrers_recursive( """ Recursively retrieve referrers for an object. - This function recursively fetches referrers for the specified `item` up to a given `max_depth`. + This function recursively fetches referrers for the specified `item` up to a given + `max_depth`. Parameters ---------- @@ -207,7 +208,8 @@ def get_referrers_recursive( Returns ------- ivy.Container - A container representing referrers and their sub-referrers, respecting the `max_depth`. + A container representing referrers and their sub-referrers, respecting the + `max_depth`. Examples -------- diff --git a/ivy_tests/test_ivy/test_frontends/test_torch/test_linalg.py b/ivy_tests/test_ivy/test_frontends/test_torch/test_linalg.py index 7d11bfa7b5531..5c1b53f604084 100644 --- a/ivy_tests/test_ivy/test_frontends/test_torch/test_linalg.py +++ b/ivy_tests/test_ivy/test_frontends/test_torch/test_linalg.py @@ -470,52 +470,6 @@ def test_torch_eig( ) -# eigh -# TODO: Test for all valid dtypes -@handle_frontend_test( - fn_tree="torch.linalg.eigh", - dtype_and_x=_get_dtype_and_matrix(dtype="float", square=True, invertible=True), - UPLO=st.sampled_from(("L", "U")), -) -def test_torch_eigh( - *, - dtype_and_x, - UPLO, - on_device, - fn_tree, - frontend, - test_flags, - backend_fw, -): - dtype, x = dtype_and_x - x = np.array(x[0], dtype=dtype[0]) - # make symmetric positive-definite beforehand - x = np.matmul(x.T, x) + np.identity(x.shape[0]) * 1e-3 - - ret, frontend_ret = helpers.test_frontend_function( - input_dtypes=dtype, - backend_to_test=backend_fw, - frontend=frontend, - test_flags=test_flags, - fn_tree=fn_tree, - on_device=on_device, - test_values=False, - a=x, - UPLO=UPLO, - ) - ret = [ivy.to_numpy(x) for x in ret] - frontend_ret = [np.asarray(x) for x in frontend_ret] - - L, Q = ret - frontend_L, frontend_Q = frontend_ret - - assert_all_close( - ret_np=Q @ np.diag(L) @ Q.T, - ret_from_gt_np=frontend_Q @ np.diag(frontend_L) @ frontend_Q.T, - atol=1e-02, - ) - - @handle_frontend_test( fn_tree="torch.linalg.eigh", dtype_and_x=_get_dtype_and_matrix(dtype="valid", square=True, invertible=True), diff --git a/ivy_tests/test_ivy/test_functional/test_core/test_manipulation.py b/ivy_tests/test_ivy/test_functional/test_core/test_manipulation.py index 6ae42c6de5ca6..902ed255922ce 100644 --- a/ivy_tests/test_ivy/test_functional/test_core/test_manipulation.py +++ b/ivy_tests/test_ivy/test_functional/test_core/test_manipulation.py @@ -636,28 +636,6 @@ def test_squeeze(*, dtype_value, axis, test_flags, backend_fw, fn_name, on_devic ) -@handle_test( - fn_tree="functional.ivy.stack", - dtypes_arrays=_stack_helper(), - axis=helpers.get_axis( - shape=st.shared(helpers.get_shape(min_num_dims=1), key="values_shape"), - force_int=True, - ), -) -def test_stack(*, dtypes_arrays, axis, test_flags, backend_fw, fn_name, on_device): - dtypes, arrays = dtypes_arrays - - helpers.test_function( - input_dtypes=dtypes, - test_flags=test_flags, - backend_to_test=backend_fw, - fn_name=fn_name, - on_device=on_device, - arrays=arrays, - axis=axis, - ) - - # stack @handle_test( fn_tree="functional.ivy.stack", From 48deaeb2dbc08946c9c80a2e17b83f12cd73a12f Mon Sep 17 00:00:00 2001 From: Mohammed Ayman Date: Tue, 12 Sep 2023 12:24:19 +0300 Subject: [PATCH 056/117] feat: make mish activation function support complex dtype (#23136) --- docs/demos | 2 +- ivy/data_classes/array/activations.py | 13 ++++++++++-- ivy/data_classes/container/activations.py | 10 ++++++++++ ivy/functional/backends/jax/activations.py | 10 ++++++++-- ivy/functional/backends/numpy/activations.py | 10 ++++++++-- ivy/functional/backends/paddle/activations.py | 13 ++++++++++-- ivy/functional/backends/paddle/elementwise.py | 12 ++++++++--- .../backends/tensorflow/activations.py | 10 +++++++--- ivy/functional/backends/torch/activations.py | 20 +++++++++++-------- ivy/functional/ivy/activations.py | 10 +++++++++- .../test_nn/test_activations.py | 8 +++++--- 11 files changed, 91 insertions(+), 27 deletions(-) diff --git a/docs/demos b/docs/demos index 1ba2b6ee7fe97..d5b9659fc0ac4 160000 --- a/docs/demos +++ b/docs/demos @@ -1 +1 @@ -Subproject commit 1ba2b6ee7fe97fff704f103c90b2ec8ca19a703e +Subproject commit d5b9659fc0ac4e6de37867db878b1d068e2c9a76 diff --git a/ivy/data_classes/array/activations.py b/ivy/data_classes/array/activations.py index 6cb2aeaf68ddf..0efe2f55c9aee 100644 --- a/ivy/data_classes/array/activations.py +++ b/ivy/data_classes/array/activations.py @@ -299,7 +299,13 @@ def log_softmax( """ return ivy.log_softmax(self._data, axis=axis, out=out) - def mish(self: ivy.Array, /, *, out: Optional[ivy.Array] = None) -> ivy.Array: + def mish( + self: ivy.Array, + /, + *, + complex_mode: Literal["split", "magnitude", "jax"] = "jax", + out: Optional[ivy.Array] = None, + ) -> ivy.Array: """ ivy.Array instance method variant of ivy.mish. This method simply wraps the function, and so the docstring for ivy.mish also applies to this method with @@ -309,6 +315,9 @@ def mish(self: ivy.Array, /, *, out: Optional[ivy.Array] = None) -> ivy.Array: ---------- self input array. + complex_mode + optional specifier for how to handle complex data types. See + ``ivy.func_wrapper.handle_complex_input`` for more detail. out optional output array, for writing the result to. It must have a shape that the inputs broadcast to. @@ -320,7 +329,7 @@ def mish(self: ivy.Array, /, *, out: Optional[ivy.Array] = None) -> ivy.Array: >>> print(y) ivy.array([-0.30340147, 0. , 0.86509842]) """ - return ivy.mish(self._data, out=out) + return ivy.mish(self._data, complex_mode=complex_mode, out=out) def hardswish(self: ivy.Array, /, *, out: Optional[ivy.Array] = None) -> ivy.Array: """ diff --git a/ivy/data_classes/container/activations.py b/ivy/data_classes/container/activations.py index 938ea09fc50fe..8ed140a8b2a64 100644 --- a/ivy/data_classes/container/activations.py +++ b/ivy/data_classes/container/activations.py @@ -970,6 +970,7 @@ def _static_mish( to_apply: Union[bool, ivy.Container] = True, prune_unapplied: Union[bool, ivy.Container] = False, map_sequences: Union[bool, ivy.Container] = False, + complex_mode: Literal["split", "magnitude", "jax"] = "jax", out: Optional[ivy.Container] = None, ) -> ivy.Container: """ @@ -992,6 +993,9 @@ def _static_mish( map_sequences Whether to also map method to sequences (lists, tuples). Default is ``False``. + complex_mode + optional specifier for how to handle complex data types. See + ``ivy.func_wrapper.handle_complex_input`` for more detail. out optional output container, for writing the result to. It must have a shape that the inputs broadcast to. @@ -1019,6 +1023,7 @@ def _static_mish( to_apply=to_apply, prune_unapplied=prune_unapplied, map_sequences=map_sequences, + complex_mode=complex_mode, out=out, ) @@ -1030,6 +1035,7 @@ def mish( to_apply: Union[bool, ivy.Container] = True, prune_unapplied: Union[bool, ivy.Container] = False, map_sequences: Union[bool, ivy.Container] = False, + complex_mode: Literal["split", "magnitude", "jax"] = "jax", out: Optional[ivy.Container] = None, ) -> ivy.Container: """ @@ -1052,6 +1058,9 @@ def mish( map_sequences Whether to also map method to sequences (lists, tuples). Default is ``False``. + complex_mode + optional specifier for how to handle complex data types. See + ``ivy.func_wrapper.handle_complex_input`` for more detail. out optional output container, for writing the result to. It must have a shape that the inputs broadcast to. @@ -1078,6 +1087,7 @@ def mish( to_apply=to_apply, prune_unapplied=prune_unapplied, map_sequences=map_sequences, + complex_mode=complex_mode, out=out, ) diff --git a/ivy/functional/backends/jax/activations.py b/ivy/functional/backends/jax/activations.py index 8df6f4d68e550..984df05061840 100644 --- a/ivy/functional/backends/jax/activations.py +++ b/ivy/functional/backends/jax/activations.py @@ -6,7 +6,7 @@ import jax import jax.numpy as jnp -from typing import Optional, Union +from typing import Optional, Union, Literal # local from ivy.functional.backends.jax import JaxArray @@ -93,7 +93,13 @@ def log_softmax( return jax.nn.log_softmax(x, axis) -def mish(x: JaxArray, /, *, out: Optional[JaxArray] = None): +def mish( + x: JaxArray, + /, + *, + complex_mode: Literal["split", "magnitude", "jax"] = "jax", + out: Optional[JaxArray] = None, +) -> JaxArray: return x * jnp.tanh(jax.nn.softplus(x)) diff --git a/ivy/functional/backends/numpy/activations.py b/ivy/functional/backends/numpy/activations.py index 2e8fd0d0e0395..031d555aeb353 100644 --- a/ivy/functional/backends/numpy/activations.py +++ b/ivy/functional/backends/numpy/activations.py @@ -1,7 +1,7 @@ """Collection of Numpy activation functions, wrapped to fit Ivy syntax and signature.""" # global -from typing import Optional, Union +from typing import Optional, Union, Literal import numpy as np # local @@ -131,7 +131,13 @@ def log_softmax( @_scalar_output_to_0d_array -def mish(x: np.ndarray, /, *, out: Optional[np.ndarray] = None) -> np.ndarray: +def mish( + x: np.ndarray, + /, + *, + complex_mode: Literal["split", "magnitude", "jax"] = "jax", + out: Optional[np.ndarray] = None, +) -> np.ndarray: return x * np.tanh(np.log1p(np.exp(x))) diff --git a/ivy/functional/backends/paddle/activations.py b/ivy/functional/backends/paddle/activations.py index 42fa990c6177b..a0dc8024a14e0 100644 --- a/ivy/functional/backends/paddle/activations.py +++ b/ivy/functional/backends/paddle/activations.py @@ -4,7 +4,7 @@ Collection of Paddle activation functions, wrapped to fit Ivy syntax and signature. """ -from typing import Optional, Union +from typing import Optional, Union, Literal # global import paddle @@ -186,7 +186,16 @@ def log_softmax( return ret -def mish(x: paddle.Tensor, /, *, out: Optional[paddle.Tensor] = None) -> paddle.Tensor: +@with_unsupported_device_and_dtypes( + {"2.5.1 and below": {"cpu": ("bfloat16",)}}, backend_version +) +def mish( + x: paddle.Tensor, + /, + *, + complex_mode: Literal["split", "magnitude", "jax"] = "jax", + out: Optional[paddle.Tensor] = None, +) -> paddle.Tensor: if x.dtype in unsupported_dtypes: if paddle.is_complex(x): return x * paddle_backend.tanh(paddle_backend.log1p(paddle_backend.exp(x))) diff --git a/ivy/functional/backends/paddle/elementwise.py b/ivy/functional/backends/paddle/elementwise.py index 09210b97a29f0..f1e37a6a08167 100644 --- a/ivy/functional/backends/paddle/elementwise.py +++ b/ivy/functional/backends/paddle/elementwise.py @@ -720,9 +720,15 @@ def tanh( ]: return paddle.tanh(x.astype("float32")).astype(x.dtype) if paddle.is_complex(x): - tanh_a = paddle.tanh(paddle.real(x)) - tan_b = paddle.tan(paddle.imag(x)) - return (tanh_a + 1j * tan_b) / (1 + 1j * (tanh_a * tan_b)) + tanh_a = paddle.tanh(x.real()) + tan_b = paddle.tan(x.imag()) + return paddle.divide( + paddle.complex(tanh_a, tan_b), + paddle.complex( + paddle.ones_like(tanh_a), + paddle.multiply(tanh_a, tan_b), + ), + ) return paddle.tanh(x) diff --git a/ivy/functional/backends/tensorflow/activations.py b/ivy/functional/backends/tensorflow/activations.py index 6fb69cc2c7fb0..c4d45f4711c13 100644 --- a/ivy/functional/backends/tensorflow/activations.py +++ b/ivy/functional/backends/tensorflow/activations.py @@ -5,7 +5,7 @@ and signature. """ -from typing import Optional, Union +from typing import Optional, Union, Literal # global import tensorflow as tf @@ -125,14 +125,18 @@ def log_softmax( return tf.nn.log_softmax(x, axis) -@with_unsupported_dtypes({"2.13.0 and below": ("complex",)}, backend_version) def mish( x: Tensor, /, *, + complex_mode: Literal["split", "magnitude", "jax"] = "jax", out: Optional[Tensor] = None, ) -> Tensor: - return x * tf.math.tanh(tf.math.softplus(x)) + if "complex" in str(x.dtype): + x_norm = tf.math.log1p(tf.exp(x)) + else: + x_norm = tf.math.softplus(x) + return tf.multiply(x, tf.math.tanh(x_norm)) @with_unsupported_dtypes({"2.13.0 and below": ("complex",)}, backend_version) diff --git a/ivy/functional/backends/torch/activations.py b/ivy/functional/backends/torch/activations.py index 0e01beb0f7131..f17d83a66483f 100644 --- a/ivy/functional/backends/torch/activations.py +++ b/ivy/functional/backends/torch/activations.py @@ -4,7 +4,7 @@ Collection of PyTorch activation functions, wrapped to fit Ivy syntax and signature. """ -from typing import Optional, Union +from typing import Optional, Union, Literal # global import numpy as np @@ -128,15 +128,19 @@ def log_softmax( @with_unsupported_dtypes( - { - "2.0.1 and below": ( - "complex", - "float16", - ) - }, + {"2.0.1 and below": ("float16",)}, backend_version, ) -def mish(x: torch.Tensor, /, *, out: Optional[torch.Tensor] = None) -> torch.Tensor: +def mish( + x: torch.Tensor, + /, + *, + complex_mode: Literal["split", "magnitude", "jax"] = "jax", + out: Optional[torch.Tensor] = None, +) -> torch.Tensor: + if torch.is_complex(x): + x_norm = torch.log1p(x.exp()) + return torch.multiply(x, x_norm.tanh()) return torch.nn.functional.mish(x) diff --git a/ivy/functional/ivy/activations.py b/ivy/functional/ivy/activations.py index 1010b947a19b9..91988c0303ea7 100644 --- a/ivy/functional/ivy/activations.py +++ b/ivy/functional/ivy/activations.py @@ -663,8 +663,13 @@ def softsign( @to_native_arrays_and_back @handle_array_function @handle_device_shifting +@handle_complex_input def mish( - x: Union[ivy.Array, ivy.NativeArray], /, *, out: Optional[ivy.Array] = None + x: Union[ivy.Array, ivy.NativeArray], + /, + *, + complex_mode: Literal["split", "magnitude", "jax"] = "jax", + out: Optional[ivy.Array] = None, ) -> ivy.Array: """ Apply the mish activation function element-wise. @@ -673,6 +678,9 @@ def mish( ---------- x input array + complex_mode + optional specifier for how to handle complex data types. See + ``ivy.func_wrapper.handle_complex_input`` for more detail. out optional output array, for writing the result to. It must have a shape that the inputs broadcast to. diff --git a/ivy_tests/test_ivy/test_functional/test_nn/test_activations.py b/ivy_tests/test_ivy/test_functional/test_nn/test_activations.py index ac23f384b6398..c94809b93af36 100644 --- a/ivy_tests/test_ivy/test_functional/test_nn/test_activations.py +++ b/ivy_tests/test_ivy/test_functional/test_nn/test_activations.py @@ -136,11 +136,13 @@ def test_log_softmax(*, dtype_and_x, axis, test_flags, backend_fw, fn_name, on_d @handle_test( fn_tree="functional.ivy.mish", dtype_and_x=helpers.dtype_and_values( - available_dtypes=helpers.get_dtypes("float"), - large_abs_safety_factor=8, - small_abs_safety_factor=8, + available_dtypes=helpers.get_dtypes("float_and_complex"), + large_abs_safety_factor=25, + small_abs_safety_factor=25, + min_dim_size=2, safety_factor_scale="log", ), + ground_truth_backend="jax", ) def test_mish(*, dtype_and_x, test_flags, backend_fw, fn_name, on_device): dtype, x = dtype_and_x From 369a21c2d0a86b41b5e893953298405d9a5bcf6d Mon Sep 17 00:00:00 2001 From: Mohammed Ayman Date: Tue, 12 Sep 2023 12:26:36 +0300 Subject: [PATCH 057/117] feat: made log_softmax function support complex dtype (#23412) --- ivy/data_classes/array/activations.py | 13 ++++++++-- ivy/data_classes/container/activations.py | 14 ++++++++-- ivy/functional/backends/jax/activations.py | 9 ++++--- ivy/functional/backends/mxnet/activations.py | 2 +- ivy/functional/backends/numpy/activations.py | 24 +++++++---------- ivy/functional/backends/paddle/activations.py | 21 +++++---------- .../backends/tensorflow/activations.py | 14 ++++++++-- ivy/functional/backends/torch/activations.py | 18 ++++++------- ivy/functional/frontends/tensorflow/math.py | 2 ++ ivy/functional/ivy/activations.py | 4 +++ ivy/stateful/activations.py | 26 ++++++++++++++----- .../test_nn/test_non_linear_activations.py | 9 ++++--- .../test_nn/test_activations.py | 11 ++++---- .../test_stateful/test_activations.py | 9 ++++--- 14 files changed, 110 insertions(+), 66 deletions(-) diff --git a/ivy/data_classes/array/activations.py b/ivy/data_classes/array/activations.py index 0efe2f55c9aee..410be822f0b4b 100644 --- a/ivy/data_classes/array/activations.py +++ b/ivy/data_classes/array/activations.py @@ -263,7 +263,8 @@ def log_softmax( self: ivy.Array, /, *, - axis: Optional[int] = None, + axis: Optional[int] = -1, + complex_mode: Literal["split", "magnitude", "jax"] = "jax", out: Optional[ivy.Array] = None, ) -> ivy.Array: """ @@ -277,6 +278,9 @@ def log_softmax( input array. axis the axis or axes along which the log_softmax should be computed + complex_mode + optional specifier for how to handle complex data types. See + ``ivy.func_wrapper.handle_complex_input`` for more detail. out optional output array, for writing the result to. It must have a shape that the inputs broadcast to. @@ -297,7 +301,12 @@ def log_softmax( >>> y = x.log_softmax(x) ivy.array([-1.62, -0.221, -7.82 ]) """ - return ivy.log_softmax(self._data, axis=axis, out=out) + return ivy.log_softmax( + self._data, + axis=axis, + complex_mode=complex_mode, + out=out, + ) def mish( self: ivy.Array, diff --git a/ivy/data_classes/container/activations.py b/ivy/data_classes/container/activations.py index 8ed140a8b2a64..18168d1e408fd 100644 --- a/ivy/data_classes/container/activations.py +++ b/ivy/data_classes/container/activations.py @@ -826,7 +826,8 @@ def _static_log_softmax( x: Union[ivy.Array, ivy.NativeArray, ivy.Container], /, *, - axis: Optional[ivy.Container] = None, + axis: Optional[ivy.Container] = -1, + complex_mode: Literal["split", "magnitude", "jax"] = "jax", key_chains: Optional[Union[List[str], Dict[str, str], ivy.Container]] = None, to_apply: Union[bool, ivy.Container] = True, prune_unapplied: Union[bool, ivy.Container] = False, @@ -844,6 +845,9 @@ def _static_log_softmax( input container. axis the axis or axes along which the log_softmax should be computed + complex_mode + optional specifier for how to handle complex data types. See + ``ivy.func_wrapper.handle_complex_input`` for more detail. key_chains The key-chains to apply or not apply the method to. Default is ``None``. to_apply @@ -885,6 +889,7 @@ def _static_log_softmax( "log_softmax", x, axis=axis, + complex_mode=complex_mode, key_chains=key_chains, to_apply=to_apply, prune_unapplied=prune_unapplied, @@ -896,7 +901,8 @@ def log_softmax( self: ivy.Container, /, *, - axis: Optional[ivy.Container] = None, + axis: Optional[ivy.Container] = -1, + complex_mode: Literal["split", "magnitude", "jax"] = "jax", key_chains: Optional[Union[List[str], Dict[str, str], ivy.Container]] = None, to_apply: Union[bool, ivy.Container] = True, prune_unapplied: Union[bool, ivy.Container] = False, @@ -914,6 +920,9 @@ def log_softmax( input container. axis the axis or axes along which the log_softmax should be computed + complex_mode + optional specifier for how to handle complex data types. See + ``ivy.func_wrapper.handle_complex_input`` for more detail. key_chains The key-chains to apply or not apply the method to. Default is ``None``. to_apply @@ -954,6 +963,7 @@ def log_softmax( return self._static_log_softmax( self, axis=axis, + complex_mode=complex_mode, key_chains=key_chains, to_apply=to_apply, prune_unapplied=prune_unapplied, diff --git a/ivy/functional/backends/jax/activations.py b/ivy/functional/backends/jax/activations.py index 984df05061840..87974add99969 100644 --- a/ivy/functional/backends/jax/activations.py +++ b/ivy/functional/backends/jax/activations.py @@ -86,10 +86,13 @@ def softsign(x: JaxArray, /, *, out: Optional[JaxArray] = None) -> JaxArray: def log_softmax( - x: JaxArray, /, *, axis: Optional[int] = None, out: Optional[JaxArray] = None + x: JaxArray, + /, + *, + axis: Optional[int] = -1, + complex_mode: Literal["split", "magnitude", "jax"] = "jax", + out: Optional[JaxArray] = None, ): - if axis is None: - axis = -1 return jax.nn.log_softmax(x, axis) diff --git a/ivy/functional/backends/mxnet/activations.py b/ivy/functional/backends/mxnet/activations.py index 614ad4a9d850e..3bb8f87e1b7b7 100644 --- a/ivy/functional/backends/mxnet/activations.py +++ b/ivy/functional/backends/mxnet/activations.py @@ -78,7 +78,7 @@ def softsign(x: None, /, *, out: Optional[None] = None) -> None: return mx.nd.softsign(x) -def log_softmax(x: None, /, *, axis: Optional[int] = None, out: Optional[None] = None): +def log_softmax(x: None, /, *, axis: Optional[int] = -1, out: Optional[None] = None): raise IvyNotImplementedException() diff --git a/ivy/functional/backends/numpy/activations.py b/ivy/functional/backends/numpy/activations.py index 031d555aeb353..3add8e250f581 100644 --- a/ivy/functional/backends/numpy/activations.py +++ b/ivy/functional/backends/numpy/activations.py @@ -108,22 +108,18 @@ def softsign(x: np.ndarray, /, out: Optional[np.ndarray] = None) -> np.ndarray: @_scalar_output_to_0d_array def log_softmax( - x: np.ndarray, /, *, axis: Optional[int] = None, out: Optional[np.ndarray] = None + x: np.ndarray, + /, + *, + axis: Optional[int] = -1, + complex_mode: Literal["split", "magnitude", "jax"] = "jax", + out: Optional[np.ndarray] = None, ) -> np.ndarray: - if axis is None: - axis = -1 x_max = np.max(x, axis=axis, keepdims=True) - if x_max.ndim > 0: - x_max[~np.isfinite(x_max)] = 0 - elif not np.isfinite(x_max): - x_max = 0 - exp_tmp = np.exp(x - x_max) - - with np.errstate(divide="ignore"): - s = np.sum(exp_tmp, axis=axis, keepdims=True) - ret = np.log(s) - - ret = x - x_max - ret + sub_tmp = np.subtract(x, x_max) + ret = np.sum(np.exp(sub_tmp), axis=axis, keepdims=True) + ret = np.log(ret) + ret = np.subtract(sub_tmp, ret) return ret diff --git a/ivy/functional/backends/paddle/activations.py b/ivy/functional/backends/paddle/activations.py index a0dc8024a14e0..a97b6cd40686a 100644 --- a/ivy/functional/backends/paddle/activations.py +++ b/ivy/functional/backends/paddle/activations.py @@ -161,28 +161,21 @@ def softsign( @with_unsupported_device_and_dtypes( - {"2.5.1 and below": {"cpu": ("float16",)}}, backend_version + {"2.5.1 and below": {"cpu": ("float16", "bfloat16")}}, backend_version ) def log_softmax( x: paddle.Tensor, /, *, - axis: Optional[int] = None, + axis: Optional[int] = -1, + complex_mode: Literal["split", "magnitude", "jax"] = "jax", out: Optional[paddle.Tensor] = None, ): - if axis is None: - axis = -1 x_max = paddle_backend.max(x, axis=axis, keepdims=True) - x_max = paddle_backend.where( - paddle_backend.isfinite(x_max), - x_max, - paddle.zeros(shape=x_max.shape).astype(x_max.dtype), - ) - exp_tmp = paddle_backend.exp(paddle_backend.subtract(x, x_max)) - - s = paddle_backend.sum(exp_tmp, axis=axis, keepdims=True) - ret = paddle_backend.log(s) - ret = paddle_backend.subtract(paddle_backend.subtract(x, x_max), ret) + sub_tmp = paddle_backend.subtract(x, x_max) + ret = paddle_backend.sum(paddle_backend.exp(sub_tmp), axis=axis, keepdims=True) + ret = paddle_backend.log(ret) + ret = paddle_backend.subtract(sub_tmp, ret) return ret diff --git a/ivy/functional/backends/tensorflow/activations.py b/ivy/functional/backends/tensorflow/activations.py index c4d45f4711c13..0f4ff5507e5ce 100644 --- a/ivy/functional/backends/tensorflow/activations.py +++ b/ivy/functional/backends/tensorflow/activations.py @@ -118,10 +118,20 @@ def softsign(x: tf.Tensor, /, out: Optional[tf.Tensor] = None) -> tf.Tensor: return tf.nn.softsign(x) -@with_unsupported_dtypes({"2.13.0 and below": ("complex",)}, backend_version) def log_softmax( - x: Tensor, /, *, axis: Optional[int] = None, out: Optional[Tensor] = None + x: Tensor, + /, + *, + axis: Optional[int] = -1, + complex_mode: Literal["split", "magnitude", "jax"] = "jax", + out: Optional[Tensor] = None, ): + if "complex" in str(x.dtype): + x_max = tf_backend.max(x, axis=axis, keepdims=True) + sub_temp = tf.subtract(x, x_max) + ret = tf.reduce_sum(tf.exp(sub_temp), axis=axis, keepdims=True) + ret = tf.math.log(ret) + return tf.subtract(sub_temp, ret) return tf.nn.log_softmax(x, axis) diff --git a/ivy/functional/backends/torch/activations.py b/ivy/functional/backends/torch/activations.py index f17d83a66483f..a50c388b33ff2 100644 --- a/ivy/functional/backends/torch/activations.py +++ b/ivy/functional/backends/torch/activations.py @@ -107,23 +107,23 @@ def softsign(x: torch.Tensor, /, out: Optional[torch.Tensor] = None) -> torch.Te @with_unsupported_dtypes( - { - "2.0.1 and below": ( - "complex", - "float16", - ) - }, + {"2.0.1 and below": ("float16",)}, backend_version, ) def log_softmax( x: torch.Tensor, /, *, - axis: Optional[int] = None, + axis: Optional[int] = -1, + complex_mode: Literal["split", "magnitude", "jax"] = "jax", out: Optional[torch.Tensor] = None, ): - if axis is None: - axis = -1 + if torch.is_complex(x): + x_max = torch_backend.max(x, axis=axis, keepdims=True) + sub_temp = torch.sub(x, x_max) + ret = torch.sum(sub_temp.exp(), dim=axis, keepdim=True) + ret = torch.log(ret) + return torch.sub(sub_temp, ret) return torch.nn.functional.log_softmax(x, axis) diff --git a/ivy/functional/frontends/tensorflow/math.py b/ivy/functional/frontends/tensorflow/math.py index 2779220159faf..fa00d014131e9 100644 --- a/ivy/functional/frontends/tensorflow/math.py +++ b/ivy/functional/frontends/tensorflow/math.py @@ -400,6 +400,8 @@ def log_sigmoid(x, name=None): @to_ivy_arrays_and_back def log_softmax(logits, axis=None): + if axis is None: + axis = -1 return ivy.log_softmax(logits, axis=axis) diff --git a/ivy/functional/ivy/activations.py b/ivy/functional/ivy/activations.py index 91988c0303ea7..6302270ce28d8 100644 --- a/ivy/functional/ivy/activations.py +++ b/ivy/functional/ivy/activations.py @@ -215,6 +215,7 @@ def log_softmax( /, *, axis: Optional[int] = None, + complex_mode: Literal["split", "magnitude", "jax"] = "jax", out: Optional[ivy.Array] = None, ) -> ivy.Array: """ @@ -226,6 +227,9 @@ def log_softmax( Input array. axis The dimension log_softmax would be performed on. The default is ``None``. + complex_mode + optional specifier for how to handle complex data types. See + ``ivy.func_wrapper.handle_complex_input`` for more detail. out optional output array, for writing the result to. It must have a shape that the inputs broadcast to. diff --git a/ivy/stateful/activations.py b/ivy/stateful/activations.py index 7b9c67035e117..348cb294ce15a 100644 --- a/ivy/stateful/activations.py +++ b/ivy/stateful/activations.py @@ -3,7 +3,7 @@ # local import ivy from ivy.stateful.module import Module -from typing import Literal +from typing import Literal, Optional class GELU(Module): @@ -147,10 +147,25 @@ def _forward(self, x): class LogSoftmax(Module): - def __init__(self, axis: int = -1): - """Apply the LOG SOFTMAX activation function.""" + def __init__( + self, + axis: Optional[int] = -1, + complex_mode: Literal["split", "magnitude", "jax"] = "jax", + ): + """ + Apply the LOG SOFTMAX activation function. + + Parameters + ---------- + axis + The dimension log_softmax would be performed on. The default is ``None`` + complex_mode + optional specifier for how to handle complex data types. See + ``ivy.func_wrapper.handle_complex_input`` for more detail. + """ Module.__init__(self) self._axis = axis + self._complex_mode = complex_mode def _forward(self, x): """ @@ -159,14 +174,13 @@ def _forward(self, x): ---------- x Inputs to process *[batch_shape, d]*. - axis - The dimension log_softmax would be performed on. The default is ``None`` + Returns ------- ret The outputs following the LOG SOFTMAX activation *[batch_shape, d]* """ - return ivy.log_softmax(x, axis=self._axis) + return ivy.log_softmax(x, axis=self._axis, complex_mode=self._complex_mode) class Softmax(Module): diff --git a/ivy_tests/test_ivy/test_frontends/test_jax/test_nn/test_non_linear_activations.py b/ivy_tests/test_ivy/test_frontends/test_jax/test_nn/test_non_linear_activations.py index 98660c17328ed..d8628007a5946 100644 --- a/ivy_tests/test_ivy/test_frontends/test_jax/test_nn/test_non_linear_activations.py +++ b/ivy_tests/test_ivy/test_frontends/test_jax/test_nn/test_non_linear_activations.py @@ -383,12 +383,13 @@ def test_jax_log_sigmoid( @handle_frontend_test( fn_tree="jax.nn.log_softmax", dtype_and_x=helpers.dtype_and_values( - available_dtypes=helpers.get_dtypes("float"), - large_abs_safety_factor=2, - small_abs_safety_factor=2, - safety_factor_scale="linear", + available_dtypes=helpers.get_dtypes("float_and_complex"), + large_abs_safety_factor=4, + small_abs_safety_factor=4, + safety_factor_scale="log", min_value=-2, min_num_dims=1, + min_dim_size=2, ), axis=helpers.ints(min_value=-1, max_value=0), test_with_out=st.just(False), diff --git a/ivy_tests/test_ivy/test_functional/test_nn/test_activations.py b/ivy_tests/test_ivy/test_functional/test_nn/test_activations.py index c94809b93af36..c5a9c50d42fe1 100644 --- a/ivy_tests/test_ivy/test_functional/test_nn/test_activations.py +++ b/ivy_tests/test_ivy/test_functional/test_nn/test_activations.py @@ -109,13 +109,14 @@ def test_leaky_relu( @handle_test( fn_tree="functional.ivy.log_softmax", dtype_and_x=helpers.dtype_and_values( - available_dtypes=helpers.get_dtypes("float"), - min_num_dims=1, - large_abs_safety_factor=8, - small_abs_safety_factor=8, + available_dtypes=helpers.get_dtypes("float_and_complex"), + min_num_dims=2, + large_abs_safety_factor=12, + small_abs_safety_factor=12, safety_factor_scale="log", + min_value=-2, ), - axis=st.one_of(helpers.ints(min_value=-1, max_value=0), st.none()), + axis=helpers.ints(min_value=-1, max_value=0), ) def test_log_softmax(*, dtype_and_x, axis, test_flags, backend_fw, fn_name, on_device): dtype, x = dtype_and_x diff --git a/ivy_tests/test_ivy/test_stateful/test_activations.py b/ivy_tests/test_ivy/test_stateful/test_activations.py index 3630421a1b7cc..638aaee6fbe80 100644 --- a/ivy_tests/test_ivy/test_stateful/test_activations.py +++ b/ivy_tests/test_ivy/test_stateful/test_activations.py @@ -237,11 +237,12 @@ def test_leaky_relu( ) +# LogSoftmax @handle_method( method_tree="stateful.activations.LogSoftmax.__call__", dtype_and_x=helpers.dtype_and_values( - available_dtypes=helpers.get_dtypes("float"), - min_num_dims=1, + available_dtypes=helpers.get_dtypes("float_and_complex"), + min_num_dims=2, large_abs_safety_factor=8, small_abs_safety_factor=8, safety_factor_scale="log", @@ -273,8 +274,8 @@ def test_log_softmax( method_flags=method_flags, init_input_dtypes=input_dtype, method_input_dtypes=input_dtype, - init_all_as_kwargs_np={}, - method_all_as_kwargs_np={"x": x[0], "axis": axis}, + init_all_as_kwargs_np={"axis": axis}, + method_all_as_kwargs_np={"x": x[0]}, class_name=class_name, method_name=method_name, rtol_=1e-2, From 0fc82a49c6cfdfffd035ea8184bc332aeb98751f Mon Sep 17 00:00:00 2001 From: Sai-Suraj-27 Date: Tue, 12 Sep 2023 15:02:07 +0530 Subject: [PATCH 058/117] lint: Updated black formatter version in `.pre-commit-config.yaml` file and modified all the files according to it. (#23364) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 19bd018094670..d303b82996c03 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,8 +5,8 @@ repos: - id: check-yaml - id: trailing-whitespace - id: check-toml - - repo: https://github.com/psf/black - rev: 23.7.0 + - repo: https://github.com/psf/black-pre-commit-mirror + rev: 23.9.1 hooks: - id: black language_version: python3 From b8227ec1a3e61478e1773ec4fa02937273c541ab Mon Sep 17 00:00:00 2001 From: ivy-branch Date: Tue, 12 Sep 2023 09:35:05 +0000 Subject: [PATCH 059/117] =?UTF-8?q?=F0=9F=A4=96=20Lint=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ivy/functional/frontends/torch/comparison_ops.py | 2 +- ivy/functional/ivy/creation.py | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/ivy/functional/frontends/torch/comparison_ops.py b/ivy/functional/frontends/torch/comparison_ops.py index eeaadadfbf864..b743b38d135f0 100644 --- a/ivy/functional/frontends/torch/comparison_ops.py +++ b/ivy/functional/frontends/torch/comparison_ops.py @@ -290,7 +290,7 @@ def topk(input, k, dim=None, largest=True, sorted=True, *, out=None): gt = greater -ne = not_equal ge = greater_equal le = less_equal lt = less +ne = not_equal diff --git a/ivy/functional/ivy/creation.py b/ivy/functional/ivy/creation.py index eeb8758f43b70..49c8e3c981ba5 100644 --- a/ivy/functional/ivy/creation.py +++ b/ivy/functional/ivy/creation.py @@ -266,11 +266,9 @@ def _inputs_to_native_shapes(*args, **kwargs): class NestedSequence(Protocol[_T_co]): - def __getitem__(self, key: int, /) -> Union[_T_co, NestedSequence[_T_co]]: - ... + def __getitem__(self, key: int, /) -> Union[_T_co, NestedSequence[_T_co]]: ... - def __len__(self, /) -> int: - ... + def __len__(self, /) -> int: ... # Array API Standard # From 8516d3f12a8dfc4ec5f819789937d196c7e28566 Mon Sep 17 00:00:00 2001 From: Kareem Morsy Date: Tue, 12 Sep 2023 12:41:19 +0300 Subject: [PATCH 060/117] lint: ignore `E704` --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 7c6ab26336f8b..0362e9b75f2d5 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [flake8] max-line-length = 88 -ignore = E203, E402, E731, W503, W504, W291, W293 +ignore = E203, E402, E731, E704, W503, W504, W291, W293 per-file-ignores = **/__init__.py: F401,F403,F405,F811,F821 From 24e561ef72810e6bee426ab3e10ef525c61fdc49 Mon Sep 17 00:00:00 2001 From: Kareem Morsy Date: Tue, 12 Sep 2023 10:07:55 +0000 Subject: [PATCH 061/117] lint: ignore paddle frontend lint errors This is because of the weird paddle naming system Refs: 9e62a00942dd3dfadc23791060bd9df8a7611e2d --- ivy/functional/frontends/torch/comparison_ops.py | 2 +- setup.cfg | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ivy/functional/frontends/torch/comparison_ops.py b/ivy/functional/frontends/torch/comparison_ops.py index b743b38d135f0..eeaadadfbf864 100644 --- a/ivy/functional/frontends/torch/comparison_ops.py +++ b/ivy/functional/frontends/torch/comparison_ops.py @@ -290,7 +290,7 @@ def topk(input, k, dim=None, largest=True, sorted=True, *, out=None): gt = greater +ne = not_equal ge = greater_equal le = less_equal lt = less -ne = not_equal diff --git a/setup.cfg b/setup.cfg index 0362e9b75f2d5..5f868b342a912 100644 --- a/setup.cfg +++ b/setup.cfg @@ -4,6 +4,7 @@ ignore = E203, E402, E731, E704, W503, W504, W291, W293 per-file-ignores = **/__init__.py: F401,F403,F405,F811,F821 + ivy/functional/frontends/paddle/**: F401,F403,F405 [autoflake] in-place = true From d7dba521f60795d9d99862bc3807fba08610587a Mon Sep 17 00:00:00 2001 From: akshatvishu <33392262+akshatvishu@users.noreply.github.com> Date: Tue, 12 Sep 2023 15:41:05 +0530 Subject: [PATCH 062/117] fix(paddle_frontend): reinstate `repeat_interleave` at `manipulation.py` after namespace fix. (#23442) Co-authored-by: @AnnaTz --- ivy/functional/frontends/paddle/manipulation.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ivy/functional/frontends/paddle/manipulation.py b/ivy/functional/frontends/paddle/manipulation.py index c62dda831e633..e7a8d2a943b7e 100644 --- a/ivy/functional/frontends/paddle/manipulation.py +++ b/ivy/functional/frontends/paddle/manipulation.py @@ -77,6 +77,11 @@ def gather(params, indices, axis=-1, batch_dims=0, name=None): return ivy.gather(params, indices, axis=axis, batch_dims=batch_dims) +@to_ivy_arrays_and_back +def repeat_interleave(x, repeats, axis=None, name=None): + return ivy.repeat(x, repeats, axis=axis) + + @to_ivy_arrays_and_back def reshape(x, shape): return ivy.reshape(x, shape) From cbd1c9616952a9c22c153ef743d0c4207919fe6e Mon Sep 17 00:00:00 2001 From: Akshay <59740725+Killua7362@users.noreply.github.com> Date: Tue, 12 Sep 2023 16:28:15 +0530 Subject: [PATCH 063/117] revert multiheadattention (#23460) --- ivy/data_classes/array/layers.py | 4 ++-- ivy/data_classes/container/layers.py | 4 ++-- ivy/functional/ivy/layers.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ivy/data_classes/array/layers.py b/ivy/data_classes/array/layers.py index 1bfbb3cc805db..550a225b7e798 100644 --- a/ivy/data_classes/array/layers.py +++ b/ivy/data_classes/array/layers.py @@ -394,10 +394,10 @@ def scaled_dot_product_attention( def multi_head_attention( self: ivy.Array, - key: Optional[Union[ivy.Array, ivy.NativeArray]] = None, - value: Optional[Union[ivy.Array, ivy.NativeArray]] = None, /, *, + key: Optional[Union[ivy.Array, ivy.NativeArray]] = None, + value: Optional[Union[ivy.Array, ivy.NativeArray]] = None, num_heads: int = 8, scale: Optional[float] = None, attention_mask: Optional[Union[ivy.Array, ivy.NativeArray]] = None, diff --git a/ivy/data_classes/container/layers.py b/ivy/data_classes/container/layers.py index 8e28923ad204a..b56ed5c84a9d6 100644 --- a/ivy/data_classes/container/layers.py +++ b/ivy/data_classes/container/layers.py @@ -1094,10 +1094,10 @@ def _static_multi_head_attention( def multi_head_attention( self: ivy.Container, - key: Optional[Union[ivy.Array, ivy.NativeArray, ivy.Container]] = None, - value: Optional[Union[ivy.Array, ivy.NativeArray, ivy.Container]] = None, /, *, + key: Optional[Union[ivy.Array, ivy.NativeArray, ivy.Container]] = None, + value: Optional[Union[ivy.Array, ivy.NativeArray, ivy.Container]] = None, num_heads: Union[int, ivy.Container] = 8, scale: Optional[Union[float, ivy.Container]] = None, attention_mask: Optional[ diff --git a/ivy/functional/ivy/layers.py b/ivy/functional/ivy/layers.py index a378c626db5da..a471d62d08a78 100644 --- a/ivy/functional/ivy/layers.py +++ b/ivy/functional/ivy/layers.py @@ -710,10 +710,10 @@ def scaled_dot_product_attention( @handle_array_function def multi_head_attention( query: Union[ivy.Array, ivy.NativeArray], - key: Optional[Union[ivy.Array, ivy.NativeArray]] = None, - value: Optional[Union[ivy.Array, ivy.NativeArray]] = None, /, *, + key: Optional[Union[ivy.Array, ivy.NativeArray]] = None, + value: Optional[Union[ivy.Array, ivy.NativeArray]] = None, num_heads: int = 8, scale: Optional[float] = None, attention_mask: Optional[Union[ivy.Array, ivy.NativeArray]] = None, From d20d4ee2afb72bf55eea6376bfba985b505b5cf3 Mon Sep 17 00:00:00 2001 From: MahmoudAshraf97 Date: Tue, 12 Sep 2023 11:03:54 +0000 Subject: [PATCH 064/117] fix: missing kwargs in `MultiHeadAttention` class forward method --- ivy/stateful/layers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ivy/stateful/layers.py b/ivy/stateful/layers.py index 5efc92331e6d5..3239d1dbb24a4 100644 --- a/ivy/stateful/layers.py +++ b/ivy/stateful/layers.py @@ -408,8 +408,8 @@ def _forward( """ return ivy.multi_head_attention( query, - key, - value, + key=key, + value=value, num_heads=self._num_heads, scale=self._scale, attention_mask=attention_mask, From 4fe1301fb427e5730cce356fc9db5a869cbfd151 Mon Sep 17 00:00:00 2001 From: AnnaTz <111577222+AnnaTz@users.noreply.github.com> Date: Tue, 12 Sep 2023 12:32:34 +0100 Subject: [PATCH 065/117] fix(ivy): Fixes ivy.pow for the complex raised to inf case, and for the zero input cases --- ivy/functional/backends/jax/elementwise.py | 13 +++--- ivy/functional/backends/numpy/elementwise.py | 3 ++ ivy/functional/backends/paddle/elementwise.py | 22 ++++------ .../backends/tensorflow/elementwise.py | 40 ++++++++++--------- ivy/functional/backends/torch/elementwise.py | 17 ++++++++ .../test_core/test_elementwise.py | 36 ++++++++++------- 6 files changed, 77 insertions(+), 54 deletions(-) diff --git a/ivy/functional/backends/jax/elementwise.py b/ivy/functional/backends/jax/elementwise.py index 84f0f7d0b6518..39257b791f59f 100644 --- a/ivy/functional/backends/jax/elementwise.py +++ b/ivy/functional/backends/jax/elementwise.py @@ -14,7 +14,6 @@ from ivy.functional.backends.jax import JaxArray from ivy.func_wrapper import with_unsupported_dtypes from . import backend_version -from ...ivy.elementwise import _complex_to_inf def abs( @@ -409,11 +408,13 @@ def pow( out: Optional[JaxArray] = None, ) -> JaxArray: x1, x2 = ivy.promote_types_of_inputs(x1, x2) - if ivy.is_complex_dtype(x1) and ivy.any(ivy.isinf(x2)): - inf_indices = jnp.nonzero(jnp.isinf(x2)) - ret = jnp.power(x1, x2) - ret[inf_indices] = _complex_to_inf(ret[inf_indices]) - return ret + if ivy.any(x1 == 0) and ivy.is_int_dtype(x1) and ivy.any(x2 < 0) and all(dtype not in str(x1.dtype) for dtype in ['int16', 'int8']): + if ivy.is_int_dtype(x1): + fill_value = jnp.iinfo(x1.dtype).min + else: + fill_value = jnp.finfo(x1.dtype).min + ret = jnp.float_power(x1, x2) + return jnp.where(jnp.bitwise_and(x1 == 0, x2 < 0), fill_value, ret).astype(x1.dtype) if ivy.is_int_dtype(x1) and ivy.any(x2 < 0): return jnp.float_power(x1, x2).astype(x1.dtype) return jnp.power(x1, x2) diff --git a/ivy/functional/backends/numpy/elementwise.py b/ivy/functional/backends/numpy/elementwise.py index 7a360eb14e5c7..2d6095aa24cd7 100644 --- a/ivy/functional/backends/numpy/elementwise.py +++ b/ivy/functional/backends/numpy/elementwise.py @@ -610,6 +610,9 @@ def pow( *, out: Optional[np.ndarray] = None, ) -> np.ndarray: + if ivy.is_complex_dtype(x1) and ivy.any(ivy.isinf(x2)): + ret = np.power(x1, x2) + return np.where(np.isinf(x2), np.nan + np.nan * 1j if x2 < 0 else -0 * 1j, ret) x1, x2 = ivy.promote_types_of_inputs(x1, x2) if ivy.is_int_dtype(x1) and ivy.any(x2 < 0): return np.float_power(x1, x2, casting="unsafe").astype(x1.dtype) diff --git a/ivy/functional/backends/paddle/elementwise.py b/ivy/functional/backends/paddle/elementwise.py index f1e37a6a08167..195a1bbb4b98c 100644 --- a/ivy/functional/backends/paddle/elementwise.py +++ b/ivy/functional/backends/paddle/elementwise.py @@ -10,7 +10,6 @@ # local from . import backend_version -from ...ivy.elementwise import _complex_to_inf def _elementwise_helper(x1, x2): @@ -94,15 +93,13 @@ def isinf( detect_negative: bool = True, out: Optional[paddle.Tensor] = None, ) -> paddle.Tensor: - if detect_negative and detect_positive: - return paddle.isinf(x) - - if detect_negative: - return paddle_backend.equal(x, float("-inf")) - - if detect_positive: - return paddle_backend.equal(x, float("inf")) - + if not ivy.is_complex_dtype(x): + if detect_negative and detect_positive: + return paddle.isinf(x) + if detect_negative: + return paddle_backend.equal(x, float("-inf")) + if detect_positive: + return paddle_backend.equal(x, float("inf")) return paddle.zeros(shape=x.shape, dtype=bool) @@ -814,11 +811,6 @@ def pow( out: Optional[paddle.Tensor] = None, ) -> paddle.Tensor: x1, x2, ret_dtype = _elementwise_helper(x1, x2) - if ivy.is_complex_dtype(x1) and ivy.any(ivy.isinf(x2)): - inf_indices = paddle.nonzero(paddle.isinf(x2)) - ret = paddle.pow(x1, x2) - ret[inf_indices] = _complex_to_inf(ret[inf_indices]) - return ret if x1.dtype in [ paddle.int8, paddle.int16, diff --git a/ivy/functional/backends/tensorflow/elementwise.py b/ivy/functional/backends/tensorflow/elementwise.py index 751a42d4a24e6..060b18230209f 100644 --- a/ivy/functional/backends/tensorflow/elementwise.py +++ b/ivy/functional/backends/tensorflow/elementwise.py @@ -353,7 +353,6 @@ def isfinite( return tf.math.is_finite(x) -@with_unsupported_dtypes({"2.13.0 and below": ("complex",)}, backend_version) def isinf( x: Union[tf.Tensor, tf.Variable], /, @@ -362,16 +361,17 @@ def isinf( detect_negative: bool = True, out: Optional[Union[tf.Tensor, tf.Variable]] = None, ) -> Union[tf.Tensor, tf.Variable]: - if ivy.is_int_dtype(x): - return tf.zeros_like(x, tf.bool) - else: - if detect_negative and detect_positive: - return tf.math.is_inf(x) - elif detect_negative: - return tf.experimental.numpy.isneginf(x) - elif detect_positive: - return tf.experimental.numpy.isposinf(x) - return tf.zeros_like(x, tf.bool) + if not ivy.is_complex_dtype(x): + if ivy.is_int_dtype(x): + return tf.zeros_like(x, tf.bool) + else: + if detect_negative and detect_positive: + return tf.math.is_inf(x) + elif detect_negative: + return tf.experimental.numpy.isneginf(x) + elif detect_positive: + return tf.experimental.numpy.isposinf(x) + return tf.zeros_like(x, tf.bool) @with_unsupported_dtypes({"2.13.0 and below": ("complex", "bool")}, backend_version) @@ -611,6 +611,12 @@ def pow( *, out: Optional[Union[tf.Tensor, tf.Variable]] = None, ) -> Union[tf.Tensor, tf.Variable]: + if ivy.is_complex_dtype(x1) and ivy.any(ivy.isinf(x2)): + ret = tf.experimental.numpy.power(x1, x2) + return tf.where(ivy.isinf(x2), ivy.nan + ivy.nan * 1j if x2 < 0 else -0 * 1j, ret) + if ivy.is_complex_dtype(x2) and ivy.any(x1 == 0): + ret = tf.experimental.numpy.power(x1, x2) + return tf.where(x1 == 0, ivy.nan + ivy.nan * 1j, ret) x1, x2 = ivy.promote_types_of_inputs(x1, x2) if isinstance(x1, tf.Tensor) and isinstance(x2, tf.Tensor): if x1.dtype.is_unsigned or x2.dtype.is_unsigned: @@ -620,14 +626,12 @@ def pow( if x2.dtype.is_unsigned: x2 = tf.cast(x2, tf.float64) return tf.cast(tf.experimental.numpy.power(x1, x2), promoted_type) - orig_x1_dtype = None if ivy.is_int_dtype(x1) and ivy.any(x2 < 0): - orig_x1_dtype = x1.dtype - x1 = tf.cast(x1, tf.float32) - ret = tf.experimental.numpy.power(x1, x2) - if orig_x1_dtype is not None: - return tf.cast(ret, orig_x1_dtype) - return ret + return tf.cast( + tf.experimental.numpy.power(tf.cast(x1, tf.float32), x2), + x1.dtype, + ) + return tf.experimental.numpy.power(x1, x2) @with_unsupported_dtypes({"2.13.0 and below": ("bfloat16", "complex")}, backend_version) diff --git a/ivy/functional/backends/torch/elementwise.py b/ivy/functional/backends/torch/elementwise.py index 4d7c767fd5b8c..a3fb65c70be5e 100644 --- a/ivy/functional/backends/torch/elementwise.py +++ b/ivy/functional/backends/torch/elementwise.py @@ -589,7 +589,24 @@ def pow( *, out: Optional[torch.Tensor] = None, ) -> torch.Tensor: + if ivy.is_complex_dtype(x1) and ivy.any(ivy.isinf(x2)): + ret = torch.pow(x1, x2) + x2 = torch.as_tensor(x2).to(torch.float64) + return torch.where(ivy.isinf(x2), torch.nan + torch.nan * 1j if x2 < 0 else -0 * 1j, ret) x1, x2 = ivy.promote_types_of_inputs(x1, x2) + if ivy.any(x1 == 0): + if ivy.is_complex_dtype(x2): + x2 = torch.broadcast_to(x2, x1.shape) + ret = torch.pow(x1, x2) + return torch.where(x1 == 0, torch.nan+torch.nan*1j, ret) + elif ivy.any(x2 < 0) and ivy.is_int_dtype(x2) and all(dtype not in str(x1.dtype) for dtype in ['int16', 'int8']): + if ivy.is_int_dtype(x1): + fill_value = torch.iinfo(x1.dtype).min + else: + fill_value = torch.finfo(x1.dtype).min + x2 = torch.broadcast_to(x2, x1.shape) + ret = torch.pow(x1, x2) + return torch.where(torch.bitwise_and(x1 == 0, x2 < 0), fill_value, ret) return torch.pow(x1, x2, out=out) diff --git a/ivy_tests/test_ivy/test_functional/test_core/test_elementwise.py b/ivy_tests/test_ivy/test_functional/test_core/test_elementwise.py index 5fd064506ed80..1bf00a9e0628d 100644 --- a/ivy_tests/test_ivy/test_functional/test_core/test_elementwise.py +++ b/ivy_tests/test_ivy/test_functional/test_core/test_elementwise.py @@ -1595,24 +1595,30 @@ def test_positive(*, dtype_and_x, test_flags, backend_fw, fn_name, on_device): fn_tree="functional.ivy.pow", dtype_and_x=pow_helper(), test_gradients=st.just(False), + ground_truth_backend="numpy", ) def test_pow(*, dtype_and_x, test_flags, backend_fw, fn_name, on_device): input_dtype, x = dtype_and_x - # bfloat16 is not supported by numpy - assume(not ("bfloat16" in input_dtype)) - x[0] = not_too_close_to_zero(x[0]) - x[1] = not_too_close_to_zero(x[1]) - helpers.test_function( - input_dtypes=input_dtype, - test_flags=test_flags, - backend_to_test=backend_fw, - fn_name=fn_name, - on_device=on_device, - rtol_=1e-2, - atol_=1e-2, - x1=x[0], - x2=x[1], - ) + try: + helpers.test_function( + input_dtypes=input_dtype, + test_flags=test_flags, + backend_to_test=backend_fw, + fn_name=fn_name, + on_device=on_device, + rtol_=1e-2, + atol_=1e-2, + x1=x[0], + x2=x[1], + ) + except Exception as e: + if any( + error_string in str(e) + for error_string in ["overflow", "too large to convert to"] + ): + assume(False) + else: + raise @handle_test( From 5c57b6e131981ef2e55c5f7f336b1cc141275412 Mon Sep 17 00:00:00 2001 From: ivy-branch Date: Tue, 12 Sep 2023 11:38:23 +0000 Subject: [PATCH 066/117] =?UTF-8?q?Update=20demos=20=F0=9F=A4=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/demos | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/demos b/docs/demos index d5b9659fc0ac4..690375ff06f29 160000 --- a/docs/demos +++ b/docs/demos @@ -1 +1 @@ -Subproject commit d5b9659fc0ac4e6de37867db878b1d068e2c9a76 +Subproject commit 690375ff06f29dd0d41b6358b339aa9135329203 From 9edecd9a100fd75d1e97eed09cad7de8945cfccf Mon Sep 17 00:00:00 2001 From: akshatvishu <33392262+akshatvishu@users.noreply.github.com> Date: Tue, 12 Sep 2023 17:10:34 +0530 Subject: [PATCH 067/117] fix(namespaces): Correct namespace references in multiple functions at paddle frontend tests (#23464) Co-authored-by: @AnnaTz --- .../test_frontends/test_paddle/test_linalg.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ivy_tests/test_ivy/test_frontends/test_paddle/test_linalg.py b/ivy_tests/test_ivy/test_frontends/test_paddle/test_linalg.py index ceff4a4f94323..8572910aa2f53 100644 --- a/ivy_tests/test_ivy/test_frontends/test_paddle/test_linalg.py +++ b/ivy_tests/test_ivy/test_frontends/test_paddle/test_linalg.py @@ -349,7 +349,7 @@ def test_paddle_cholesky( @handle_frontend_test( - fn_tree="paddle.cholesky_solve", + fn_tree="paddle.linalg.cholesky_solve", x=_get_second_matrix(), y=_get_paddle_cholesky_matrix(), test_with_out=st.just(False), @@ -382,7 +382,7 @@ def test_paddle_cholesky_solve( @handle_frontend_test( - fn_tree="paddle.cond", + fn_tree="paddle.linalg.cond", dtype_and_x=_get_dtype_and_matrix_non_singular(dtypes=["float32", "float64"]), p=st.sampled_from([None, "fro", "nuc", np.inf, -np.inf, 1, -1, 2, -2]), test_with_out=st.just(False), @@ -523,7 +523,7 @@ def test_paddle_dot( # eig @handle_frontend_test( - fn_tree="paddle.eig", + fn_tree="paddle.linalg.eig", dtype_and_input=_get_dtype_and_square_matrix(real_and_complex_only=True), test_with_out=st.just(False), ) @@ -570,7 +570,7 @@ def test_paddle_eig( # eigh @handle_frontend_test( - fn_tree="paddle.eigh", + fn_tree="paddle.linalg.eigh", dtype_and_input=_get_dtype_and_square_matrix(real_and_complex_only=True), UPLO=st.sampled_from(("L", "U")), test_with_out=st.just(False), @@ -620,7 +620,7 @@ def test_paddle_eigh( # eigvals @handle_frontend_test( - fn_tree="paddle.eigvals", + fn_tree="paddle.linalg.eigvals", dtype_x=_get_dtype_and_square_matrix(real_and_complex_only=True), test_with_out=st.just(False), ) @@ -728,7 +728,7 @@ def test_paddle_matmul( # matrix_power @handle_frontend_test( - fn_tree="paddle.matrix_power", + fn_tree="paddle.linalg.matrix_power", dtype_and_x=helpers.dtype_and_values( available_dtypes=helpers.get_dtypes("float"), min_value=0, @@ -795,7 +795,7 @@ def test_paddle_norm( # pinv @handle_frontend_test( - fn_tree="paddle.pinv", + fn_tree="paddle.linalg.pinv", dtype_and_x=helpers.dtype_and_values( available_dtypes=helpers.get_dtypes("float"), min_num_dims=2, @@ -840,7 +840,7 @@ def test_paddle_pinv( # qr @handle_frontend_test( - fn_tree="paddle.qr", + fn_tree="paddle.linalg.qr", dtype_and_x=_get_dtype_and_matrix(), mode=st.sampled_from(("reduced", "complete")), test_with_out=st.just(False), @@ -871,7 +871,7 @@ def test_paddle_qr( # solve @handle_frontend_test( - fn_tree="paddle.solve", + fn_tree="paddle.linalg.solve", x=_get_first_matrix(), y=_get_second_matrix(), test_with_out=st.just(False), From 6e48cd9b9558298d5243fa68cc70bef4af8db9e6 Mon Sep 17 00:00:00 2001 From: Yusha Arif <101613943+YushaArif99@users.noreply.github.com> Date: Tue, 12 Sep 2023 17:00:57 +0500 Subject: [PATCH 068/117] control_flow_ops refactor (#23465) (refactor)(backends): modified the signature of `ivy.if_else` and `ivy.while_loop` to accept a dict of vars instead of a tuple of vars. --- .../backends/jax/control_flow_ops.py | 5 ++- .../backends/numpy/control_flow_ops.py | 14 ++----- .../backends/tensorflow/control_flow_ops.py | 21 ++-------- .../backends/torch/control_flow_ops.py | 12 ++---- ivy/functional/frontends/torch/utilities.py | 7 ++++ ivy/functional/ivy/control_flow_ops.py | 38 ++++++------------- 6 files changed, 34 insertions(+), 63 deletions(-) diff --git a/ivy/functional/backends/jax/control_flow_ops.py b/ivy/functional/backends/jax/control_flow_ops.py index d4fa05bbdf250..ba981aadf58b4 100644 --- a/ivy/functional/backends/jax/control_flow_ops.py +++ b/ivy/functional/backends/jax/control_flow_ops.py @@ -2,8 +2,10 @@ def if_else(cond, body_fn, orelse_fn, vars): + cond_vars = list(vars.values()) + pred = cond(**vars) with jax.disable_jit(): - final_vars = jax.lax.cond(cond, body_fn, orelse_fn, *vars) + final_vars = jax.lax.cond(pred, body_fn, orelse_fn, *cond_vars) return final_vars @@ -14,6 +16,7 @@ def body_fn_wrapper(loop_vars): def test_fn_wrapper(loop_vars): return test_fn(*loop_vars) + vars = list(vars.values()) with jax.disable_jit(): final_loop_vars = jax.lax.while_loop(test_fn_wrapper, body_fn_wrapper, vars) return final_loop_vars diff --git a/ivy/functional/backends/numpy/control_flow_ops.py b/ivy/functional/backends/numpy/control_flow_ops.py index fbc642ca5c1f0..2f20a0615cafb 100644 --- a/ivy/functional/backends/numpy/control_flow_ops.py +++ b/ivy/functional/backends/numpy/control_flow_ops.py @@ -1,23 +1,17 @@ -# def if_exp(cond, if_true, if_false): -# return if_true() if cond else if_false() - - def if_else(cond, body_fn, orelse_fn, vars): # back-compatibility - if not callable(cond): - cond = bool(cond) if isinstance(cond, bool): v = cond cond = lambda *_: v - cond = cond(*vars) + cond = cond(**vars) if cond: - return body_fn(*vars) + return body_fn(**vars) else: - return orelse_fn(*vars) + return orelse_fn(**vars) def while_loop(test_fn, body_fn, vars): - result = vars + result = list(vars.values()) while test_fn(*result): result = body_fn(*result) if not isinstance(result, tuple): diff --git a/ivy/functional/backends/tensorflow/control_flow_ops.py b/ivy/functional/backends/tensorflow/control_flow_ops.py index 9b6f42c456d75..aebb0bbde82d0 100644 --- a/ivy/functional/backends/tensorflow/control_flow_ops.py +++ b/ivy/functional/backends/tensorflow/control_flow_ops.py @@ -1,30 +1,16 @@ import tensorflow as tf -# def if_exp(cond, if_true, if_false, expr_repr): -# def true_fn(): -# return if_true() -# -# def false_fn(): -# return if_false() -# -# return tf.cond(cond, true_fn, false_fn) - def if_else(cond, body_fn, orelse_fn, vars): # back-compatibility if isinstance(cond, bool): v = cond cond = lambda *_: v - cond = bool(cond(*vars)) - # return tf.cond(cond, lambda: body_fn(*vars), lambda: orelse_fn(*vars)) + cond = bool(cond(**vars)) + return tf.cond(cond, lambda: body_fn(**vars), lambda: orelse_fn(**vars)) # use pythonic placeholder until the graph compiler supports callable arguments - if cond: - return body_fn(*vars) - else: - return orelse_fn(*vars) - def while_loop(test_fn, body_fn, vars): def body_fn_wrapper(*loop_vars): @@ -35,7 +21,8 @@ def test_fn_wrapper(*loop_vars): if not vars: vars = (0,) - + else: + vars = list(vars.values()) return tf.while_loop(test_fn_wrapper, body_fn_wrapper, loop_vars=vars) diff --git a/ivy/functional/backends/torch/control_flow_ops.py b/ivy/functional/backends/torch/control_flow_ops.py index 1a22035417f80..d5becfe778623 100644 --- a/ivy/functional/backends/torch/control_flow_ops.py +++ b/ivy/functional/backends/torch/control_flow_ops.py @@ -1,24 +1,20 @@ -# def if_exp(cond, if_true, if_false): -# return if_true() if cond else if_false() - - def if_else(cond, body_fn, orelse_fn, vars): # back-compatibility if isinstance(cond, bool): v = cond cond = lambda *_: v if callable(cond): - cond = cond(*vars) + cond = cond(**vars) else: cond = bool(cond) if cond: - return body_fn(*vars) + return body_fn(**vars) else: - return orelse_fn(*vars) + return orelse_fn(**vars) def while_loop(test_fn, body_fn, vars): - result = vars + result = list(vars.values()) while test_fn(*result): result = body_fn(*result) if not isinstance(result, tuple): diff --git a/ivy/functional/frontends/torch/utilities.py b/ivy/functional/frontends/torch/utilities.py index de8e4e0f97a73..fea89a9e3af9b 100644 --- a/ivy/functional/frontends/torch/utilities.py +++ b/ivy/functional/frontends/torch/utilities.py @@ -1,6 +1,7 @@ import ivy from ivy.functional.frontends.torch.func_wrapper import to_ivy_arrays_and_back from ivy.func_wrapper import with_supported_dtypes +import inspect # --- Helpers --- # @@ -25,6 +26,12 @@ def bincount(x, weights=None, minlength=0): return ivy.bincount(x, weights=weights, minlength=minlength) +def if_else(cond_fn, body_fn, orelse_fn, vars): + cond_keys = inspect.getargspec(cond_fn).args + cond_vars = dict(zip(cond_keys, vars)) + return ivy.if_else(cond_fn, body_fn, orelse_fn, cond_vars) + + @to_ivy_arrays_and_back def result_type(tensor, other): return ivy.result_type(tensor, other) diff --git a/ivy/functional/ivy/control_flow_ops.py b/ivy/functional/ivy/control_flow_ops.py index 16f1f87bfa9c9..2096df6b2c1af 100644 --- a/ivy/functional/ivy/control_flow_ops.py +++ b/ivy/functional/ivy/control_flow_ops.py @@ -1,11 +1,9 @@ -from typing import Union, Callable, Any, Iterable +from typing import Union, Callable, Any, Iterable, Dict import ivy from ivy.utils.backend import current_backend from ivy.func_wrapper import ( handle_array_like_without_promotion, to_native_arrays_and_back, - to_ivy_arrays_and_back, - handle_device_shifting, ) @@ -13,7 +11,7 @@ def if_else( cond: Callable, body_fn: Callable, orelse_fn: Callable, - vars: Iterable[Union[ivy.Array, ivy.NativeArray]], + vars: Dict[str, Union[ivy.Array, ivy.NativeArray]], ) -> Any: """ Take a condition function and two functions as input. If the condition is True, the @@ -39,8 +37,7 @@ def if_else( Examples -------- - >>> x = 1 - >>> cond = x > 0 + >>> cond = lambda x: True >>> body_fn = lambda x: x + 1 >>> orelse_fn = lambda x: x - 1 >>> vars = (1,) @@ -48,32 +45,27 @@ def if_else( >>> print(result) 2 - >>> x = 0 - >>> cond = x - 2 == 0 + >>> cond = lambda x: True >>> body_fn = lambda x: x * 2 >>> orelse_fn = lambda x: x / 2 >>> vars = ivy.array([1, 2, 3]) >>> result = ivy.if_else(cond, body_fn, orelse_fn, vars=(vars,)) >>> print(result) - ivy.array([0.5, 1. , 1.5]) + ivy.array([0.5, 1.0, 1.5]) """ @to_native_arrays_and_back @handle_array_like_without_promotion - @handle_device_shifting - def _if_else(cond, body_fn, orelse_fn, vars): + def _if_else(cond, body_fn, orelse_fn, **vars): return current_backend().if_else(cond, body_fn, orelse_fn, vars) - body_fn = to_ivy_arrays_and_back(body_fn) - orelse_fn = to_ivy_arrays_and_back(orelse_fn) - - return _if_else(cond, body_fn, orelse_fn, vars) + return _if_else(cond, body_fn, orelse_fn, **vars) def while_loop( test_fn: Callable, body_fn: Callable, - vars: Iterable[Union[ivy.Array, ivy.NativeArray]], + vars: Dict[str, Union[ivy.Array, ivy.NativeArray]], ) -> Any: """ Take a test function, a body function and a set of variables as input. The body @@ -111,19 +103,15 @@ def while_loop( >>> vars = (i, j) >>> result = ivy.while_loop(test_fn, body_fn, vars=vars) >>> print(result) - (3, 4) + (3, 8) """ @to_native_arrays_and_back @handle_array_like_without_promotion - @handle_device_shifting - def _while_loop(test_fn, body_fn, vars): + def _while_loop(test_fn, body_fn, **vars): return current_backend().while_loop(test_fn, body_fn, vars) - test_fn = to_ivy_arrays_and_back(test_fn) - body_fn = to_ivy_arrays_and_back(body_fn) - - return _while_loop(test_fn, body_fn, vars) + return _while_loop(test_fn, body_fn, **vars) def for_loop( @@ -209,10 +197,6 @@ def cmp_isnot(left, right): return left is not right -def cast_bool(x): - return bool(x) - - def _tuple_to_dict(t): return {k: t[k] for k in range(len(t))} From 964cf142a0324cdbbf41897b8fb935fa0b762033 Mon Sep 17 00:00:00 2001 From: Kareem Morsy Date: Tue, 12 Sep 2023 15:13:43 +0300 Subject: [PATCH 069/117] chore: Add @KareemMAX as demos codeowner --- .github/CODEOWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index f375d2cf90da1..fd2748e0c2167 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -16,6 +16,7 @@ docs/prebuild.sh @KareemMAX docs/overview/contributing/building_the_docs.rst @KareemMAX docs/overview/deep_dive/building_the_docs_pipline.rst @KareemMAX docs/_templates @KareemMAX +docs/demos @KareemMAX # Docker docker/* @ricksanchezstoic From 3048f60b0b2fefdbf4722e741ee387d14b6509f7 Mon Sep 17 00:00:00 2001 From: KHETHAN <100506743+khethan123@users.noreply.github.com> Date: Tue, 12 Sep 2023 18:00:45 +0530 Subject: [PATCH 070/117] Sub ivy (#23192) Co-authored-by: khethan123 --- ivy/functional/ivy/linear_algebra.py | 141 +++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) diff --git a/ivy/functional/ivy/linear_algebra.py b/ivy/functional/ivy/linear_algebra.py index b7c3f450047ea..fae9e303755c7 100644 --- a/ivy/functional/ivy/linear_algebra.py +++ b/ivy/functional/ivy/linear_algebra.py @@ -645,6 +645,48 @@ def eigh( Both the description and the type hints above assumes an array input for simplicity, but this function is *nestable*, and therefore also accepts :class:`ivy.Container` instances in place of any of the arguments. + + Examples + -------- + With :class:`ivy.Array` input: + + >>> x = ivy.array([[1., 2.],[2., 5.]]) + >>> eigenvalues, eigenvectors = ivy.eigh(x) + >>> print(eigenvalues) + ivy.array([0.17157288, 5.82842731]) + >>> print(eigenvectors) + ivy.array([[-0.9238795 , 0.38268343], + [ 0.38268343, 0.9238795 ]]) + + >>> x = ivy.array([[1., 2.], [2., 5.]]) + >>> eigenvalues, eigenvectors = ivy.zeros(len(x)), ivy.zeros(x.shape) + >>> ivy.eigh(x, out=(eigenvalues, eigenvectors)) + >>> print(eigenvalues) + ivy.array([0.17157288, 5.82842731]) + >>> print(eigenvectors) + ivy.array([[-0.9238795 , 0.38268343], + [ 0.38268343, 0.9238795 ]]) + + With :class:`ivy.Container` input: + + >>> x = ivy.Container( + ... a = ivy.native_array([[1., 2., 0.], [3., 4., 5.], [1., 5., 9]]), + ... b = ivy.array([[2., 4., 6.], [3., 5., 7.], [0., 0.8, 2.9]])) + >>> eigenvalues, eigenvectors = ivy.eigh(x, UPLO = 'U') + >>> print(eigenvalues) + { + a: ivy.array([-0.78930789, 2.59803128, 12.19127655]), + b: ivy.array([-4.31213903, -0.63418275, 14.84632206]) + } + >>> print(eigenvectors) + { + a: ivy.array([[0.70548367, -0.70223427, 0.09570674], + [-0.63116378, -0.56109613, 0.53554028], + [0.32237405, 0.43822157, 0.83906901]]), + b: ivy.array([[0.50766778, 0.71475857, 0.48103389], + [0.3676433, -0.68466955, 0.62933773], + [-0.77917379, 0.14264561, 0.61036086]]) + } """ return current_backend(x).eigh(x, UPLO=UPLO, out=out) @@ -1791,6 +1833,53 @@ def qr( Both the description and the type hints above assumes an array input for simplicity, but this function is *nestable*, and therefore also accepts :class:`ivy.Container` instances in place of any of the arguments. + + Examples + -------- + With :class:`ivy.Array` input: + + >>> x = ivy.array([[1.,2.,3.],[4.,5.,6.],[7.,8.,9.]]) + >>> q, r = ivy.qr(x) + >>> print(q) + ivy.array([[-0.12309149, 0.90453403, 0.40824829], + [-0.49236596, 0.30151134, -0.81649658], + [-0.86164044, -0.30151134, 0.40824829]]) + >>> print(r) + ivy.array([[-8.12403841e+00,-9.60113630e+00, -1.10782342e+01], + [ 0.00000000e+00, 9.04534034e-01, 1.80906807e+00], + [ 0.00000000e+00, 0.00000000e+00, -8.88178420e-16]]) + + # Note: if `int` values are used in `x` the output for q, r varry + >>> x = ivy.array([[1., 2.], [3., 4.]]) + >>> q = ivy.zeros_like(x) + >>> r = ivy.zeros_like(x) + >>> ivy.qr(x, out=(q,r)) + >>> print(q) + ivy.array([[-0.31622776, -0.94868332], + [-0.94868332, 0.31622776]]) + >>> print(r) + ivy.array([[-3.1622777 , -4.42718887], + [ 0. , -0.63245553]]) + + With :class:`ivy.Container` input: + + >>> x = ivy.Container(a = ivy.native_array([[1., 2.], [3., 4.]]), + ... b = ivy.array([[2., 3.], [4. ,5.]])) + >>> q,r = ivy.qr(x, mode='complete') + >>> print(q) + { + a: ivy.array([[-0.31622777, -0.9486833], + [-0.9486833, 0.31622777]]), + b: ivy.array([[-0.4472136, -0.89442719], + [-0.89442719, 0.4472136]]) + } + >>> print(r) + { + a: ivy.array([[-3.16227766, -4.42718872], + [0., -0.63245553]]), + b: ivy.array([[-4.47213595, -5.81377674], + [0., -0.4472136]]) + } """ return current_backend(x).qr(x, mode=mode, out=out) @@ -1955,6 +2044,22 @@ def solve( Both the description and the type hints above assumes an array input for simplicity, but this function is *nestable*, and therefore also accepts :class:`ivy.Container` instances in place of any of the arguments. + + Examples + -------- + with :class:`ivy.Array` input: + + >>> x1 = ivy.array([[1., 2.],[3., 4.]]) + >>> x2 = ivy.array([5., 6.]) + >>> out = ivy.solve(x1, x2) + >>> print(out) + ivy.array([-4. , 4.5]) + + >>> x1 = ivy.native_array([[1., 2.],[3., 4.]]) + >>> x2 = ivy.array([5., 6.]) + >>> z = ivy.zeros_like(x2) + >>> ivy.solve(x1, x2, out=z) + ivy.array([-4. , 4.5]) """ return current_backend(x1, x2).solve(x1, x2, out=out) @@ -2289,6 +2394,15 @@ def tensordot( >>> print(res) ivy.array([[[[3.,4.],[4.,5.]],[[6.,8.],[8.,10.]]],[[[6.,8.],[8.,10.]],[[9.,12.],[12.,15.]]]]) + With :class:'ivy.NativeArray' input: + + >>> x = ivy.native_array([[1., 2.], [2., 3.]]) + >>> y = ivy.native_array([[3., 4.], [4., 5.]]) + >>> res = ivy.tensordot(x, y, axes = (1,1)) + >>> print(res) + ivy.array([[11., 14.], + [18., 23.]]) + With a mix of :class:`ivy.Array` and :class:`ivy.NativeArray` inputs: >>> x = ivy.array([[1., 0., 1.], [2., 3., 6.], [0., 7., 2.]]) @@ -2508,6 +2622,33 @@ def vecdot( Both the description and the type hints above assumes an array input for simplicity, but this function is *nestable*, and therefore also accepts :class:`ivy.Container` instances in place of any of the arguments. + + Examples + -------- + With :class:`ivy.Array` input: + + >>> x1 = ivy.array([1., 2., 3.]) + >>> x2 = ivy.array([4., 5., 6.]) + >>> dot_product = ivy.vecdot(x1, x2) + >>> print(dot_product) + ivy.array(32.) + + >>> x1 = ivy.array([1., 2., 3.]) + >>> x2 = ivy.array([1., .8, 4.]) + >>> y = ivy.zeros(1) + >>> ivy.vecdot(x1, x2, out=y) + ivy.array(14.60000038) + + With :class:`ivy.Container` input: + + >>> x1 = ivy.array([1., 2., 3.]) + >>> x2 = ivy.Container(a=ivy.array([7., 8., 9.]), b=ivy.array([10., 11., 12.])) + >>> dot_product = ivy.vecdot(x1, x2, axis=0) + >>> print(dot_product) + { + a: ivy.array(50.), + b: ivy.array(68.) + } """ return current_backend(x1).vecdot(x1, x2, axis=axis, out=out) From a71c7cee069ca69ece452ed82543059f338c57c3 Mon Sep 17 00:00:00 2001 From: NripeshN Date: Tue, 12 Sep 2023 16:44:08 +0400 Subject: [PATCH 071/117] manual lint --- ivy/functional/backends/jax/elementwise.py | 11 +++++++++-- ivy/functional/backends/tensorflow/elementwise.py | 4 +++- ivy/functional/backends/torch/elementwise.py | 12 +++++++++--- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/ivy/functional/backends/jax/elementwise.py b/ivy/functional/backends/jax/elementwise.py index 39257b791f59f..6f26e19adebc0 100644 --- a/ivy/functional/backends/jax/elementwise.py +++ b/ivy/functional/backends/jax/elementwise.py @@ -408,13 +408,20 @@ def pow( out: Optional[JaxArray] = None, ) -> JaxArray: x1, x2 = ivy.promote_types_of_inputs(x1, x2) - if ivy.any(x1 == 0) and ivy.is_int_dtype(x1) and ivy.any(x2 < 0) and all(dtype not in str(x1.dtype) for dtype in ['int16', 'int8']): + if ( + ivy.any(x1 == 0) + and ivy.is_int_dtype(x1) + and ivy.any(x2 < 0) + and all(dtype not in str(x1.dtype) for dtype in ["int16", "int8"]) + ): if ivy.is_int_dtype(x1): fill_value = jnp.iinfo(x1.dtype).min else: fill_value = jnp.finfo(x1.dtype).min ret = jnp.float_power(x1, x2) - return jnp.where(jnp.bitwise_and(x1 == 0, x2 < 0), fill_value, ret).astype(x1.dtype) + return jnp.where(jnp.bitwise_and(x1 == 0, x2 < 0), fill_value, ret).astype( + x1.dtype + ) if ivy.is_int_dtype(x1) and ivy.any(x2 < 0): return jnp.float_power(x1, x2).astype(x1.dtype) return jnp.power(x1, x2) diff --git a/ivy/functional/backends/tensorflow/elementwise.py b/ivy/functional/backends/tensorflow/elementwise.py index 060b18230209f..a44d13c771f44 100644 --- a/ivy/functional/backends/tensorflow/elementwise.py +++ b/ivy/functional/backends/tensorflow/elementwise.py @@ -613,7 +613,9 @@ def pow( ) -> Union[tf.Tensor, tf.Variable]: if ivy.is_complex_dtype(x1) and ivy.any(ivy.isinf(x2)): ret = tf.experimental.numpy.power(x1, x2) - return tf.where(ivy.isinf(x2), ivy.nan + ivy.nan * 1j if x2 < 0 else -0 * 1j, ret) + return tf.where( + ivy.isinf(x2), ivy.nan + ivy.nan * 1j if x2 < 0 else -0 * 1j, ret + ) if ivy.is_complex_dtype(x2) and ivy.any(x1 == 0): ret = tf.experimental.numpy.power(x1, x2) return tf.where(x1 == 0, ivy.nan + ivy.nan * 1j, ret) diff --git a/ivy/functional/backends/torch/elementwise.py b/ivy/functional/backends/torch/elementwise.py index a3fb65c70be5e..26b2f58919506 100644 --- a/ivy/functional/backends/torch/elementwise.py +++ b/ivy/functional/backends/torch/elementwise.py @@ -592,14 +592,20 @@ def pow( if ivy.is_complex_dtype(x1) and ivy.any(ivy.isinf(x2)): ret = torch.pow(x1, x2) x2 = torch.as_tensor(x2).to(torch.float64) - return torch.where(ivy.isinf(x2), torch.nan + torch.nan * 1j if x2 < 0 else -0 * 1j, ret) + return torch.where( + ivy.isinf(x2), torch.nan + torch.nan * 1j if x2 < 0 else -0 * 1j, ret + ) x1, x2 = ivy.promote_types_of_inputs(x1, x2) if ivy.any(x1 == 0): if ivy.is_complex_dtype(x2): x2 = torch.broadcast_to(x2, x1.shape) ret = torch.pow(x1, x2) - return torch.where(x1 == 0, torch.nan+torch.nan*1j, ret) - elif ivy.any(x2 < 0) and ivy.is_int_dtype(x2) and all(dtype not in str(x1.dtype) for dtype in ['int16', 'int8']): + return torch.where(x1 == 0, torch.nan + torch.nan * 1j, ret) + elif ( + ivy.any(x2 < 0) + and ivy.is_int_dtype(x2) + and all(dtype not in str(x1.dtype) for dtype in ["int16", "int8"]) + ): if ivy.is_int_dtype(x1): fill_value = torch.iinfo(x1.dtype).min else: From 4a67bc09594fd1a47a9f3c93106905d788c0dd02 Mon Sep 17 00:00:00 2001 From: Shreyansh Bardia <104841983+ShreyanshBardia@users.noreply.github.com> Date: Tue, 12 Sep 2023 18:16:03 +0530 Subject: [PATCH 072/117] removed unnecessary restriction on dtype and fill_value in ivy.full (#22404) Co-authored-by: ivy-branch --- ivy/functional/backends/jax/creation.py | 2 - ivy/functional/backends/numpy/creation.py | 2 - .../backends/tensorflow/creation.py | 7 +-- ivy/functional/backends/torch/creation.py | 2 - ivy/utils/assertions.py | 25 ----------- .../test_core/test_creation.py | 11 ++++- .../test_ivy/test_misc/test_assertions.py | 44 ------------------- 7 files changed, 10 insertions(+), 83 deletions(-) diff --git a/ivy/functional/backends/jax/creation.py b/ivy/functional/backends/jax/creation.py index 06240484f03b9..3e86a3ba043da 100644 --- a/ivy/functional/backends/jax/creation.py +++ b/ivy/functional/backends/jax/creation.py @@ -136,7 +136,6 @@ def full( out: Optional[JaxArray] = None, ) -> JaxArray: dtype = ivy.default_dtype(dtype=dtype, item=fill_value, as_native=True) - ivy.utils.assertions.check_fill_value_and_dtype_are_compatible(fill_value, dtype) return jnp.full(shape, fill_value, dtype) @@ -149,7 +148,6 @@ def full_like( device: jaxlib.xla_extension.Device, out: Optional[JaxArray] = None, ) -> JaxArray: - ivy.utils.assertions.check_fill_value_and_dtype_are_compatible(fill_value, dtype) return jnp.full_like(x, fill_value, dtype=dtype) diff --git a/ivy/functional/backends/numpy/creation.py b/ivy/functional/backends/numpy/creation.py index 3835ce807cae1..2fe7fa72676c9 100644 --- a/ivy/functional/backends/numpy/creation.py +++ b/ivy/functional/backends/numpy/creation.py @@ -116,7 +116,6 @@ def full( out: Optional[np.ndarray] = None, ) -> np.ndarray: dtype = ivy.default_dtype(dtype=dtype, item=fill_value, as_native=True) - ivy.utils.assertions.check_fill_value_and_dtype_are_compatible(fill_value, dtype) return _to_device( np.full(shape, fill_value, dtype), device=device, @@ -132,7 +131,6 @@ def full_like( device: str, out: Optional[np.ndarray] = None, ) -> np.ndarray: - ivy.utils.assertions.check_fill_value_and_dtype_are_compatible(fill_value, dtype) return _to_device(np.full_like(x, fill_value, dtype=dtype), device=device) diff --git a/ivy/functional/backends/tensorflow/creation.py b/ivy/functional/backends/tensorflow/creation.py index 05f3487191575..5f7e7187176d0 100644 --- a/ivy/functional/backends/tensorflow/creation.py +++ b/ivy/functional/backends/tensorflow/creation.py @@ -193,11 +193,7 @@ def full( out: Optional[Union[tf.Tensor, tf.Variable]] = None, ) -> Union[tf.Tensor, tf.Variable]: dtype = ivy.default_dtype(dtype=dtype, item=fill_value, as_native=True) - ivy.utils.assertions.check_fill_value_and_dtype_are_compatible(fill_value, dtype) - return tf.fill( - shape, - tf.constant(fill_value, dtype=dtype), - ) + return tf.experimental.numpy.full(shape, fill_value, dtype=dtype) def full_like( @@ -209,7 +205,6 @@ def full_like( device: str, out: Optional[Union[tf.Tensor, tf.Variable]] = None, ) -> Union[tf.Tensor, tf.Variable]: - ivy.utils.assertions.check_fill_value_and_dtype_are_compatible(fill_value, dtype) return tf.experimental.numpy.full_like(x, fill_value, dtype=dtype) diff --git a/ivy/functional/backends/torch/creation.py b/ivy/functional/backends/torch/creation.py index 80a063961e0e1..9ea906aa08d31 100644 --- a/ivy/functional/backends/torch/creation.py +++ b/ivy/functional/backends/torch/creation.py @@ -242,7 +242,6 @@ def full( out: Optional[torch.Tensor] = None, ) -> Tensor: dtype = ivy.default_dtype(dtype=dtype, item=fill_value, as_native=True) - ivy.utils.assertions.check_fill_value_and_dtype_are_compatible(fill_value, dtype) if isinstance(shape, int): shape = (shape,) return torch.full( @@ -266,7 +265,6 @@ def full_like( device: torch.device, out: Optional[torch.Tensor] = None, ) -> torch.Tensor: - ivy.utils.assertions.check_fill_value_and_dtype_are_compatible(fill_value, dtype) return torch.full_like(x, fill_value, dtype=dtype, device=device) diff --git a/ivy/utils/assertions.py b/ivy/utils/assertions.py index 7e4858a8c368e..80dae2ba2ceff 100644 --- a/ivy/utils/assertions.py +++ b/ivy/utils/assertions.py @@ -1,4 +1,3 @@ -import numpy as np import ivy @@ -190,30 +189,6 @@ def check_same_dtype(x1, x2, message=""): # -------- # -def check_fill_value_and_dtype_are_compatible(fill_value, dtype): - if ivy.is_array(fill_value) and len(fill_value.shape) == 0: - fill_value = ivy.to_scalar(fill_value) - if ( - not ( - (ivy.is_int_dtype(dtype) or ivy.is_uint_dtype(dtype)) - and (isinstance(fill_value, int) or ivy.isinf(fill_value)) - ) - and not ( - ivy.is_complex_dtype(dtype) and isinstance(fill_value, (float, complex)) - ) - and not ( - ivy.is_float_dtype(dtype) - and isinstance(fill_value, (float, np.float32)) - or isinstance(fill_value, bool) - ) - ): - raise ivy.utils.exceptions.IvyException( - "the fill_value: {} and data type: {} are not compatible".format( - fill_value, dtype - ) - ) - - def check_unsorted_segment_min_valid_params(data, segment_ids, num_segments): if not (isinstance(num_segments, int)): raise ValueError("num_segments must be of integer type") diff --git a/ivy_tests/test_ivy/test_functional/test_core/test_creation.py b/ivy_tests/test_ivy/test_functional/test_core/test_creation.py index 3dc85ebc92191..e9f69fb77cb35 100644 --- a/ivy_tests/test_ivy/test_functional/test_core/test_creation.py +++ b/ivy_tests/test_ivy/test_functional/test_core/test_creation.py @@ -4,6 +4,7 @@ from hypothesis import strategies as st, assume import numpy as np + # local import ivy_tests.test_ivy.helpers as helpers from ivy_tests.test_ivy.helpers import handle_test, BackendHandler @@ -408,11 +409,13 @@ def test_frombuffer( max_dim_size=5, ), fill_value=_fill_value(), - dtypes=helpers.get_dtypes("numeric", full=False, key="dtype"), + dtypes=helpers.get_dtypes("valid", full=False), test_instance_method=st.just(False), test_gradients=st.just(False), ) def test_full(*, shape, fill_value, dtypes, test_flags, backend_fw, fn_name, on_device): + if dtypes[0].startswith("uint") and fill_value < 0: + fill_value = -fill_value helpers.test_function( input_dtypes=dtypes, test_flags=test_flags, @@ -430,12 +433,16 @@ def test_full(*, shape, fill_value, dtypes, test_flags, backend_fw, fn_name, on_ @handle_test( fn_tree="functional.ivy.full_like", dtype_and_x=_dtype_and_values(), + dtypes=helpers.get_dtypes("valid", full=False), fill_value=_fill_value(), + test_gradients=st.just(False), ) def test_full_like( - *, dtype_and_x, fill_value, test_flags, backend_fw, fn_name, on_device + *, dtype_and_x, dtypes, fill_value, test_flags, backend_fw, fn_name, on_device ): dtype, x = dtype_and_x + if dtypes[0].startswith("uint") and fill_value < 0: + fill_value = -fill_value helpers.test_function( input_dtypes=dtype, test_flags=test_flags, diff --git a/ivy_tests/test_ivy/test_misc/test_assertions.py b/ivy_tests/test_ivy/test_misc/test_assertions.py index d2dddc60f04d8..b341038151198 100644 --- a/ivy_tests/test_ivy/test_misc/test_assertions.py +++ b/ivy_tests/test_ivy/test_misc/test_assertions.py @@ -12,7 +12,6 @@ check_equal, check_exists, check_false, - check_fill_value_and_dtype_are_compatible, check_gather_input_valid, check_gather_nd_input_valid, check_greater, @@ -357,49 +356,6 @@ def test_check_false(expression): os.remove(filename) -@pytest.mark.parametrize( - "fill_value, dtype", - [ - # INVALID CASES - (1.0, ivy.int16), - (1, ivy.float16), - (1, ivy.complex64), - # VALID - (1j, ivy.complex64), - (1.0, ivy.complex64), - (1.0, ivy.float16), - (1, ivy.int16), - ], -) -def test_check_fill_value_and_dtype_are_compatible(fill_value, dtype): - filename = "except_out.txt" - orig_stdout = sys.stdout - f = open(filename, "w") - sys.stdout = f - lines = "" - try: - check_fill_value_and_dtype_are_compatible(fill_value, dtype) - local_vars = {**locals()} - except Exception as e: - local_vars = {**locals()} - print(e) - - sys.stdout = orig_stdout - f.close() - - with open(filename) as f: - lines += f.read() - - if "e" in local_vars.keys(): - assert "not compatible" in lines.strip() - - if "e" not in local_vars.keys(): - assert not lines.strip() - - with contextlib.suppress(FileNotFoundError): - os.remove(filename) - - @pytest.mark.parametrize( "params, indices, axis, batch_dims", [ From 8f0179195de5f0e7273398ef64807b462e56b010 Mon Sep 17 00:00:00 2001 From: AnnaTz <111577222+AnnaTz@users.noreply.github.com> Date: Tue, 12 Sep 2023 14:41:34 +0100 Subject: [PATCH 073/117] fix(testing): Fixes bug in test_function when used on functions that do inplace updates on the arguments. --- ivy_tests/test_ivy/helpers/function_testing.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ivy_tests/test_ivy/helpers/function_testing.py b/ivy_tests/test_ivy/helpers/function_testing.py index 072aa0449e6be..13c024473e976 100644 --- a/ivy_tests/test_ivy/helpers/function_testing.py +++ b/ivy_tests/test_ivy/helpers/function_testing.py @@ -199,8 +199,12 @@ def test_function_backend_computation( else: target_fn = ivy_backend.__dict__[fn_name] + # Make copy of arguments for functions that might use inplace update by default + copy_kwargs = copy.deepcopy(kwargs) + copy_args = copy.deepcopy(args) + ret_from_target, ret_np_flat_from_target = get_ret_and_flattened_np_array( - fw, target_fn, *args, test_compile=test_flags.test_compile, **kwargs + fw, target_fn, *copy_args, test_compile=test_flags.test_compile, **copy_kwargs ) assert ivy_backend.nested_map( From 73bc79192aeef9cada4039605d46994761c114d6 Mon Sep 17 00:00:00 2001 From: Aaryan562 <82304628+Aaryan562@users.noreply.github.com> Date: Tue, 12 Sep 2023 20:23:42 +0530 Subject: [PATCH 074/117] Topk method added to paddle frontend (#22934) --- .../frontends/paddle/tensor/tensor.py | 7 +++ .../test_paddle/test_tensor/test_tensor.py | 51 +++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/ivy/functional/frontends/paddle/tensor/tensor.py b/ivy/functional/frontends/paddle/tensor/tensor.py index ad68778a1c0fe..bf06db284f024 100644 --- a/ivy/functional/frontends/paddle/tensor/tensor.py +++ b/ivy/functional/frontends/paddle/tensor/tensor.py @@ -745,6 +745,13 @@ def argmin(self, axis=None, keepdim=False, dtype=None, name=None): self._ivy_array, axis=axis, keepdim=keepdim, dtype=dtype ) + @with_supported_dtypes( + {"2.5.1 and below": ("float32", "float64", "int32", "int64")}, + "paddle", + ) + def topk(self, k, axis=None, largest=True, sorted=True, name=None): + return ivy.top_k(self._ivy_array, k, axis=axis, largest=largest, sorted=sorted) + @with_unsupported_dtypes({"2.5.1 and below": ("float16", "bfloat16")}, "paddle") def remainder(self, y, name=None): return ivy.remainder(self._ivy_array, y) diff --git a/ivy_tests/test_ivy/test_frontends/test_paddle/test_tensor/test_tensor.py b/ivy_tests/test_ivy/test_frontends/test_paddle/test_tensor/test_tensor.py index 7ceeba34f4bff..b934bca73234c 100644 --- a/ivy_tests/test_ivy/test_frontends/test_paddle/test_tensor/test_tensor.py +++ b/ivy_tests/test_ivy/test_frontends/test_paddle/test_tensor/test_tensor.py @@ -3436,6 +3436,57 @@ def test_paddle_tensor_tanh( ) +# topk +@handle_frontend_method( + class_tree=CLASS_TREE, + init_tree="paddle.to_tensor", + method_name="topk", + dtype_x_and_axis=helpers.dtype_values_axis( + available_dtypes=helpers.get_dtypes("valid"), + min_num_dims=1, + valid_axis=True, + force_int_axis=True, + ), + k=st.data(), + sorted=st.booleans(), + largest=st.booleans(), +) +def test_paddle_tensor_topk( + dtype_x_and_axis, + k, + sorted, + largest, + frontend_method_data, + init_flags, + method_flags, + frontend, + on_device, + backend_fw, +): + input_dtype, x, axis = dtype_x_and_axis + k = k.draw(st.integers(min_value=1, max_value=x[0].shape[axis])) + helpers.test_frontend_method( + init_input_dtypes=input_dtype, + backend_to_test=backend_fw, + init_all_as_kwargs_np={ + "data": x[0], + }, + method_input_dtypes=input_dtype, + method_all_as_kwargs_np={ + "k": k, + "axis": axis, + "largest": largest, + "sorted": sorted, + }, + frontend_method_data=frontend_method_data, + init_flags=init_flags, + method_flags=method_flags, + frontend=frontend, + on_device=on_device, + test_values=False, + ) + + # trace @handle_frontend_method( class_tree=CLASS_TREE, From 3097fac3c5b9f0e256f2bc046432afc9951bf59a Mon Sep 17 00:00:00 2001 From: Ario Zareinia Date: Tue, 12 Sep 2023 08:06:34 -0700 Subject: [PATCH 075/117] Added jax.numpy.linalg.lstsq to JAX frontend (#22870) --- ivy/functional/frontends/jax/numpy/linalg.py | 18 ++++++++++ .../test_jax/test_numpy/test_linalg.py | 34 +++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/ivy/functional/frontends/jax/numpy/linalg.py b/ivy/functional/frontends/jax/numpy/linalg.py index 2edf9e838dfdb..d6b0bf6735c14 100644 --- a/ivy/functional/frontends/jax/numpy/linalg.py +++ b/ivy/functional/frontends/jax/numpy/linalg.py @@ -4,6 +4,7 @@ from ivy.functional.frontends.jax.func_wrapper import to_ivy_arrays_and_back from ivy.func_wrapper import with_unsupported_dtypes, with_supported_dtypes from ivy.functional.frontends.jax.numpy import promote_types_of_jax_inputs +from ivy.functional.frontends.numpy.linalg import lstsq as numpy_lstsq @to_ivy_arrays_and_back @@ -53,6 +54,23 @@ def inv(a): return ivy.inv(a) +# TODO: replace this with function from API +# As the composition provides numerically unstable results +@to_ivy_arrays_and_back +def lstsq(a, b, rcond=None, *, numpy_resid=False): + if numpy_resid: + return numpy_lstsq(a, b, rcond=rcond) + least_squares_solution = ivy.matmul( + ivy.pinv(a, rtol=1e-15).astype(ivy.float64), b.astype(ivy.float64) + ) + residuals = ivy.sum((b - ivy.matmul(a, least_squares_solution)) ** 2).astype( + ivy.float64 + ) + svd_values = ivy.svd(a, compute_uv=False) + rank = ivy.matrix_rank(a).astype(ivy.int32) + return (least_squares_solution, residuals, rank, svd_values[0]) + + @to_ivy_arrays_and_back def matrix_power(a, n): return ivy.matrix_power(a, n) diff --git a/ivy_tests/test_ivy/test_frontends/test_jax/test_numpy/test_linalg.py b/ivy_tests/test_ivy/test_frontends/test_jax/test_numpy/test_linalg.py index 9317b6956dfaa..126dee4ded2ba 100644 --- a/ivy_tests/test_ivy/test_frontends/test_jax/test_numpy/test_linalg.py +++ b/ivy_tests/test_ivy/test_frontends/test_jax/test_numpy/test_linalg.py @@ -500,6 +500,40 @@ def test_jax_inv( ) +# least squares +@handle_frontend_test( + fn_tree="jax.numpy.linalg.lstsq", + dtype_and_a=helpers.get_first_solve_matrix(adjoint=True), + dtype_and_b=helpers.get_second_solve_matrix(), + test_with_out=st.just(False), +) +def test_jax_lstsq( + *, + dtype_and_a, + dtype_and_b, + on_device, + fn_tree, + frontend, + test_flags, + backend_fw, +): + a_dtype, a, _ = dtype_and_a + b_dtype, b = dtype_and_b + helpers.test_frontend_function( + input_dtypes=[a_dtype, b_dtype], + rtol=1e-01, + atol=1e-01, + frontend=frontend, + test_flags=test_flags, + backend_to_test=backend_fw, + fn_tree=fn_tree, + on_device=on_device, + a=a, + b=b, + test_values=False, + ) + + # matrix_power @handle_frontend_test( fn_tree="jax.numpy.linalg.matrix_power", From a8b7251884a46c15bb58fa2b049a86e7015bd5db Mon Sep 17 00:00:00 2001 From: MahadShahid8 <93673498+MahadShahid8@users.noreply.github.com> Date: Tue, 12 Sep 2023 20:28:19 +0500 Subject: [PATCH 076/117] Update meta.py (#22592) --- ivy/functional/ivy/meta.py | 56 +++++++++++++++++++++++++++++++++++--- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/ivy/functional/ivy/meta.py b/ivy/functional/ivy/meta.py index ab885bb3b207c..840060eb7d61e 100644 --- a/ivy/functional/ivy/meta.py +++ b/ivy/functional/ivy/meta.py @@ -24,13 +24,56 @@ def _compute_cost_and_update_grads( all_grads, unique_outer, batched, - num_tasks, + num_tasks ): + """ + Compute cost and update gradients. + + This function computes the cost and updates gradients for optimization. + + Parameters + ---------- + cost_fn : function + The cost function. + order : int + The order of computation. + batch : object + The batch data. + variables : ivy.Container + The variables for optimization. + outer_v : object + Outer variable. + keep_outer_v : bool + Whether to keep outer variable. + average_across_steps_or_final : bool + Whether to average across steps or final. + all_grads : list + List to accumulate gradients. + unique_outer : bool + Whether outer variables are unique. + batched : bool + Whether the data is batched. + num_tasks : int + Number of tasks. + + Returns + ------- + object + The computed cost. + + Examples + -------- + >>> # Example usage here + >>> pass + """ if order == 1: - cost, inner_grads = ivy.execute_with_gradients( - lambda v: cost_fn( + def cost_fn_with_variable(v): + return cost_fn( batch, v=variables.cont_set_at_key_chains(v) if unique_outer else v - ), + ) + + cost, inner_grads = ivy.execute_with_gradients( + cost_fn_with_variable, ( variables.cont_at_key_chains(outer_v, ignore_none=True) if keep_outer_v @@ -38,23 +81,28 @@ def _compute_cost_and_update_grads( ), retain_grads=False, ) + var = ( variables.cont_at_key_chains(outer_v, ignore_none=True) if keep_outer_v else variables.cont_prune_key_chains(outer_v, ignore_none=True) ) + inner_grads = ivy.Container( { k: ivy.zeros_like(v) if k not in inner_grads else inner_grads[k] for k, v in var.cont_to_iterator() } ) + if batched: inner_grads = ivy.multiply(inner_grads, num_tasks) + if average_across_steps_or_final: all_grads.append(inner_grads) else: cost = cost_fn(batch, v=variables) + return cost From 0a696f26432eab7658e57eadf2fb8e147559fe61 Mon Sep 17 00:00:00 2001 From: Dharshannan Sugunan <94248626+Dharshannan@users.noreply.github.com> Date: Tue, 12 Sep 2023 17:51:55 +0100 Subject: [PATCH 077/117] Implement_stft_functional_api (#22581) --- ivy/data_classes/array/experimental/layers.py | 56 ++++++++ .../container/experimental/layers.py | 128 +++++++++++++++++- .../backends/jax/experimental/layers.py | 109 +++++++++++++++ .../backends/numpy/experimental/layers.py | 111 ++++++++++++++- .../backends/paddle/experimental/layers.py | 117 +++++++++++++++- .../tensorflow/experimental/layers.py | 67 ++++++++- .../backends/torch/experimental/layers.py | 128 +++++++++++++++++- ivy/functional/ivy/experimental/layers.py | 68 ++++++++++ .../test_experimental/test_nn/test_layers.py | 51 +++++++ 9 files changed, 830 insertions(+), 5 deletions(-) diff --git a/ivy/data_classes/array/experimental/layers.py b/ivy/data_classes/array/experimental/layers.py index 14f3b1f841de6..8b4120fd4550f 100644 --- a/ivy/data_classes/array/experimental/layers.py +++ b/ivy/data_classes/array/experimental/layers.py @@ -1117,3 +1117,59 @@ def rfftn( The result of the RFFT operation. """ return ivy.rfftn(self._data, s=s, axes=axes, norm=norm, out=out) + + def stft( + self: ivy.Array, + frame_length: int, + frame_step: int, + /, + *, + fft_length: Optional[int] = None, + window_fn: Optional[Callable] = None, + pad_end: Optional[bool] = False, + name: Optional[str] = None, + out: Optional[ivy.Array] = None, + ) -> ivy.Array: + """ + Compute the Short-time Fourier Transform of signals. + + Parameters + ---------- + self + Input Arrays. + frame_length + An integer scalar Tensor. The window length in samples. + frame_step + An integer scalar Tensor. The number of samples to step. + fft_length + An integer scalar Tensor. The size of the FFT to apply. + If not provided, uses the smallest power of 2 enclosing frame_length. + window_fn + A callable that takes a window length and a dtype keyword + argument and returns a [window_length] Tensor of samples in the + provided datatype. If set to None, no windowing is used. + pad_end + Whether to pad the end of signals with zeros when the provided frame length + and step produces a frame that lies partially past its end. + name + An optional name for the operation. + out + Optional output array for writing the result. + + Returns + ------- + ret + A [..., frames, fft_unique_bins] Tensor of + complex64/complex128 STFT values where fft_unique_bins is + fft_length // 2 + 1 (the unique components of the FFT). + """ + return ivy.stft( + self._data, + frame_length, + frame_step, + fft_length=fft_length, + window_fn=window_fn, + pad_end=pad_end, + name=name, + out=out, + ) diff --git a/ivy/data_classes/container/experimental/layers.py b/ivy/data_classes/container/experimental/layers.py index 49d94bb795211..9b2db319037dd 100644 --- a/ivy/data_classes/container/experimental/layers.py +++ b/ivy/data_classes/container/experimental/layers.py @@ -1,5 +1,5 @@ # global -from typing import Optional, Union, List, Dict, Tuple, Literal, Sequence +from typing import Optional, Union, List, Dict, Tuple, Literal, Sequence, Callable # local import ivy @@ -2280,3 +2280,129 @@ def rfftn( norm=norm, out=out, ) + + @staticmethod + def static_stft( + signals: ivy.Container, + frame_length: Union[int, ivy.Container], + frame_step: Union[int, ivy.Container], + /, + *, + fft_length: Optional[Union[int, ivy.Container]] = None, + window_fn: Optional[Union[Callable, ivy.Container]] = None, + pad_end: Optional[Union[bool, ivy.Container]] = False, + name: Optional[Union[str, ivy.Container]] = None, + key_chains: Optional[Union[List[str], Dict[str, str], ivy.Container]] = None, + to_apply: Union[bool, ivy.Container] = True, + prune_unapplied: Union[bool, ivy.Container] = False, + map_sequences: Union[bool, ivy.Container] = False, + out: Optional[ivy.Container] = None, + ) -> ivy.Container: + """ + ivy.Container static method variant of ivy.stft. + + This method simply wraps the function, and so the docstring for + ivy.stft also applies to this method with minimal changes. + + Parameters + ---------- + signals + Input Arrays. + frame_length + An integer scalar Tensor. The window length in samples. + frame_step + An integer scalar Tensor. The number of samples to step. + fft_length, optional + An integer scalar Tensor. The size of the FFT to apply. + If not provided, uses the smallest power of 2 enclosing frame_length. + window_fn, optional + A callable that takes a window length + and a dtype keyword argument and returns a [window_length] + Tensor of samples in the provided datatype. + If set to None, no windowing is used. + pad_end, optional + Whether to pad the end of signals with zeros when the provided frame length + and step produces a frame that lies partially past its end. + name, optional + An optional name for the operation. + out, optional + Optional output array for writing the result. + + Returns + ------- + ret + A [..., frames, fft_unique_bins] Tensor of + complex64/complex128 STFT values where fft_unique_bins is + fft_length // 2 + 1 (the unique components of the FFT). + """ + return ContainerBase.cont_multi_map_in_function( + "stft", + signals, + frame_length, + frame_step, + fft_length=fft_length, + window_fn=window_fn, + pad_end=pad_end, + name=name, + key_chains=key_chains, + to_apply=to_apply, + prune_unapplied=prune_unapplied, + map_sequences=map_sequences, + out=out, + ) + + def stft( + self: Union[ivy.Array, ivy.NativeArray, ivy.Container], + frame_length: Union[int, ivy.Container], + frame_step: Union[int, ivy.Container], + /, + *, + fft_length: Optional[Union[int, ivy.Container]] = None, + window_fn: Optional[Union[Callable, ivy.Container]] = None, + pad_end: Optional[Union[bool, ivy.Container]] = False, + name: Optional[Union[str, ivy.Container]] = None, + out: Optional[Union[ivy.Array, ivy.Container]] = None, + ) -> ivy.Container: + """ + Compute the Short-time Fourier Transform of signals. + + Parameters + ---------- + self + Input Arrays. + frame_length + An integer scalar Tensor. The window length in samples. + frame_step + An integer scalar Tensor. The number of samples to step. + fft_length + An integer scalar Tensor. The size of the FFT to apply. + If not provided, uses the smallest power of 2 enclosing frame_length. + window_fn + A callable that takes a window length and + a dtype keyword argument and returns a [window_length] Tensor of + samples in the provided datatype. If set to None, no windowing is used. + pad_end + Whether to pad the end of signals with zeros when the provided frame length + and step produces a frame that lies partially past its end. + name + An optional name for the operation. + out + Optional output array for writing the result. + + Returns + ------- + ret + A [..., frames, fft_unique_bins] Tensor of + complex64/complex128 STFT values where fft_unique_bins is + fft_length // 2 + 1 (the unique components of the FFT). + """ + return self.static_stft( + self, + frame_length, + frame_step, + fft_length=fft_length, + window_fn=window_fn, + pad_end=pad_end, + name=name, + out=out, + ) diff --git a/ivy/functional/backends/jax/experimental/layers.py b/ivy/functional/backends/jax/experimental/layers.py index e65256730b950..ffee33d2745fd 100644 --- a/ivy/functional/backends/jax/experimental/layers.py +++ b/ivy/functional/backends/jax/experimental/layers.py @@ -867,3 +867,112 @@ def rfftn( if norm != "backward" and norm != "ortho" and norm != "forward": raise ivy.utils.exceptions.IvyError(f"Unrecognized normalization mode {norm}") return jnp.fft.rfftn(x, s, axes, norm).astype(jnp.complex128) + + +# stft +def stft( + signals: JaxArray, + frame_length: int, + frame_step: int, + /, + *, + fft_length: Optional[int] = None, + window_fn: Optional[Callable] = None, + pad_end: Optional[bool] = False, + name: Optional[str] = None, + out: Optional[JaxArray] = None, +) -> JaxArray: + if not isinstance(frame_length, int): + raise ivy.utils.exceptions.IvyError( + f"Expecting instead of {type(frame_length)}" + ) + + if frame_length < 1: + raise ivy.utils.exceptions.IvyError( + f"Invalid data points {frame_length}, expecting frame_length larger than or" + " equal to 1" + ) + + if not isinstance(frame_step, int): + raise ivy.utils.exceptions.IvyError( + f"Expecting instead of {type(frame_step)}" + ) + + if frame_step < 1: + raise ivy.utils.exceptions.IvyError( + f"Invalid data points {frame_length}, expecting frame_length larger than or" + " equal to 1" + ) + + if fft_length is not None: + if not isinstance(fft_length, int): + raise ivy.utils.exceptions.IvyError( + f"Expecting instead of {type(fft_length)}" + ) + + if fft_length < 1: + raise ivy.utils.exceptions.IvyError( + f"Invalid data points {frame_length}, expecting frame_length larger" + " than or equal to 1" + ) + + input_dtype = signals.dtype + if input_dtype == jnp.float32: + dtype = jnp.complex64 + elif input_dtype == jnp.float64: + dtype = jnp.complex128 + + def stft_1D(signals, frame_length, frame_step, fft_length, pad_end): + if fft_length is None: + fft_length = 1 + while fft_length < frame_length: + fft_length *= 2 + + num_samples = signals.shape[-1] + + if pad_end: + num_samples = signals.shape[-1] + num_frames = -(-num_samples // frame_step) + pad_length = max( + 0, frame_length + frame_step * (num_frames - 1) - num_samples + ) + + signals = jnp.pad(signals, [(0, pad_length)]) + else: + num_frames = 1 + (num_samples - frame_length) // frame_step + + stft_result = [] + + if window_fn is None: + window = 1 + else: + window = window_fn(frame_length) + + for i in range(num_frames): + start = i * frame_step + end = start + frame_length + frame = signals[..., start:end] + windowed_frame = frame * window + pad_length = fft_length - frame_length + windowed_frame = jnp.pad(windowed_frame, [(0, pad_length)]) + windowed_frame = jnp.asarray(windowed_frame, dtype=dtype) + + fft_frame = jnp.fft.fft(windowed_frame, axis=-1) + slit = int((fft_length // 2 + 1)) + stft_result.append(fft_frame[..., 0:slit]) + + stft = jnp.stack(stft_result, axis=0) + return stft + + def stft_helper(nested_list, frame_length, frame_step, fft_length): + nested_list = nested_list + if len(jnp.shape(nested_list)) > 1: + return [ + stft_helper(sublist, frame_length, frame_step, fft_length) + for sublist in nested_list + ] + else: + return stft_1D(nested_list, frame_length, frame_step, fft_length, pad_end) + + to_return = stft_helper(signals, frame_length, frame_step, fft_length) + return jnp.asarray(to_return, dtype=dtype) diff --git a/ivy/functional/backends/numpy/experimental/layers.py b/ivy/functional/backends/numpy/experimental/layers.py index 757e434c7b834..2ae1a98ef67e0 100644 --- a/ivy/functional/backends/numpy/experimental/layers.py +++ b/ivy/functional/backends/numpy/experimental/layers.py @@ -2,7 +2,7 @@ import math import numpy as np -from typing import Optional, Union, Tuple, List, Literal, Sequence +from typing import Optional, Union, Tuple, List, Literal, Sequence, Callable # local import ivy @@ -1044,3 +1044,112 @@ def rfftn( if norm != "backward" and norm != "ortho" and norm != "forward": raise ivy.utils.exceptions.IvyError(f"Unrecognized normalization mode {norm}") return np.fft.rfftn(x, s, axes, norm).astype(np.complex128) + + +# stft +def stft( + signals: np.ndarray, + frame_length: int, + frame_step: int, + /, + *, + fft_length: Optional[int] = None, + window_fn: Optional[Callable] = None, + pad_end: Optional[bool] = False, + name: Optional[str] = None, + out: Optional[np.ndarray] = None, +) -> np.ndarray: + if not isinstance(frame_length, int): + raise ivy.utils.exceptions.IvyError( + f"Expecting instead of {type(frame_length)}" + ) + + if frame_length < 1: + raise ivy.utils.exceptions.IvyError( + f"Invalid data points {frame_length}, expecting frame_length larger than or" + " equal to 1" + ) + + if not isinstance(frame_step, int): + raise ivy.utils.exceptions.IvyError( + f"Expecting instead of {type(frame_step)}" + ) + + if frame_step < 1: + raise ivy.utils.exceptions.IvyError( + f"Invalid data points {frame_length}, expecting frame_length larger than or" + " equal to 1" + ) + + if fft_length is not None: + if not isinstance(fft_length, int): + raise ivy.utils.exceptions.IvyError( + f"Expecting instead of {type(fft_length)}" + ) + + if fft_length < 1: + raise ivy.utils.exceptions.IvyError( + f"Invalid data points {frame_length}, expecting frame_length larger" + " than or equal to 1" + ) + + input_dtype = signals.dtype + if input_dtype == np.float32: + dtype = np.complex64 + elif input_dtype == np.float64: + dtype = np.complex128 + + def stft_1D(signals, frame_length, frame_step, fft_length, pad_end): + if fft_length is None: + fft_length = 1 + while fft_length < frame_length: + fft_length *= 2 + + num_samples = signals.shape[-1] + + if pad_end: + num_samples = signals.shape[-1] + num_frames = -(-num_samples // frame_step) + pad_length = max( + 0, frame_length + frame_step * (num_frames - 1) - num_samples + ) + + signals = np.pad(signals, [(0, pad_length)]) + else: + num_frames = 1 + (num_samples - frame_length) // frame_step + + stft_result = [] + + if window_fn is None: + window = 1 + else: + window = window_fn(frame_length) + + for i in range(num_frames): + start = i * frame_step + end = start + frame_length + frame = signals[..., start:end] + windowed_frame = frame * window + pad_length = fft_length - frame_length + windowed_frame = np.pad(windowed_frame, [(0, pad_length)]) + windowed_frame = np.array(windowed_frame, dtype=dtype) + + fft_frame = np.fft.fft(windowed_frame, axis=-1) + slit = int((fft_length // 2 + 1)) + stft_result.append(fft_frame[..., 0:slit]) + + stft = np.stack(stft_result, axis=0) + return stft + + def stft_helper(nested_list, frame_length, frame_step, fft_length): + nested_list = nested_list + if len(np.shape(nested_list)) > 1: + return [ + stft_helper(sublist, frame_length, frame_step, fft_length) + for sublist in nested_list + ] + else: + return stft_1D(nested_list, frame_length, frame_step, fft_length, pad_end) + + to_return = stft_helper(signals, frame_length, frame_step, fft_length) + return np.array(to_return, dtype=dtype) diff --git a/ivy/functional/backends/paddle/experimental/layers.py b/ivy/functional/backends/paddle/experimental/layers.py index fafd86275beb3..1369a98b8d6b3 100644 --- a/ivy/functional/backends/paddle/experimental/layers.py +++ b/ivy/functional/backends/paddle/experimental/layers.py @@ -1,5 +1,5 @@ # global -from typing import Optional, Union, Tuple, List, Literal, Sequence +from typing import Optional, Union, Tuple, List, Literal, Sequence, Callable import paddle from ivy.functional.ivy.layers import ( _depth_max_pooling_helper, @@ -496,3 +496,118 @@ def fft2( ) -> paddle.Tensor: res = paddle.fft.fft2(x, s, dim, norm) return res.astype("complex128") + + +# stft +@with_supported_dtypes( + { + "2.5.1 and below": ( + "complex64", + "complex128", + ) + }, + backend_version, +) +def stft( + signals: paddle.Tensor, + frame_length: int, + frame_step: int, + /, + *, + fft_length: Optional[int] = None, + window_fn: Optional[Callable] = None, + pad_end: Optional[bool] = False, + name: Optional[str] = None, + out: Optional[paddle.Tensor] = None, +) -> paddle.Tensor: + if not isinstance(frame_length, int): + raise IvyValueError(f"Expecting instead of {type(frame_length)}") + + if frame_length < 1: + raise IvyValueError( + f"Invalid data points {frame_length}, expecting frame_length larger than or" + " equal to 1" + ) + + if not isinstance(frame_step, int): + raise IvyValueError(f"Expecting instead of {type(frame_step)}") + + if frame_step < 1: + raise IvyValueError( + f"Invalid data points {frame_length}, expecting frame_length larger than or" + " equal to 1" + ) + + if fft_length is not None: + if not isinstance(fft_length, int): + raise IvyValueError( + f"Expecting instead of {type(fft_length)}" + ) + + if fft_length < 1: + raise IvyValueError( + f"Invalid data points {frame_length}, expecting frame_length larger" + " than or equal to 1" + ) + + input_dtype = signals.dtype + if input_dtype == paddle.float32: + dtype = "complex64" + elif input_dtype == paddle.float64: + dtype = "complex128" + + def stft_1D(signals, frame_length, frame_step, fft_length, pad_end): + if fft_length is None: + fft_length = 1 + while fft_length < frame_length: + fft_length *= 2 + + num_samples = signals.shape[-1] + + if pad_end: + num_samples = signals.shape[-1] + num_frames = -(-num_samples // frame_step) + pad_length = max( + 0, frame_length + frame_step * (num_frames - 1) - num_samples + ) + + signals = paddle.nn.functional.pad(signals, (0, pad_length)) + else: + num_frames = 1 + (num_samples - frame_length) // frame_step + + stft_result = [] + + if window_fn is None: + window = 1 + else: + window = window_fn(frame_length) + + for i in range(num_frames): + start = i * frame_step + end = start + frame_length + frame = signals[..., start:end] + windowed_frame = frame * window + pad_length = fft_length - frame_length + windowed_frame = paddle.nn.functional.pad(windowed_frame, (0, pad_length)) + windowed_frame = paddle.to_tensor(windowed_frame) + + fft_frame = fft(windowed_frame, -1) + slit = int((fft_length // 2 + 1)) + stft_result.append(fft_frame[..., 0:slit]) + + stft = paddle.to_tensor(stft_result) + return stft + + def stft_helper(nested_list, frame_length, frame_step, fft_length): + nested_list = nested_list + if len(nested_list.shape) > 1: + return [ + stft_helper(sublist, frame_length, frame_step, fft_length) + for sublist in nested_list + ] + else: + return stft_1D(nested_list, frame_length, frame_step, fft_length, pad_end) + + to_return = stft_helper(signals, frame_length, frame_step, fft_length) + result = paddle.to_tensor(to_return) + return result.astype(dtype) diff --git a/ivy/functional/backends/tensorflow/experimental/layers.py b/ivy/functional/backends/tensorflow/experimental/layers.py index 94c60963871b5..da6dc3578bf87 100644 --- a/ivy/functional/backends/tensorflow/experimental/layers.py +++ b/ivy/functional/backends/tensorflow/experimental/layers.py @@ -1,6 +1,6 @@ # global import math -from typing import Union, Optional, Tuple, List, Literal, Sequence +from typing import Union, Optional, Tuple, List, Literal, Sequence, Callable import tensorflow as tf # local @@ -1427,3 +1427,68 @@ def rfftn( else: # return result return tf.cast(result, tf.complex128) + + +# stft +@with_supported_dtypes({"2.13.0 and below": ("complex",)}, backend_version) +def stft( + signals: Union[tf.Tensor, tf.Variable], + frame_length: int, + frame_step: int, + /, + *, + fft_length: Optional[int] = None, + window_fn: Optional[Callable] = None, + pad_end: Optional[bool] = False, + name: Optional[str] = None, + out: Optional[Union[tf.Tensor, tf.Variable]] = None, +) -> Union[tf.Tensor, tf.Variable]: + if not isinstance(frame_length, int): + raise ivy.utils.exceptions.IvyError( + f"Expecting instead of {type(frame_length)}" + ) + + if frame_length < 1: + raise ivy.utils.exceptions.IvyError( + f"Invalid data points {frame_length}, expecting frame_length larger than or" + " equal to 1" + ) + + if not isinstance(frame_step, int): + raise ivy.utils.exceptions.IvyError( + f"Expecting instead of {type(frame_step)}" + ) + + if frame_step < 1: + raise ivy.utils.exceptions.IvyError( + f"Invalid data points {frame_length}, expecting frame_length larger than or" + " equal to 1" + ) + + if fft_length is not None: + if not isinstance(fft_length, int): + raise ivy.utils.exceptions.IvyError( + f"Expecting instead of {type(fft_length)}" + ) + + if fft_length < 1: + raise ivy.utils.exceptions.IvyError( + f"Invalid data points {frame_length}, expecting frame_length larger" + " than or equal to 1" + ) + + result = tf.signal.stft( + signals, + frame_length, + frame_step, + fft_length=fft_length, + window_fn=window_fn, + pad_end=pad_end, + name=name, + ) + + if out is not None: + return out + + else: + return result diff --git a/ivy/functional/backends/torch/experimental/layers.py b/ivy/functional/backends/torch/experimental/layers.py index 9d35ad4f31232..88f9f97a71efe 100644 --- a/ivy/functional/backends/torch/experimental/layers.py +++ b/ivy/functional/backends/torch/experimental/layers.py @@ -1,5 +1,5 @@ # global -from typing import Optional, Union, Tuple, List, Literal, Sequence +from typing import Optional, Union, Tuple, List, Literal, Sequence, Callable import torch import math @@ -1010,3 +1010,129 @@ def rfftn( return torch.tensor( torch.fft.rfftn(x, s, axes, norm=norm, out=out), dtype=torch.complex128 ) + + +# stft +@with_unsupported_dtypes( + { + "2.0.1 and below": ( + "float16", + "bfloat16", + ) + }, + backend_version, +) +def stft( + signals: torch.Tensor, + frame_length: int, + frame_step: int, + /, + *, + fft_length: Optional[int] = None, + window_fn: Optional[Callable] = None, + pad_end: Optional[bool] = False, + name: Optional[str] = None, + out: Optional[torch.Tensor] = None, +) -> torch.Tensor: + if not isinstance(frame_length, int): + raise ivy.utils.exceptions.IvyError( + f"Expecting instead of {type(frame_length)}" + ) + + if frame_length < 1: + raise ivy.utils.exceptions.IvyError( + f"Invalid data points {frame_length}, expecting frame_length larger than or" + " equal to 1" + ) + + if not isinstance(frame_step, int): + raise ivy.utils.exceptions.IvyError( + f"Expecting instead of {type(frame_step)}" + ) + + if frame_step < 1: + raise ivy.utils.exceptions.IvyError( + f"Invalid data points {frame_length}, expecting frame_length larger than or" + " equal to 1" + ) + + if fft_length is not None: + if not isinstance(fft_length, int): + raise ivy.utils.exceptions.IvyError( + f"Expecting instead of {type(fft_length)}" + ) + + if fft_length < 1: + raise ivy.utils.exceptions.IvyError( + f"Invalid data points {frame_length}, expecting frame_length larger" + " than or equal to 1" + ) + + input_dtype = signals.dtype + if input_dtype == torch.float32: + dtype = torch.complex64 + elif input_dtype == torch.float64: + dtype = torch.complex128 + + def stft_1D(signals, frame_length, frame_step, fft_length, pad_end): + if fft_length is None: + fft_length = 1 + while fft_length < frame_length: + fft_length *= 2 + + num_samples = signals.shape[-1] + + if pad_end: + num_samples = signals.shape[-1] + num_frames = -(-num_samples // frame_step) + pad_length = max( + 0, frame_length + frame_step * (num_frames - 1) - num_samples + ) + + signals = torch.nn.functional.pad(signals, (0, pad_length)) + else: + num_frames = 1 + (num_samples - frame_length) // frame_step + + stft_result = [] + + if window_fn is None: + window = 1 + else: + window = window_fn(frame_length) + + for i in range(num_frames): + start = i * frame_step + end = start + frame_length + frame = signals[..., start:end] + windowed_frame = frame * window + pad_length = fft_length - frame_length + windowed_frame = torch.nn.functional.pad(windowed_frame, (0, pad_length)) + windowed_frame = torch.tensor(windowed_frame, dtype=dtype) + + fft_frame = torch.fft.fft(windowed_frame, axis=-1) + slit = int((fft_length // 2 + 1)) + stft_result.append(fft_frame[..., 0:slit]) + + stft = torch.stack(stft_result, axis=0) + return stft + + def stft_helper(nested_list, frame_length, frame_step, fft_length): + nested_list = nested_list + if len(nested_list.shape) > 1: + return [ + stft_helper(sublist, frame_length, frame_step, fft_length) + for sublist in nested_list + ] + else: + return stft_1D(nested_list, frame_length, frame_step, fft_length, pad_end) + + to_return = stft_helper(signals, frame_length, frame_step, fft_length) + flat_list = [ + item if isinstance(item, torch.Tensor) else torch.tensor(item) + for sublist in to_return + for item in sublist + ] + result = torch.stack(flat_list) + original_shape = (len(to_return), len(to_return[0])) + result = result.view(original_shape + result.shape[1:]) + return result diff --git a/ivy/functional/ivy/experimental/layers.py b/ivy/functional/ivy/experimental/layers.py index 5003ef0472ddf..8b8db56bd375c 100644 --- a/ivy/functional/ivy/experimental/layers.py +++ b/ivy/functional/ivy/experimental/layers.py @@ -2803,3 +2803,71 @@ def rfftn( raise ValueError("s and axes must have the same length.") return ivy.current_backend(x).rfftn(x, s=s, axes=axes, norm=norm, out=out) + + +# stft +@handle_exceptions +@handle_backend_invalid +@handle_nestable +@handle_array_like_without_promotion +@handle_out_argument +@to_native_arrays_and_back +@handle_device_shifting +def stft( + signals: Union[ivy.Array, ivy.NativeArray], + frame_length: int, + frame_step: int, + /, + *, + fft_length: Optional[int] = None, + window_fn: Optional = None, + pad_end: bool = False, + name: Optional[str] = None, + out: Optional[ivy.Array] = None, +) -> ivy.Array: + """ + ivy.Container static method variant of ivy.stft. + + This method simply wraps the function, and so the docstring for + ivy.stft also applies to this method with minimal changes. + + Parameters + ---------- + signals + Input Arrays. + frame_length + An integer scalar Tensor. The window length in samples. + frame_step + An integer scalar Tensor. The number of samples to step. + fft_length, optional + An integer scalar Tensor. The size of the FFT to apply. + If not provided, uses the smallest power of 2 enclosing frame_length. + window_fn, optional + A callable that takes a window length and a dtype + keyword argument and returns a [window_length] Tensor of samples + in the provided datatype. If set to None, no windowing is used. + pad_end, optional + Whether to pad the end of signals with zeros when the provided frame length + and step produces a frame that lies partially past its end. + name, optional + An optional name for the operation. + out, optional + Optional output array for writing the result. + + Returns + ------- + ret + A [..., frames, fft_unique_bins] Tensor of + complex64/complex128 STFT values where fft_unique_bins is + fft_length // 2 + 1 (the unique components of the FFT). + """ + return ivy.current_backend(signals).stft( + signals, + frame_length, + frame_step, + fft_length=fft_length, + window_fn=window_fn, + pad_end=pad_end, + name=name, + out=out, + ) diff --git a/ivy_tests/test_ivy/test_functional/test_experimental/test_nn/test_layers.py b/ivy_tests/test_ivy/test_functional/test_experimental/test_nn/test_layers.py index 2e131e2c7ff48..a1cf596950cee 100644 --- a/ivy_tests/test_ivy/test_functional/test_experimental/test_nn/test_layers.py +++ b/ivy_tests/test_ivy/test_functional/test_experimental/test_nn/test_layers.py @@ -250,6 +250,24 @@ def _valid_dct(draw): return dtype, x, type, n, axis, norm +@st.composite +def _valid_stft(draw): + dtype, x = draw( + helpers.dtype_and_values( + available_dtypes=["float32", "float64"], + max_value=65280, + min_value=-65280, + min_num_dims=1, + min_dim_size=2, + shared_dtype=True, + ) + ) + frame_length = draw(helpers.ints(min_value=16, max_value=100)) + frame_step = draw(helpers.ints(min_value=1, max_value=50)) + + return dtype, x, frame_length, frame_step + + @st.composite def _x_and_fft(draw): min_fft_points = 2 @@ -1243,3 +1261,36 @@ def test_rfftn( axes=axes, norm=norm, ) + + +# test_stft +@handle_test( + fn_tree="functional.ivy.experimental.stft", + dtype_x_and_args=_valid_stft(), + ground_truth_backend="tensorflow", + test_gradients=st.just(False), +) +def test_stft( + *, + dtype_x_and_args, + test_flags, + backend_fw, + fn_name, + on_device, +): + dtype, x, frame_length, frame_step = dtype_x_and_args + helpers.test_function( + input_dtypes=dtype, + test_flags=test_flags, + backend_to_test=backend_fw, + on_device=on_device, + fn_name=fn_name, + rtol_=1e-2, + atol_=1e-2, + signals=x[0], + frame_length=frame_length, + frame_step=frame_step, + fft_length=None, + window_fn=None, + pad_end=True, + ) From 56d2b96437fe77a4aba2b8d46586804f0885fade Mon Sep 17 00:00:00 2001 From: ivy-branch Date: Tue, 12 Sep 2023 17:20:53 +0000 Subject: [PATCH 078/117] =?UTF-8?q?Update=20demos=20=F0=9F=A4=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/demos | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/demos b/docs/demos index 690375ff06f29..44432cb5f746b 160000 --- a/docs/demos +++ b/docs/demos @@ -1 +1 @@ -Subproject commit 690375ff06f29dd0d41b6358b339aa9135329203 +Subproject commit 44432cb5f746b9258cce660539ae120526567816 From acc8335cdc33bb7d5999c6bcbec6c6e1a598a9d3 Mon Sep 17 00:00:00 2001 From: akshatvishu <33392262+akshatvishu@users.noreply.github.com> Date: Tue, 12 Sep 2023 23:01:54 +0530 Subject: [PATCH 079/117] refactor: improve function import exception handling in _import_fn (#23473) --- ivy_tests/test_ivy/helpers/testing_helpers.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/ivy_tests/test_ivy/helpers/testing_helpers.py b/ivy_tests/test_ivy/helpers/testing_helpers.py index 0bbd9d3ab21e0..7577e37d345de 100644 --- a/ivy_tests/test_ivy/helpers/testing_helpers.py +++ b/ivy_tests/test_ivy/helpers/testing_helpers.py @@ -179,7 +179,17 @@ def _import_fn(fn_tree: str): fn_name = fn_tree[split_index + 1 :] module_to_import = fn_tree[:split_index] mod = importlib.import_module(module_to_import) - callable_fn = mod.__dict__[fn_name] + try: + callable_fn = mod.__dict__[fn_name] + except KeyError: + raise ImportError( + f"Error: The function '{fn_name}' could not be found within the module" + f" '{module_to_import}'.\nPlease double-check the function name and its" + " associated path.\nIf this function is a new feature you'd like to see," + " we'd love to hear from you! You can contribute to our project. For more" + " details, please" + " visit:\nhttps://lets-unify.ai/ivy/contributing/open_tasks.html\n" + ) return callable_fn, fn_name, module_to_import From cd27e2086003fa598bfdf20b7d41a5fd23c95fcd Mon Sep 17 00:00:00 2001 From: NripeshN Date: Tue, 12 Sep 2023 21:40:43 +0400 Subject: [PATCH 080/117] manual linting --- ivy/functional/ivy/meta.py | 15 ++++++++------- ivy_tests/test_ivy/helpers/function_testing.py | 6 +++++- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/ivy/functional/ivy/meta.py b/ivy/functional/ivy/meta.py index 840060eb7d61e..8cba29098b076 100644 --- a/ivy/functional/ivy/meta.py +++ b/ivy/functional/ivy/meta.py @@ -24,7 +24,7 @@ def _compute_cost_and_update_grads( all_grads, unique_outer, batched, - num_tasks + num_tasks, ): """ Compute cost and update gradients. @@ -67,11 +67,12 @@ def _compute_cost_and_update_grads( >>> pass """ if order == 1: + def cost_fn_with_variable(v): return cost_fn( batch, v=variables.cont_set_at_key_chains(v) if unique_outer else v ) - + cost, inner_grads = ivy.execute_with_gradients( cost_fn_with_variable, ( @@ -81,28 +82,28 @@ def cost_fn_with_variable(v): ), retain_grads=False, ) - + var = ( variables.cont_at_key_chains(outer_v, ignore_none=True) if keep_outer_v else variables.cont_prune_key_chains(outer_v, ignore_none=True) ) - + inner_grads = ivy.Container( { k: ivy.zeros_like(v) if k not in inner_grads else inner_grads[k] for k, v in var.cont_to_iterator() } ) - + if batched: inner_grads = ivy.multiply(inner_grads, num_tasks) - + if average_across_steps_or_final: all_grads.append(inner_grads) else: cost = cost_fn(batch, v=variables) - + return cost diff --git a/ivy_tests/test_ivy/helpers/function_testing.py b/ivy_tests/test_ivy/helpers/function_testing.py index 13c024473e976..729ea54850492 100644 --- a/ivy_tests/test_ivy/helpers/function_testing.py +++ b/ivy_tests/test_ivy/helpers/function_testing.py @@ -204,7 +204,11 @@ def test_function_backend_computation( copy_args = copy.deepcopy(args) ret_from_target, ret_np_flat_from_target = get_ret_and_flattened_np_array( - fw, target_fn, *copy_args, test_compile=test_flags.test_compile, **copy_kwargs + fw, + target_fn, + *copy_args, + test_compile=test_flags.test_compile, + **copy_kwargs, ) assert ivy_backend.nested_map( From 6d603cd804bd972cc0643002a9a44e53c3c11ee9 Mon Sep 17 00:00:00 2001 From: NripeshN Date: Tue, 12 Sep 2023 21:44:25 +0400 Subject: [PATCH 081/117] manual lint for comparison_ops.py --- ivy/functional/frontends/torch/comparison_ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ivy/functional/frontends/torch/comparison_ops.py b/ivy/functional/frontends/torch/comparison_ops.py index eeaadadfbf864..b743b38d135f0 100644 --- a/ivy/functional/frontends/torch/comparison_ops.py +++ b/ivy/functional/frontends/torch/comparison_ops.py @@ -290,7 +290,7 @@ def topk(input, k, dim=None, largest=True, sorted=True, *, out=None): gt = greater -ne = not_equal ge = greater_equal le = less_equal lt = less +ne = not_equal From 52bfe1cafd1486fbeb2475f5122267f904b4f062 Mon Sep 17 00:00:00 2001 From: RickSanchezStoic <57310695+RickSanchezStoic@users.noreply.github.com> Date: Wed, 13 Sep 2023 06:23:24 +0530 Subject: [PATCH 082/117] Stateful changes 2 (#23313) --- ivy/stateful/module.py | 40 ++++++++++++++----- .../test_ivy/test_stateful/test_modules.py | 34 +++++++++++++++- 2 files changed, 63 insertions(+), 11 deletions(-) diff --git a/ivy/stateful/module.py b/ivy/stateful/module.py index 20fe67cbf0878..20ca90b20dd69 100644 --- a/ivy/stateful/module.py +++ b/ivy/stateful/module.py @@ -110,7 +110,7 @@ def __init__( """ valid_build_modes = ["on_init", "explicit", "on_call"] ivy.utils.assertions.check_elem_in_list(build_mode, valid_build_modes) - self._dev = ivy.default( + self._device = ivy.default( device, ivy.default( lambda: devices[0], @@ -118,7 +118,7 @@ def __init__( catch_exceptions=True, ), ) - self._devs = ivy.default(devices, [self._dev]) + self._devices = ivy.default(devices, [self._device]) self._build_mode = build_mode self._stateful = stateful self._arg_stateful_idxs = arg_stateful_idxs @@ -705,7 +705,7 @@ def build( ret True for successfully built a module. """ - self._dev = ivy.default(device, self._dev) + self._device = ivy.default(device, self._device) # return False if not from_call but build_mode is on_call if not from_call and self._build_mode == "on_call": return self.v @@ -726,7 +726,8 @@ def build( # this creates weights for this Module only created = Container( - self._create_variables(device=self._dev, dtype=dtype), dynamic_backend=False + self._create_variables(device=self._device, dtype=dtype), + dynamic_backend=False, ) # build variables based on locally built layers, if v not passed in constructor @@ -774,7 +775,7 @@ def build( # update child modules to share the same device for k, v in self.__dict__.items(): if isinstance(v, ivy.Module): - v._dev = self._dev + v._device = self._device # build during forward pass self._forward(*args, **kwargs) @@ -785,7 +786,7 @@ def build( created_n_found = Container( dict( **self._find_variables(obj=self), - **self._create_variables(device=self._dev, dtype=dtype), + **self._create_variables(device=self._device, dtype=dtype), ) ) self.v = created_n_found @@ -832,6 +833,20 @@ def train(self, mode: bool = True): if isinstance(module, ivy.Module): module.train(mode=mode) + def to_device(self, device): + # moves the weights and buffers + # to the specified device + self._device = ivy.default(device, self._device) + # moving weights and buffers to new device + for key, obj in self.state_dict().items(): + if isinstance(obj, ivy.Module): + obj.to_device(device) + elif ivy.is_ivy_array(obj) or isinstance(obj, ivy.Container): + obj.to_device(device, out=obj) + + else: + ivy.to_device(obj, device=device, obj=obj) + def __repr__(self): return object.__repr__(self) @@ -846,6 +861,10 @@ def build_mode(self): def built_(self): return self._built + @property + def device_(self): + return self._device + def show_graph( self, randomness_factor: float = 0.1, @@ -899,6 +918,9 @@ def __delattr__(self, name): else: super().__delattr__(name) + def state_dict(self): + return {**self.v, **getattr(self, "buffers", {})} + def compile( self, args: Optional[Tuple] = None, @@ -1005,9 +1027,9 @@ def _build(self, params_hk, *args, **kwargs): param_iterator = self._hk_params.cont_to_iterator() _, param0 = next(param_iterator, ["_", 0]) if hasattr(param0, "device"): - self._dev = ivy.as_ivy_dev(param0.device()) + self._device = ivy.as_ivy_dev(param0.device()) else: - self._dev = ivy.as_ivy_dev("cpu") + self._device = ivy.as_ivy_dev("cpu") def _forward(self, *a, **kw): a, kw = ivy.args_to_native(*a, **kw) @@ -1069,7 +1091,7 @@ def _build(self, params_fx, *args, **kwargs): self._fx_params = ivy.Container(params_dict, dynamic_backend=False) param_iterator = self._fx_params.cont_to_iterator() _, param0 = next(param_iterator, ["_", 0]) - self._dev = ivy.as_ivy_dev(ivy.dev(param0)) + self._device = ivy.as_ivy_dev(ivy.dev(param0)) def _forward(self, *a, **kw): import flax diff --git a/ivy_tests/test_ivy/test_stateful/test_modules.py b/ivy_tests/test_ivy/test_stateful/test_modules.py index 84c801221ee81..8970707e44a24 100644 --- a/ivy_tests/test_ivy/test_stateful/test_modules.py +++ b/ivy_tests/test_ivy/test_stateful/test_modules.py @@ -246,12 +246,12 @@ def test_module_check_submod_rets( k: {"val": v, "atol": [1e-8] * len(v), "rtol": [1e-5] * len(v)} for k, v in sm_rets_orig.items() }, - **sm_rets_orig._config + **sm_rets_orig._config, ) module(x, expected_submod_rets=sm_rets) sm_rets = ivy.Container( {k: {"val": v, "atol": 1e-8, "rtol": 1e-5} for k, v in sm_rets_orig.items()}, - **sm_rets_orig._config + **sm_rets_orig._config, ) module(x, expected_submod_rets=sm_rets) try: @@ -373,6 +373,36 @@ def loss_fn(v_): os.remove(save_filepath) +@given(dummy=st.booleans()) +def test_module_to_device(dummy, on_device): + model = TrainableModule(5, 5) + model.to_device(on_device) + + def assertion(x, on_device): + if x != on_device: + print(f"{x} is not equal to {on_device}") + raise AssertionError + + def model_assert(mod, on_device): + for key, obj in mod.v.items(): + if isinstance(obj, ivy.Module): + return model_assert(obj, on_device) + if isinstance(obj, ivy.Container) or isinstance(obj, dict): + for item1, item2 in obj.items(): + assertion(item2.device, on_device) + + else: + assertion(obj.device, on_device) + if getattr(mod, "buffers", None): + for key, obj in mod.buffers.items(): + if isinstance(obj, ivy.Container) or isinstance(obj, dict): + ivy.nested_map(obj, lambda x: assertion(x.device, on_device)) + else: + assertion(obj.device, on_device) + + model_assert(model, on_device) + + # track submod call order @given( batch_shape=helpers.get_shape( From 4f21c7d1b6b39d9577b3f742648820b452b6cffa Mon Sep 17 00:00:00 2001 From: ivy-branch Date: Wed, 13 Sep 2023 01:18:18 +0000 Subject: [PATCH 083/117] =?UTF-8?q?Update=20demos=20=F0=9F=A4=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/demos | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/demos b/docs/demos index 44432cb5f746b..db394709294d5 160000 --- a/docs/demos +++ b/docs/demos @@ -1 +1 @@ -Subproject commit 44432cb5f746b9258cce660539ae120526567816 +Subproject commit db394709294d528601287b0bab03f6dd012ca350 From e119a413b4d9dbff544d62a05832ee5e03cd29bb Mon Sep 17 00:00:00 2001 From: AnnaTz <111577222+AnnaTz@users.noreply.github.com> Date: Wed, 13 Sep 2023 02:47:46 +0100 Subject: [PATCH 084/117] fix(torch-frontend): Fixes torch.pow value and dtype errors --- .../frontends/torch/pointwise_ops.py | 16 +++++++- .../test_torch/test_pointwise_ops.py | 41 +++++++++++-------- 2 files changed, 38 insertions(+), 19 deletions(-) diff --git a/ivy/functional/frontends/torch/pointwise_ops.py b/ivy/functional/frontends/torch/pointwise_ops.py index e04dddf76454c..87955ac81fdf7 100644 --- a/ivy/functional/frontends/torch/pointwise_ops.py +++ b/ivy/functional/frontends/torch/pointwise_ops.py @@ -424,9 +424,23 @@ def positive(input, *, out=None): return ivy.positive(input, out=out) +@with_unsupported_dtypes({"2.0.1 and below": ("bool",)}, "torch") @to_ivy_arrays_and_back def pow(input, exponent, *, out=None): - return ivy.pow(input, exponent, out=out) + if not ivy.is_array(exponent): + if (any(dtype in str(input.dtype) for dtype in ['int8', 'int16']) and isinstance(exponent, int)) or \ + ('float16' in str(input.dtype) and isinstance(exponent, float)): + exponent = ivy.array(exponent, dtype=input.dtype) + else: + exponent = torch_frontend.as_tensor(exponent).ivy_array + input, exponent = torch_frontend.promote_types_of_torch_inputs(input, exponent) + ret_dtype = input.dtype + if not ivy.is_int_dtype(exponent) and ivy.is_int_dtype(ret_dtype): + ret_dtype = exponent.dtype + ret = ivy.pow(input, exponent) + if ivy.any(input == 0) and ivy.is_int_dtype(exponent): + ret = ivy.where(ivy.bitwise_and(input == 0, exponent < 0), 0, ret, out=out) + return ret.astype(ret_dtype) @to_ivy_arrays_and_back diff --git a/ivy_tests/test_ivy/test_frontends/test_torch/test_pointwise_ops.py b/ivy_tests/test_ivy/test_frontends/test_torch/test_pointwise_ops.py index 0577ad06d862b..fbdadcaae9f56 100644 --- a/ivy_tests/test_ivy/test_frontends/test_torch/test_pointwise_ops.py +++ b/ivy_tests/test_ivy/test_frontends/test_torch/test_pointwise_ops.py @@ -6,6 +6,7 @@ import ivy import ivy_tests.test_ivy.helpers as helpers from ivy_tests.test_ivy.helpers import handle_frontend_test +from ivy_tests.test_ivy.test_functional.test_core.test_elementwise import pow_helper from ivy_tests.test_ivy.test_functional.test_core.test_searching import ( _broadcastable_trio, @@ -2311,13 +2312,7 @@ def test_torch_positive( @handle_frontend_test( fn_tree="torch.pow", - dtype_and_x=helpers.dtype_and_values( - available_dtypes=helpers.get_dtypes("float"), - num_arrays=2, - large_abs_safety_factor=2.5, - small_abs_safety_factor=2.5, - safety_factor_scale="log", - ), + dtype_and_x=pow_helper(), ) def test_torch_pow( dtype_and_x, @@ -2328,17 +2323,27 @@ def test_torch_pow( backend_fw, ): input_dtype, x = dtype_and_x - helpers.test_frontend_function( - input_dtypes=input_dtype, - backend_to_test=backend_fw, - frontend=frontend, - test_flags=test_flags, - fn_tree=fn_tree, - on_device=on_device, - rtol=1e-03, - input=x[0], - exponent=x[1], - ) + if 'int' in input_dtype[0] and isinstance(x[1], int) and x[1] < 0: + x[1] = -x[1] + try: + helpers.test_frontend_function( + input_dtypes=input_dtype, + backend_to_test=backend_fw, + frontend=frontend, + test_flags=test_flags, + fn_tree=fn_tree, + on_device=on_device, + input=x[0], + exponent=x[1], + ) + except Exception as e: + if any( + error_string in str(e) + for error_string in ["overflow", "too large to convert to"] + ): + assume(False) + else: + raise # rad2deg From 7944a66b68bce3eeacb8c9824c7cdbd5bd950e49 Mon Sep 17 00:00:00 2001 From: Mostafa Hani <71686115+CatB1t@users.noreply.github.com> Date: Wed, 13 Sep 2023 05:40:46 +0300 Subject: [PATCH 085/117] fix(ivy): remove exception traceback object printing in functional API --- ivy/functional/ivy/data_type.py | 5 ----- ivy/functional/ivy/device.py | 1 - ivy/functional/ivy/general.py | 2 -- 3 files changed, 8 deletions(-) diff --git a/ivy/functional/ivy/data_type.py b/ivy/functional/ivy/data_type.py index 36497f99eba36..b221d538c4b90 100644 --- a/ivy/functional/ivy/data_type.py +++ b/ivy/functional/ivy/data_type.py @@ -839,7 +839,6 @@ def __enter__(self): def __exit__(self, exc_type, exc_val, exc_tb): unset_default_dtype() if self and (exc_type is not None): - print(exc_tb) raise exc_val return self @@ -857,7 +856,6 @@ def __enter__(self): def __exit__(self, exc_type, exc_val, exc_tb): unset_default_float_dtype() if self and (exc_type is not None): - print(exc_tb) raise exc_val return self @@ -875,7 +873,6 @@ def __enter__(self): def __exit__(self, exc_type, exc_val, exc_tb): unset_default_int_dtype() if self and (exc_type is not None): - print(exc_tb) raise exc_val return self @@ -893,7 +890,6 @@ def __enter__(self): def __exit__(self, exc_type, exc_val, exc_tb): unset_default_uint_dtype() if self and (exc_type is not None): - print(exc_tb) raise exc_val return self @@ -911,7 +907,6 @@ def __enter__(self): def __exit__(self, exc_type, exc_val, exc_tb): unset_default_complex_dtype() if self and (exc_type is not None): - print(exc_tb) raise exc_val return self diff --git a/ivy/functional/ivy/device.py b/ivy/functional/ivy/device.py index dd35ffa45aa82..7fc4f58fad996 100644 --- a/ivy/functional/ivy/device.py +++ b/ivy/functional/ivy/device.py @@ -132,7 +132,6 @@ def __exit__( ivy.unset_default_device() ivy.unset_soft_device_mode() if self and (exc_type is not None): - print(exc_tb) raise exc_val return self diff --git a/ivy/functional/ivy/general.py b/ivy/functional/ivy/general.py index 9b11f59bb7bf7..1ee603980cc73 100644 --- a/ivy/functional/ivy/general.py +++ b/ivy/functional/ivy/general.py @@ -83,7 +83,6 @@ def __enter__(self): def __exit__(self, exc_type, exc_val, exc_tb): unset_precise_mode() if self and (exc_type is not None): - print(exc_tb) raise exc_val return self @@ -174,7 +173,6 @@ def __enter__(self): def __exit__(self, exc_type, exc_val, exc_tb): unset_array_mode() if self and (exc_type is not None): - print(exc_tb) raise exc_val return self From 63ce8e5c40d9eb96a815483f7e2c099962b9915d Mon Sep 17 00:00:00 2001 From: Daniel4078 <45633544+Daniel4078@users.noreply.github.com> Date: Wed, 13 Sep 2023 11:06:50 +0800 Subject: [PATCH 086/117] fix: max_pool2d of jax backend to return array of same dtype as input --- ivy/functional/backends/jax/experimental/layers.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ivy/functional/backends/jax/experimental/layers.py b/ivy/functional/backends/jax/experimental/layers.py index ffee33d2745fd..477e834133371 100644 --- a/ivy/functional/backends/jax/experimental/layers.py +++ b/ivy/functional/backends/jax/experimental/layers.py @@ -209,6 +209,7 @@ def max_pool2d( out: Optional[JaxArray] = None, ) -> JaxArray: dims = 2 + odtype = x.dtype kernel, strides, padding, dilation = _validate_max_pool_params( kernel, strides, padding, dilation, ceil_mode, dims=dims ) @@ -234,9 +235,9 @@ def max_pool2d( ) if data_format == "NCHW": - return jnp.transpose(res, (0, 3, 1, 2)) + res = jnp.transpose(res, (0, 3, 1, 2)) - return res + return res.astype(odtype) def max_pool3d( From 8ca3cc40fd634391a19c7c3747b81da350717458 Mon Sep 17 00:00:00 2001 From: ivy-branch Date: Wed, 13 Sep 2023 08:06:32 +0000 Subject: [PATCH 087/117] =?UTF-8?q?=F0=9F=A4=96=20Lint=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ivy/functional/frontends/torch/pointwise_ops.py | 6 ++++-- .../test_frontends/test_torch/test_pointwise_ops.py | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/ivy/functional/frontends/torch/pointwise_ops.py b/ivy/functional/frontends/torch/pointwise_ops.py index 87955ac81fdf7..8258c0e6380fa 100644 --- a/ivy/functional/frontends/torch/pointwise_ops.py +++ b/ivy/functional/frontends/torch/pointwise_ops.py @@ -428,8 +428,10 @@ def positive(input, *, out=None): @to_ivy_arrays_and_back def pow(input, exponent, *, out=None): if not ivy.is_array(exponent): - if (any(dtype in str(input.dtype) for dtype in ['int8', 'int16']) and isinstance(exponent, int)) or \ - ('float16' in str(input.dtype) and isinstance(exponent, float)): + if ( + any(dtype in str(input.dtype) for dtype in ["int8", "int16"]) + and isinstance(exponent, int) + ) or ("float16" in str(input.dtype) and isinstance(exponent, float)): exponent = ivy.array(exponent, dtype=input.dtype) else: exponent = torch_frontend.as_tensor(exponent).ivy_array diff --git a/ivy_tests/test_ivy/test_frontends/test_torch/test_pointwise_ops.py b/ivy_tests/test_ivy/test_frontends/test_torch/test_pointwise_ops.py index fbdadcaae9f56..56d549d44b568 100644 --- a/ivy_tests/test_ivy/test_frontends/test_torch/test_pointwise_ops.py +++ b/ivy_tests/test_ivy/test_frontends/test_torch/test_pointwise_ops.py @@ -2323,7 +2323,7 @@ def test_torch_pow( backend_fw, ): input_dtype, x = dtype_and_x - if 'int' in input_dtype[0] and isinstance(x[1], int) and x[1] < 0: + if "int" in input_dtype[0] and isinstance(x[1], int) and x[1] < 0: x[1] = -x[1] try: helpers.test_frontend_function( From 476d591b94d2e0db6481f8c8ec8bb2304867ff8b Mon Sep 17 00:00:00 2001 From: ivy-branch Date: Wed, 13 Sep 2023 08:17:40 +0000 Subject: [PATCH 088/117] =?UTF-8?q?Update=20demos=20=F0=9F=A4=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/demos | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/demos b/docs/demos index db394709294d5..23d8da0a4d814 160000 --- a/docs/demos +++ b/docs/demos @@ -1 +1 @@ -Subproject commit db394709294d528601287b0bab03f6dd012ca350 +Subproject commit 23d8da0a4d81478830d9f3d562c0e866b86b2686 From 3d0c21e1d2503aa1e6d6a04204179881ad3f9045 Mon Sep 17 00:00:00 2001 From: Abdurrahman Rajab Date: Wed, 13 Sep 2023 12:58:50 +0300 Subject: [PATCH 089/117] ci: add semantic PR action and update welcome message (#23382) --- .github/workflows/auto-comment.yml | 74 ++++++++++++++++++++++-------- 1 file changed, 54 insertions(+), 20 deletions(-) diff --git a/.github/workflows/auto-comment.yml b/.github/workflows/auto-comment.yml index 178124f651f00..1d9efd8caa0b2 100644 --- a/.github/workflows/auto-comment.yml +++ b/.github/workflows/auto-comment.yml @@ -1,28 +1,62 @@ -name: Auto Comment +name: Check Semantic and welcome new contributors on: pull_request_target: - types: [opened] + types: + - opened + - edited + - synchronize + - reopened + workflow_call: + +permissions: + pull-requests: write jobs: - auto_comment: + semantics: + name: Semantics + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: amannn/action-semantic-pull-request@v3.4.0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + pr-compliance-checks: + name: PR Compliance Checks runs-on: ubuntu-latest + timeout-minutes: 10 steps: - - name: Comment - uses: actions/github-script@v6 + - uses: mtfoley/pr-compliance-action@v0.5.0 with: - script: | - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - - body: `Thanks for contributing to Ivy! 😊👏 - Here are some of the important points from our Contributing Guidelines 📝: - 1. Feel free to ignore the \`run_tests (1)\`, \`run_tests (2)\`, … jobs, and only look at the \`display_test_results\` job. 👀 It contains the following two sections: - - **Combined Test Results:** This shows the results of all the ivy tests that ran on the PR. ✔️ - - **New Failures Introduced:** This lists the tests that are passing on main, but fail on the PR Fork. Please try to make sure that there are no such tests. 💪 - 2. The \`lint / Check formatting / check-formatting\` tests check for the formatting of your code. 📜 If it fails, please check the exact error message in the logs and fix the same. ⚠️🔧 - 3. Finally, the \`test-docstrings / run-docstring-tests\` check for the changes made in docstrings of the functions. This may be skipped, as well. 📚 - Happy coding! 🎉👨‍💻` - }) \ No newline at end of file + body-auto-close: false + protected-branch-auto-close: false + body-comment: > + ## Issue Reference + + In order to be considered for merging, the pull request description must refer to a + specific issue number. This is described in our + [contributing guide](https://unify.ai/docs/ivy/overview/contributing/the_basics.html#todo-list-issues) and our PR template. + + This check is looking for a phrase similar to: "Fixes #XYZ" or "Resolves #XYZ" where XYZ is the issue + number that this PR is meant to address. + + welcome: + name: Welcome + runs-on: ubuntu-latest + timeout-minutes: 10 + needs: semantics + if: github.event.action == 'opened' + steps: + - uses: actions/first-interaction@v1 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + pr-message: |- + Congrats on making your first Pull Request and thanks for supporting Ivy! 🎉 + Joing the conversation in our [Discord](https://discord.com/invite/sXyFF8tDtm) + + Here are some notes to understand our tests: + - We have merged all the tests in one file called \`display_test_results\` job. 👀 It contains the following two sections: + - **Combined Test Results:** This shows the results of all the ivy tests that ran on the PR. ✔️ + - **New Failures Introduced:** This lists the tests that are passing on main, but fail on the PR Fork. + Please try to make sure that there are no such tests. 💪 \ No newline at end of file From b54f96889e45c45f22eee75dae79af8b518faa32 Mon Sep 17 00:00:00 2001 From: ivy-branch Date: Wed, 13 Sep 2023 10:12:41 +0000 Subject: [PATCH 090/117] =?UTF-8?q?Update=20demos=20=F0=9F=A4=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/demos | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/demos b/docs/demos index 23d8da0a4d814..8163e5e0714f9 160000 --- a/docs/demos +++ b/docs/demos @@ -1 +1 @@ -Subproject commit 23d8da0a4d81478830d9f3d562c0e866b86b2686 +Subproject commit 8163e5e0714f9690ebffca626f3ce3b943e35063 From c964019723946806d2eb46179e1d4259457e9fa2 Mon Sep 17 00:00:00 2001 From: Vaatsalya <96314403+vaatsalya123@users.noreply.github.com> Date: Wed, 13 Sep 2023 10:56:43 +0000 Subject: [PATCH 091/117] Update the XLA compiler engine. --- ...ful_layers.cpython-310-x86_64-linux-gnu.so | Bin 965920 -> 967856 bytes .../xla_core.cpython-310-x86_64-linux-gnu.so | Bin 5265424 -> 5308608 bytes .../ivy2xla.cpython-310-x86_64-linux-gnu.so | Bin 830472 -> 684624 bytes 3 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 ivy/engines/XLA/rust_api/python_frontend/stateful_layers.cpython-310-x86_64-linux-gnu.so mode change 100644 => 100755 ivy/engines/XLA/rust_api/python_frontend/xla_core.cpython-310-x86_64-linux-gnu.so diff --git a/ivy/engines/XLA/rust_api/python_frontend/stateful_layers.cpython-310-x86_64-linux-gnu.so b/ivy/engines/XLA/rust_api/python_frontend/stateful_layers.cpython-310-x86_64-linux-gnu.so old mode 100644 new mode 100755 index 1ed2a0484ebc9b72aa411d1f0eb019f46c602ba6..ee1c50b955fd64cab48c565eabdf0c7b89e91352 GIT binary patch literal 967856 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≻LXVx-@bvg7k2BPQTQ?zR_T;|OO&yjYcw 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�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

2AtKl=#ib1%Qex3M2&<2B>3Lc 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�=ElI(1VbFHFe?IvNCFUMo~ih#ICxpHV4 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 z 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ⅇ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 Zr;6xgTjBNLQvN^w6U(1C20OQv{{w%K=)wR1 literal 965920 zcmeFad3;pW`9D4ZqEU$x7c{n_(Z(7VG*P077@dItcW|PSMN^k1EWw~GDa>eafk-Br zag0W*m0GRQ){0A8+;PLO$>J8VYSgMxtKKmpNGn8C=J$R-=bk%eP{Rx1x{C(fJ5kgd|xL8ny<{a5B@#MS17!L zT*^-qy~7kvKYe)$W*ON`7Ax5MeCr9NlYW}{q`WPT&Yow_(Mq0vn)&tssc7f`VJI55 z>$5CZ=J#{{EF<+UJuguA(pIwd``kCWor;hx8i7MtN&jN=}KfU?ZIr+TLLMNYo zvJCzvd={DZ|E9cZC*RTmszC3v($Padb$RC^AMNtr{+wp_jd#k+)DAsPdHU&?S8Pcc z^qH||Px67Si@FSS6kz&52PVDw-48Y`KBY7;bjQj+?&E8G;IEI@?<6b5UyJ`KSLxr( zWPmvSb{^H~dpFu}qX4`4rwZJ3Mf407Jgpy%=o?R8cL`6p$-_hqQpkPLR)JA*#oX24$rIlHsxbs7BO#te2C zok9Lx8SHR<2K`Z8UjhF2{*KGgUWp8LTbx19@(kscXV8C-40f0X1$Q?-Jdy!FC4(JS zXVCw58RXoNL7zDp{3Mv6+)0__XRyzQ8T8zcq1>-CwAYOp_?t468_l5S8yV`A%+QX1 z%uw#S4CP*wp}v=8u>S)Y@Iy1m`7VQhUXVe~UK#XXl|jx!8Tjim@DIyS?t~2XJU2u8 zK9`}rsxsKQD1-bd8T5Z3L%G8<@Sm5#Pnt56yDWozJA>VhM|}(MzxQ`V27C5_ad+d! z!5Q>fok31{2K-?e^dEzAkMt@DIGVvei!$KXWYBX~CO^;6ERan zAKXTMJ*e;gzI}a*7b@7bVQuXtv*ygMofnxrH&R>cs|`)8s-0FpxBikD^CIaTp%iE06e??3M6Ys&qhJx>!(KN&YN+0{iw+^XA&E(pL}_}7dv|H+}d-df^%;D zw6s_!%jl-5wG-w;>ser%!&1nOGNHzd*_WjG$GU1%)khlUOmh^OG<(L>In(NEL)2&X zn7MOixfv;X;{3*XQ}CEMb7xIv<)khXSnuljS_X5w3=^Y`GwW*|4h2Ak2{UI*Ri>(} zN1f+PHL{PIGplj(Tncmy)s!x%J`zF|eYI?|sh8DGZMdv<`s5k3S!h<{95kYORLz+d zomn5Qzr22?vOXA`W*9TKo}wLS?5NsFvnS4ltTb-a%=*c5&##|++4%bD^U`RrR@Sp5 zomCmqHIuT0QqV=xc5L+-Q=*Yw3T6*xV5DIh=hmZnTy&_C*-(V4wQe9)dp4|Fc?ShiR1>+(wsN&~n@5aXJ-@1cR&AAT z9z|4cOM+uaYJK(`Am+@S?`ShVIy*9BR{i;t=gwBHM;=0>o510i53Z@2(#62QO1d`i zs9N1JTHDHcI_YlJ56+(EIN8Ygk@|U1PrGoM;s~}T&uf5;Fgm$7Hqxqc^l}pB%)UhB zp9jaTrMa|9j?&8N=UiStclyjZS7^IyEk(?%`dL$FHKwzUpE0#Tdp3uNiF2cRh#)v- z^2~V}c8d$mBNs;$EqXk>Ld>i>w^9p*jGR2L-qF#i1Z_SRLkf)k6Hk+x)7Fq|dfKGf zaK)*Y)lZ|#ftIc{y{1|QF9&z_9GYd!+{v@*omOL8)sJMav@n8#sC5p`n>2Au84#m5 zz@XUCvl=6;1ic6qa=O1;*zEebGp0fV87)mMOv3pyA`RloV33}N4D_ekvu4k^Vz#s} zD~ATH4bPc#S(GhNSwC;;+}hB*%E^(*QoM|Rj=h9cn|IWh=n6`HnlF?n<0&P$`dgja-jq_&A zQ`$q6>TK0&(A7@C2Cbem@0{5)=LhFrvbz$Ivubit$a%_s_z$PBx>q@%C)AZJ7}Jg9p9+0j{3>gU#?5s+I%jhfS_e3#CnJjyZa zs9h#h*PZE>YylZn99Bnch>B`baNY!WxT!q{bHgkQK4WH1Mhm$ue|CLS1U(OJF|&SF zJ*MhuB#K_>SJiXtDS@hnX3vOlj`TF5({%OIG0)G4_-4+SGWCRcb51zLH#Qs^IjZ)g z6HYpz)J+d}qNo4w4;77>auC2}{QXa~jG6v_BkWpwmJcB){znb=O2^*OpUnU?(6_gP zJ8;GfoKOt#9R!#^eSU$kQ+{>L>RIfZPiTCBgD*H)S;)mZaE%9F)K6`EzD5r|aFd{Y zi#_0mU5k`_ zEl-!b^#@g7+r!^`m*UsuX#O7^eqD~n=lrPTv^nK#{L&63r^AB}IQ3oP!SCnDS?a-S zImaOiF!{N{O;5C1l2Oo9#10H;*BfrjrU+C~R zc<`FP&4X`q_&YrK>g$w!7JKk@w=4Wo5B_(KoMj%omeb|IYdJk0{GS{-y&n9QR#mR; z!FS!J@V>HL?fjS{C&z#wbO&w@|Sq<9X(2Zmj|!quk+x0JC*!i4_?c+J@|s>m3*JG|D)}r<>z?t#bG7C zz=PNFi#&MW1|>h>!E5=J2j4wR$?rcwwHvm-)V&HvP2Y)Wyc0SICC9+;t$FdxGw}Nu z__o@E9;VBiA=K4{=A13${ZR~z`z2EN9?bFWeV z>J0oijl{FTz*iagMgw1M;F}Emc?Q1Oz}Fb~HUs}F1K(lbFEsFr4gAFhzSF?}+Q2U{ z@KX)^QUhOa;FlTrOALIMfp0MI>kRy*2ENF$x20p)7`9HR8CC=8Q0t2r&6s5?(n{j%vfghxW;8|wi+4uA>VBlG{{#gcIj~%2{ z8~DMR8P6I6?_5z5YMp^k-^_yF8hF1Ur_sQ>d()sd8TkDS{$>Mjp1-ykczsoh**XmT zfrgyL27ZWv?=!r2Y0t1D|8ye_`PB41A%1&o}VDH1Gum{%8YV zWZ;Voe6fK)#=w^u_+t%xz`!48;4K3`%)nP0_!A9$je##V@O1{h#K1Qg_>&BLqk%u! zz&9EA;Re3hz?T~MHUoc(f$uQzryBUh2ENR|cN+L|1HZ(;pJw2f8u$?gewl$k-N1Jl zcqeod>N*2|hQZ%$;LkMhJqCWHf$uf&l?L86@M8`976We?_*2HrOC^9}qK z1Amo)?>F#Q8+f0yZ^qc+8UvqW;1?M9JOl3>K^xTnoBY2P*u4e*?LTaX)$&!2mB=nh zW%;buuE>B?x7D&bXO(0~m3{*B*3?m-;^9BsM}AocY)yWgN~Jmk&I0S!-vpXw=_@891Z<+g%Ba5!GeAcDU6_IK*5oZN%zzm6cVPy?sJ{y{07m^?n1L_q@4^gtQGXX^po{vu zFauoF--Q|2qW&(-fEM+4VFt3O|Bt%<1p+Ko#|OVFswEzYBBY z0QGla2CAsP3o}4P{au)WDeCXS3`kLb7iJ)e`nxa#P}JXrj~Dm|7iJ)e`n&K60%y4} z15(ug2VH*#qG*2?W&n!%yD$S!w7&~8;6(jhn1Lqh@4^f)QGXX^V2S#>Fat`|--Q`S zqW&(-021|gVFr$hHn~ z5K(^@K2P9lU6_F)>hHoe0#A0~UkQA!3p0R3`@8Uk0++Zj14-21g)bI(KNtSBz*#QL z02B59&Z$3UgD1b^Y=Cy;_z(D154^?$zuVdEEz)=r8 z%LCVY;EO%*BoAEWfk%7b3J+ZBfrokEqdf3X4?M&J=X&655BwvJb3?_j};GJ@D-wxZMM{df;n3aMT0O^1$^T_+k${$pcq; z;L#qq!ULCj;9(y4C=Wc;0}t`QxgI#%1OK?))BYZKlL!9H1ApLw-}1n(df+u4_yrIA zqzC@12fp6}-|c~K_rUEQxYYw+;7K01$^(z~z!e_2)B_Lmz(;xD zp&odM2hR1t*&g`EZJze`z?(eqXCC+i5B!#fgXe|f+k%rq@gIW|N0%wn@zv2B3y7mJ(_R>k_g);lD0p%3 z*Q+S>H46RKioYG4IB7ye&7j9Dre zUrqVZV<>zB^7S2#_00-o3bwqGvJz$XXDAV}a_k!sNM$G`$4c+ ziKaZe8JVoakZ-<4;VnB-(S5CW_P+?Ex*|jSeqoh&M-D?d=Tf=$(g9E4fueSxe3X#< z5tC>*any)`1AJAj|BCDtN)0MZ+CYN$wimN}RQ``pJ`{h6)qKT@ueA?D-cSnVtcm7? zpj)k*B7bMaK86_;vp;qdp_EZ3{Y=tFD|&V){#D=pYyf}jK&+vt>u4x` z!R}i4t>8pEp9V&igYcJ`SZ0RfTTyvevc)W-?-=;~gI|l^uY6Yd{Q$I-v>QD43%e3+ zgx2X3-~j;ih6|kzAFN$9X@Yg_SA6rtY8hPUL%Ix`1oUMz(=;nF=qR>^6^|7bVew4= z%YJW1mTx)~DDY)j@j->aYCB2q{+BNe$YwE8*&-(&hlKA{A=nag%_Fd^~+GRTdtZMW`&tK?jJYzLqZw z)Nw+s}3^`O=fu}jt9CTjf9r=u6&QoBaL*+3es7szz zRNndlxUWO4QQ(aZl`r9e+K(WD)?12##Cne*AvIlx1UHeWe>bW4k+Vm0zNb027@R1~ z<>Yl&k;(5)h|GK>h@{K{Ch()oBBt4P$08w|0VIedr^N(*r@TX>wkPg!GGMBv9pi$9ioF+=(zp`nm#NSf;?G3bY++w*rGtDr%1cH#*dQ1^(9Q zHT+@)3VPn57AWwj$5q}U1^&gMS_(YXX_smRc94o%bU?E9;l)Vv$J%u=&D!0EgxFyj z5_){-GC0?1&JCKg7YQ|Fp??{iTQuiZ&6xvbv=8SY%^yq8XPTw|3<)W{7zs5x#tO>} z&Vc6R#|eb91_^FnvUYV2C+**eG>o>=8JC(A*umHf)Wr(?i$m>H;FS)wOM$04)O8A+ z=1^?~-sn)bDDZ5DnkSP`_N5LrUx8(yvZf7SmYQCQgy`Rl1Pw*~+epQaoE@6;R?WEt z3Ed(~4bEj`@WFb|YR+yXbSw85oV`rb&Yx(`ek7owl&)V)Wa<2dhj8wVgy>m-gy!TI z4)`NyG1HWJ5)#rc)HIxZ6uLf59RMB;amIfXSf{`<|E9nO1r|BfHU;kGcyEUS_jjmE z6?mc3r zXTRp$p*izVLG1(ibQ|V^S&CG}PkmI`q1dql7a*WkEAU>&hBXR&)}b~ja33dcvjYF@ zP&*ZPv6FX+0&jAt>lB!B+PzzWO`y`w1pvj)MKc4^L)nzIK99Y6FMoVMotQaEATEl9uvFaE1C zb-w}^e51g8oM3@E&>7VW6xiTU0}6~jrkE`Sj(4aH3OvuDHY)IRhuWdQCmqjUtiWG7 z)MW~M%E{ZMz&ui6ZGP~GM_o+kAk80JArEO8bM|L~RKE322BwRdz>l0|OtS}_hJ^F$IXjWieQk-sxm0sLra9Lk!Op_cyB$s$ zE|7-(Yn|4y6_|%$gw!0U0qTv9DlkuhJ2oq@Sb@KF@|G#^UknNw9~x}{L^dtr38WQ5j4#bE9_S+$33Vl$6ju=2eG)ufFbGZE+==9BIT20Dbi?sFM?pTA{8** zs7Mz$q$WiwViR<;EEr-Lo;r5#LYhAsdL7d=-v%Vae7y#zZE$WO1FO1KbLOBN-FSIO z^GBKaOjG91kPw;0NN7Knb)dr;(3~SRXAKg1+^Hkg;cU>Hmuk*tgR{-yq%jsFjRr_K zBXXyJ!D5oS6sgD|ty82jl59o#!fBH&iWDFz4|RmVudqBJm@i<^V!BL`N}OB)MXII| zIv_!ea4QmGge6GO#n~K7NyU$x%QWY+nzI`TTArLe24}D4{6us1Bf&F%a!Ok`GIOAb zl+L&2r1SzLv|)=##gC;IYtEB2r-g)`jH?aK8qIluaH9HkNWd~ba|Vb80fRNPOq(K| z;E*~Lsm{7~1)NCixKwcrc|e(U89`+174%5yD6AUf|K<_Jk|W?q=_o~t^rF)!#fpTE za$8{!C7@=Fcxrn$A+2lN%rxC|5fWmX#YpI}rPJVCqB$SZoLxxh0Dhgp*{wM@YEIkW z++uL{lY!l6hvv+K?z*+}k=A+^Fiky=L_+i|LxL{A+65d=+N>IB*y#mlD6SE3WC2M{ zinP|Ta0JirI?cI3I2lVIfpAX7l^&+s z7j-h@wJVqK0mJqSHv5))mFXB<5naS*yBgCiNoSwov`n?b(xg~0fMW(%Ec-Yn?o=cc zds|_Fu-{nN$mg{yq+ICTi*j`xY^3?4t+ybpViqP?!yIJPUYCco=FDfBt@1M@gtOS- z)ZfIV%m5iUfREIiHAv{)={lsf%m$_@^HR;(Y;d+2oE>DK%v&|*5+w9^xYXgKZMu+# z&kuLTgmnUrl=0kFq`MsBZ&4%}&+|}aOQkj#hSdpYzs}*UJ zQ&x>471L~sAsnDow-agpXqY8Pi#t8Ugmi2qc-NBYbp~fQ({#~|n$t#tZAH#424}zK z+@U#n&ZC=31vn102(JE0Xy27DXEC2<%rRnXL1%PC{8<-KAQvK){hTtXd=3 zq-t}KkgByIp;tB?q~b@;#hUX@&AAi_UQeZAml>R0nscS*>_I{sq1WKFHRqR_lS?}M zCo*%Gz>lTpG0i5}9|_U32nn_zOD{G!%QWX{!U>}VkU;gT{-COF2{^Kj_H0n3q0R`? zs7MVYbtuw6hqRa=&|Qi}=Hy<*va?eOwgq(OgdT|c4NhrS>N^1S6V7}jbj=Eo=8u*u76W8I?2HIy0xs%ss5Oe3?^vr& zQ5QSZW<}j`pR#nDqINpeC5n2Z(^N|pRZrpFiaN|Gw?|QzA}?!N1W@dKEE1xB010gq zi&Xr`S*)TBN954%JfBhwfI?YDHb= zP#YC>%3?)rQq*pTx>!+%-~*^Es8dmSqOK!|YUSXmjhcrve>8tS({!|- zAt4P_Y;cwtoB_=_QghZIp}SC>!P%fWFV&pQNN`Z1CT#|1hvvLhb1p$bp9d{9IG1V8 zXEkRx5?W@D!$|{dK^pB~vp9fxSh=8;7CJp3U%*9M9BP@Ou63vZMeTQ}b&7ha(+&-a ziY_dY+Z6RK$I|>e2q-s4sCO0GR3rW=ZQM)kXyZ?i5F7U+p>>y~6&T2w0~sSmgi+K7bov0fyY!^Tr@ z5NshAe$3I&G;{1gLJTI|97yKMVV8H(c}6EG2M;&1$ zuEJ|R^YS94%~u`ii|k|j-r?n}(Z0T7D={J8O1);aZj9uXthdK%!ol`Tog56heZre6 zhn2Vn*O*d~Z2R=LfoWM))c0E}fq@dGPP0xPB)4@yeU1Frt}X+*r0c?qSK&>gGUGN+ zWnmGn^5D7@^s3mffqgtum}j*|fQU~l%(r+5%3rg!c2FS`_C*$}6)r47F@tq6m#~fZUhyvJLExJ4wspBVDe`<`1w>`y2f^*!1u-{Fr<#i=u{Fzrhn+Dq5oj0djn zoe8-u>r%^X45VL`Sx+kA$ptTy04lxuZ+w9N4Lvyw#)GY>e_b^h$rI?4SnoF zF;?V!djO0G%^!Xlnm;ATCS120@&~|^A6$E3VQ`$3la?;V}7G>W zDwTYU(^Grt^S`C0Ywb&@1&aR}y3yksk@KKx)-(Gar}%Lk@rFD~sJ--h$+X#?`>Is! zmEiL0JklppdZJ?T+mQ6iIU=b_Ns1m6T%Pc7{}SAAvjb2)=zlsg!At0C`>}!J*(U)5 z<(ReIleG^qm@vZ+alcpDQpS<^3fAXUX#J_O%b(X|`CjAz0Iw@jf|I0x>w)vaiQFGh z?H4&MmUN{`=Ku&NvK!dUiQIburAn_7IYa&fT&nbsKtb6r(2MN%SZvGc0@_PS^vBLc z8wO>(!QJ1IZ7<3ko)~iK8zQg_k8>~GBUSp5NL~*qb!PTm zTI5dnML1FV#Ap5ut9;)O#lHVr2=xflR_)a z@)Yh^X@V+zq7*(VRXPghN|k=7Tl&U{&p_|5+UAn}O39pHqiFllQ=~=1L@1@+&AkVEoD`-DX zsjB`xTIA0laza%+SrxbO_CxSII5mdy7rAl?o`(r~l08f1q8Cijx&DRM3|x18hAxQ7 zFp<6CRjJ9#ct{hDZ{S>%Dt!!^rAjA4q3Y^X=@0Tm*RzjID|C()9f#;@Sn5OMLEqul zNUHQ+v0nBKs-mN$e%C0oy)D2}0ZP}4dH*1&HP9kedbkw*p#1x9`F9fjwHIR~bxgtj zA=dRvr8r=zJ}4`##E@@gV$I!#$GHtph&p$Qpco2s4NN`l@oA~oYpKuR{fKZpDR$ka zhr>h27|%XZGu;LzjCar&H6IM=s5zGbaFW?ljc(xJDoFvJ6JW^cqQ!@zUg_&n=MqK7 zzxGqFNDYfo%ql1f<=j{j9vXDWAeI-_TkN>un{@c2$%D4D$Tj77IE9Z^qkg9S!k{6Dz73QC9 zP|TQVp6Sb}!s8ZP><^`K_=?I}`$EXV`z`)h6Rd9B{|8zklS3GpoC2uNZ!ctRARyb$ zMyh0E3EtpI*$=*)u44(B9(8RF4n9gx&?I613WTf0@RaZ>nm^u`-pYWs=f5R8&tN(TqUC?kaxVp+X-HD z_kAd$y>wy>QPRuu&eEe$C3yp>F!Mi{Ti&6 z39I?ETKHLCp!vI;=;6C(nckv!4&6OjQDa?^JaK~AG{Zkv!?UKLmC-V+k^MfcjztpO zj5WROd3J{PgkgKAd`u2HOAc6q@wI(9_8F@rmG-H2h~xV@xmIc)+W8~TH0yqDr>~(Jb(M+1n7^6K`X;Luj&ATEx~d~w!Tae z7*nj&y3{IvEJ~2C>0tDccYsc2jznpgON~TsJ9~Db;&WCnzSaH!9ZvVh3(y}iR8N%- zocjnl+ACTS{$g4`PqLLB3?X(d1_f>EdX$Fxf1${hEVYLIUhW~cE~e#EzEJr^g*nj! zt;E?F6XZ3rH_&`5?8E!;x}Cr!D+I<85G9_Qigo#0KSkhc#V29F->7=std(r4?AN5A zT)$Fo9YR(|VX3mUu-fotsUL_KH8Q5=;DDvXusaw5LmdR5O^|%S>bkl%ejW_2QY^ zrvx5jw$Cju{=_twWBX&5_IVZNgH~rKt>&;{+AHjpqSYZvtNVboyWQ~+dqFfuqVxi2 z`_uM$1P$&eA?-8xr%DX)Dq%qh2;58DqT*OQ*gjjnO1DpzD$=hw*3oKcpGH)ey>TUC zz;yfUK#A%0`3ewZ50#v`W284#+#>?E+P}k4uG{BKxBx71x>P#1o*eBJ%U6gc{F1HI z2GV{Bd9~FV@8DKf|CtwECNrko!6troAFZ zX*HkzfcS=56&qUdPpSg{Ebu>VpTTJ*qT)s7SGb80NO2v+MSG z61IjVWPD5Ht|3Qz#fOMzFp|uXY^C`SV*e7IR=3Z-qE>d9GSpu;z`OU3bMSzlxvQX3h%MuuX${t&$O`fhWUuQL8+Wt`ify#VZg zA4PLk{6K}U^M0z8@?$4236Q_&(e@`WVPAoL9376^OX#?39|v=LX(#eCvTK9}*z|6x zc#63Yd~NSZ^3|o_@});mUsTGMJS&9|X!!;~U{vxYPZPx3f+!^77(sj@2so`TnJI93p65%CW}oFWLumC03t7%7NxL_8*l3k1QH zqI^FqxO}=GxUfog2*+GOFuqDI6vX4A*Z?5dWYPrw)@G16-#+pz+HD>HL^H`@crTd^ z=5H-$3L#xP$;Sg=G~{nR4k<=@mlJ&$fZ+1ABAu4%>kKachd?w_->t#ry#mojeX-#3 z?E;B`=81s@iKb$pDT2rogcwM6+?F3L2r*DVIED*C40NI(0)h|&9VUqL1tA8?6~r__ zh=G0(do&6{4AduxYXp%6HIwfNA|#p;QL-s{-jA&2Ste?fpsGw%v7pW|Q9}iFfr;8% zP`@@&+kc?=S`+mzLDiY4Hv~1sL@gIo%tfi4;3b0X^k8ol?8AZ`fNCk;s|EXlVA)6t zJ6*7w1k0vS*lNKJ6-&|D3VWJht6YOBY@uLr#^&s~E9}0&S_vGbA~z1N4%LTO4=n@! z+SNRuFy4pfNfx^PkIS-r$+D+YDgWUyFklF1uXvDgY7e3bV`hM=RL@pq)L!r3d; z>p#4ie5;%%_~#3M>0CaJ*=fbMSS!9h(^}C#(8_wrdShnv;)j)0t0?{xW2kj_s% znk!0?*B3o*62^{a!HHq@v&dkKMjt+gktz8k#?EQ*wU@GU!IyP0^19_SfAR_t9et!; ziLupIi!Y0O4qUe@oc-3{pe$J&dIM{>k6@I3(D_l#Gky9A+B_Y>_?GyVV2hpAR3DnU z&RX&Dpz?|SoU5xxw{|UWg5_Hl99T8!mM(hRb|D3+?*G6P)a#I9mFg z=Unm4sW&k@+F!yO`2mw55Q&wX^AQK_FZRPAt4TC+Bsnl2x}SDgHNAr<<>tvpMbYfJ zkD#gBOMiS{lZ(;u}y;w?E>_z9=jvDi8Z3T_8sH&Gn2LUC#)xf)lusLSFN6h zv|9U8twroFyA*lwPvjiLBA*idrH&t5MiH!I!WFjSDNMR2fzj;ms(r7(p6L#L6{KRy zO6`TlH zh8sUX!R*TWhgt>|a)`htFTS25^=o;N>iO_RBN>${Ar)T`dS6BsCu`rj=^gl@(J>EZ z`TAZk#&=gf>+xU8{}%!eQ$Djg^4Bjh;2N3Ay94dy)ys|iO8D#P~!BpR!bKebRUWiSn(cSb;0?;)huiS zdS!I~y~82Plq66OkRRU(er#_4D#)PM%**Y z-I=`<0z>f+`wmV|kfFrcIiZ%9Ab)-2WGhi_;Uh#=`PjVZ=k~SmWG-%S=?Po8)y%7z zF*FoY$JaVS<^S}@u)^fpt8XaATPHtu_<-b|qE-GTj$8>Ou*qI#m2Z!}hkm^3FYM71 z3*pe;V04<;pvQAG$=>Fb6eE9wM5DQ6@X%;PQdhV zAXE78S2k*q+!KI()>1M-&sspTN|LKm_A26G-+c6<(IV6zYZW=FNm^$Sz(7?bxn|80_H*`yNU_^m2ue=Oie}pr{>sH> z@*MEmW06ks$lR`A>PV(eMyhW;v$5f$&-A?j`~C6H+J53BH&53lT!Y#r-^S{}z6$w_ zF^ymK!sn~QiLo`|#3glB{0fW%a~iDp`ADDBz~iYsLM=sEkue;I_6)TgUzim+mbc>e zRKvQArx>R>!S7X@N3`FvlNASH<&H-&m^u40!JmP91yi~z5{~-Q2l^RF~ga+wS z5gcMP6}eCnc~3`PuEFtrZG}&iCjW%-t!tO@6L;4bnk;Oj|LV4j zKiNMgdTkP;Rn+2RfMm0VfsI3Vf$0`OPm50`LL$JXgYBTKs=8JC;L;f1^Hk{i9h6irkcLL zH9b8Sk#_1W3~sH*DnBWpk#n&CQP=|&Hn1ypC#(xk6xUAk+6pUPoyKw-L_2gE%eYJX zavJ*=g%vkWV>=X9W~RLy%@Fh_T*q~hxB}=ptBafnr1n1-d1g3q8T@WjWVq{h$Br)9 z#&|6>^`%ha>NVo8F26t6@={jih{v0u%hew+uy;OrI9aSCzAW#IC+E|Q9#VYPgTpyH*RzE_>` zD*L-XVpNX55kh$0UOK!^579@$+1-&1{b_!lFLFD^^bKHFyk#!$HgKZxKkBJH~VO|R#g^64H2+XEm1t2*IyR&|t@$evIA z5l&yoI9L2pZVcmVqv@CuCTs{Nt|;Cmc&sVus>1Z#+6l`j`#NSl%}QL650hZoV88qg z*S~8x)7H3Gq5Z8-aQtmq-4Kr3y0!4tL&i_%A?V(`9G>m7??I<=KL2?MkR{vfp${l& zobVav;QFn69rGpjtvVKkFagCyZ|3JgKe``QYL#{hCl;W2u&%^O<#8qJ!|@%+5lp=e z-eet&smC9jkP>P zVMN6U;QPk459$vyOvcw`@j+N-2V-R+7JD-b%R*Q*#ouzn_r7w+KPw@KA+wc0B>js! zC8kH9Q_Uu~S_Wi|i&rM|S+Trv@dND;hV5|tJ29%<{`~_K#jeNGVW?dcW^fjYe_$n0 zAT~$*u{WTtBR*1CV49U-DGD7(HH$3lKw0sDk@>{JmhUgXrOauCd1^M1cwkMIRo)jl z6I01oGK*}o3;vAe``oc3+BH%FZ%zDx9c)rH`dZ$vbzgf5N+y4gp%wmfbzy-$=1+j* zfawMA0_a=Ay9%!peI;7g{tPKLbv=h;W&vR5kvu^#*@Y~oz4WgtXthT}mE^I^xeroC zye07t|6a(5(h_8e9FBmM_iV>F_dyDvflz4 zdy{Cp_R?QD0`I+#ws@hF*p`84uDc}(U*1Eafx${aytaUG!fpX~-zKhibWNiVK=4;s zEkWj67bzukT_snmdU*Bwf&4$!uhh`*Tt~kwrQhPTev-_f->3I_^cy7lg_VA6n%$7e^1l<{-VeCpX!qrIR*Nl4~st4|Fb?loxA80Qsq7Cly|8vPl9mQpLeal zD$jDtv;ODu8g?!3-8-cISL*tMny$Zmg;@4o^SBX#Z&}ZUdblCRRvwXk4e~}Vv=TLl z{4v^GCvAUKjxVy1tH7I~x;~&hnL{W(6^(7#30To%S);ZV5qHWsYj3=V?u`*Q`CCZQ z<9*8~*gH4}x3-A1+6V2+1!r8!DPQMrQTNv>F@(!#xjK)N75yJ7ArN{$G6%bTGB->| z#`)P07rh#W#GJ92tm*wuRSsUSQDw|O%qm|u4+EOI^5hX8KvlN%eZX?!Tat6l`3Tis z3=2A2$^lhn`z=&1xd-^H!~vM%)NMm*0fw zeup*^cI^N5Kp1Abf7uIBkkpUalSd&-M=}HY@%nWa3ys z^#?WWt--ihe$krz=oVaodI|Q)Hzs{GPqmLK#>F}-KB!ROvlG*P%$LoT+huOdK8_le zkIKPy%uqR65y!PL)RX(gxQP@SglF?r1wMa#15&|e4CzRJD0wI3g+@%o2Z>u>L>q_T z0+{aRa2pG|0#&6jIWFD?6L<0y1pLJ z(z|EN=X(aqwnVQ8Zd*~{Z!JXu;lx>1VB6XPe=GYbU-=*ZQ~fO| zY>sj7sUj=!3Uq`J#hjUC}qgBaXUD0UM*(z5V=I1YKz6 zkL-i)c0K4h^ss%7S3sxv*+9T`r?x{8j|wg~^8l^C?s)0Ei% z#4;}j{v(;m_lVi#O{jB9HacBz#~)c+ z?1-aFMjE+VJ~_mAOT8Nvh|GfF(FyHEe?Z|KVwdQC7ejBZQIe+vOFd-8!uU7-wIq`h zgn)4-`}enI`EUqT`r!Gvl!`pqL`*&@RK!g@eeXy8`>u26n;GE8l|AG*5jnt(^~3E@ zvadzYC&qar2VJ#4+U$?9e{8#6>g$g^27nVFPVcl;!18CZT<5UdL6-9E*gs0#Q3JQ; zVhMM5$K_$cw4f2gz}_9Q%Uix8GQ}DZS&9C?3~attX@9m7FZ!6Xd}rieSf@lc!>31- z?tv=6A;lj3P8{2;_*O=q$3Pa{Y>gPwdkZFqzu{Xn2&~sfasLzi$pnmndb0HfMZO8+ zw5STodB^6O<bOB7d_62PSQnkepltmIv71K6kmts z8YZkaoYmW*;l%kE*=x(#`f?HWopAg^V?BI8RboG@C7Bg!S&`HCwqrM?$EKUu~PZE7!jh zUBwO3U@xx2??$&833jy(U+VJC2CusgKf-0W@j@-EiVVg&{0g*g-($v@Li;*z+rlE@ z!~@IVh#V9S(q~FAY5I&93V**%wkC3-!*O=> ze`8bRBr7o*vcJYqg&xn%5_GdjQ>q&R9Szh5aTRLgZ#@AOw`&)(GaSYDJ^u%#lViZE z;{lG>*6(1ngyT0DzsdlI6H>{(`eKPtnitcMMjEnxF;t%=rDnU83e-(dW1)ZSG z9)QZDdJiMOmpy}&KX$Ne@&;d?c84jfnmiTyPJ{{**kAUp%~?chDYR{)Xw;A5w z!$1R=s14fN1Mi@@XoCf?&v|uKiL)E3;;>VEOpS!eNYt@dXFK}jLG0o*Zr1_qpnK-2Yw&Nh7y)rzoOUEm(@l3 zFUiIE7@q8py$3!FAZ0&^Hxm`lpv#QLG`#&IR`chTi6Qp@QyJe;8GqYe{Z9lDa#l)_TBaTD|a~um>Dd%5(z?&R+YXxDFG~PWRz$|SbIA;0l`yfrVgXLfP zZ+euvO#Rkc@j04rXJz?<9Di&pbBq{r0dk;ofFamEE>#)dXiZ(uyGK^mcka2)oGfeV zO4&?J=FtWG`wiN&{H?1$54Nn#s*EqdSoF)v_>jSnB+iih`bRf@^0yuX6)}|T+Oo;b zFs~#3|Gde>Zz~~JUP@#e=P_XWUIiLrAB0TT+a&q1q0zTFp3ZF zdj93+bY;E)p7#p!>;S8XQ8ll%%S!C&Z_T3##hy59##TGTW)Fuft2}#_i2NtACdc5P zbp5z`YJ>D7FU(Rez)gh5Yk0HWK=@(UQ0l+gt|6RIuzePR*#MmRFHyQ5D+DuP1LVAX zJ0VIE;@eIu{IO?HB`bj_AlF8`%V`}A@)?rJE5E}Fem;!6_Afc+aR6D*%F5<>fZ5{N z|G)rh#Y@-VQ5kPSOQ@kW*$GCOA69%e$Z8*cVzO1~r|r>t4ur!Vh1YR9UBOW=pKY0E z-*A&^N2m*2GF$l0KyT$Hp}%!31l!ZWldPaF%x9nKG(7@qNJuhvz$CgCM5Bb2tvK$z z#~yVnxz{E)^>e%p;j&PlKQ!X%75VF2;hM zB^xSHh8Gek2*+G9^!|fb-raNF(L`->>^blqs?Dt&f9zM{zTCv;<(pN=z;lPWA>KEb ziFfBX64?K|ky-F6>jlc+!ilAHd!F0GxU2)8-IKteH>eP+D!h#Pk-eU!AojzBR_yU3 zIEkRW@I@SKC4C5X9-2sr-0dLWet2JPO$*wx+`88q9z7cWR-Q?A}@qVRG)pjRu(9-`WI>Gw;BmXTTOA zX|^QRK_^@Ea<0MmgZj7uGLUt0_0#P%9BsgMNm z9OKU?>d)+5T(VwyhF(`DN+&O(4{eVeXg>|x!yESrC9cWodr)HgmsJVu zeNV|L*|ek}Iw+Kwm5=CZ^hiWk#c|cyk3*kBDVV~8sefPsx5vUD5Z^ao5dZ(J+-pq~IX*Zf#6`l=F^Y%CmIf}1LpaQOt5_#ByHS2B0` zR)6d_OvpCCifFH68(dy7?b1pYvJ!u9hF(}mZD*v;cb4B~*7k}y=wkK-ZK_t2fGgQ3 zq44I^TdYy$FTWE0s@xiZas$i6WK>MLtcBS1u<|TyBYRL{;1lW>#%M#y^g21 zLxYko>`x^Q#Qy961kXIT6zx|>zYKA0nCPUMV#g1WfPSwBpx?9CEB!v91Z;|N2z2cJ zItmc}x5+QTX6rSqF`RT1s+DY{x2g4e+_TTKA~GCR!9Gu7NdH1M#2b-c;36~DrfMH( z3-0L)z5H!|>mOY?+D`^y{RZSu9OY1|yeCvH*+#$YK^LK4Vqt_WIsKOsF>mXd*gsch zO;$)Bmo?l;l2rZ4ei=qpW{-XflRvRQO#arH%H+AMvzyBxM-1cCTTVv4aj(ON5UegS-m`!?1a-{CM}!Rh#eo7bEQJlc)NLc>?e zq!}NJxs(S0_|n+P!{sdtf9oFNo2vtA+fm|_&+SeqyQ2SaZh*jF^Yh;x5yrY z(j?MGY0=+-(;ffpms_#WRJN%GvtM*yHL%j39Mki>ul3TE_JqOBa?r+Jtl}1+~7WOH{I6O_VD^NPxdK2#mNkw%fRfX$NLb{?{J03{8?uCDxaaHZZ z;`YFK+#+4@XN)mUx!8Wijx$<7jWR2vUyUwVKQ7_fejTN^Ur&?m*PF?oxVeTk!HwOE z!-*i|b}cwGEq5YA#!FneUD2=4bl5#o-+rLm;A8`a$+Fq!?yFyS23>R2$|&h zg2{=U+yl~_L+Zg9eIV%r%9t(DZ{$0BxJ@pB(?VFsz7%IA;!OPLg7_200OK4nI|y0g zOxyjfe?yM{%AqbX9O^7O)Dq=T_tK%hK#_fSC_B^p#$n_*3-c1*vBh+-UX4wNNEX8N zsf;u@1E5uMezSzxA9noj4+L%npvF5nB($%SI^j^T?|#!W8?|2Mzf#+k|S%k53BUw%ZiF%7~mUrBQgc@>=S%V&Uc`WX+^-Ielmk_S7h_SvkAa!Cxb=w(r8wi0Q8HBm35c)PxrpnjkgITd|3vFpef; z+t9jPigaOgXW?9D$T;9{JqR5)lsG>}_3CDSjIUh=-7}~A9ZnGLMAA8XL-+_FWcbUs zfSq$D|4rv%w83g|f5+cto#>ARfVD=<#F^7qxJ_91KaJ2_&O|Qb%!2#S*q*`13K@j$ zl(ymyS?j-J3 zVira+9Hn0mS*XrlYFJzoDGiUfnkVPUQq(AHG(EnAawJgVB&8UaqO zvSRN_qkBDyuP5x{QH*5M9+d<{--AanXmve`_ts30!YWOB)Pi~H46hk162N2MAyTV+ zX-gFXsk#s%5$yNkMk866sM86V^40qIN#L+fEVU}PU26g&$zkT@*69B-2F1+4bcNh*-X$h`)Rm%IZk_CD82zT^`C9ItGHRCMG= z#Ek!y!m^4T122)+-RYixz8b(8;y2svI|#5tg%eRY={EmORRGdX`cm3S{|PlbPP(3& zaU>HbJuaf0^g>F&>r;^rl#iYxWu@%PK2|<@CSZI>7e~7v!bd-V4C&u|cMUHf?$q~& zd6sWNxIOo; znB9Z?`?f&lzU)r*x1oA9%RkEV`7Qwd2H>9^B|H^ZA|o8mz7K-~xAe?gx~~mpX}qPo zYgY~^wOV;IDV+FaIB_}bIgLAS2+VoI1}##03(U_wdiOLDcVBYUp%+d-Fu+=XFl=R^ z9vHTedKyb+TzSb^>2E!q{c*&w4~U7KNC4gC@9@L;DhwZO-=czQK^ZFmkz3$J(u1@A zAfb81U_Q#5u=jJ?UJR**{rhSj*`d_?0L#Is*7G;bKhg7d;Njt%(vyD}Ofx_KQ|4QQ z85@2wN*#ccJgx&9u?bW@8k-?^g3JX=nS8t%13qr@vfYd61vFS0ukQwU$=9;mzgEsG zYvou9RZ?TgXHvO=+?|CR`(wi>G(M0+Y3o?@4er~0(}Z4n8US_AffLtLJ^t2SA#b)f zf($#g_5jwk`6I04X`++&HmjZs$ZoreN~u$;Fc4_li^U9ExtsJJsBDrW*_yIXaMX#g zb@~lzf9wh|K}BaVx@Q7M=G3|cIN8idze(MBrL$cyn1v`4Jda~ib!^pI`y+>z&Gufb z=Xe0>d=qyRun1sxDzXxL@L9l{88`C}ZSszB0WU2VC8tXUU}oS=&!ckTo4lvJY2nY4 zzekI?^B-s3aJh|rOmwvHh-sL4zY6ir8s*B&H4p4z>_%0!UjyGiY62w5_*fbLyfVI(=e+H?f5y~_U7FwHlA67`7cz!oFmw02_6R5Di`fdQ z;ydI$V%jr=`{czX>sj4=*-uQ&d^NwS{40Nqe{LL({U_XCb1vZFA|4!E-NVDEF8A#7aNDW^f9shTFL`Wk$>qFQF$-;v;x^tIi}%e!{>pE0DQsPkZ<@#E z!atD0;L1M$TAA=yCJq~2{&KW}r&VhFJr$L~tvntoE%!)|z2#1Ge&eE$rqp*xUCrBi;V_^!U93w$dYY=@YoWdTAEM?@btv(RTZt#x7Kb z@qHLlnDO6)*Vit86$5y%CRvW773F6*M|lQvs>)xHD}T*Z<*!C_a51uV9*5@m*RnH% zE-q&`VH}ZQZo_fgkZ8I~Gk(S;z$+ofO5htlya+h#ctBw^7}}S&0r%yRNgjmzTPYE> zR-IDjBh)K42txdA`zTk2bE40@mh_9yHp4u+J1?TqmyZP^`20vXGMoS;8+YgMqVIYbglN$?P(w7r@fSq9tmF-A3bO^b}~lFhWq*AoA@pw zuZ%tpgR%KwMCFwnc=$eg?(cE+RQtzXRpqZokHhC9kLI3l-$Z7p1C$OgZj=|9$3Yt7fNS?Ia9naK=7<@htPL{;;F1kyfLF>;)HQn+#In^|xlvDe~pMuZ- z0zA&-{tmh?M}Xk+(QImr1Id3o_C~qKpj>wPB%b%PC>EUZ*vBd089Rx zWNsFB`8NhY_-OJH#fO)cG;0l66HYl*n1$pb=dZvQN?e>5l8gQWak-HJRP1S}$mp&3 zS#(9r#vk!RAz`#uu{U(vi1<02;4Pfhe8Jk^dMYIHb&y}d>d9k8jerk*+mHVbOWp!2 zN&iZ@OY{wFQhs6BEPJILWwJ^~LpPqlh0FV+Ns6wfXpAhsLX5?!FW{^sk+ftTRZQWD(TGS7{7`NK zT_Qoo_gRs>a9s(*3e*cF_NY?VIEt;7@3W#o{H=k-5Xf7MH#J~#QF|Wfgp7aJ`C|iF zgAw>vhs3`tMTiW)IMPK#&UFZa>{*QB5pfms9uo@TW@G7MEZR8G8w*KN6)%Dx-*bXrsJBb;We8bu48$!P9A9n^#ebb| z?|+#%Gz=$3o+liPRQB-4&Vv#diS4hUmlm;z!o>iKo7rGolU~k>wpTpx3yfs9Vp|Rs zXyxCV)~{ElQg31l)qa4Tm;B4IhEh}Fpma5ZVsLID9W5ytml$u$rAOx;2*wR_;ePi% zw9@JZe6VAeiw(+j=v@B~ZEpe|Rgv}oqY#Kn?5Loqpi!g7Q4~g_U<{L1a5_w)C@yiw zjlms9P<-8@bdR)c5O>FA#BE$>bQBd?LfCOej6~b?Tf`XQ^dHEHzacD#(2az=X!sOE&l05T-z7t07eXazmIT6d&IcmSNk&-Ld! z{G`4T2cJMHT3&M#t+H(>KK?$AHJ`W#cn|MJo6qqxmj446Fe7Uvboeimi5ea7@Y5!- z(GP>r+DxD7%&GVSI4n1{GpS-W%69=-csEU(JZ61`(%1NOrgAE#z8Jdcn(e|v zs4(k?uIY`o@|>8{Jh-wWjgNX5AMHTVnoKPd4%40%gxPmm=US%v?=v7Y2tgO6m%DU) zd`3Op-MSz@;;y!9YI$F#rC>Pz7=;?8|DefsrYIQ)0cJY?LZ7n=Hg0AmEPJJox4y>RCa zCbXZr_jeqX4ZYN4N=tK-#m#(TliJn-$@%pDd1_=6VhVn`w`@YjuH!!7u%YvA{7gQ~ z&hN)CEWeg!FbpTazNf|OyuPxJI%K;alUoUm4Ml&amIV`i; z_kf}WdC-4}r}-MTib3;0>iot>X0BWXy%Y7?)zSM%Fpx062=0+Ay(?fPBXk+R&Li?< zyDYs+Szw)?r}xDaar8cX!>*A=3PGo*h~STRo{{%jQ+&+MJa%+`m6~X{3KMg&vusM7 zU3x6}Ir{%JqW`&Z+m)!xgm{Af7x2_Kvz@s}-X4CKqkr6aIr@hxvh=?M=sf+`WIAtu zvgqqw^G{%ZzCZj5?9a;-f5Nv9e@BdAE1@HuD!7CSjXNF`=#D#XLOFmBc4T=I>@gjW z@4(1`QWTJaDb8mqK_%?jH2F9fCAja;xV=vd7c!_$KIO>S?4mBg7H8$i8BQ=2mun7A1Uh%&MJzQoXZIL% z254w*2QOdD{`rxLB6u^E|NgU4-7RRV5ZJB!1ebnpL?fj#^)v>L@9>L)cr=Dh4!`m7 z_mXLv2@|zneb-m~UU>rb1~-0cH7yzE+w(QOq?!(-3^bwqYn31@mp=plH6$iH#mB82Gr?9tbODrb*AO({=@8?i?RscOW{%V$g6==d2I z+2oNGbIW4Mb?nhU!tGMHbM|N=XOC88?a{))-X0x;Le$zG-CI3qX^&$4B3#a=aMm7e zka2}QS|tsFM$DKg#Fm6kf7KoxajeOGtj~Sz$pVpRPqIgkQBHCvCE$cTG?>mFov3Nw z*`qPux+ZP^q}f!E0JL9Teue`<0EYxsbB5G&q^!Qkq&c|AzW7Pv#u-v0XpIj?HK{3! zu$8|qtvb(;Do~)dp|VeBM9N+#Tk+H`P$UQPxRub(km@6%lcCw2cOL5gg~Fd~g&y`~ z^7;2P<<-c8t<}Dz_&@wbwz1UAm_DB|qP2NhTUie=#*+Q811qny$KUhi zV~D_`$+C;#g5VUBv2H3dGqrmIb=XpXn)D#n1hmKSA6nv;D~(c(7LR{KNNT7Ix6w%8 zW1D-TfHoI<>U8SS8ez1Ewax!QAO)Y&ijn_&S%iM6C;SI2a1AfRf{^`~>AuxzzttYC zlB$_#X^&cQ|hy*n0peu zPP)C+6 zNt3*uXYKuy_M}-m#%a!C>=%elYi3x7ZfL7cZ>yc)*G3vbkq0Wo2S5j^A|kB>yV$MA z(4vF)e{Z+;H+nCacod_)j@_z#4ASFM{JP^t&ZA{-w~pX(19t0%LY%+dZaqxaT-I*w zM^ibyy|vwXB=~f8>*vVBzL}j{+O0zXKC)Y{1fj^5-)6Ta7&X>^^NA=b)mb1p&HTF~ z^X%5&b@O)XF+8BJpU2PSYZ!OtK{TkBw>7(UQ^Z2fZau(AK&SR3x91UJ`=Tp$>ytF- z?baO-rNJ*#A*Bm#DtGz;_UlWuowZ;4LKj*4^&k>Cg@0iU+*A_mP?wViyHNmz{~Bs= z3V*phN#VaRvR#LrB^~DgI_;$7|G;1pG57A z_}Vy?!Tw*`qu=5~{Vn$B5(d-L@fstw|6lFV@6^P4?9tcB&(Za#t?bc1i`UE^{RJA? zs6F~H_{-Au5~#@QHQ1x+TG7jIw@0h^x=pZ{3XSR?7I`|VZ>}-=&g?*i4MqF&le0(b zNI~o$Y4R|8^cEN(C_2IH(GFQTl_O7Yk6v+Xjy#7u@?1nSjy&(Mr&jjp2?#96$5CfT zq`au}|7?#=clLmr8Ya#J_;3wb!9B1~o;@nw7;y|tIeoS1q$w^jcz-e^0a z7PPY0GhNpuSah_Z#%9|q?L#^3Ws1q_ns%H9)_wd16y;Q+T&!TF=ADMhE4Dz)4W=eZ z*tO)fiWP`HOof|Zdav|0%zig{$C5=nI=gy*5GW_ue5wlfAmQZ9I(Ge2Zk(N6|1TAe zxxz`2z2s5Qizdw<9efPdt$UA0ji=6|MJKm{q54{W@ySsIOkt;WoeQ}`6z8u@Qr=;)4t5t9zP z&})zQrwjHwag#RR7xdzPTxPd0cG|DEmWmu%O3AiWY~dR;ZisL=v3@yIa35fk))g*g z6vWcQIcm2#=uEchYo4~_5c{v%@>MK#E1v;` z`2}_cXd{NJRhJv5Oy%jo5gda9AB%BG($8yIKS?JW(TLd!1<3=3>WH7S^cbE8mh*@K z(g@G9qVQ$bZ*s5k2J;_C_u?z{A|FETbNU%3LN*gxHF9%w1!PZKJ5v|@Us-~XrDH=^ z+DC0oCTU+PR)y7eFGg7+(9yY zpGSNGZB#F92nIV=EKRqDy@)Yb{C!n%3C?boh@d1Mt?UCSG_0r2+CfwgkZQ%|heGHj zvffXIW4z3JgyF79SMF-vDcD9$Ue1K>#xb}RMCB2;vFS30qM?1Mx1s#zf}F2uX04}$ zS12O8C+n~Jnej%*nWPCWWRNsD-wZ8I9xfidk3nFly16R*P^CucQmP9Nf#QQhc#@19 zN8rb;M9s~$YA5nP`7^Cp332VxfR5!kpOU5X$goxKFASvq*~!weWauxEPL@jOC%TUn zmU;7-<-0fI^@hBrC{K&vrSZ2|1pg!UJn;r3>O9%*S_HqHW!b@JR~{x@buQe>PG|Qa^x>a-xzQH*pv%F`{Fq zFYByt4C4L<D9Xc9_E<2Fx^qI7pb+Di7VP_yUWcq8-4=~^lQG3`%+O6LwH(G?h2^BD{<$v@7 z?dN=92~yq}S845bKduTFQtt~nFKgV^TEQ@w7<^vs_gm(LbcW3?Zm)xN>ks<6VVtp~ zpXsqW9=3F_bt9N@N5a`!MAw~I@&?vtC2ZvLX*HI7!k{v5;fvx&hmG^s9m(F?uMY$J zlN({6Sr%DK473B+ISh!E;uGb6ZOyxZ~i$ zjE{$g5xU%{R3T9Y9K@ZBj@8i1bMP0lT`$ll+so5r8WIu$r?%-JEeWBA_;d_c=49#P3QolGW)nU#P`AIuR<^ocoqe3ENb1 zs!VagM4Cx)pjuS01EVl|h*<8Ju)e(3=^X&k(}1`F%#r~RYnZq`7ZXdrp-v7s>Q~|_ zH#)Ux#F`Q|jL}DH@PX?tkfuM+lYcB03|A5fV&M@Wc2F$|2O>vntuYXizswRD-5r>`cSj4L-1@u(=Ck9w0LGMGwcNEsr zcDmdoKPVo&xBm8eAy>Eb0)nd5Gjw;0Khn=o!nkR9+GjNsQo55+afVLH0Syje9u8yGdj*P7@OzZ^LpV{7Holx= zb+BtH8cz6*Pi&u#qtQN~uU5xyR6}7VxjFjhQ;Vzq+KdNKNc%Dr?YEQ7pUu=a5180_P3pbPZguX*?(?* z@0wUr{;L-I(p+PPUd0+*sAxIU;qFzCnXMDK zJ8SpA8bj>et)~6KCmPScN~6?)P2%9$;VX2>+qG=zbNkdhF48~P_V&E~T&4SP6#tW* zR+H%^)^ZcM*E?gWJE%`a&YOS0$Vs=rSGC*krWj*kwOn~<-!*PsBTTutI_>W{J2G?r z7p(JI5=(9l60oT5ISAs6;zQYOaG2&6A3~w=@u6EG48I9(uF;6rHEIvs1h8bHcVlPK zD?WrNK2Ynt`mxT7eMYlduk#vyiLK>I{YQ&wEke{(5=%~`vE1gjMe?L;^IKEgZfEU| z#O8_lV+*FCzL^djN8gXLE|^zz9Q_`EA4P~xtTOTAB1B&TkMl!~oWb&~rEXoLm_&oF zzhDC8HK?hbH73qc{$6>`9xH$O=QeJu+oo+nVMEa#`mwEUhw{XV#vrb(ZttLIvDIy$ z8N85V!GEZvruXz{pWDoJ_qo{`MR+1Ou#Xfn=T$gOwpm5NJ$!9W^zKL5lXs$1y0-Vs zSSpE%Vor3M=Vk%A=yF@Wtzo}8(hshJL|Akc8&yQ^l5L$G-})`f5Z?%Ohj*5KJ-sis z(ed>D9*IV9tVv8WAC^XVBMvgoe;;{O@g}jqK5wO&m!&`Gb;7o#0G!h+5e=FjVyVmc zN~FE&5J>x65>dO<17CC}Vp5a23OUzdlkf|`lK*Uu%D!M_k)o{sQTh<`{4ikFzSir* zPo@r7+e@yC`nx(9rSjw4_tCyQ@lS1<{SFuupTji*x(iJ6B}{{*$(-in)R}_Q4-n^w zW%4(WeoJ>0SzW@fTOs*xlpxPJdzgZuh?F?z6N)&_*~kjXNfd(K`{@>ktKgibbuV#< zrS7Cq{Cly>G3fg7k&CJl4a0RO0ozim(<|Jq4twz`;v|;DiIJj99oAxP*rg6L(4*p+ zUVSrL#(V$Z)<+JjgPz#`W6Og(SLQL%v$Pt?W9a&a-;1tiM(ZPI4zFN+K?Fc7eIF;1H6Yl!%t6-kl`c~ym>2ze&c`>S9LT`d4QS0fNq81A z~wcM-b1Fu~X7UcS3RUsMn4# zUZlQL9YJmw&EM=Asm>)?)miZzr&a+FuT8h|ulbrb05!YVzuA)66G!SvW-SL!=`_nB z(zb>Tca|56EqG=7=DpR>e%QGZcxCD|uk2Lf$e>`qC&2d5=~l^imeM(=>-*Q7sXK+t z;Lp7n*ynlDeIToYIwdcGOw8~8evnZ5>L|TB=tX+TjZVHOzEh^Ou7vCh{G2D;Mdfvh zvEm0?D52#;x%X=hBI`T%UjF+)=~YpBp{&I^)8B(Pcq5E(P!FoRoqXfvrEXrK+<@Ro zUu1w?FFQtFDa*e#o4*z}HH~|H3YB$)+FMPo#WUEicTDLZwMU~TQ{xH#%Ry?CmdOjR zveNWV=0U6Sd#$JZDY^2*Kh)ki#z=g>Fj#XS>Ax&@_=V_uFz)cp|CW!$J_mUAd54<9 zrk_={>o4PJev@D%jhd?@d>Bab@Uc*GXQ=vL7MyB@bp15aWoY=~3RP4u@DdHaaQ%}E6SXk)B}?nc39neDFTq!^BSJ$i?){hiAN#9r_V?Xc{bZsOM1{J~i*aACN(AuP!41 z+_SagV@PY@|F*vnq!+Dv(1wqK41*q`Mm{PXuzdv-*JtAjx4zZz0S zPtwGKcgTYWyM()c=jaN#QQyCOb)&&IZzgyM8o3#vhXt|1ji3q`OwF|94gUR7CuS|U$5;$ud%wU9Sx_+ zH7N%SywYAj(15Rp4!&pLBSOv+gDBxFPYE9YzCTu_s!cE*62O%Z9_#QXSHxKQ6^=ES z7>tDh!l5~Q+NCwG34h_c3v)=kRhwQ0Rl~Y}QkR1Iu@uD7ILCq01NGFVZjx{Y~F@0`n-PRqb~8Gnoe5NPCp;JFGF{`n`belI`D7X z@V!=Y`8UE5F{qcu>5T%>oJUW4BO(x`;VQP=m|R&N6bi0he*TyK)17oTm$sR#mc6Np z%ic6sUNdH>Q4iXB_aE6zS#aY@@*0W?`4J-x!rGgIKWFdV<%}r*Y^kzO*71XEA`4D| z#bt315CxRkRLG)lM2|8PW|@l?L~R}s0stkCiXqeRc3bHIeK9Mk|SFN?!}u5{zc zq-#21F(*_=X@YefDMlEJXs#yXB7F3$UF@cJ6>?M0wpSN%+R(FSzpy@fvk!Xs+9u3`a!WHXMYt=uTc= zUmle0?dFv~pwfg(zG+Ff>*c@iqfTe}FY3Lgm%ne6Ufx8N$eWKI`<-k56-YHa&$pkR z=1Jod({rNwv!rsQoO42`xpW;uPhVjOU9h|*skGTm2-TTT4dvs$YC$jE_sgS~6Z7aL zeAgJuY(2tV#Mn$jR@i}=5AjF%BbM3+A{Tg2A82Q)HHrxS4AB_$R&F-eL1hZI0Ep<+ z;H&ZBiD-+1*Z1)yohf^Oh$B*=}~YAU*&$SV$+RYIqqsFghl4&Lr( z;bl;~r6w~13YunX0Is6x>dcV{eb##nC4tkT`9Atq}; z0)RK=`}i&`9hU3TN9t0mPV{%3xP2FOqB6e|XGEwV-)Q$s>|_%V}Y(vyViSge$IkrIeN_WON8TcX}swJ$8;f+lwSdjaaZWqP7jnkluqHr zRwVV1jEHwhE06SOLN}c>E5-D{I5V$aX(Ui3eB8_~npkqF_p)7qu=2|ulUo=T%maP5 zkw+E{cc~KoY8)Ek>apx&VPdLbVxiBIEQuv+@tx8=^0GKM178%cM%^L)5}~L1cpQx1 z103}9VZUk{LRQ*HPjlZS&}@@!D>_q}Ye!hsx8>+*u~NaY?q;wQ#e=+3uGmBA!BJFz&?a5%v( z;yBkoBEG?yw@dYzh^+Lq)gS6M4X`gdfr-ZOfxFIVnB|zur~xnOTWBzeY$Ss(k^S z2uGug<;J-zcmb&(`K0v#;gW@U@JI00reBFhIR|RhZN5?wQ}{R3k1-~k$^lhVmmn<*n(Xufq{S(Q`Be*BB{BIMPL*GL6;b z04bm2A*_wVdq+OJAs&Qy>_y1=#Vx~f%s2T`KLX8>%CMiwBmz>I`bBh55{M1INSpSG z^a(5+A_VU{XvPj-ov-I;waZz!7g!9#1Xq&C3@HpZUyy?>f9xLzq8viq*jFKnk((!g z3^WYwZ^X%U7=Xz=I0i^`L7Cuq4N2&I{GWnb-Is}9S2|Qeb4fi#^~VaUMf|W#O3GJ) zPgRjbEVDcs2~wEPTD+bfOFmBJE;}nyV}DmCs6uk}2DB3wG0>qhYEPi)FI@bwIv=gM zxfr47!dECV3h&AO)wCEUKQfUxwhIeW+wq~(;v34hGodIQTtpxO85B$}MQB%nRn2un zkoBfdsw2O+Kv-D?NwbVAXD}E1EtKTU_)kd1CrdDHyjEBbco< z=~w*}#29GC5!z=^pnvAJRcctrday33dgR)6!#|>odRL}tNd3`NJpW$bkZ_h_J&_-< zoheY0nN(U&xtfe$pXI%KHJC(#HoWvhL(nzbfgsK8&VXwu{~m@DTF{$iwLMB_!hZ0y zaN|@qFMM0rHimQ+5$&1{r<~%j9!5YU!QaL`a#@@&rzgB6A;Lk|kg<~_@Sstzi2qB^ zObZ0*XUHpL_Pg7*g0#jUmF*A>WnHjlSHPWxlr(Tv)o-#i#p2~yy{?n8M-T`4T6Tx^ zZOQ9S{2&kp2~4v&hJ=e~ia9F?6ULNM;cOMNPWjV7(>v_jy25Y^=ur;pJ9h#=J4o?` zdExtfB(LLj$~*zhFngE7gAxOo^4?~d6#Up(CdGAPf>>&MxP`3gP^xBdK_ok6R&y0j=+<5gm>M6woLXB{ZAKnKE`sc~3IZ~z~m9VT(jHfP;B=_y#n5XTrCtdPk~;L5vt9YhFwj8QKxYtj~aoTpQGr z$A&+^LK=weyD|`uYA!?UcO>Bu{3g_D0)~%*?{;+AiLe7>tFIQ56P7c4`szKf8PQwY zwR#4D!59Y1C`l0>+nn{jbW(fwFrOb#z)RmQ~{mV{@K z1@|POzWTU_G#%O$<^_WQy9mG{7{X38&nJ$QRG5+10zW23u}0=-ZqF%PjF_`rhie{DP6q2sMbzHS%M_qlErL3Id zaLhu{4j(y1#;~Q2HdKBP<#PjMTuApmbKRS2`c9252v8XDWTcX{D)f5o|3!Z7e zdgRaQ>f_+wmY&MJ*-+GtBwgLCRabi#o8^`5>&2e6sw6iAzHI$M4c|2Y?@XpT^K2R+ z=8>HBJ$(E#Co88i*o?c3I!#CR(g~T@aFOI{cKWN;W%iu_5k{jHqi@3jRP1~9rcIwL zs+HhuC_0QJJ=>4?t#@{w74tM4JP5<7DhVdTE&zw8{A6M-A|(!3iBHy?6h8Bd_>H@W zsW^CayNHHJb_pA>-TfENd8p4O*=KbUCc>3kYQK}yT_f>lqRzTs6SSIB%6 z@;Q{{atBRF6$NjzRE60mM-Uyj#?5pw(ir~1L+$RWtSn5S%e3Nm{5R_s; zxIRGbDJmridla*Z!?Q=fjxEI7)m>aY&cPzo&Vy=1nlM(IaJH%2p1Sw`Z}IXgNZ$e1 zWz=FTi;v!if1+(q*3(*PF>`=1D_sPrpG=m8v+xB3=XCO4GVj@!;L~&YW3a1L8oh#H z!j-1*eHmQKut1LCTe5>&PsoYYABQjT!tN_)ID(PexT>ncQ>Zi;H^SGf5z&H%EIlpg zx_+sUn+@_sLUtBezexBT$5SL;Jkp}CqYpq{Bu26@bYkq*r+8_k7lA@ z${anSIf5(+mP`-PRhM8d-}5f&>aG$6WcP@mk){9$j86nmE9Ic2oiu5IaJoDwJw?bj ziy-xNq`>;oGfT{x-f1g`WyA*V_=4umBf@x$pLAg?lBWT2qs?gUx=eNgZEYZO zb+v%+c$u$3Qs@kd0{6?d^xVClbe3!&=|}Kpm+(E*FqyP*)|SVTM@iK_oa(cngnxEbqkG#P(QDH{iccfZZ2qF_k}ja7M9Of&A&qtE)Hi#G`6X<8Z6^x)3N z7^^XCa4W=EX_0@=Yi^mCc5TU64C+!Eq%VDI7-RR*!rG zA64hq+pBwprWFvvTDOe~+BhO}@=8`@(?BO0SUnGt3LlgHAw|M-9tt2z82Xo~vGO@! z-J?dJ!AHXnizQ?M1dVs^Uwb=B};cpY4i zTMXA|6W6mdCmJuXIzH7jqG$Ccv%*E>K_FUrbBEE2Qzh<>*aq1hyuKw?*3+x4{%Z?N z6%00`uh9L>OJ)u>|^Z*!J7V}I*k)c3trQ2 z*J>pw`*;#s|9pOFRdxFH>fQ@tqv3S<;y*$Jam@31kQb&iJ151GcvAE+JE#)6y|^kI zjsA)FzyQ>7!8~CA7V^|DGaUP`wo5hUU-f2hpii9#^z3RA-&Pb{+D>HkZ$5^i>~wC? zTX=vGO~GBf)maK-iq!9?O6&P3B9hj8Dw+K!!_>CV?8{d;bT6TMHYAZes4&8zRCf3X7 z4>t{BYjAe-s$u)8t>iU^X6(YOTxY=s$`03CtPT#m*bVWCWa)4Q6X?N;Q`Mw#tDM#Y zkkkb=ZH?%TeU(m`g?9x6@oPx4^I9}ZjW)V-eyhcd267?j57u>x2c*f?1Ppu$v$N?; z_>Qc7KOWKanDuBz{zIg>4>lcPddTwNhmJ<)wsyqP1s7!*UCgRR3%QzFdp3oS(L6tu zf?^~ZJmeTlP3B!VU)Wp)BWT&oy@(?1;4@mUtr#`~TDOzSp!MqBGokhQQ1R8JM#0s+ zLA2XpusZnkBExCwDG4(!VqEfm7MJ1E~@1+Ig>bOLmD-_+3k z!pj<}uOuZ5>Ft5eOFJkqqB5gGGcaAeFH435nIhuT+7lKM$jts=9+faCy*;2QaW#O3 zXY&xno7Q)J#bXe0i}Ne)BE!zF_#Ts3L~kw%wK6Et-OdN$aY&;3AjnI(LlXr}e4@t@ zgud(LMXy)J$N)*Z}Ojy&f zk1m94AG`h@t#D}Q{L`DN@Abr)?e0!&GPh6jqP+E_nZwK6*O|jh^g#88TUegP4=+|u z6%R}+S6QN$U;E#KQ&m=$G?W%!A7 zJN8ucBd5GSZ8FSg5*j9}u>-42n&$$?sL8$Ig)xSQdV0dy$)4Jo6mOZ+){nVgxgN zwSw?=*K%fyM8i=Qb)Te>OK(HmZ+J&Jiu-mDjaD&pSSR157FoS{yQ1G}0en|$k@WzX z@|Uf}h52RQu(EQ9vt5JJMr|YP17x};SyV$uJitG05#ck7C!H5zJ1@<2*LO7)mFKQC zJn>XwPlhK_{gq1H2A;Sl!5unxJVeT7kt=-*h3k$)0jq0rEquv(A*g;7HQ5>YsXes# zCUblVG6kyqPvSp6B)EfS^i8&)huyV*<$?DoO&n;!N&L3GwmSVsw(IHuwD<8EdPGj5 z?N*31Y$5x(3_Rpq2<7p=E`c zf`j;gQ{#H@ldxU$8q+a`-c0!om{Xz-EtDTkKbmpA&om>zmOgR+sR7W$rF$>_ET9aO zcNj2o7APkb= z`A^Uj6rR)EJYgr8r15%Unm+E`P&6DN&BmoOW}qhBexW8c?UqU<&=w@wzeE=b8rm_z z^d={guZc|OU1Tq6>+JI2013jl~_6vG5+WdlFpgV!?2Qk;eSVk$I}B7RoTKH zGOOuEBSg+Nd|)C+R}CR@^6uBH*8M7rai6!4J8C{oS=CsVtx-ph>zw>ST43)0z)`w- z4rok&(DP+~UcS!1JM}bbNY_!m@jRk6VMtwh@^$2o1>8%-xL<(Pm%Mf0|mcH za>SteVSCw`V+SmG#*Y^=WNZPBiu4SDtM!-g{^VDH7*9H;LA)_0F^DkmV&^0*3cr)i zt>Xg?ol1j=n@X9(WzY#ou{VZwHYJ=0esm{evL4!DXY@}Dx&>n&A}*$)Yv2~cQ5M^@J7cQVLhQ{@=t~$p~99a z;9<0NYSBJ(CjVNS>%PLaGr|)AKzOx+a~r* z=i9LsJo!I+21CY2Y^H-^M-T~*K3Z~Zj^5OtE}Ot~_u@WaJsmPP96_&g{8#X}ssBY^ zNwc*>EWd)ESbF&%GEB~#W>NNjZ8n(#!a2W}5sxOaJ?xdvptvLwu4k6G!zr#P3vOyF zCDKiKds9+Vvu2=+WLfFZOXlq0Lkbz?(2Y^An?Q8NUZKs2Gq ze1$>M1XP)%%Lg0Ea|%g@I@G65=+T>N%CAmut=9LGWMj(T47Xu$RP$Q+n|n%Es}*)~ zr>A??u>Di%kHXJQajgeaz}y^^@PTRbZr!`#!+5p>FARZdF+82Gvg@l12e9>LYgL4| zM_ZlL_kJ*!yznTOmJZUSbT^D;tu>qZvFxVNrkT zBLU+MMVPvZVQwh;c`8kg7iXTkDejObCsLrHXnRB`wi3GdK%Q9B@^%}9eO=>GNU!(_ z;W3nV>+LJLy+l2>d3+?Z{ZmTVcW zx&p%VoV+|j^jO<_bu2lJv4Y#a{g&~zixS;DjHk1?|I)C`)o`&W{mg4Vw*B^`uHiRbPIir--VyQhw@8)YL3heN51i z12vPf{FzuZmYv4am2JEz~tTs6#yB2gL5gX#}-TxLs4_YXmx-ABW+GT z4Ay^qn>GXCyCg&Ayb&YdjJn1Y{W}qIypa3}_H{$$aW6?{_$>G<3=XSde=8UL zc`@e+h;`&*J{>x_IG?6H83sIRi-41hpV1+a;YQ^mUCf>rvtcs)8>QU+U$3i){4K_w z3?CtX!&35(NCroS;~>`@8TLhcm0p)8CA&f!St)q~?fQ8GgL{(|d33mHIf)&h!)>z? zR3|026bwofy-uEddsmO}lgcpfW?Igr*vRIpj z-P%fAU4D_gKjyCqR{r2c_BNWHoXFmi>L8&GFPP*$bdnCTak3l28B7u~dq_X#T!ST(+Dkb>vUDUy4qd zK)nqGSMd{%-G{bNJBL}J>Bxq@Rc*r|ro6Wa28z6h@SScOLj?FsOZiNeOpI_#q~DwV zull=Ezv+RigU42rKHCh7R?HiAv8mPMGy9WO?$*d|XbLxh0)s7-c_xiU@N@nTXJv#d zcuD=XF0=;D%h4mZGk1e=o%uNVQW?2g-@O2t8#KU*kt!edSj9A+liLz?k4w2FE7J8o+2gO#r=UDoaavj)~wYa{9~!jNCSjQZmD9zs`d{ zs9=0_2HKIXpBn#p+@u77r%$CT;Tdk+CC-|Bz9y!yzsoncU$s8h(*iQ$nnd_Oe%e{X zYF@bS1_W+oZBPs8l&5Ql$9utz;qeVeM;35t4u&(N0_jTDy+#IhCtqb@%PODO@MPz6 zjYFY_yR)JdnW)~?!HN~G(n955;_`#@Xvy$91%x!#3BSi%R~>$>zh@Xu>uDc8x*`$w z%B7*_82`2n4}>@w?+GI1#$m$yW>o0L57`o)OHUmAyYi1XI*?Ok9HrxpyYQicvbGzs ztw2g?rAYWM4PT4?4WWPG){f672rG^4)!yG|VN7rm^rG?omesBK?M;ft)z+XD2D?#- zaaOp4Q*K;;5q%eEsRchqe0zQN`~FzzHRui39DTC z0(eG9)9KMIkQUBDszl~#KPofx^Z`u(`TnTA?TuL%Sg#{^ZH7A;l8r&f7O#)g*O|(g zY|e(xi%>9`oj>6xc;^ziE^Mw$6+nR#cBbo%w`&}>Vkub`{C==T&;cSxZa*>(kj)Y^ zf@`EFjXH2S{DLRVq>fSIjjC(%{nTYV-h?=f~xOr*{aYkJ-gOc?X{hpQ00zs;*Hz$)^!o6k8OB#{#;VoMuf5HP)j+KoaZ`k=F$1 z>9Vux;=5`OQ}}1B*9iiuu`gL6^0zs79_~|jNBHg@ZjGpr_3Lp4s^Ei z#DFIOImeSvpXPXSp}yM~PudcDxq^oV8My^j7Uz`X$$+oTO?Ehdx?^@u0xWrS)-DA( zM<{`Ftjr3%O=(^k_t(gcmcMu=D+10c9TMz9OXgIA`(3r0f;)XH<2rbdx*evMWWA_1 zY>=pg+GNRjL;2er+TpsV^;cuTpOZZZn)SDSKCM4=y88$#-t{xph3*-|!JgJMu!neZ zn}fSZx=F_T*Xy)DQ0+GqJ^u_WA}ylvD@I1EV;U-Ne-a*&mOWsCUoC^9U*e1{|yg1CeXbUj69 zv+?D+Tzq*@F1|d(#g`Rxw&1cN&Y6N)pSpTX^sAm;(jR?hkErr0GS9NiWNE!J`BPzb zyb)P`773q=EdK&d5TWn>TSf9B%PtC`Z+twNO0hv*lP;dj`j%Wgxy;9tOI$p;4UJIk z-Kusy@#H!2S0C&_x1FHOF-q}r83vEL{ko08=Rpx0cV}Aa2R>#8Nto-+XrUOKTMj&UU zAIV6UO2L(#l~&BI>>M>!-D1&7P3D>UHtH&iT@J;JKuokBtJW#M>-sP?;miC`aWs$A!(_O-=VuTCK8q~&Sjq%mQ{2%~l8tPhzM^MP^I@{Yz; z#psz@r(yBZC3rJh@Rh2YML|_pm96fvTy-}aU9jk*0+xhHw^BSSTi-;;Zk_tJlD$lQ zx$tit^+{{}kiU&|=pcsVExU!tx&{o3NMqk%VyzrMm)y7+5v z^&(Sn@d6;qlt00b*glat6?r9*VDZ<%jDIfv+Cz#kiZrq#{D^7J$6vKgp5hQS->V-4 zaZ41$t%~BWvdc;WZv+8reDZRDjW_lN8qvh?Emh*w&)0f8nE9#m6R6GUm3=Kl+Vv5p zll_ilI{Abz^7@x4`1eN!|5yAZGTr9u2Qb1)rb7HjobL^(*Dj@ui@BeRS|Aupj4hs{#^#4mS-EA6@cL71`(@^kW66wpS87DD`ZhQM*u0!YCZ-5eD7bNKU? z5Z#z9x>9OPrr?tK>UHz=aWU?1|Be2T-K z&i?&h`AQrtuFIOlI3xdmkF;&5Yl%=w$A(Su+0H{VAg%xgQJd_(@` z;a50c^Th-&*5_-krr8bqns;M(oUb{}Q_k1Chx0Y-{M;zy@eH_wfL%mRIyZ7J>rI|7 zwG`E>2l?DD@tK>{Qa=eO=iKf20Tj?sbpAhRFc**8ajc*&N|Wp2anpGs9(Ne5O+4;2 zas3HlCq%W`FStVYqMmF#uB#I3jmO>ci7c}g@wnCVsKw%O|Fowla`L`Xsa@d9Y~*Ad zaEn>H8WFbeDgSyr?iurK)C&_?`dWWnZOl6Hw1`8GZ{MKIv*m;5!ZU+X#*LqEOFfhbl$&NQQ7xrlB zPX2~v9NQ#488-8jBg6H?(_Z^Hj|^|13w|C(?)>|mJUV<>iQgDcD_oGJH^tK~=2Ity z_OvGp^Jx7b@w8gyBxftZ1h%sX)f9lMpu!Et)5-v|P4FO)i~3zy#);fUycLkpi+7vk zT{*TzJZ;+t^s@6n{TN-%r8(kh$4caGG@jN2k4U7S{MYf?51CQW2PO)%-#}kG(sNH= zWjtn$mY|4N1WOQ~V*oV$OS5ERGC7`nx0m{)?cZ^%;ER)|3YX&YqD2o3C6_hn0+Xv5F z;{_73mzgU*scukRi@DM*DOiIK+sE!6Ktd6_o666+vAds5C#zRI!L2Y2UV!jByrZ(O zIG<}SpGs4Ila?cR5P;pNYr=ti)A%mXIDN1iLmKor3@NzQ$0ZI@flnyF7fVp9kd|wh zW}MC(6j0Qwq{F}Ct0Da7;$&MG^UetGc5nz?qtJE2cZ~;ti+IRwx%po2hv@yQ79-KP zhrO|1B6yX)*;G=8ulVG>(Z%D9m4GMx)@&P=f}#0hE?+&*nal3nqbADO6<)gQ*Nos) zfu0!pV<{LKnn@&-_yoVU(TA()0AEx1e3buYP@-~AlMV($Iq}JHAd%%AZ4xet@yT|Tj^QUnK2fHg5;U|g4LQ7pvvc|2 zA3eWo(3dvQ^Sg$+=v?#zURJzoyqCTJk_ZRb2pjp&^ZA36-^=C48{^i>NwgB)Z&6cg z*WzJ~MpKrK+I#0c-`;|VE-GJ^sl$E7XsF=sReplMpQ!M~YxGU8MVEDFF5~r zgD5=5hNFu1p`-6?|D*gy7nh4S9@<8ube8&dTCN_A!;p8} zILxFAVMVr{V1Umbul(Iye()*-Y4YY82&8ea2&02_-SLm=>*BwAmrueEon0rqwDI|K zlt0bo8(u!O3anqoziKc`SG{uhzsMNZ%4Or6i(EX;>iN-N%+)hDm(G?ia`co^C{(o0>4n(I2+;CBA{!Lj8{@#FIEG}bQ87C!qmkcau~KgE!a?Ij0%+MliFUH1NDweDjnsdr8K z?QZ{*nxoXnrYgOeYtL?P*gQQK&;$Esk$&0#^$I_n7^j(7fBI;QelrD&c@^8sIq;Qj zUuFts=nIl#RZ8ysZMNj0q1}7tvKskghuHOfx+8#V%^cn%`@MO(qN=rR zENWez{%AX!oc$gQ^Y>ps+M*G5cMRc)pV*+Li-KtLQ>M3;{{EWGIr3ZGE@6{70H@mW z;NOT?RWa1qr)*C&n32mkMz-@%a1<4~(H9zFg^t)wGe9j%>z8pcUHPLJTXr$s>$p?t9$rmIRArBa#ihqFXn_*Rf1=gs#wih z4{9}puD-V+ChsrHuWyv$pZZq8mI6yF?r-|5c~V?!TH82OK?njjxqJJFQbZ>>;gi4) zQB>efI*wqFn__@9&O=@FFvuSK1~Fa?v9!8Uwtap`Vp2 zBD8fD4%ukG*ilrINFNJ15q3V=9B6+ORB3842O3jpZpr%=t|IZe-{Q7?XE4B)68nA_ z_&8MNf!@nimZ_yCw>$6D{H}ih4SUf4WnFIp`biNa2Ei|afS3Yk zx*^wSUR$57)7D@3B#Y+fp(@|j(d)J~yG2_slk#otyWY0yyBU%)SJAKI<*XRB6Q5+; znz}iOif%W|l4|`K313+?Hk}J{U~7)+y02sMy6B9~-Kf{;@<-W-6OTcYv`WjT$mYtc`+b8!^9hqluKsys_k7Fl z&!Y)bM4#`j=!eZa5-9@+xy6!+lBlUSS~Ugxcy=m(vQ?WIKGIeW>hdwZo5^uIK$Hhr zmLJ*bn-04~-*B?6V=V0#HpYTK+weL&3IiE_MAvOIkT<=Da?aB4L2oTvri}}BH(U=v zz$w0_|8y$0_J?r--<<8Jry=k9ZlI}2b0(Zl!g!_)+R_n+$~%p#y(Xh0CNV~YjX|;G z>-u!$A*GD`&+3lbI(V6-{ z`^uvxi?+bvbU`s41Q+ON#jlBbmN2o{ISEcqr~4t; z)mB!7r~7g7ttYo&bZ5s6L$v5YqFc3k09M*y9EvEpYY}5>3PgTF?ei;Eqqsvy#AJ1+Vi^-w3nr|zU39r~bVIp5-fJ(MJFR85H9 zMu9#mzV2vT^;;}RNB?H=rn7skvdQ-KQNFI{r6pwICAFDztR{2{f+e0?RFh(`MNo|@ zazo-^UaHD71y{YUpKg8h)4Q%qEVYCRxr1?cG88gR9$O~FYB{j&x-9SXhDN}StuG0# z=_D!RLT#d^0Y(`=nC7BuyN_4HnesB67g`Tf7#zU|Dp;cb+*!#5G*F`h){?b63H%Z} z>=gc~=DaG{d%Q*;Wl$WgKdCLs3x@VlZPV4YLH2?+O8gCcz1;FDZrgscMelW^!ej%` zaC`%z6Es}96$_fG_x(U@>h4|e5lqRDK1U(s(NzD(`jX=C;V>Y<;D z%7Q0~-M~aW7$ie5`PAN2=vc9aZyckR+Y^MD#e6K;$*@@7&YE1Aufhe-#B4d;s19Jm zsSy8A;T$^@{A;pW5#ncrgSQ|?GlouVLRFDDQMo5nD?VS|u&3B4_?m;rI^W@bRN*rz@f`>YTgM)nAbM5diPv;1g^ z3y`t-^)`JAAPv#YaJm)_y+bbZhJwB~v}pq$4uh*2oPiOnfw4|Fm0qPs@m=HiE||S% z7CC<%BDj4lc_F&-HD}o@ki3P4cq)jcx{-@8ynK>=TAF>VcVL}FA5kFFM9u`15;j7) zERFEy4XgZyh7~mI>`IxQseg1-m8bHlw|Md_%q6#4oE#BSH3LiiPWw`Dd@~Waukn5! zTW^5LNK-?@Am`tb=qc29zsIh}FHxHg!%A~`IDd=MpIUL3_R4k9%c9^Giu|Q5tfn1h z0U)bj%knlxFjDGs8)E+8EtVNJS=v&7&-uj8Hp<>nf4?D=vxY=rw^*WlAeF&8GG6jz z#kV$pn@HXP!kV%PgHn^mqk0JI0ctX>6xnNOT)IcFmbWeH8IIBrfxXonSBI|K$p?K1 zQ=ijlTWHb6HY1kLpRKPrBp+U*{f?~CR{LV^v!yj#c;^dDyPEVXEdDn(r`(K=qI^@# z1<)XAy{7!Xry0hU`u1xROAgvrRaif(>~SySb(?Tv$r5Eb>uH985%GH+=%8OYYxCd| z^+7gZGmuKBVPA z9IuAK@~S$c^-@Pf}TRYbLe*VanyC7IR!J;5N{uxf*`s>o z?8L|~bWt^T+P=Zw=8-=zDeqd$UDb*KosMTD6YHC4AMZV9_@0OY&Dek+g@DePmKOqA z1T4<)Ka2HaALs6SAH@s(1+P$C1fOarrgw3@FJ6>~t{t!OEPrq{VZFAjtk^oGbWtDu z*w*F|Jh7Bw&Gfb0_x^Xp7WchBft&>YH5I#mLM72UipqDC$XiFT);2b1wEH)igWhlW>ho~KUtQF{CWwlWyn&|ZVU^Eb&GL6qb-LwVtbpx1vakiKd$`wc@qCa z9P!d>+d2PHEQNl7C7LX)=Og_EL#WCq>?zHaj>3|B2T4u2&PeRiE46z*qPU`uEVuLa zBaP6Yush)R_z8`-iNsC}|JWWdv-`y&O2QxCn4_dC93`DiGmesWw5N!Y`YDxa?qL0O zYd{}_8AVA4=u1&eY}A3Gq|Lp*HAhE_!oRTH!Qn%6BPST*=d)TCUOVxVPd871ROHXz zw74$aECWBH8_|!an+d?->E?Zt4-wtWr;HOm8=)IXsEyOjZIm#&Nsd+Z4M!wKep$se z#GEjUs_!JxrS@h+l4uP)XEgLE-mfeT{lQ5hlz=P^Eu5T3L!wvKKUS2#{9GRWOj6=^ z(9a&CpW5EUp&9Gu-~h(bM8fk#5gMP9N;Cj@G^Qq<8}H&t(RdGg6RB}IRk^X=&z_>O zK0&F}ZX%jUYV3gsAq;(^7M&N4xMj(f3}zDeph@L;d9f(e9ammnprw|m09z<}&Sy;^ zX#}#;Tpbo&CWh<-{Zl$g8KD4B>!Dq#eo2=>1AM?4jYL>pR)a^)iI~wfCY|9A3k=@EdHEHj|kn ze1{dAO&gW&Fzh_l!;12k^@iG`H=7JQU3b*CSEzZ|VDTKL<$M+5&ibfO+hHfm)xL%X z@)nr>b0^xnc7~!pEzcMVo_uL-^ODio@znj=cPZ<3KaRxnMkzfemmZC_i6v=QaD^*X|F)?rzvIyFho7J-15BYIid>tI;96aQU^a#J= zEuHc7Ewl1u7DUq?KpvwSYbZXzR7V`TQx6<@WSW@W7k9Qs& zX`D)e*Ip2X@MLu4DT;Af@`R_~i4!CgXM`VdG%+!~cJ#2}3U#jFpZv656Voo78sso+hO5dm8cZ?P&1L z45!hQ`Wc~M{c<%vWv5>y6KmleBfKi;v^hwqD+`=`$=p`mP{a18SU<_2^qb)rZ2sJO zNtN%S@~d6kT3$2xBS^* zO6k&*A;2$t&Pdj-UGyWTi6=Ww7KO{3n>2BUwV<)_UdA!}6pm}WMNjj1%C@hA_x(pU zVtM12HxJs(7@tzI-mzoE%WTYaaDT9cfQ6qIFZOul=x_E3fA*Zm>)kmzSwH>lw{iMI zUFY60*9FOsR~eG9iN-vg=g}W{&0|%^>hUSv*V~B*3F?eBG0^dCQ1PPc&wWvUv@f7b z(24#a)4PO6I)8}_-!@Sncd_D6kWneJBu_TrMs5>c47-lW z_WLl9=cL+S^T#J@{~ty=lE<9&1wVJP;*92h7#=4-qgaVfYezOqBPq;}n*>$6%SUBo zV6q#PkTQcf$1YsK0GVADyb3<>i-nb+SiKR{)?ME_lSYD-3(+GHXgs6srUT+r{*m8p zG4xb*_c*Pq9~0ce_=eAOeU;;rsDGP#e--$!{vE(rD!>>15b=l8`qu*oZMK3VKIO6X zg79{3-B@E= zI!n}9)2Es|EJn3%l7eMP!5>fJ$||t;83MrZ6WaRSS_NUcz%5t$z8qQ*M z;a<8B90$?BimNV?BbrLi8IhG+8>d=qiHhT>Z4$aWwCcb!pCNF$Pf!jS=V zIhEygD@SwQf8orrBYjslOlcF;yYrdCg5!Zq9lH)2EI9ZHdi5uImET4*FfE$+>u+mf zGn&W_iUhVzQ8p+xmhmY+Ze&<;Bxs!<4IgH8VQHvYBvNlq3_E_u{)$gIp%o5IFV;nr z7N-eLV(4UFjYDUXvR3gp-9HBbxN(r%6N^#Pzmcn(gzLAEy?IC#^eyyL1Xm|t?S@Cq zYI*^h%*R4*(sw9`PZ{h{nngrD8QIAH9vNe^9Sjomd(26_lJFg5TDEE+x-5T?*Ms2v zdJVN78f@*%&qq>QX_CFX)g*8UAC1gNlL?fFDb~gQGD)RBDJJE4t2EAvPke z;~7`I9H4}NQKC+XIV6I8;ic%HytR1hy_@eF+?85eshh#ZE}|gX0M}bREMrH8u%A2S21l_fN2{f%CrV*K{B&|kS zfGxA-wfX8|x5~e(i$CMXuSs)C@ez-R40Q7N5IVuWgJrSQLOJ>IcT-YVx%i>9L4`at zp4@el7}?Fup$3fuxu;7E*$H5KW=FUkXI=DVRI0gE&8ylA<;+9d zh7utfrcNU(yE2`8)#XkAVc}d8-hO>mVrPGGa+5D*J`EhUO(KxOSaNsTqX=)a_p3i$ z!ua4tbVVW6Q42oWM8me+KoPW5P^y_Bfyn$_b|PV+7XD z&oWR$Ic`uvoaE-52u)y}0kNLRh5sY2NQY|m+?^*?XH8}9TEwqb0`dmT!N42^0{3b( zgkPbumwS(cPsai|^cKgG_d4`8zn#VIQ9C>Ax{|`K?NSB1wJdl{O`m574(@&s=6Hcb zmLJq*Lxc%FllajzaIJAkZVsIQykRdaO%n`mVD2jbYpgLJrK3aqz^ooDOcq_jkI{-B zm^=Qa?D8W|=f@c0bvQK|6wW0F3SEZ>aUu3|lZA~!e9FxXO86%FYq0IZ^r6van##K$ zRh^ypf5fUjfS=$|3`KA$3$jkD08?%0u9}$z_-@G6j2R$kJE*HORrN5cZe1$Nt%9m9 zK4rEFUM7V!Q&~dG!9BZLTT5=y48n>(SADJ;rf4PZ(8inT$L*;V>CWx zp^634j=7{zeHdgb86S9GIcKvtQtKkyvB*lxuWb2BEV&d~lyokM!sz;rInpgIu36K%_?71YwF0L=}DF^T9!d0(kLfLGQ;<7IVX?AJqTllZEG!;FRM+UmB8{bp zAevcnXNxh=byQZN4Zv~@z@NqHWS7v4r7c9@yOA(uuTRa=Bs~pkqCduv8LDWvEZ?s>}Zl30zHr8SL}h35Gyh z;@ZWmlJBg43@G@;&YchW{eKLt6x{0(j^>ZSXhF`-B`S_`6jh}^@Z8MeIJ}d}2 z@ArTK(u1F1`Yyb2EBaFv0GmfDoqa7*p~6>MrBb}VgZ6%1q8c-ra8324?tsv&p627GA^%*6m<58qgMO4-i zM4msfi5KB6fGQZWL-4Y~Nm|DrwpTCEu0Otq7B~IQ;|Tc`yhe^mvhWwW;G@rbGSD?? zyo_r*OU@y9%tF@f>e&9q`89~QSn}dc1dyBF%(A4b+3|``IoivdmVgUJ1Gq0x8DH7F zF2$>lLE{yi!tkE&Re(iXP(ZM2^e$Y!nknp7CF#-@{8|i3PK~v9ljw^iVOzTg)4Y^9 zi^MwOrj!02I>V;*EE=fXd~I-!LdbaPiSQeraw>Q=S$?HWqd>^GM?x{`q8rcnlz*uy zF6EVcma5V)5Jxt(ivRx~dv6{eWs&@kKl4mNCQo2y$Rv{hAq)^8Fad^3ZZgA_00AS1 zMnRI01QH2J%z?o(N|ZnVqoUv)U03nOU3JlAW4u;8c11bUFM*jh$RFbdMeH1B2p-sSbj$ zs+2&1Ms`DSUfXyz*%4m$e*$;BsG0YWJu7RBmi z_rf%PD;Nb#5JCL(y!dsRdts2&M~iD{E1Xi2$u653E2ufx3lI_}w8i zOE!E@JPCyYXzlQQ9E7I+Wa?zP=_tJk(fzW=;n>){p4vM>C!iHCjQhMgF?=9y!V6lXU_ z4NJ#x(dj}xiurBm!`MCIrHI8KpnEOtD5pQ7!Ea~c{b%uWnT=`mBM|C~rj2PXf}%CV zS)xqmvUd6p$U~|8<;*F9PL_BXQgqn_jrPz=1+{Xyj5z_yV~GC)DqzZb*;*+yW7*DZO>QPr{u@iS8SjYQz)uEtJyh*N3S<{7z?Y z+m;3zmK}*-!=x8%{rkeAsGH^Qbhh9@dz^GMii*wi`5cFtHP6o`JT(9sFXgFGdH~9;?JeC1S&$OV3bru zJv-V*6;X-@wWy;Avs6yKJsKZgj>!W{bJ763orq{$jwba$L>pjGLO+;oiL!$zQ+|pP z0eG#&(ph+Q`GP5 zmedW*8}Yi4p5>rl5^3i>Wh$TrQ~h{Ka`Dt}#4r2AE}&Qb9&o^49y}x-Bg_U%dWU=; z1i}xe#$HB;lI=K(z%7bY*&59c-khzEv2c@KftaDLms z{%>}5GyDKn$>N7I{180}Wq9S_FoyHNpBUEReB~+seh8?ZkCCsS3%!NCf<7BQE#Bhr z((9vMoaoR?Axd|@yXrXIwQ>!myPJu)C*5s|4$)njQ|PV^j-*&8A3rQV4iUrhD7|vj zxCIcsFi9(cY+67zPpTCf4UXdnLxloBfa4}U;41@4JT&?al*mu@`LDl%rYbzQ$D?&` z8fkGk{bKN>IMOIRT(pWd=yTJu{qHg@wTKG2G*0(I4s0IwZ=s&f!Mn{5;vu!g&2#W> zGsgaQ2Jmx6d?2A?4~m}`X#5`QO8VwFHlH+s$W4csO=wIDa<*>g$%GFCXvLT7Ul&cw z3MzweLKUYwvcR!DRbk24K555qo8Fpho(=1=u3> zIM@Fa43hc>rQ#O~xBknwxMsMtI@d4Rjg)~`VzpAhe`l9D9ily6mI%F`KMl18M@ad_XXqadVJ=nR0Y(3KMI=Y#($#pE1b3Fw+93e_&OMo9 zPP3edELiiR_l=1ROeWB=!G|$-$H3G1_U00#nQhw_Hep&WAbS z4)9*wd=lma^y+#Be3%nxK2R_4kA;_EU?S#(T|7b13{aYb6%lPPXf`UKn3?Ba)>^{DUZwH4q@q0kwP1r7e58QdmdJP%r zK6(BG*e*?3dF(z-k5ENkvC%VuG}03&7Hu};@OV{Pscb*#`1WZ!=Ke^G1-|bQdtuB?SH>!bJAeRn+ue$~I5BeTQi9ix*gOlB?d< zINyzx@!N_Untv0S5tpvV^MJL6qM-J_M%k6#cUjRAP`u|}niWtyj@=aO;*^rR~ zTJL|nNvd8brYfDCNPp0-{1nP&1Nj&R?#cz*Rf1!_PcGw<#Ydwhp1V6sRo+( zY!pk>#YMQ^&qZQK@*x#PNc0GM79^teD@!Ckj^-c3LJ5F$wGbK>Qq6@~P)lnNl^{e( zGgbW9zZfmC1V4^t{97;HKY(h*{ z>+~jcHFBMvjios)+9@UD>|UyGPitj(myrEIdQt=3WTIw)q;3-tnFd?@N32Y;{Zla} zyk%I+s-Q*tEkFrP@1Ox|o-|>r(D2c}cUzY|14R*I`M_Z6B*yYCWJ1Ss%_XFIHI}&J z-LyYAn)lFX;-2EASd~LjC!T{B;Vk$q6>IiXu@$#9=Ck&rGqw=L%)2k4mh16BnFpag zd{AZ=5Zr}xNVS;R_{UVpnP{l}D=zMCT#fs5P|`A`2?UQ|LV$JCmn`OBAw%mN0l4_C zE#L2#m~?;g7Gg$E0Lbnz$470A_cO;`@}z)bzylDMEQ;Eod6N%7Tz)6(g5L@Pj_!NN zZZ;iaZS(2DB-+=}T?ao4-xKu%k{V02Nv8>F5_O9If#a$GiysGt#?=P%X2`@rhu%KB zxCeTVt!WN-h_`N&rm+D$??0_)(r3vQ5d5IMN%A5v&C!^^slvS0kwymBL9alC%J%(}eu~XL=@mM#@tP(m|5zjXjfoAxW!1`h?_K z+cW7mk~9yb7fP-RdM5oRlkQ4uAj@i=G*sxsKcHvoKS;*)AWfExJ2&^JgpVYt9i#)h zH4P1tV%w$IZ=BV;c85CcwTXd`>Btchoeu!K1w2vI6FzhB`j=fOB*%dp?SIDOVKLuI7=s(*3 z5V05BDO9SLkQeF%KCND8E@CkFPI5WG&^MTS1D?C}BmAxo?$O{b(!SX!$M3DoLXbW5 zF=ib;s;li!58ry_;vPpD@+IZfP$KPIaoqKEZ6d#1jIk5N7@iZkn05ttD;dT7igR~u zudH3rbM0}mc4^PGhj8(7`uypO#GKEfBy*faIk*_>K6<4Y#dxJTD4N;U7{$f+r!_!% z*%$x2wy*r%HOZhCy$_AG8cBvCeq=-St9B*5c^$*|zH7(Qj8hM0AumT?hS?7hc* z{&F0G?;{FK-~XXCzpJ;A~Ep`Z7c5`3*F_pbyH*n_)TivD?jHlc0-3jF-j z3BbEyA?4|W!dFGOd;kH5L&{(ETL{%5%IW*c-<$&&$23Gwcm4eYe~kOGEH58f%4qU; zl(1)#TUr8FG7YH^9mR4Nk1qP}lq8nGB`9sd=Gni8_-zsV!u;Sf-+|9*=LRe0GXY=Q zsWcoR->&%YydlsZjRnsqu;KOk6CeX-GjHH`c+N=l3;1mR4`(wzJMa`903&_6&x5e^ zGAVBB(2r3c%7jEaqC1g9yu90PmaqFvsV0)dPndGjsHvb_j`x0w2<98Er<_kOJl}<` z(hJXvfq!@rb#%wfC#TU#$OrzC576p;kUCO7--PQ&gc7lTAhrLO`uPseZAtrzgKStr z23FyDr=VVP{U2;%z2pYk@l-CpW|fz-RU9|*(b6>j_u(C#&X`-vpye>EJJ3MubPGY9RpPWN6Jan)>MiS-m z!G7Qbw)F<0i?pl07y-gbzgxv5;bK;)dZj8;pfb5CldUpd$^>r4;hWGuj%WhYp&P^n zI)n?iFdcsX8Ucj-ClAuVe=fTEoT?H^t~oBKj*S+<5e|D8+=!zE`Z zpxyX%oXn{M7rx&`QhkNPb)o$#D_mPcHT>3WRsPhsV)cL_FzrR%kqV9ig;s1H}_-Fdq31#6Y9Iwv{k|z~^|P zE@)S;`s=CjJHUlY6|FnkA_K#!d!xd_YpAGec22@ z_n+Fv24ZVG10=u?7B)cEo9CZ_!ccoIz~@;(ChR#u*mE8N0$G|Jw91}O>%pGuW28M- zA``UdZIG3fChU0~Dhhi(4DNIU+QmqlI}+SH_3u_J*q&3QJr|%a zr=t>|H099yNz|b?m{!_zDcN%}th3vmaaK?EoOU$Co=e%D`HSy-^7A~)NcLPrkFmp^ zhgHL*P8I;n7U#=Az@GC09x;#{_+AFdHy*E14|C!4HUf%ebT&p9xorHyv>sy zI*aV-bHC}&(yzwimKbdDwK}#%VTK>z!MRYsD#b56fK2#Rioq2$R3N|F4_f6{FA%SY zd18U`D{rV@jb}k&%fheh?6q`S8IXQ80$~nf85>0_OVLXAJYm9Z7=NZ>!G2W(6wMQK zB9tilLcUTZI<#5ph3APX@~b{Dg>Jv95`Og@_PD`$qKf@$PVo7MCsbs)xEZ=8zxrDR z{OWuF%ASE_|GFOq>|eQDh=!i>w-WJ3p>h3f3|_ZA@51P|EhM}72p%HiC1NPTmsn0> zJ(C%3zXkt={gO-bIxS$v;#>Nc-vRw#5Ch-i0ZBE#shfdfrh9h-sRnZ%+XXMk0=KGp zgzW4JbTu+w%>Q4?JBdG{(SSZ5x32R2FWdPWe=LC8@qf?$B_8t$BtSVmkITd1?Tf9< zf3|)O)S_8`Guos0ML`7P;Qup>hRplo`>p7;_*mns2wU|1&vA&9-M_5*C><8>=ra~r zY%{R|Ekh2^BL^z+J&C-w889sTvT8Gb2n6=X-#IIwB|@IRS2+zW4L9v%0z)tZsJI(p zfAk~XG{$)}2)?!g{QpnmXSe;;Vi_;nA^Z6~W`7Y+{|Zy<_KVqX!7t{JU;G^gtKuKN zcZ_2W_WUAJP4gswH4<^FG0#5~g`okg%GS6EWFmllW8n&!*wRn{`}%?I0M_4zqKE*t zBT6m|8j%Su49;YK2nMj5QBf=m?#8Q!v@p1il+!$E!I`3!%g{=90Hck(|9r)QyTPBt zhJ(>Lb(-i41tr{!qcM7nX_2Gwgxj08Ci+$9?f_OV0$AMpApxwO1K3n7h=pX^P(x^- z201iyH)IRUm3YyGSIERaYmcbHi;Jj-9aUb+?i3bJ&o23Rv5>fba1 zYTeO?ZuXpElD~_!qt6k5@To+;)klx{Uk;zauV;?OIO5kcC(_Hx5x%lD+*gYK3ttHo z(Ce0Gp()(T3{=4ps0WA1Q6d^^`5!d)GA|IH=Lzhe>!blJz-os^|2-fAt9lrc=Kn#< zXP{+&3&w;OJv0wJj8GfdN7|`|4E}$m&@XTXLfvstciw?9!=KUNdcQ6@TpxIaUdV~i z?uKx)tNq2H4BUox{B36lOYXwJ`3_WqN%yIc;2~%CUwc?^ld7t~|?USM35TXC~XMl@($|N0y6#B(eCL%cC!ALl|h<&3FpTX-s$?MyDC9k7` z*QWK7m%O+98p=bSDWGUvk0-B#;r;@_ZVjr@WO9dKM<<|r&^KG-bg=dBf^|jSPQHt# z8r;2aAQd!E3Y2lXbWgjLFzVj+(_B?Obs!2U{C^fl4gMX+hpL6X0Q6~3CdM1QC%Y0IDUW_LTJFY4CjcI*lsWyxuOke~+g;?*z;vbC%t*xE?| z@I7N*f7kmb0)?rxJ}{VF@98kt6E^p2T`zDimh8t_ad;?N2Qfo^?`M3BigH1&1scIu zIa}iz>1;ZjEvzxTb{BaF?mO@_yR~TbSGe_Ow3vL*AE>~VkQC-bYk}{vISp$G;o_C> zZc%jjmE8(Fh&CeG4aUlYaB{VHQ3SidMW~}9qFh7pWbZpL6g7Qq$5lv+Yy^*y9$}fg z*8sjY5<7HBLL&TB#CsR(8#M@B1#%E(^`M%;;c=@XqAx$!#T#V7*Ii{qo#S;ED2?QTfgaCXLRChLetw_> zCNT0HSit5)OWfnS5GMQ+nBvGm{)Ia~fu+ES1HdQ zhOM~oz_lz6&IhG2y^4(nS;9vvVF^$75EMTMR{`!og{dHL6IzQ@+q~nfocuDd5u|M% zvK2pEL#hi}&SG*oS^L_EHngt+m*|jxHpy5E8R06y7075H>EXLQ)Pm_1>f=WcS#b}F z=4*=-J*ecsBiM=1a6bbQ6oFS7wH5hnzH2bZR1eS_+&Q$t7aE zGkJi11tSBDFf~FxWq7_emXqFK_8ssC?S_j3r*oA27jr)!VjicUNV$c1UI}I#nbayy zJ)e3isa+!F^QnQeUlzu6H3|a_;$EfLPF~DY5B+BXsrDU#lThe8u%mlC@v6*L z5+R=AGffUk{)nM)OD-huaYORfOT=|{(5Qqlti=(b?|>7>>Cz0I_O)F>ZQc5s@Pq^u zNmM&fz5~k{u^Tja#lC@2r-!08FzPzA6S)5S2$f;nz6c*TGQiMr3#{aJ`5k?l%7Vl0 zJFtNra0>|+3!-3&G>G*m4&~%)W70T~`Y%5P>ix;PGHOF5pAQdxCSmGx-7!V&YOqD6 z`!hRUByC(DHcHz_xN@u+q%zUpf6D&WqX>$O>@W2~_}5%n4RyA1PkG35fb|_%8>-;m zKuJer+nIE-a6keA&*KEgci3!aW`ZWF@d=7a+^2EFft$IKu(wgA z=9%MgZ_c0gD(}nbk?f1lM6b#HCUukEE=YTU6o+Q7qBl5LJA%aD{2Et0`ee5{&h2h^ z88?jh6cjdaqC|bzjT$Xc?{}l70OdQd6Nn&R;U8Zj=#y|ugX-ch{`d~m2NBT6b4e82=`-{qVcc0c>U(9*oPGrZSY-1CdTq8b?WVkl|@qn-{$oyMs4P*fIB^t@sHGDuX6(CRzz zJ(pie)^{>WVdDPNsB|z&Icn20&Dp@vp|~ZWF&ZJ1(Bl|Qj}mj)>m2L;-Oca_xA0l0 z1Wz*RgHY5TfI{>1oew@H-u#5+alpU*{icE6H1L}Se$&8j8u(2EziHq%4g98o-!yQ% z23%*AmX(*diVDli%PQ8n3SGIIYD+6CT$O9iC@HGdv?VKYa$MCVHFf2+uCvNeR9EXN zsjjZ9c9m6t_CzMjpM&ax_pmYJJ)WBMk*?ap^(8f~ib_{u^}4zZB^9+buJmDN7rWM# zZ7iu6sYTG0)E5@jmTz)ZR+I!Q{2V>_t}oeiR%LZ@ki3Qk7)EWctE_cxfW&20=-S4@ z@;XSe79vMhag7+ZVnl@8lBNa>YHD3;N?bLy)zFOKu4zI3g%z&4ijw*&XsM+5r#UaE zs4c0ksxHBQS5awU#kvw#F#xVK+ErUxhK4GO>v#yD^$O?!ZI)LS7K3Y9MQtUk1eqY0 zDsZZ*t}H64sTqUcf~{CnSyxf)+E9YADR!}1mq7&;B_+i*E(odAFNZ!qzp$dBvbM0c ztg@meKfk+|CABUzR_H1&E2<5SzNY2puRp7}WNjg|`D@5E(Z?F%RkUesT}2T!G$w+} zS!LzrFpXMQQC)R4Oaq-RT)P%ZfkJ`rJvsJyVI+ldNYHFawQDv}F#Pa7cu z!U)+1NFjnpExd0{U2TbS)!NERXtiRKa%*U+uA-{2XuT^v=!^(6BL!EySu31t1iDaL zvbL@~zr1i$Np;N#ZlJKdx}>m}Jgx-Y+)!0njS(8-T3Rx?tfH*8tgyVSh8@KPFC}xW zsjHGST2bYODhiynWgB3z;0S%VhSiXcq(7ra;}8l*xQZ(|5F#WM))tkzs_QBuDv_+q zN(-x@?l~g*)^4gQ5x!ib+Aef4QrYxR)05`Njn1mBgniYNon4aQs)a39mk60*b!cyG zb>)W9qRXR4MJOG<1+#;l6(X=8(jsP*m*>-~P>6rw@qO5sDK#z%>s6IC&={<~JQ#7% z|L~Yk^&`e%%-rhAD$FT}5M+t6ksj!B$|_0-mMo&Kuqu#6iOHZSw~$(O?g>SiD*g4tOis{L$YH6 zDXgum7F>>(r$_yTG@W0>mYF{x-;+Onj09Bh%CD@>7ZZ@SF&}f&8Tn_Gl&veRMYI-y zB%ixSZJ!xYUS3nXA-}Ams;)MFV?0q-vmOi8@aAtQticmqzM7hnY8vi& zv?#%Rg=s^uSW{Mr_F-G0pe7wVC{TWcfca%L`9+o0s0M?BypT;qg0qF+lvRYW)AAz( ztf0` zzEK5uwU|S>Hsp;EAfE=NkRvZU7j>_&uBK2f*R)y$?h4U0=8zBZYUSKPh|qaQz#_aU zPc6-_$VZqIlN1PwB4A|*`A}O)6=IyMxV{9_OL>hZ)u$Dg)l?xgYlSt1)zwI){cHKC zQ@Ei%{7D&ZhZtJ)_O&UfZ~at#B3Ax`tKFf^58Qt44K^0cI^G3BZ-rW$Vf+0Mk@e zxQefir)*#Xqm4aE3Sf+Ic-Q!nvKe=3u3@Aldx?)9LGi?|qN74!6 z+X}=sF|%PA1bZ*m*3^|Dj#kuY(!=Fs1u0btBpp^fW>~Qn77;)o!nc|`v^gS>R$^{E zs~XYAHAhTf!)lI;byb_{*)LrYGHY^Iph3UoC514u2!b);-T|@=l|M}-eJUR=UtCv2 zLjdRC+UI`H*sWKa?s z(6IZ1)oH>lF&YR;aL$sAm{>H5HYGJ$VO3R0MX@GkE{$hT7%jFVT1{zT75Y+xC{>~% zxK(e`Fyd&D`Dn$oa?xr^%Gbi3t1H(qLyg8xJG-n3ZGwE`CapBT7MF)Y2riG7sg2Uk z(l%<^{gb=8mgDkzy1FLg8iQ*Xt`uC?0Y4X49`e`X+BLDO>rq?>mw|qHSJ$c~U0us? zEyU%+H3io`lzC8Ai~OH(wV#BiFPGwm)!MGEb8EZ0l9BetrQ!Oj1h=Aby@|^Mcm=Lq zplQUFxUQ?~x$3U2A90o5-qp3Y4KMWl4rRFNah2mLz_lD#Hm(`CJh;+uCF7ccI%T-7 z!SyVz=sUW)GI4Ff^+#Nv;TmygS63mf-MAjRrK{@_TuX5c#q}+C9L9A&uB&lv#x)W$ zs8~iR7W35*^ z*U+}7WW$=0VysLzgcfT&zXm5kv4#jGC?dngbefNeuBc=UqHky^^i#8`qNuELOmNOu z6SpKgzW?F%)$&2j>D{8J87`cD9cEX#qzJA-LMbBahn7)3xSk0g#voaQKDFTbMhO_C z6dARiQS9Wr8@dDutI=H^Vgw-+a_ORWdx&h|e-s9>G0cY^C`24r9=R?K!m>Hj0Q2;@ zx~64;ZPn;wT?NQDOgPrngtP@|E2UG9bsdh@v=5Q4>V-AQv}0X+kUlm2SXZq@)7&#a zhxEwIV_lyjZ7V$1RgXvBQ}NVwG9F|1<9j=H+^ugv?^xG<@H>R`Iiwv(JCJrF{R*k; z{9|1fv!=BoO+ng@lzy&3y8w?QA@w3HK-!749jUzmav}8~^&_o98WjcnMW9FOLAnZQ zJJP$5dhtOn`r)!xq;~X2!>7w8BDLc)hmA;EkRCwVfz*!vwKN~=@*-_VO24Y<+5vnF z>g_~5q`63!BW=Lvwp)?fFGqc(`;hvn{EA~;3D8Fe(uqiOcYz*hJJOU`$a~eXt}3M7 ztB-YcBK2HztZS|T7-=5T_G^!Il_T|B2lhRC!U0O4r$dx7*9L!Phq_JLf)6r z4$=buu`X9U>bbkRT9Gy^K#X-jp2DuKLzLctxSRle;7=x+>FlMNsoriH&^yM`V$!rU zl%?RZxP?wNt9`yTd7&-tEK7ry*?-!o328%A{kh0=dO$}A`A2;542R|iUc(ruKceA9 z@;S%4&;|Y@ToW$(rco&FI^ezmPVuF5AFi)}Bl+l$a8FPQe)Rkr{Sod6u5*DKNC^Cq zJfGrf0?vz6TV-_>SuKpGZ))uWex}4108g@~0DlDd+Xz3~YTu#HvnFph&$YTPiJEIo z-yA*H>N(f2rUT)#CWBzM)sh`|lEBPWm^pECf$@dV%)^+IjOT(see+7mNI25{MBtLC zP5e=M3vgcGCKKXRt9`S6IwS-RS3AmzQML=I=Cj%_(X*_{o6T8P&pN}JoMpB9td@mw z#mLX)d{&&k6u=w?X2-D{QSf2%@qS=Ie>u34fqM?PMncRF_C1UG4(1z;kp9e#J2@1b z7YBvRQQb%N8MsQpuQ%cX-s5ClZr7nQlG6nZl5Ei3+_(k7yf1E{LTfHm*^Bx)sDCQe zhdkSLG_u05+UHR{*gDV@o0@)xhWgbBnkPUL9R&bGb}5bNPV;Q5Ye&>!Z70mynHIfWBZr(621A z0NKa+s7SV0k9yCc-U6yuZnZDLh)zMa1^D|BzgG|*^6ez~c9`c`UE9g#E{TRLn|p0y zqYFv{(hOYBq3%KOyaRm{I)aWbF~jsWN10zVK?V#vdkt3VrfG(sQ`kF{Vs1M6g_i@xMCY!`q?bIQu?1QRnUTBJ}D*BJQ zA0WQd7y3f#Lie)&7@y6(%yUs2{p!|$3*^tCt_5E-eG7Rl+iE}4Al>UwKAQ-~vrf=F z0h+}`!@g9+#X0DCHc&Lbe1-C1&<(YJDakJFV_vv@%(b?fbeP~2h=gv1n#Y_t((fwp zIYNJ|G;kFp$mhWQ#QW8EWQY$m%QmSi|^To^Xd-U8b8UqPD!+C!k7 zhPn44^9+WSlv8DPE_NgyZ?t!LJ zOlywKpuL;MvfI~S%R8c2m#}5&YxAsLbEByzRiY}{_FnM%VB)c^kA?!^I49%B9#@X_ zP`dfKp(?0hJ_N}T&|ims&<~|i+Z=D$FE_)FJo@iN#1alVH?9C620(xd;t)G2qzjP3 zwRSG946F^l*?z3+QSvYDPq%Gw?BM?RD0a-X=APYS;Fuq0o(@l(a(qac6Q|ewL;;$k zKHY`(voAf?^%?Ti{tmLu?d0E=L{Z&j*b&@&L7WeHlq1iRPjG6=27UqK7gL{TzR@hu zD|q&yJpKsBYaigMNp}R}N(WsI)=O&%f!J#=v|0%71^yP`IZo)S7*BST2Yhd=r(P!d zpdBLC!>F@?sz>>AC|@f5JhU%xCZ1d22w`*U+_>rp%)B_V!zaLt)@mz|MvhB_&(612 znFsY8(lIj6^gJ>-P*w|AXJD;&#htgBSYv{PRgjI}&pKqgGq=?mGug;5|TRtQA~<7al9 zxeMZwx-s7fvbk{>&-4YMQC8T(M66+Ft-zWd?Q3(bUVWw2;z2eC_$fCX>)I%NPL3&f z(t4>tN2KjuD9PB?fI5vgAL|+~eMbE7TqKt4mvG_ zzZfvqtip-0)Zv)a2_7HZdaR3n`cqpI9^a4iVec|%9-JehJI$YD!!wW`+hxB4TdNCipn2%n}M=$217xU4J`PL>}Ex7jJYQ?n| z*FIeP&8TlK{SP%lom_-=7ejkFO5`B ze0v(Hb3nUN%0(ls`2MGS%-`C2n#06Axg2K)hu{~l(4-QvhC0svu!gF!?lqlk-Dk>2 z3gw$<8a&=p=Ea%4(UP(NX%;BWyUfTfL)}G4eZVxJ$-`*hgT3-aNJHA^bw_vmyvAoH z_tS)Me1mi2%-0cn60HR3Y-;dbU1Ex5gZh`8>6QTdZ8V#Q&koFZ0{b28zkgANeJ1nf zo@GF%zcO3*Lxy(9upcsj_90zjijn#V6K~>NJJcXByk2<|GE8{|XE(FMWa!@Obgx$y zT3dDfV(VTVoz*c8aEex)=A+})GCR&Zw2!33IAFios++$ukR!ugf)wMR??RKSu$ZV% z?&>-v^;fjtVKV<1ZS`X?JJAb2`UT83nnI5k0a`g21)7sVTZlA^Xx(OsS&9@sZZfCn z$ejY30!afgNUpCSm-mFOt~-)`gPw4jQprNAJPvRp?XR|n)0G!Ww!T-$MWlef65>z^}}T$sK>_okua)vlXwwj#P@!;6<8 z^}(ynTceOW8JGg3S%ev;BX>$D#unA1PJMA^6VX!brAX&f?UyVewF$%en+TiUx3#P5 z!#O`8Cr0d&UOewX2DABKlJy{>+9Avp2eDcOW?awugU7vK0uAIfLk2`&(2hqh&5+7m zh?M#DGfNuuk}(hiL#jOoxenn|9+dy>?>7znrh(ry@S6tyw;CvD5d2&+l}7}chJ7gg z^%e;hU?QW=6%KwNDAgHr`e@oTY1#lmM#m8RM_U&9^T={KOQ1hh{;y+Q^f47a$E6g< zS^QUkwGRE^*oXg;X+MlVZ1?!jCFR4`i2vw(j{YPW8Wi$~|0t03bjCq{IC|kfS~AdI zN18|#e!uLPk~3X4B-!wJE7dsGCFF0C^j#8GdhC>NgB0*v37;&Q`|bb#M;e$S?JbhM zN7}JhrnxdLkZF}n8)Vud(^i@8lWDt556QGcrkyg?#)+2gGIhz+BU7(Tb7fi}(<+%Z z$h1YKtuoyw({`C2l4*xbJ7ubkm+i~cB~y<~y)w;}X@N|uWZEFp7MZrnbe~MyWqL@a z9Ww2dsWw5jFH@IHJu>ymG*_ktGOdzngG^gw+A7n1GHsXXA(?i_v{R;hGY2BrCki=S zGWE#RE7M$=7RanG_t)Hv)`9KzF~zmhIP(p5Ex`Yl(5^HI=LCEtY-^AmKrQlI;Nfo_iE*U+F^=sC^( zm2|5lUCS@N|58m+{Sn*LNNG`TCxmpRcby z^Yir&jqbUA)|@%hTdlM5XDI#`)Mw$4qCyV+`~Gn8Cml zBM$<{%w)i8+&L70mw{}fKQ-!OAlJBjEP&Yzhu{DhkUlp0YO zrI>7%y2{uLwZtr7pw@VQIe>+6e?=GSjq5N@F^l6#(+$Q0(0$Aj+Zq5D8T1@Z%!w9y zWNWkWR||lXZ1ntSlkqVzIJM7v47 z{Ti-ijM(14L#RvK@@UJ9W1Vu)q2v{AO2xLBS>mT5mwv<_guMvW-K=vTrZ zVi!B!2Eul}7x}H$7&BEQ8?asG0cIOxY!{9}zFFkYUIv@!-V z?LZm>`V-2wiRe<9&bF8Eg9QFGJk1t2+DhhRn~Q;s8^ig&H^G>qeWqk$46`1C@zWb& z^}YK$nt;+zNk@Kw<2EjiS&c!oSpJNLdhNr%ct^ipkY|u?dm50%Lfjn#2sK!sY>VTe zuQbtYU@!!Llz?6DrEb?jr>i0$&+o>M02a4Nh6wj1kEv`xtD1ipAJSo-XuEH zj1K6tQ$7LHsMqi>;lWtguF3I;WjBI_u^GjV_UP}B*Ni_v364kml7%E2pTIpFkJ+gc zF5?X7)A3j;0qI5oI5_@le;J9#*f<@)Q%p9+7>1gTgMGh5iPs1?0X*F|&A^ifh6^ z73(_GsW)E3h&o>FOAqWc7}X8{N0?iau@`>fcr88|WG%+23jn+xeDSb zYc-bS0qAg$6YVv|WCD1PyS>l2D+|C!@jK9k{l;oc1&%?{DC-0efg8;bz^v+o~Ka>RIOA%L&q9|6!|+zu0SeBG}NjrxttiS;-A9DQ*VXe=bM zZ~K)3_$KiaIELfEz*-eR8o(||({HuVHO^F{M zOgOs47@3^E8wf}H!ty2<(12>?wdTmLwWlgT*T?!^GxNZ*o364`3ftJR+9XU~)F#OwnNzrA;R1`BZ8*CEN>LzI#S(@UUqeWUpm6zQD^xB7l(JZavNjQ`0r2z2$Me?St4D9Y;>55m8PoU@R( zSmxqC(~?Ott*D^WM`iVgoyAObhN2>a>ZX#UufUV^Kie*4(kqE{7n;09k!pGZCOQ3p za~~5tNCf1MPj(Yr0ms+>lK27>yh;Ql=}|?{kH|HBFm$XxV*Q$_ek3Yt#|b+SA`z7o z=@4n^QR@(tSuE-J&!oN}X}4zdf8>Moaob`hU8YFag^_CdQLG2_Cu}uLwMkKtvx&B| zA&I8|4fc6h4!9j0>a4pQtw!I7B0#uqZyn2L0JY8fE=9_@(}w%>xpPwf5sSspHiZ;f@funq9!lKB1lJG&Nkx%Gw80vu zW;J~ojrI$I@7t@8e^KOL8iM>wiDYuRM)|`+j%N|W^_NBdbszGtB!17$m=GYi8HyDB z$fLU3()2b2N&VHt;V83MG8CydNXkZKi#ZKc-}fgsv^`0F`h&>#MxfPyovy&5js=LODZ(SMB(Sg2AaVpF_3Hw!l*hX$5Nv% zBZXql6b90a03v{6YTPF%NjGkz&^4_uHRCZZpcp-!fhk4|WOK}5z-v5v5`dX3K{j+m z3emOIz|At>4RQ6%`N(^1rC?*RRNz0`%@$;Yo;gRdpg0#;eLq$>4xjyMu5cs%_osN+ zO|0nyG4_r*iGOB_NAaJ>@0}nO=SG4fR|4PBu!dfQ9po}ON@M?qxN2OegUJT_cob>I z_oo7=V8CL$vj9LP19oFSeAZFLK(g@&hQo0t11{r3^wd$!({Q@cAI&*xY`wtEW9S%a zM=b+WjE-CYbqsio?@|D46!VNhuURpbJj8~~dbC@|bK-Ji`ssqu$& z0Ov4JWjsK`buI(-#u=vrIFGqC7^e*da6S)8lkpvvV2%s8q{XmP$%R~JkMT8)(?txl z8dpOrj?E10HI|$Rppk)n#=&d=7jsYd8*X@w;}R}uH_WhO#})<-8lQ~=(8RzYqk`&e zW#EVrOG}<@40IT8AS^hxGvGHSE(EZHflk9t-QLN-H;I0lu`gu+tAh72c#g}s4_FnP zLCU&<%djeVo_ev1A^iHYK7uCIE6tyvMpCY0cWen3jYh^IAouj4`9w4BAaA&uD_9I0 z0oTOSL}EAStl4ob1Ib1aDmt!XCAy4exV7W@M4C?E8no!TflE9_Us~ziXs3B$ig67| zb`!Jq8ae?tGq-F_Kg|p1cV_yboTOyO?_+N!xt`7f@>VI=B3f|WCgu7JvCy&CK{FHP z>1)ZPZa1HaI{H$ob7%6y$XhJ!_@7L1<27WuBUe8HQA`oD7v+;&j=N)rVA)_CBZs_) zRjCp_ zHRFfH03J-<0l=c^w`TzQlX(otEEa0kVj-`ySZFjX7V5jjLQG;;Vz^uhLoKvQG_Hb6 zB&5aD8eB6zodsZ6-vJ=77*?1=!tlNnDD1|3B1>noWbiqc_&f`i>51`nT3{Ma!p{;N zyr|WT6HuI(z#-0J_?H7nlwq+im?k>8BsrDdp-)WWIxaAMf|zbXuugZz*=eE5Or5<2 z(^R|$syX}gz6w)}B^6(u(45xZHv_OUAE%AUl8plB&1n}l1g8DK2b(n5Mei3)&`=^7 z(s?E^L;MSKjpKX)^r4R40BXDD$fNb=W>eoQ04CX!rbj(VB1v68PD`52c~B%L&5KS%Trs>b-lX|Sl$Ttxn2TNOb0Sc~ zfaeXGjp{6jUJ$IaP}YH8X-SJ@9anPF>gkAfFRM&54F@Pz@((dI!{+wAf5-+r}?Ien~17+YSzcXkB+m0` z#SR#QU9+77kMExle-R*CHS{uIfFR!n%N>xy`K0ue=-Br_z)Sm-UUCOJ4pxv7V*DF6FNpbj%-mzp zbNHzr@Y-jJkwz_j$R%D)Pl9o!er$dhm0Xt8e?*sn(_nncOH2LShWM!snS)s@wKJaZ zL&m~eQ@@C(9|#ySk@8>0pNTxA>V^6i%Xb(8i{&5qpOl;WX)JwQT}bs=3Q4FL=gdK+ zf5x~0VAi;3ETFHr>sNNjMDlb zI%sc$0w+{N=om|UVxk5UPLJ=6rI3;G1M;zdf{D3YD=cY<1gv}4$wcn3Q~{yDhnkVHZArxh^05h6dp1C_*!{GwIJJ4F5y z(5EGc{J97&!zBN7%8L%z&WA9=h3na}CLy29^hvG}(Xpcumsk-alwX~U!5U%Ms32ME zLsRNZWaS~GP7kk-h%?6mWqTX*PP%M+ELLafQtqQza;FQoPf8yd9eaanV`OL>BQ18C zs*{bmh*~4XB;~>x_ep@9<_*-qvm-QZludMQ0Akgs*&=_{H00-q{NotWQFBH9`T@wF zDDuz1MMvd|{2wv9k2*=@^_9pk6Ztf_;;7{!e>L%0!Fj#bjjsa%>RkvMZmaavqcH&Dtu*r_8|?_z?t~<&;xgVxP<0RBev+pS0aCx52vW>rkhhIS zLm9Gz(B-maZ zgM5y_f6)*5#Ug(M<~?SK$WO?_^1{k_eLmL9}m*=9yMZ*qth}f zVN?n&g>0<|jbo!({-h~mlVT@eLgc|3n=A+GLk#WMe(_XbH|PnNvHj!8u9J<)@XoOV z7;qWO(D|_`45S+uVs;rjkPoLk=s(G$Z-C#8O*OxPe3EypE4GYUx*PU7c5u>Eo7h$cH=`_UDNgozQwlo5f_%fo42o zY===`H;gm@W38m_WMex<+v8!^bZNt=Cl(9U*NK^DoSE)w#Xf}9p4~IkZiP=6C8wic z7BBrYV-%Jpo=je)VeLwE9U>6lUs6vdlgqNe`^(Z?yS zjBurFs93Z4;JA}S2-VA>yAxsu?gL3I<@ZgFx0q992PZpZ2bV*D$zrWMNopu*_)8q=sG({UmceV3-M!FFV_ZGa7Qy(w?8P=SS2Z2Jm9VX{~a z>Tl1&KDT$$6c8p@v9c{SO--?kA0!82>Oi@8cn#~jsi_J2TyoFcDF6mJro-Z0#$RE* zQ-vDRjZ>k=se`!}9%DQFYw8dNrWg}p{ZohXMBz1_hjmU(FD~vq2&D2p`C(pPHqdzr+>l7F%GXRWcpxDshpi{>%P-^6-0~j0s z&>Vc?!FWCnfX8k@7pfAy7^11;cnwg1yH)ohE=}vhu7-0#=)$&lrKU}f<>NhDGSo3$ zEY)on!U(4i6b^bfV#W+HvDl74h?y3FKZ5pV#&TZY4iB4^kgx|VhUeo`X33_^S(X)u z=*GU)nwIJ7%b#&E8o(-ZHUkzT-VR_+zftI<-LPR$GUq4E0)u3uZV7-a?w!jh7!M$u zfi$BE!;ravfpp{V@T$xu3}hHfiNVr79&qy*nd1N~k6#M`rWk%q0hucp@EUi(elky% zW77Ks0H-jJYve;?nWst34@GRvT**Lz@uy(`Rxwa&ya%3{h0bKOQ)N61L&z*iIsrh7 zLC?Bou4A%2##m@Rvy5eHHQt5WWUl9uy~h18(adtbkG0P@8__JYg30z9|2PRiC6l!q zJK?yQO}*)q{GhSl3t%gI${}MLku`IjBgT)UogG}K!|Tq)Y&p z@zUm-5oN_%<}S|TjA}9I?W*2Spj#H4Lv1O*Kspl&0jA?qcR0g=jb_?x)l_b&$A3Nv zy$Tr-^Jv8S?E;Zl2gIHPe)de4y$uw`Gc-bO7F9FiVdt3{iBv$he~5*C_gLUYn<*c= z99nW^j!fZgaM>8GSn6*^NnL_A$^yUb~3 z%3CaCkruW@9F8!CM$Tf{4!rFGi0vIJta%Gu(tCo)=Y!BYS>#Kg5U*T+79-4h<@%HM ztKOx8rg{j6mTjJ4gvE#6NulA`{GjL;qRLEiVEGaF}LGaa=g zr8`H)CSmw_7Ico1lf>Lrpm6h=#BR7p0}ydE*%*etI7hRtTzK}x3}}q`HMEj6#W^W< zIdP(o`Z!OBCa#*X99?ow=5>@N7oGGi3a4CjeoM{C zMdxDlz$sVikDx`T)YKqEbEni)EDYW$SL$zJ$>dxow01iD(kJ4F?JoHBY`4H)1zBfj zi2Mhz=-H!1{=VUwHpe-Do?@_lgJ?QeEtC`SX%i9EY%jqf=YJ_?@wwoWHCrs8C(J~C zj>x}_K4!^<^#H`JtSo_dV4;?kE%HT(lvxWz-nJb1g(CkMl$(_!@?RhZXDt@_$&evy ziOAm%y=9#!^6!ugxg!52B6HSBB46%BeyPZBFd@HOgI+4G{ zKz^ghUvV1pXNml3OnzDQBL6nYze(gTB-=P!T7mIuo$=NLORfv6A+eQ8n;=e=WPshBNwNvD`ku6;+ z@^2EpMdV8{t7cs$@@J+Yf4RuV&{$s~@=wA?vUZ95^)}?M6#0j+T*$gg-YlWvcP0wry529%r*)=S{U!RWCTUhI{de7fGsVmPnj_UG42zP z+y`VodOS(|jEK29}PqC?3!LoDOb8Buentoi%U4-WY ztXo0|b`e1hrjsQD2NT}58IG_dmGfqMlnr5NWV-39sMxWPXH=DjYzp558C64hH!03C zs)F*F^vy!MyB>-Gw1i5M-ECN-ns!H}d=0@|?)~`;O(1BnyBU6fviSrJ(fZ~gYyiwX z2r)9g#Pnp^7R{UFSo1zFnBi~qI+U5VNAqJyqpw8_H|>zXZp_D~ozaQZ?C2USI!u>F zClJtw{_TeJ@?>fBC&c=S=!7l6p>Jt1vHwEOP4_cQ!(zmY?jDZ$%QQTO7s?hl?Y&JS zV)&+s-F!<+)oB;Y`m6sKEU; zjLP(^fJ)sfF>I#i1XShzdJLdLF}ymfcV9RV&A zEL*7dU*@AIL(psPhX@)ipeXm_aAVUL%U~iTUOuE5k0J<}JeFZg0K}SGr9t)eP!kI* zY&MQb2#}^a3tQLN`#`wS5*=%9m?n%bqt6l)pKXyg*&I^^Vxn$P=u6}i;~BRj zbek?z=<5~ra15>KB85(p&(~*kVl+&f75Z99%~C(1q&90&_amQi-6YU9S{zgmYu>Fm z51kD3#R`2zD2o^v5_ddCi~5>aBxBw(U1D)iL9E&Sz3li6G*nv@i%S*fLoq-%DRi?! ze?I}}tqQ$Gp`V9lP1_WDvqIlO?$WH#7f3Ys@Kz;WlooXr@)>uMQ*5_5s36vSwqkK6 zdB+Zg{)f_C4H@fBg{}+btiji~;}$KdlQ^5n%rCV#s36wd^l#bmSv0gQ3au%9+>Ygm z=`w|G{aR8F$7;iLxk7h-C()l%$FET64vFR-HYquxwWxaJGrluGyUXIBf>?89u!q#( zm8vUeDD)64&rDY-&WBWk2Tufgw?elo^u>dL-lKRID<<#xfWBIxzf#(}*9r7B3cXrU z-*zg{*D7?LLZ1o$FkPq6OC_57SdR4o>$;Z~wF>!+MWm1GEeG7rz-Tj(Lmp%c#l`;g?)j(S)tuQG@1MF6go|zN%^K*6ndaU zb7!V{WM>R5Y9#U*1#lbF?=220zznE#{qiuNZ&fUuiu1ou0QxqCwkh;WG%|aY+wE0M zUcpRlx?NFUr6R~siez^vv_&zQZU_2Kg*GYl^Ay+bQoLIgZx6-dyOl*%De75Rh??$E zOukdbaFks7Ud3dOV)9gPp!X?si$bT&2KqjQZc^wclYst%LN_S%WC~kD-tI2>LwK%9C*4!a!Gft&3d`$K4kV4NUyMA18Zdd4l z7w9Jx^*)6jI~?dI74KGseue_TUlqDVq5p9T&`+tsZBS?@mSv`c3SFhp?~x==D|CTE z(?`Bc&nN+N75W+C{j6%ztI)ay=;xHKJqmphEnfeos9g#@=tQ9ZuF!UczBUi&=M`F$ zXx8=x*bA_>ZCX?%@)`SRm3YYFpaLv=bh(sSNvok36uL#CnZ;v@MPDuI55yup2edC* z98?f%u2L)p(PHT(h0ayzr)XpjE8#ljYtI=QNqa9VbcaN9gA?GUEVEUM8j5_z35!7c zip4<%vF1aH#h+nSrdJiZJ(R^p#R7Y%wZx*Hoal(fK?Sj9j}mS@>G?H+X`K!(9b%6en;^xQ0VB% zKzAtWT!oIIx!_$jT$+-*io)T0syiJfIk=b7`tW^4eMq4@$ZS4P)cX|rY+49@sL(A6 zT}5vCks97Ag+6=|&>t&wu0sElROMGqdL^2NHy&N$;f>d#bmTKu5N*KXpaO&;#o_~; zc$hv>XicFXp9S<$h3?Q~AAcab{#2pcC7K)DtQvG^Q8md|`1=0d%26yMH>+Un;Z~%A!!Q=%+=UL@f4B1MNR84k|!A?GmvU1_Shu zmQ>o?LSnDbo2L_RwZ+Ch=ED|hKKYX=mT&KB?pJ76tP9BEz7|!7(FLfNfYRMRV4gHtV*Ap~bdNhJ3(x>jHpQJpu`fkHUiW>c02(NuYWnsDSF-H!TE|CZGoQ z7Fz!f6Ht?TlMT>t0kyaf(0YD^fcCh(1%T28)asr>S{f;!z3!8i02(EreeM@2in;}~ z-+h$gLWY3a-7Cm$M+@kndpV897y%t}7ZK%H0UdECCjs&ZsKfm*G-nzoAisM*?MucB zsMCEXEXp)NK;JmOr!~?<0cj>@GfhO31ZXii2hi%`1OeJj&Mj1RvH+7!&c8STP7$EX zmm7 z|0DtWP0pK$c&Pw8P0mbsuxXh9zrhPqhCE6 zOxB$#=J7CXFTyHO;-dfhpY z47gZ;4Z3qJtuIOh*rYqlh67wHz!u%x*xV5{z|T>-F6fO~c4WfW`9 z5a2%DX{Ly|UV!^`XU1TFj?* z=LO zHid*Fn`B{G14NnuLJxsZr8h-N00oqebPz)kX(B2pcC3Jfq97nusVXXh1q<-L=giDy z0e|oP@$QEc=A1cY=FFLL=gz$=(G*KSK52ejqFI)J4OF8E5-qRj`vk`mViYx!p@cGK}*0As^C0{9=KFkdk0)6Z!DE)L+^kMGz%<~XsCC9lbDxFG}b%dmsFrDB%0zKaFg!OS4uR?JHW3t z&{YyG@D5l*J>^-67J3KxQ}13a(RSVeQ4|c%NwmZ};7uC6D2dpP_lSDUq2OKT}x>=&zy#of2pSMVKmv=xHikz(y-R&KK6ZzPGkmz3TfOl!h zzfGbCy#rQL?n@Fq<{j`Ed2+i%&w2-(BJ^d6UiKEX{b@*g#n-Y3<5=Qn6x|>C)~Cgj zuccIdNW@0JtYqx-4Y&?+)vA=4Si1nE3)Sn#i7ycM%-V{)Ta*7P0l8@dMXuE3^)z=m8Y*&yCa2Q$usl$aOBLCV z%KJV#9xqX;#!bOCHKg9lprIbB#(s3t@3Lm|5{b9TF*8CN9b>joZRA_(?+ZhN>2MvE z-C16OuEg3D+5=;BkrdgF4jni!nW$TZ#Ay^D^%CpZK*Juq`n;5%SG*8?HJc$4&rx(I zCe|xvHvKg!uE{J9wu@B6Hi`A<6gU>i>f=?3Qz^bvlj_ssMO09I+so?}l%;9C`ei6? zgBnc(eE)z{wRH5drt)x`sI~)wP37S>?`n0i)M_dZx8a=Q0`&8yjV&KTrfO9>&*ewr z4H1T1wYs9#MB2 zx2;zpGH<5yiO94^g>fsLm-6YhdHrt>oq;~cS$COn;zP)Cmi?UgjYHc>qFT1cKI z>8X^Nir*rq%mRLDXx~9ApjwJ`Dp+l!dx7GgUX78L!hqCH=f^;411ZS|8*P7wGM&F+ zVml4fMxAtioSJScrmWEPnk-+#01}p7b0IXt^H=eQlI(dL-zR*n#`g&OR+>0`l^?!9 zM(QX#5mM=yVJe;?BHfJA!cITZ!E4)Y$21@bbm9x~xvv8&Fp|tc*Va zebV^hw1-$y6I8%g(i(69ZPO`+cBVCuMHKeSG&!tRmU17I%2%glY2~Za zTHaCqoQLvtca*P_%HhGq1<5k^?L~6uP&H!x%=AfTm@=`{EYcZe*W^^1Z6o7O@*6jqy`T zojq;9cN1AH5QFqqY0gz3{V?En0^|~#qZ+2>dRXYx1UMQ1c?5}`ghT?V5AXN@9Cjcd zAn_U@c|ba0I&=Z@;uZ*TNSQXwluh~9IFoE`(h`|hMn4FJw3mH2NHy@a#NCRN5sXiylN3}f3NQtu&%RR9?YkpY10x$}kC@Pgvz z*fG9O4tui*246(hPvE%($f*R9TN{B0$Q7YD4fZA|jy?D@3#2CjtgG)@t*Zb`MqWf{ zrPg%6RuHeX0KX{+#^Hb* zFCg2H*bK-eHb)RBmw}xJI43Gc)L=&x9ER4%RS6AINm>gdd~+$`_zro|=m(*I?88v9 z(5%WqV$6x~eT1rV5m}?aGYpV(9mtDFYy#vGTQji`y8Q6x5=iF&d6j6URi;s$66!L} zhx-m|4OOpzEdqm0Fd*kWAj6Ru3UGW5gzh<(07M>*-!Gc#bP8REFXK{pop(vy6|j8( zf!%5_9$d98Ubux6Y#g#NNHiTr#~Hx6qa#)s=_-8GG?9~dD;FUUgE0C zHE>i1t$Zcf_bRn=2(r4PES&((XjPt6Xb{fNm01DIbykVz1G!VwY%0=Eiq8=3Nb_IG z*3~pv_22Wc?QTiB+d{r9Y|OMyr&7sc?`%%B!&WVNr7W4C|iNix6mAK0J)6B zcZ7TbBnyLRDj@qCL`%(eTy^0kD#)}?g!KpX{G7|kngO0^fC8#~UBzdJ?cnpu^z!A3 zzmNQP0M4GOc3g3(I@nZwmQ+`bu(qIDk?MaS@~2iEZmRYV_fVbD2;l>8PPj{T0hKz@ zR9yfO!#BdpYKMV+Fn~*JH`NaJRJ#fJ&y(81yVQbUQLRvf267fFgj&m~+ouZ<_^oOQ z#vi3!h)QTTeo3kIR9jT8F|sZ|`x!tkkPAyI%SpZ zA&k%zYjaZB6m0XrPwy59cexU122K5uP0>7tqS@;)3R$mF=oB^9OC7{>Cx1NWPo^wx z`PWU;XQ>9hcI&@nj+*B+n0f`ke$;&4IC$;RJXeu_1>pWf>7kaVeM8y5#DniYFH0dK zB9EF)v))Nz?&Qm;X1A8LQ|4oFe-sU9PJyRotY%8fm_hgucv}NdF`9?#tLh`9QDEcO z6yH}!GtIqY!8IJfbMIj!_7g&LZ)yDfXJpL(}PN%RU9OJ*HY>}*#V6G zk@YQ@j+0bZ;=_%F1Ca9&kljeUMo0k=2i6#NKrSh8j0WW(un%bEF$Kt2%(nvog>!*$ z+bmKh4N(vYlc!RwGulHJwN1THEK>m7Hk!{{RD#cRNEz~b0$lSwVw~D#qQJ!#N;gmv zy7`0k#@Q6>@7ltOP`uCsVE1c2Z=qAQNDP;0?Y&Tp0RY!9rIT9RwB{~Tb?FDH4UR*O zujNN|zJ~&LA_by#n+;iawT9aP0f5GEgp+fpU}y z%17;Kz+I-HVd$i$rMql;^MIRnzsW}r9!+Oo zLH=ccvs|s>JOyhr<3xD8kKG6eq(Q-3MxznS9uUPn0RRbyG#OBxy%1L1K^vUrBkdCNUYk=bzkgt*W1i<6%qUWVGL+DfuNx%b* z6zi)L1T@~xgxcuLub}J@K(xF7^%-gbf#HC!C&1?T0U3XRi7!VMt50bH^FWr3%m7flN~l_o2NrB{mI_u8607Hd}kd;Bfa znoc34_%bb*fVUF>UU+wn%%jDpyKp$xu9e=#MR*lc`nzD?en z+nPs1)l2eDTi!RHNijsLC&@xYil0F%n+!gA9FY>wiuq7-L`riiMB4CS*eQujF*lx*m(crs6r^)IGW7+ z9yZ?ZIGW7+9kwQhc+bh@ogg#rO)V-%M?2?WYEWrkS(|KmQjH>BzNK*dT-KrA(`1yhYTJ^ zh?8^Z44bhrIe}`C(S@{aOm=aN7qe?GZxK zh9bUwWU*xT2u(K9ZO`?DCf{V?PWcp}M`5#9nRDO*{3i zB;KCO;PSqe)P~JrBk}Dd-s!Y`=!VqWNpi2#_H=V#A0;*5{XyKQ%IW7*tITL%)Dj_x)Ikoi~( zmK#VPsi5Qgmx(yD8575%ql$-Ny)7VfR~;tKB=IF6%m!p_u!4Bo#zn20h%Y4r+Vm~& zhY==;-?f|!!Wbg*7O|1UuX5f7fllyet)u>EmF+hITYcq#Ec*J0;Uiu4CkWR8*=c=* z_*d1-BK)aeRZa36hUgmy8#=0Itr@ zNe8{nQ%13`<$1M!2DzXd^XOm7pqCg<2CYdA+m8hq3)&dC$+#fhJzR11liJ|!*k0P* z7i`?EHp1L0fOPi-TN$}KA6q7A0O{@vwqfLMdK~&W5!u}rY*WbH_0he30n*)I0?ul8TM?;bWHUC*8!#PzdV~qo}Hu^3^Rp5;>);VYj}f7!yDK>mVN9s`5a$f*t{nwF~Ni>g|yYba0+jeM{tZ`(*=d24OuBxq%MK1`2D6?MVPP&?Qv`iwLcWiXF1~ zeN_q{QRtV0r!#;Xs9H9P2;c_#0*Utkxg^64G#Vq*06-pfr(W}wKg?8^A{a|;jb9`E z)aJLq|1UuHvs^6b0IYvF5|03yQLl21SL)0*-*^?hn=jYOd-FVc8pp$Ts(x7LtCSfJ z`)v)YqPaL8w!qpZfN-_BSL36mwW9i1J{g;2+n1_7enb_;SVdKrZNJG@K#g}63iktK z+i$XUrnY|_L;VW?+4h@keW~pS=V8YPAlrVEZ7hku2EuxPZ2K*;?dRoVnFNq+zg4z< zX#rNZ0NM82W!oPI;Q$f2?O%~?|ELqUy8v$cajFUyQ5l4ay=wD2P^OyWZSZUZa6{3l zvCaT)o54Z?{ z--E=50NM6ll~VY>+CJjmynEYzq$+_Sbjob|?Y4&AFve}~)dDsMIxJTL4UmOCPpkj0 z70E0>J2M>YAN#>;sMR9KTE;^rgJ-R!wi2F&c@A1?8%$3iGkDZpYMVd}n>G^PiZqtX zua5}(Z)jtqzfCUny!z_-yT6T}aA)vD+TX^@qf}bY^|y5;EjC`Y4Up|>W6J|&jHHdl zvT3j_kiMClZtGeI^u%!(t)-(`j_xv;Q+to> zPucCrHkYHjtrwSr)$~y1;6nAV1yGJC$}57(8;QJl2%QEBu0@9Y-&p0u#3nn;jl`xT zkbzc+0%Wqv8;LE6NRO{C0*bi+HxhYi_^TT?=73^y$c@AS#7IXBngiNkkzlbkB<-tP z3NaUu^$Ypf+s5z2_GyKM2Ox{rV^-M=tx)-NK-Rax;PjDQs0)h!FredZ6uE1;_PN~_ zNcOUxrG=W^7D>o@u&pKbMXH*(lcc!>QPo>fCPIOt%88~uHpY0Oaet;*D9Tnb8x{>_ zq`5Du?7hf-OJ_IKYYKOjC!|LYBm1yvq=RtVJu-gs%qUYCWUmJNg!1@W%wg62n1@^x zXH#7|2-iyCH;3j!iE^RZXaKFP3D4ucc9xprP-5%;zMylU97-%H zgx%J+VA6Iw1leCDTB)aYerB#LkJ^8nKwM${HP=zeRkBHOp_;ViDO$Tsmg9=$y?a?Y zn=Ic*7SVc;NjjlP6HU@dO`30#PHECQlXO~>UNcE&HEEwo`c9Khnxu1@^ovP4uSn^( zAaq0HVq!fy=|ZO_E+z5~K)UqR_Yy<1 z(SMRbBIClPE&U~_yg$>bYcCl6IML4_9TX@ZC+1V2V3&dx*sBu_3I+Z9ft$T_l^PF~ zRa%{xLhL6{%&!0)3##H#@j&$X8<@_F%0w&0cU~J*7QkCxTW2X9q5@gO!VlpdMgC}j zGh?pImwUn;={F2^n+n%Ug|C2d8-Nw=F%_OAg_8~q*jO_`fun_q!fb;h`cU75whroNVF5xDr za4HxlX@zN~LO$dK7B1o2$lpZ@FWjwgJ`{E|6>gRa{{Z7Pt+0)$a5t3@EUYjE(`q8X zdE{<|SHZNwRJcVd90bOG0Iu>tQ(3ueAoN3oLNtU z@f%V|TeOo+g%zam)kI(3o^6QRmU;j-`h>}|9X#eHX$R!D0XTQwWmI%L3odMAs8D!a zR%13eXKIbln;QLVz$#d{8lNEl1Jd~XT^c(UfazUR;V!9=zQXpeR``jja5yOh3oFb9 zPZNN1@!bk5!BlIAF5w$e;b<@p2XLJ)nF^q znZX-eC5fqw@CH*!VhcuigR3Nwx2H0AgR4Z!r1il%u4^K%<3!sd7*GtCTvzO)GGI)1 zCE9uN=M}OmF^}{K`#%NXJeR;L5F>q*)Y)_HS++!5tEZ%!!osPk)Poxh_xuatGxzh`=<&O4x(>%1}{lj=-o3)%r>omVC} zN#{`z=uxMv^D0^AVl1PI09@yOW}S7)tn-Eh>uMO`I`07gD*)k|{hxJCqxhSp>P+t7 zI?s|tlXaezAiEjYc~(LPk2)_k>-=F8=ousR41bb+FQAPtW0lV+Rp&7YSybl>DA7?s zcMjRH>Vt%BMwVlPew?iAQ}Ea{09SUo(r5^s^47X1Bv@ySRFypq{x8YXEVR)S(kWAD zQi65278>3a`%VCP3T&$>gePa%)lRVfL5y??jGhR51#o_>czDPwsZba#JyA_jR8RoH zA+q@^D?q^&DgQ&4o(i5GH2MumNF!Gm9Uo%H4oPs3V~t|jG_J=UkbtW6>Yjap^2yI@`M6ux;a@{q~t7SN)P_V zzyxc1dZrCaODV>CN=p?=!q2qSbF`{t&$})4yvx!|C5J_TN|_DRTh(nS>h%cM&Hh6( zSS(VJx&a0_Qj4S)Wuz7*IPZ)|MG0*@BDJd-sh`q~T!Ae4b~WZ5J_qpN`i?ocmO_V7m|zWr4sIF0?${d#2y!AcZSKrfp z_3v_Dgtl2!-3~n#Y4hw<_L|6){~^Q%j8*QlNX2bY`<4LdK8tkUb0Dk)u={>eIt($N z+*don+7&w3eJ8U0rbhNFj$ul23=E~^_z0~2}2LGD? zOi~Xff_^$)u1yx8kQ_UwWnJYW>2!Q9RY%x=tqt>*CX+vr6?|G@K z8~w$*yBQrjM_kJf5 zk|hyg@0SV8rdr`CsF6iVl0_2ss8r& z#n(mF7je99O}EX>2X-Ki4=YeSC1EBqUQ&8v$7{VW#mR^DtoNljK2c!n_8@rQmU^X; zcO)X(E_5PmnN+iwwD4+qSscfZEb6kj_IHZ9EUq^p)b7jUEtkLyd#W9t+Ixqhm$gK9An#bq8jrS;~+W)z(bwp z^A`RntvS@KLjDSX>nWv&hB~!HhXJreQR$Au^{7JP)r}(`8|D4g4t$)dgcn!#?+g?+ zP&&@C6NO2opDP3`f_> zs;l_$o2V65kITBFuzDP?`fbZX!CND)-ueW3D#8y0YU;kiD_Q?LePwC`@ct7k`(Pha z??15|3K zF$7@)3RgeljExaD1*NbXWaGP7`EcnSFxwhY*qw{D=7s|)qnclkRz<6aDGE-+23n4j zRxaL&*cPm))oTrrcLaIAQyv>R66=yiPGd+n*2Ky==95X}Kf-yIO{I}Kwhb|6G@zob ziS0;1nvp>x(AwA{MmEI&StpU!Wzayc-ED~Fr%mZ;c;|3qtQvt@SE$@=9=Y3fZaO#e z&e%Zuv14xJ*J6VPA>I0Q5`&^$l2y(QyJGnf3~pI*zGio95LX9Y`w;~+#;S(Zz@f^xo|xH$BbEnZ*|K@c1h;v< zY;#O)BzYu8HdkeZ4)-H5T&avMv?cj<4EsJK4qXyR47cB#t$`ek;m!UGZ_;u!CZ2Zw zGu~~>NIoMSNx`ugeh`omMGB6^@IgA+=Eq_Zsm*0iIv&H-NuwQu6EWO34-2tfp*c+lbmgYU%J0_Acl{#*P@- zC2ykc86RL30E&wdeqy*IhF@uw4}f;Wl(P-q9oiWqkMnW4cE<4INm<+jF$Ru8(T9iZ z&PXU+tK3_JMZYw$Hm3a@6KiA2cxd5gOlxCC5|VD?&+4w1j)NjP4{Z#KksbYYQna!W zQ=Fv8)~OW8a9N_(vyg3zr4XDD6GFUD$Ca5GBfUpoMl+t0y3=iKNOozAp4F>XEsnsV zdU7nNWzsokpMVZr>Oag#C$UP;WTRy<$yDGkO7X-VAP**##W;yLrZ>(Z05VU6u`&+$ z8zB4u$m&xc#6GgjtIO~Z03dI6%O&v^2!8|kb*xBrTWvrH|N6yPr%%PyLpS10`@kIl zR}01CIjy*FsZuw=M6wzp`jJpg#oP?^w@f;o%^7y#($?Cj*@xdCJT!F9kPOm5<1J-s+`s<+oS!A3#5qG zmfNEvSP`#Pw@1s_Y&70c-X1Ma@l}gwnGVEdTiXN3%h4Q=G6n-;jMh7$&GwlSSsz7n z!?%8(C{c9Bwl}&ybv%lU7C^s>){-i}_ch?V$|pf`JT{>=7*|a_((CqWR88 zs_7|p3cGh0ByU8?zBUYDB8zq-iX&Y%^o=Oq859@Muuqu5rmjTs@+4y|E# zqU0pY>Q_XyVfCzJMN~Js-<0ZCMDZD5TkRBRS*hBL$MRLGX|Py%Hi~Z_WtVzB3g?p> z)6h9X)hTv{uG0+Z8tLR2Q4dRR&@ySJr|OweJWbemo_R|C2(7S>s|mhQO6TLI&QUVN zq|Q-Me1@1$$&8AUJ%LL*TItV=hLSN+{Bz2}-kJu4tC6x-z7Lf$gs(=*sC^o4K(_jN zLKQ}M5=x^${WX#URTR;06d4aka!1$>2h(Nsmj4^8ZqLEU{wc*^vHT6CaeeSa8<5F& z=N%&h=vMzU2wxMiJ59x%BK=9MMPD5B0%X$pdZV)x-w48bB3`8Vyd<&~wcL3S&Jytw zjWb;$ISfL}v5fPbrQkeLy%JY0RQlz}2w;{p3O9%7a4Xkqv2qH4NGZ z14PFZ3pR+5-KGyMjJ8Gcz`X@oF94jil%1}ashm5Njs8GH$L$b2OnP_H4dL#{V5%b> z-1ruddlk7-{J?Hor>Y zCW} z;6lR~TO<8O5+l9*e;7;$0qj)&!FcB$kV|a*1%P(IiU4@bLB}I>;CY#;P|Cw)9E-Fb zodrFiU|R@@`GB0dK;A&&b%0BUv3WM}pgNm~b7kHO=1;~@!LFU9TmlawR*ml?t*Nu2 zi(2qI=s68w*Undbh8PY$)3tQopdr9H>Sdzta34m2MQ+#+I7kp-^8x6tnc7Su@U8?!9 z`a`B_ox*I)mvJQ0+6^6#ozouTZ2=tK9ZWMiRb9!LCdYzzG{D*8E|cz>Pf5;E=;%+) z(TiFvUTI9DGk_TUaOT{5K?R!3JwS0&t8Qm*oXxBHD>?-(vt`}B%En|U{ZuOTUa*W5 zY*9I+;8(36(a5sNkvC&u5RfD}f@eGo6GpN^eryROMIyEnHXx}AY2O@3n$(6} zSL$F5BExu6_>HXbMstHm-T}?v*{eZhFPgnlu^ZhWaujjWc4H%{k2WM4MXC=Km5&fc zXrzAE?Izu0ZjX=?@2PpD0bf+w9>FK~`HM>1Blrwz#!(k@wx?yd?Jb(fUXS3p#Wpyc z5&kW*Ze7{~sTyf?dp!~fKg=V$q18seU~L?TkVkOoSF(+R5nKk_Wvc6;h+2iD_hoX) zp@;^AwBAmFW6_0;Zz3Ad&!gEUNF;<1tn+z6aV>&hD>U(&O7B84aw~#wB(Uy(j0)K= z!eA%XpHCGXiQWEw5%KIdc&J}QHce!5OMgHFuZl&hr2&xZ5fNzVZOZhBD82w+XZ%gu z*gYafpFyK$a0PorJjp7?;@P59K&{X#f>!~;zODcQZ6f3fa4z)8wRxKeJC#aSr>!z< zKi(9~c@c7Xe3*>$4M$#te3^_w7ug=?p!1bQS`s-UPc*$}Mfe%?9U8fR)-;0m z&xE~zo=_wx3-oCMX(1s(-m$TTga}?6@;WO~E8UR{!5Z+%U+)l7|uzJ#RG&0vj)v6L3A1nn+f3lFr=_i3btYvIlAXt);6>wfuI^jdgd@(*jc z7Cwf3<)r4s@2NA=Gn5f|^TGFfA|je{guOE@x(|oT_@+<9NMnb?A0lJwvs~fw6gP*{ zk#HH*WiAMQEq{hn*oP$o+Y>Htv3imk&fgQxH)k3Ag4v#M-dU#>-y1HM|A?x+;oR>; z(ZvU$A}}n3`p^Ep_=D@y#`1q-Ro&OHW_0E;3-`A*Lxflk$Qn#?4}|%UT&m zySoSYj`7erE!Ax66?-!Nn2+1g_ z!$Ia1`p`6cL49L5Fk6uC!nV~Q+50l~ssF;3*?1DyL+zQ>v=4J&K9@bG{J{w86T zbeP~Z)6)>?3y=pj3Qb0xvale8T7_8#YN71$C=Wmi^*4od$`mRJv#i!aYf!FfR5m^m zGTju?DO0FTnB^-ibbSJjrvOAYecW%067oXfQKYV6K0hv|^%#({XyjWKT%71gLn34n zo-_j-1wb|;v5b(`K)Pd#wiCcn0%R)^8vyy`KvJM(E7X&}Kxfk1nbDiQ8qshaKM{k)e!NjGHeor;s%fSfi!a;IUc1>`t^ ztVUu5A?ZMlA#oUxJq62Zto+uE3p-c%>W_X9aQmY$@d*B@pv-Rj4<8?b&d95VuoFJGf3C& zP+W%4DIeV&z2dD+$t(P23>ZE{#`Ea3gly}TONd{&`THI+egHQpM$Edj2x_ZY)i4wqRJ z^?O7_&E>L#(qqeFuc}GH-9Y93j@(3*E4P)gq3nE8m$Cx4@pBbI9};t~P)JSWluJ2Z z^HZ(DwOwTgBHQe%*rVpos=w$;#mj#BEqqo*%@ydQWy%7)}Fm zKdg{g=svEB?0Z|d27A!SrtdB2&l?JnaVp2lOp$52jK{-#f>yvx1=vLP2l#thF&pA4 z6=qhLPY*EU5>j{=`OY5{11%Mu>1qZe1PV5Qp_3vRBGdr|bHaS4JPQSc6z)R4o8GUW zD;*W6gl;OVe2@&EyPxyO{m3I%e?W+M_9~tNXgDEx@Fuc5V31Ty95GScUQ{osb-quCpD`Sf7AP&2oY}kF8C|lr#*~({@-Q2%X1}I z0@6iB&ME#p;ohdm)Bts#l~vBXyR3PhWj*SlCRg!ugVPsc=#y5|;8ebBEIYNmhZSRv zaLrd_vWH+c?s0F9hb{CGItsK!Nnk21IREWh5%fzsxp+wQhQ+~ zAo4$ei3-@tLvb7*0$Bytg@Bd^ft*z$SWojc{W)+QLPotA*#83XB=M)>GK5YwRQ$%F zxY{pJsc4cI1^y8Lm+x?y?Kw&0t#IK+KxG(aDnTnc7+3oiDcTCQRZy}7;CLO#4@g`j ztxc40Us*lNI8g=Vqm9l2Q>{+b*J=;P`HeuGcpgfaDMTEA=b=JVMyLFgj5!ar18*CE zv!mjbt8CXV${lnoEaw8{r=F%xoziog@lBlX0V)UiX$*9Z0=ORW(0i96o#7>P%3oLL zM4WHKN~LH8lq@4f=CHL8$f=cB25UrAi$gs zXnWr*jZT?fTo-1kzeZI)8K!Jx?ttRg(MFZ$Gp<)u2EH{$ee%UHOCAZ+BB(C}I{>7` zfDy8~UP7l#i?4)PW@w9HPYdw?z!j`h{Usmgaav^jp-QRNCt8eiGzgX0_i&k zxd2B?AeBh002Fotl3j{FS*m1s7bOoR`#!|?;PWWiWMusU#vcJWBY~vN#ql&iF0nbD z2IXsDdjXE6Kt|2O;}U>lJ&-d<93bRnAoJ$q{ZN48T_C?A@gpIh0dXyWn*okvKvp3! zAHatVJ{%$aj%5-R>eUe6>uXUC>LU@@tOx_RzEC`#zUEK*HW?{}@miUgQtCbl@oTmY zifC8A2uj=lUWG45Vkv-E;qN2y9ze#xCnI%Pbjn}pH$DsTn?>rVTt7nKdw}b(;=ik0 zw4$z$4GX#qGgVU(Z~!R8_pnyvk1d3n09SftDWN4 znkK@g`$5%kv=DzAB65JkNZ@NJ)VFZXGR5SDG2J;#A-o=+|0wz-(90E_Zzrma6N|j%*9V0&Ks7fPG(sU< z&Dt2f$>`@HYf1e zA-=sy^=@QVU5v*tfSesb1|U%e5bhgFhVD_PgO?rK#oVC4J!+_M)fp`VjlSj!1|Ia= z%Q;$hu*QnPOX!ptRM$g%uTU8%sP@9-CjeKh;`aO`^)MHJyW|$%nO@A=bUxT2n}70N7e>3VK|I z(5VKJaOc6+2=ERgYY~c9S8XhDvbAEYAdpZT|?xR|G86vP73{qc(3tY<-eLd)MCPzVehZ)irj6 zS?E)j+?v0m0N1Gy6Yp{G1gbH8XYE^+f!T{{ybs%zn;?7~S&gwD(h!hCFa6{r;Q-_k zo8uxV{eYDL`1Pqjlo~_mltq`PGD*J8u@$_l0JyjcsbeIEA#}>D!e?O?`f@#2p%b<- ziUF?Xir=%xnI%Rf|b77WRn^lP_p=32F z3Q+v_l}Kk;gie`?&WBk_w4#rp*vd5%BwkVJM3yHr0u5GL&-o=^wB+v%r=UBR8~z0oieKy7jBubRkhK4D47f3Hrk2As{n4J zlSmu`a2qvI+6umDu3|DA$fOC3MQPpAc^OnC#QQ{wO5I0$kG+|9!6g(^RBW zrlQPnOU)ORqGzFGB`K=RD#%2ebE zw^V9Hm!RYVDe8NVB6F16t1__orBSX+xTWzn)h;zwVG0LuyV#LP2ILYOH%Ih9gI+t?3;6hz41E63w|?S^El-pJ{79QAKeX zLZ^Zx;m*<|bv2%e0bJe_WtOLpggdNUQfX;D6*hPUc=k6#Qr%f9`(-Q>R&q9db*iIq zcUBqn@a9dK;cKa-m+$Ve9@)orc0)6Joyw*?_Z!-3jF~;NpEUmtvTMD}ixzzM&Ama9 z_ko-y$-b7~&5u&ix8d9$DV`(X>HOb3&g}!FdRoxB-xw|tTEt6e*HTM`pP>J5naz(0 zrS%6?F5NgZd_^wX=+;B#4pzCOI#cH2x1UYbi#$|!yia(&hw4%H<(^i#>r1@EEY59@ zVhOiU(=WMcouY%}bSgSmd5l7+TnD6TUyCry^^(e_D*tq!<@Z!BRk`}B_g1;Onj=Yd zyv)VV@HM5|DqQY#l})M(?-O>ZTvFZdzT8PFm#RF8b7hq)6`iWQMA1=3=eLT^Rj#jQ zHY(Q#QnjyTw^^>XDx1o6_CCwKR4$e4)_u8KJX8noV29vW5KYyGJyhq&Y>xms@1eTO zeYqiOwxKGI;9Obd0!61PKc(nY?Nx~R%KrE2`XqFJs#R5n%ltNSchQA0je z`Pch$JE&Yz?Y~o1xztqsxXLEgNiy4`%CkIF7u}cpvC5??_vKt!g@$13 zYgB1$q-Z6=IeoHpH=hYC>Qx=1{jJbQI!{v9A#?hw(b`y*`UWOMe&zU9sFPT_VdU=! zDCW;J-wG`zjx8Y2*1mkD^>!$qS-Jwkc_KCogN56n{6za7dih0O%v z34lD*Uq8&qUh*b>Doqu}BIe^}+%Up?YrIaY9UzYPkpBTB-UT>g6yslLL}!XZtW$7m zqMt&@5$uFgbwCo;-6@wvk%3FFs7~Wz2PT1ou;J;z!20(5_ZZw5BvKfTw-U=WR;VAu(L$ zv_G&C-;)Kn=) z(@sT)TK5l%o`qJQiL`c}x&O?|sC)f|V$WNQR5S(Hncq&RqX4eH`x2!(a&c^qle6nqEF1Z)g zm;d3_JBn7>Nxl!|mo(>B;>0r`lSBXeP<|!s00{etxXQ}P`qQsNJg^48AqL32U}xea zBK8EKgoxi^sRy&O76~DCfUp^m#YYaW$Uc_57LTa_@+G~WLiu3dBoH12@XhQoRa!&n zls}ZwW&9E9n~9Qh;C=@FeE`=*#qSwQ?rn-lq0|{7a3L+)(53KSd*ME!^64ns<1*jV z#r!DgAH~DFqTVWu_)(7C>5I}Fklr=EfPCVS08kEvHh30(_qD{TU%|DN)Cc*?P=6we z^2gc%`!tl_-&r=9>m+-dcJ!4ten^ zZ-bd;+GZ8-KghH#_y0}Tnm!Sv2~^i#X1`Y0YVHAE8dW8n`}dmXGfbTs-Z;^SvP!S_I} zD;Yd7rba1B_syx&0*CR}VHN>6MPI3cz6axOq>wl?!(6DNlWHl#8L7y$w05Q{gx2Ct zmqMCA0Lsb~$}^T9{e~|+LF0GhD)7Ez3<_{6L^dQnz@pE_oeGJi>57i3o(|z|AwS4` zMj<#wem11~572I#+Y0jEhRB1q&vys%okDg80XeS_el6*uLb}vN&LxS+Y3rX5yrwq+ zgS4?KM1GTvHs>=~`KpjGDoiR4O|J?`<8t+W1n;!3R@(U0!sk5gJQpHgOXJS|Tu3m< z*kY(Sm8t^ryDAFdAO2kv!aoKh?4P>9H(fcT^}8^P7yhS*@NYe27;QO|-&vQ3v!{m? zk{=Rlry^%YNC16Q#@Dh;t+H*aLP35y#Ge$&sxA$&U|^(?%3e&q6G8I4_n;3@;f1uG zz!&eR#2HVvVPr5PrJx{Z8rS7h8xSLikN4U>ivZ%KkcVg_v?%0iG7xWt9IXMws*p1s zfm8|NF9g`;hXCf^R8>elZy>%32@D2OO(A^1xQ0Ube6gQG_*`*Kg;X?$Of7}f zXbi+(A-AatwS)NBvTX~o)loS+38@>zzk_YFCjqG!RI3g(;ccpEeZ?kdA_!19)d^{! zasmixsE{y18Yw%w>j7!3kc-(s0u{mspMw;_2cCn2_`tJ`4?2g)964O{53zK60qtir z2=?jrYAqy!kl5D?iIzzG8;L|4BtD|v@Hi5^vXH1i;%|!Z!${;%SR6-!4@!>;v9zAN z8x`c&QilcArbbqu8XOtqLl|CnSV$bX4IubvP%Rt&7p->?iG>#4CA8QS+G7f>)I#47 zsmujE-vr6GA6oxSq{+=dIxqFepBcXpREJ(yz)uOik9_`SM%7>@*RpP~xnP$cN(>3+qt>EzTay&3NyA7f8;uC&8cU;*(t>C`)ij!`NsCP_?X;GS zCaHra?KKs5^i26&l*gOqo&>eJdX1NGC%6cMETQKw%3Yg>CRBzcc=%j7Kzs?C1_;Iua8I83^PS5`Pdf7RXpsc__d!4ahMhz67+K z4`ib%XOt@Li)3&yG7|RU01Lpe7RUx9Rs%#!dO57$ZI^IWO!KXM-25{^Lavq73fNVPb zcS_X?wRs$E>K$3xe2yH#qc8U!eQP_6F8Vd~xtM{8M4PPjBoG^eaYX zIQU~e$5T!K2YwX|FZqKj|AkFQe7zc-#Q?~a{vzhlLv983c$fHCV)!8AAbms z?*JV9!8f$oO5~ft-wXK<0XX>WU*gqx00;jmB=!MviJyai*H`!eEr5eR;4qFk0PYF? zc5p^@v?2%pc;pWSaPa#a5#lBx6#Pqp%mZ-n|AoYL00;k2RZboJn{WxdlW?h5{B zRVaMPL#1jeQxyCyP+t!I889^t;1T@kir-76_63Mu%X0nZ#jF6D6Wp9OXj;F{?rYk7b0-~LP13ca25 zA^4LiG-<;s@*5#S0j`tEcXtKf}uxtQ0CIa~yiNl0E4dfOQe*;`Im32CF#M^u+ ze@IuLlloddGw`Co5%MUkM{!}dN8$GRCgid8?-&_o|WYf4nz9~F`p$)*k8G^)MLdZ8JLcVzh z)CH77zIg+Q*8%LCOGsQGgnZ+768#n6`u0a@?Elj@+3nxO+Dk1zs)+d%N7n=WUcs^> zrw_7v!&Xy3&O{&yrxDD6oZ&z^AkhwxOEQk9L3t6_IDlg*ki>7XUOEe8J&^G8nAXn$ zc^Syc%h(zNINk+v{U^u)9G?Lx{#A&#uL3y+r052YtNjV&0+4w0fk}peL$g4>@kIv$ zIBo+u;ctlN0cAC?*03x%JE^P@CoWGai{`}DNo8rAxG~95fW(!$hVTYB9s+VU$Pk|c z99@C*#hw1HU?Ba0Y>hHRLx5v6kc31-;EU||QE?zgQs5haV=jh&ob!$Vc0`#%$#yOlSvK#cN0i!_%tKOj^##>ySf`};yvMk<@xS(Uz)7zkR z03w^-+uoxXy+r6#Ft!S+^(QgbT7Ye*_J+6uXphgyV3mk$M-Wo+9WZJG2Aoo&mm#1= zeCP9s0lMg*B`nZK5q~u;l#+#_pjuxaf`#Bo`1uH^DFftW0%`oPA!-0};(>gG#A}4q z@kD%p3@yfr36Ng_BxM@T64InS1&B{aEX4tiIY63I0?=|fkXQsTT_N&6aRee|tC3;! za%{qX%fPq@;7(SI12B=j3no+x`(-6=MW$NnH92;Qry%<2GRfeTbr$Fj@+w6M=^K#$ zU^T&YJ_tMe0bETJAqV^c>Ti@obq?xeh{k{%s&g|W3IN&k5y@(%?%kyOP;8A$r28DQ z27;%YgpL7OgT!-yTw-(l49cg#J_7K$?uS&J@WR*=P%}Q(`WdOAxv4>CL(~Ph_Ik@| z=(!0xgeyeZAAQ%tjIxBwN9B?Kr0+z&3t6QwF$k^hXbGe(R6hh5SPbMX<-_XIU1&29 z#~^nUfX~x>pg8dMqe^)zUSm_NABXe`@Gb-3>R$%=`7r0`kHmW*z5^II63AD7OF2I! z?idB)1>~Lsu&JYp!w@=UntEB8s#byz8UpBIKlW9A#BmZ>HQEGO>SB?{ezb$n25^0= z3at$p-WJ$pRqFJNUYNZRRM7Cn~%hFLa6uE?1~{6z>zo`iK&E8Bv$K&ssp(9 zorT8!U!h>s`zC_=7=U}<8%Vqg;NJI*Dnc0f3|D)IG^5_feXnMBLsSE}&nq^HooujB z{MbdwKZ}3&52$5$b!7fI5hB zsP8RAVm={M<6TI+2H?K;kkXBzi*%#!^&#EV_s)Rl1PM{!YtRF4765XIjr(2!uxtW) z{XqQ8j=-kuK~jUh_aLdEzBdm%vjDCXHHYY!fmY$_t?d6--)p7vbl-zNT2kMuP@W2s zHbpwxpZnn+nE4ox_g)Zw2SssN%#>osFHgmWo}uEM1lv#G{}GUL2uR(YI7khUJ$aVW zP+JLdPi~6rOaOPUWr_oxRi(`CrF(Ku@OCGaP)a>n{3Er6%Kv1qo(6^~04})Zu$a7x z6`$jdeP9^{I~9Br_+J2U!4Dwu1wf7q)s>C<+6Ijaza#rPfD2wvaTr3U%!2E2p>Z!P zM*wtj8;7bkMr+SN8+Qw`B;fvv+t>*{2f+2Va)WMTb6jX)21o%3_YJBwhlFWdcpQS` z0CHTQY#tZh0O@7GfX+&E5(M5AvBtCTO)Y4g8M-q|le9kWQIG zBZ7Q(X`u@cI734Hhwa8917&awMhI zU`C}TLr*CbT8?80@&pjt*y95V{=X0diZA@1!r$>h)+wksA5eG;3SZZTHJ`T_4nFfx z!tcocl?-o$2D-Fi@}&&PBxM0#z9I_~gRJk81sZme`xqht;JR^7OJD&f+_zL+r?tli z5FkQv#ZU6(u4H?9(*Kkp%@Pmg^9wWo;F;TCOJ^Wl~Sd}dsKkNgW{xQCZ)0&RFKY2~3d zN?E`j<7`-{46@E63l!H8{SEN|07q%=NDKE(RY?lgo!~`4c`ukbsR|ve;Ei?;=@6@w z`JW+8A3a@c8mIR@ja|6CISJ#%$m$KlT>v=`0{H@o_X)`c(rWA`oD>8a7sCUB&skJ;7svo zI{DhK@v969FVT*q-VO@NASsIM&5+m#kZs#YF&d(R7(WcMjMVB6f$0l?tA&Sp8X6`; zcmChIajX|2@n_)S+d#qw8NvrpR^xpYoz#o{8pz6`AakL=MzGJ9T7Sl194r91nm3j_ zI`Xj87Ws{!r2-;1!R}}bWE@xr14@#WN-D|(uyTx!H0|srJN1KovQH~F-GtCzWM_Tg z-F8f2y@i0TLBb7#eTHk{%ts8725_DTy(28L=_d`gJRm8!-Jb`k#=()tN$N9Xjex`u zfNQ_vF?Xph{~?7CJ$xXu1Af`yA4xKI>?R0%T&y7i>SAj6H$Y}CM|k}T^;Bv2XF!Rn zuNCfBj&PSN8vW!$5Yvl|bm5Fu)t(QsjE*&T8l6hMFDUn6uyb^5LaAQ>i2QHRru3wy z{}Acc%;C=M-@p)OBW6p>gVTdq*Yor>%b69sy7VNV?3q1jW3KHTFsxXDbLR2(6*k=zhQV6{ZrZ)l3M?J4YR7`Mi zbySLD=Wj^d0J!S@KVs)Sb2O|V9nrxub{-gQh+u$gpUVHw*hwXa4ujjaL-yKvICM0S zZjfsZ;F>*N3w6YW&yG9;{rSN@Yf;AbR5MqjMlw&u1kd~MBDLywrkGBJuo|d%Fxclu zswS!EKE@DT0j}Q^zYZ_%U!+Ly?0SjeWWQCgPb|vG!ZRQ^nS|GR4A^Saln(wYSQoyM zgo}bBA0c7toEIQ?j)d!mNFLKcdfdj4JV=#_hUDpxgmPKQL2GpV#)@ZU294E&UcW=$ z;X(dFky-eQI~ne8R0fUX=b2uNr&7+3Rk?W_&!74}7KAAOkPB#_89-`|#dZ^b`y7+w#6wQ3ye6G*h|onIoqGrJw*g%KRiqEhhQy+WB=Q%-T04kbB-1N^ zM2*8B4q($em0BO>F>v&UO~3Fc`XGQ!zh|1R@HBl6`DXyG(7R2O7s>EO*rM0@^De>g zi$G!?GejW3@g|UcNbCe~FSlN25zH<+b`cFui=j2+@xgC^6sl69r{ z9_%wt3;8~djc|bM3k^)6l*I_AM}mEJ5F-V0S1@$~xORDi8+ZgNPt zOK|WF5~f&O0>MQj++6YeM=Tc5L@}zN)=^11N>pcm3$kyJ{G%%0l=qw{Qc;rpz3*e9 zfb8+h@aq>q{)F5a0C)B&{#t*<66hZh>@x@cmz_PSk<9a~7$-a&?a5GiDJ4U!{(rO?vS7LARoFz z-f@TQKCQ^q%|By0ck}hCJhGdgKx?Ed#UtA9(KKg)>_C~<1GpdQM^{yt$G`zOc1-Cc z?D7FzGc9*JhE}{H|7WNvh0ffUoic@12U{oIC&c~qIVEI>-BhHF!Pd8k zk%qz5(7XcR^7oZh(w*GA0Xt|~yh_4bf~_~S@G%G;CgEN82#-QV*zz|@*djugLwIYj zHRFo1T;~Y`pDTs%#(RX(=gFtbw@H?nF~)4s zAW0F5lDTi(y1P{>R7z=4sEG=dNS0|)L`ADmk!*>ml#SAB`F@{s zuDNFP`F{WS{^rq3=Y7t5Ip-ONV++SezC_KB%A3_-8Bp2ZFmq_~OGV38zn;C0tgy(WOt`tJA@bT~IUwt!cZ^ zdIrQ5<5^h)`Zl@JHs^HZiEyl1-zKZ?VyhhJmB6cmChtN8c906*Ea?+?1Ry!q^4cMU z9}3Y?(QDt28l7Ylnaz-)$hY;g`~Sg%x{ah>2**H=8zEwn9G^t!o{8L$1o7z*H{2cZ zE&=g*5VzdJ5)NoDKg>DBzG_%@eiX%xn`OzGMM-m#hTY3PGU#zQL`;(7ClP8ni4hgV zQxIDSd8J=aST5GOIoyCMoeti5U$w0>rO|c$vTpK%4Sj z+eO&p6g6$z`bUTw8E=1w=U0%Z8R`lAsx&IR$2nL8Uwk-3(R~pw55&hpTuPuP@KT>! z&2wn3{ou9DHMPseKS%s%6K@(p^54d$gGh?yW^?o$N`785lX8@OpsU69xg>mmsdA7g zwH$h5F14$U;qS>MogLP;Ig{GjOZ;p{9zDj^w29$-4V(0C`90R-Z8~4h6f@GKS0Xxym^}e?x;KcxH9)3fWMT%n=|D!~zd%eU{sGWl9BX#51>#Fj3RJsU z;kC5zl7%Gw1J7Tehb#`oB)Rj;gyO6Cr~QKwuMJQgH`sc@+6;7bXk2`j({Tqpw*#X? zlH;Od0a5coXVoP?Uopk%hH_X*f(bf|9Qw9wjghB)_%- zJ(AL}&(-hHPV!R}-`iHpSF~u_(Wp*Id!kn(v{JORfN1^*838e2iFYC9KB#dZem2CR zQ(2<~og`-^N1N76>1Lv@yiqO9)d1430Gg{O3CsY=7h4!+lPjH>BFFUf37g%5(X7Ly zA8EMchd!iJ>cOgzcj#6~Bu82anV<_3J(Ji01xB<9dQ7ZtcF6sR)}&vxpN4v)Zc9JC)awt|E8aVb+)v*L(!$9qVsH;t`+P;v`IoWMySCTy2aBS!X!l5?y9 zyE~*qjqV@mmS>IZTgvR;7_93XY>}1VV_FPmH>;(?;Z5is3X&^qdhqTrZ!pHm`lh_{ z^5#|XEbUePT+z?fZ3eIXjhu6M$EGoR5gXsvuhaQY3iTjKOR=#8l#GMeO<*U830r&y zF{$Y^Y7k!lv53GtptJd%Rwr#obdIf7eVbI2Oep_`XCLr741(GlFuIV*C_;ButA2!J zWF&0Gk4C%(pyW=7(+HdbSTN4vU;fsZQJNoY(hWIVIGJ5}n3X^FK_nwT)Q^r4o$^i@ zi;7!7a%1IbfJ$r49A<@?B|U-gZ}sb3wAa?mYX~j}S~GtW*aKq1rZsccV@w!;*39z+ z76Gl9SfI0y=%i-$ONOLoB9BMBf8jGVb85ioLZ&39W;#elq-Ksp=m?-Sb0&e)fS3Hq zmMN&2DW+!R6T^5n*m%I`ciN43^h`TKFljP&_MSp(E)fAbF=(*}|xx40{6$atSSR zO#}aCXW=F!hXS>5Ah1vtw(uP3&w#|Xe@$IT8Eo}r{9~e6q!HEEHt^-BzgEvrh)VqtIt|9)rTVAsZRmz?XYtTKBt=-RW$M z4s7%Z+epr0SOSUJAsaUP_XnyciRv30_%Ars4*}%pwu`#THpTFN`zvM|4NPyaSF|biQCsaj4Ngo1|t1H{kO={D#UJopEM=~&ZX z61Fg$^y@&fw6X;~X5eh>3~Xf0#m0yRerNirKIS#ZzU^%632bzNFX#si=5Xi?B!^eE zu}eSWv>erJ8rv#j1xj9KnF;=t1H1P-h3Xa?N+`ST$qp8PzR$1 zj~MiMXZv{cv<7PXq(H`n+FF^w_J#2F0*MPPuNfEBek}kg7_F` za(5u(LLIA`oCoh5F`1Vh9xv6TyBm=f+>+?!OqMqA%bdyeh`;YlPOj!^#)Z07HTeg; zzk}oo%j@1?HwtIRRV(@CrY-D6e|#-O!+EUD1D$s-wmTSb&ZAv@d06Hq$%o9luZ3p- z&>8ML0#AZO>FcGvoBhw)e>F=_H#* zuMmyZ87*Btb|AYIBpxmb=R?*k&b3n1pGehhkUgpf`H(e>wog%qphVUzu3dn65EC|C zySQ{AZ%v?U7aN{ow+HCj#ev0q4*^}fI8ba%(s!T20BaZ5^9XhAV%xKPw}BE_yIB1k zw|_uP*mMnJ`tz&-0$sycvozwp26PR>>5O_a7NC;I15qNs6vvY=znc zbPc2EMb;dEu12(ans3JlDY6>Tlh__WS0f%Fa4#@VINsb9VK5T1){!UXWL{cx89g5) zyILOCF2~T=QjKh+EM>Gr#LVcu)Co3T7A(5hcm$GnhT<(RS&&;c%^I34_}&c3g4Zmo zuGFI3vPodRCD3#Jc`vc!2=oa2PXxXYLQcKE{W6~iKu^6dUCzEE&~yIf3%L_Tuh(<_ zKfS`x2lSl(`mbzlmChk#xk zSm?6h%@EyO9hf1ynWIe-zX_RW0n>)9leGJfp z_q&_AM(;w%=%3se$VNsF-p_&mNuUSs%L%Ll=Ei_`l+loF<@CnD9+H0pdhq@b%fZP3 z8%lRJ+>L?4l?;Y}KfU|Yi45!ApN>Svfo8U6pvZ-+9=#>-s>^IY_%8>V*?R~~1m>1N zKdYg(71Y~9&yl^`ePZnJ)e%MYmWbt@;6AF(ZaAu zlT8k=M6hz7^`}kc8n!j5Z=RKQjV83>*g1Gc}03?ci?(bOq9JRy#;0Sb=;R-l;&B#fH2Y z@dg217JG!i!+=_n709f`jFn_uS0LXZ{#Br>hy82;*(ifYRvja zfgDC)2#5(=#Y$v3QSSiNb@4H#UiqaH(Zxz+FQ@CYw;82?>KaR642TJvu1J0a^#L#| zlCoCibTJ%@u2kdNy$(TKHk1E`@lT+;t|z|3WDdlHO?O?#LER2Yud&%V4M8_rd5N|5 z4PXwUcaqi5n}U3oi!D|^V-nDn&$rO@D$teBdo3f?x&%g6K97(B%F1Wj}IMbV+nCfnSA?CDG)&JY#UOWch$qRuRf}YTfeaRq$R3G`sf@ zmgj zIW{f=`muP2m5VA*1W!u(HeE{kGm@9;#YV5qiycdp67Pp6>AL-&S=;0> z-E*PfVAzQbqLV~}tVw39W%38i;$_E7Bg8WoQyUJ8zvLK?g{gVK*w=oX@U(>Sj0XNT z$9MruJ%mxSF(P1ep&%RG8u)n>O|$VP0xLvF*HC8%LX$-MxefeIju1TF@5r~(bYN_ar6C_+G>;J&2pYeku{6d^JGE(B>vh^eBE3VuO~ol zJf9D#^KDg2y*z5-Ik=ge7>bsPJr0#A-&U;@=4-9n&J2IE zL~#ey{cef2ygnj(ETN9Y{;-64Hr#6o^=!D$66)FTuYBXOuKt!$^TCoEIe}%@XRE&@V7X)iGguLC7&-)T9{4ghPt_ z-V=^T_|XEt_wvIDoK+a@-L)=(a|m=hl0X@O?@9=aC2(Q^foBOEeFA|O38+89kwt#j z%xvzxt82kE1z~@L>k6Dd!bZZO{s=!Ru>J^>fzZ#Dgmwo)=LbT6I-y|_HKsEeC@M5= zM7P$5IIPfkis?E>($%Y8M-ye74|Xcd3OOH46>28&RrpSz?!vIT-cH?Tfx7Em)V@Gu zVp!yEmtM1u%fW0HbvRMRJK#%&zO+`a>p4UdtHZ$X07E9OaZJbz>f=EEsqlpt>rHYGsTmh*Y%aa&`-`#ekDUh8$(%vq0NDz zzbY9z5E$AW81jpZp*E&8xka`#hg32`v@yaYSPRLaj};eE%noLJ*wWXGB)`Q>YGnGx z9?T5XPhp3A^;6jMeKuu*@l)6-U;Px$SjXHM7(a!b^3_ja<8lr*t|wCc6n4s2KZV{$ zxNG?^V*M1JV)|9*jSN?yosK^#6FKQ~<_f?#FYIC>H-8!No&xQ3JUZ1xHvfv-51S=Y z#--EDpfX}B3m%}Ij!&nX$kp39L;>3AFxAzF*ZU^oZQDVlIz;T2uMQFK_>Q+TFb)yB z=hu>oz<=;);}Efj86A55#G4fuhlpqAYbPJ~Gq>-6afo=1;oR~I=bC_Vh#YL&VGSv%b>|?iwB2 z!VAPWM7$zj9U{JmiF-iLmAplJ@rp7YwP)q$N@FRESn!9_1$tHb)&E>$;+%(e11NAgF@UMt>DNs*yX@7Il z5F#f0>S^w6Vpah4G#6(^(iT+lG`AS$p?UC#o5dGNUkKFGT+0IyuPIPZb8`tiE`+#Q zto{$J5vZrRuWUY>*e+dHl!>Rgi{LmLsHeG439JXk)7;pbP1%a9%&8(Hp61&9%a2=u z#AM51Jk5=>PhULE^?_F!2OkH?#i4kKW42?QB|c;`#Y&tFxdj~YH1`ZD)YIHmnCcBe zp5~sk{Ar#GW!OwcaI^TmldR>j*AGIT=324!uAb(m5Pu&~Pjfp7YzO+`u-BG~wFa0Y zxLLeNnv#4toEc%w7sMn*KOCk)Jp_`c-fC*gemH0;&9Y$eNRz^OAJK=Wxkp5wd^T)G z@^g{C$kvhjsFFHL?y?zip61NAv-}!xg`361TFaz;?y|dVH%Qs)XkI|4QGcc{=Bq!` zdTD81U65R6IV<@yO`p6{FDLtxca4^>R6bSlXu6ni$fN0!{-!*v>-@dnB z9!<|g^GQIvT5CVeD+1cpCZ+SkKOiPD+SP81rg@hD?P`OnrFjE^b~QPks$Fe9F>`@- zwVuPB*~O#>U2O~LYk_vPcQVqvWk9>yb%$U8#DrhF+Nssi1+=TJB(Mxr>1u;_aptm+ zmM&c_b|@P|K)c$z1YQ$DoH|}mgJuP^t1Tz66lhnQYxC*4+89wLT`j9-nwJi=tIZ_v zFfd&$ag!+$; zY*#xCQzwB?SG(Hs^HbV36z*#0I?1%!oE;4$)v2Smn`#%2rZY&J3bd>JMqnq+WOv$fxnNU+YHK__vTAH2#jHw*cDsGjr2CA86yB zL*M}r6Mk*{SJq4OE(6;5-zM-HsM7c+@fE(QzHR)?^0>qcwDG?{;AtVG@u%`RO%Js3 z&m-^z(8hnc%_pBE`ZrRPN#j2N$8Hf3cfR)&q_|2~`SANf^ z*UpYXj$MdPdHst#mS1C+SQD?ETe0*NP_LaCtTab~dhKjM;20sqYv(xxdI%w2J8vQ| z45-)6xddhlAznM*C$I)2hg<8x3S%XYrr$Fol0RMem>m$kB5kU8G}V&Kw8JUxl58j_ z$piT{US?TQOY%2%_W>`lgp?#woaX%tkvws{vHt&9lGGqOtjdSQEb=VEkn23rr63=VvnsIC7zmSGOJT1uWOA)e1fa@+Ku_vdu_YXhC@F5o`s0S(!85WAYOy`HNo%7g7{j9oGxkJb18_=Aewef^L{xU;v0y8 zXYteSXQp|V{|3PU>(QOPEH-0n@ef|4Mqgg2ff=JOZ>oWLqvLG|Jk%@AyAH$?5SuSd z!*wCVSr7$%sfx=XdP7{&Kh68DA4Dlc{{g%yL0oRnj=Gi!B#7S%vHrR=?-~%l7h>oz zS~KW79b*4u=Arc!SGIXqSz-~y&(kdNB1HSimRJcf-^siSG42|h@&QDL$1NewaW}Y> zuOV8DGAVi^Roo-V-=W@Ojx|Oy3o@r2K@*To#6dSR8UWoyoKIjjhzXl+B7O_C9hAOo zRZK)s+B@G$i5+8%TI)+xtR>D}C8D=fvLuI){7`FH7f8Ah$VS+pQGjzkla|MB2Z->-9{_S z;WH6#T##{HQ~T)h%hJ3oppR}ifg6B%bi%8T?s=jXfzltWp1w#|JUVG?P3*KI^yvNy zjP4SnM-=$ah*8P0&&2Lu6eho`l4EUbQn7VPU1O7DpOF>?nb}9#M;AxEAYswI+lPfGU4?>K#D)8SFqf#eNpzJY_Y-PW~Ds@1qlhIa~ z!+;`uQbA^uX0}egL~tW8bt1`HC;7KA%LJwOThV$5xH`GmW-nAHPX!j_5O1WUAY-an zkUF^vHGP5B$qNLY1*T4fSL@_gqJ9LWZ&*DOthtIhIpvOn>f{5<+h#hg)-I=HuC_T}NJ{;Kb5_|46 z7VHC%;jX`Blktx?`BMe{cCjGsV=`*)2FbN{aB%Hov>Wbj39{Epgr6(OJmnahy)_7~ z0w#Nstl7)BgYPd;dcPGt0RdADUgDbZ2W783$lhSF@M3{~t5}fiorjvUL2|L}Y6oR+ zPmsMCBD|8_Fqgea2u=hhdy=f#TT9eCpj14^$=}DJ>^*o_rR>S`X=d42-w_LM6l9*& z+*Ux*op{y;IvSr(pa+Nvn_8L!H3O8ESQQ-+l!}nC?UV_XEXi}~W!cia;s}=BD)1+W zC8>x#sM!sY@7n(5DniD#jz2NY=#Iz*EwKD6MC);UFah1dztgIXcrL__qW(qJ7G!)b zCgdc+t?-WkMrdLn z8ETORl71yf++aUVrAYjBl)c7XaP~Zw$zojySyt4`0v`vOCZ(b2lY;05$%tHunv15H z&Z6VvQW<=~vH3lu?*_@GA&c@nJ2L>$7PN~#5`ubJSc0T>U^@?cFo{=;c8hm#XaR-rnF^NZwcF zX=sy{?c}UPWWIn z9o3p9eHuj2q_+f_DvnY?`wF5@Qeth=WoR1VEIPh)uMEDRNv|XQJ&@dCUo5UsRy65f ztpyG=iG}?I(XC=Z9>F2^P^Tbyjcu7uS{^}atre8!8YScZ)ui{@;r##5q@@{Lf3<0Y zv@OX)y+Xe~3N;U%vEM=RFtSPsBoFQX;ypCl3j1DhHH7mDGv7Q8;Q|_W8G^$>Nll0! z2y6i{VT&J0O#gdXL<8~TA=VIh9kfp<;WAV%I!lkLeu(JoMp9xD9|#~ODV;AOCi(5X zhC5iUDF4$$NEQdcam{2yN4(+;)LnN}&4r?FBuS;HxsqoQOp&a}nzNY3>0? zWKUfku_N9Bn}Y9Y$$CMdA2}X7(md)-W)=^Uee6@V&BKUyl*1b{kolqw1-l4h0K9!b z{3?j;1U>^=FyDR<)9`J`EtN!$EsS0)2BlyZ-N)-57@-=0kP8Jz&sr8n?{h+%5coI{ zsv8KoP|(R+6-HM%p)U8+OM$Ue5D2+YQ17h^qx+rER0JLpA^nJ~9|(0igo=w7Mo*$5 zwBk0x^btr7z00(VaKGvxUUT~0=NgO)9qXR-;e|!xMUnV=$$Eh050Jdi@&_%j!q>~m z;*c%&h|xzovmv*X9%)dRt6n_YqUU(gb%EswbcMa?v>_WE<&4c>3RQFhPj&%Bf8;Iz z@nsPI68H`1QNJsKT9BUyid5)_CkvxA$W9MYK9FoXp>n!g&MjBGrBPPGzzqa-hG%UIY_*0`K9F< zvu^E*Wg0PiOJQ`IliG#Ek0Lc_MumA#8|piD#fwArHgtlsds|_n^C_TK?om_N1OdrE zEmu%Eu5!(C3?tJ2#ryC++tTHr;~><_TZhLxpp&bBk={5Hoj#fQlQ-asiiDY!5^k?Z zc-bYCBaxbH6U1}T22D}VMJsI9jprgc7*<9_<)6ee+zfF(#p?>92VVqtV?d2G^!t{kQN*{PNU>q!rvkIg42&43Y zIth~XESDW`(|>qZvN_3d6-?+r7eim(!xefD3(@f3W=m(p5+Ou#5H;5P*>aHN^9`&B?Ev~I;3=dDssPBCi5ZXo@ADPwu4v6*?GjB|4Au{e{fGw9U!eEPMf1>(=c zhJqIJKBby4N z?cgz&rcpQwXq7d3*eGU34qq@MUqSjyVz`Y>4-8um1k%5s4s0wI8=DKG*NP3v_}|Fx z0f`&$uE@A4?#7_FTSRIrJ-|tw@fe%(AUX7&3Mo5udZ`&!P-f}t5itp&PfvdAqtIs8 z&g2<$Lm#ia)jG=2(=594)p7f<>kcxeJkN!KsTN00kAqDg_U&ZrR^X);S`+fHFCkeU zwmYk;4?8%any5R)D6e?f9jrMXHo1uGF7)S%38}LMsGkdx1MjU|di|7?!>*GM+17za zy*>P>U$iP|#Y9JiCBS+(NTxySNVw-+g$(8CoX4zvvSyK>u8_&;e(Aa&vT*RiF5w<2-x(A&m?1y12477 znvf^non(FD?%=RKaTXe+vgqDDrW)nuiHpn8j@BGcoLod27WsEN6B|+gmYC3Cr$@l( zLc!<75k>wh|1ETJAml>9=S9;ZfA>K``l=rg2u+eK9bM!XcCw{@Z$_H;0x+-oVFBYx zVQf+4pX?Z4p2_|iFrE6MA*R@T8Vh5eBEL)+rG<=tg54aDTxqMuwJ-fNmT}}tE6k^H zU4$|9ePT-)TdKaUphs>Ici<5VP!mSibql)}xVo6r} zuE)wXKznDKQN|ko=7xIbTcp1V5~qx=$PKDx0ql$GO%!*~5s7pw$S&-RYFT2-c{0t* z1a?uIWL@~`M$~Dbbe9#afq+yDWlLOThibd#E*1{C!C4qA7A`2rzSCKF3^h{&3zDoB zJ}2rUvCzPZ4l)*uOO|ARJFtrl?a#(Fdiy6?L}+Ox@P#DCdKY9*J(=RXNz$>iS;7M) zJLUbO=KfNxr1#~Sj zdB|<1dV@LF;y+z|n>Ic$sFF??idx?uA6~|Mrl_FwGb`F0f#5MEPrJ`ps`~Jx4`&TEX%TO+7#m&? zy+w>kPJ5&Ae2}bJW%?x@N_*oeR+z^k!nYMfUv9OjD9m1<^hI$DDSu{vRKJ~P2S#1U zolTG2SrGljWxLe^zN|pseU(5y(O%AC+XjUTOO{|zsdZEfrYF>EZkV+uW=TdE#kB# z(9CZPEOde|n6O<*dS8%uBV@s<-WI4HEUJeU`7KVh^)efgnNIb$f$AAy)nAeR1xWr7 zQZ1RUsQF(53*};Ac#%KOStxiW&C3Ov`9A{-UQsCX=aAkFBx)U8^AY*dTq^(0X}y2$TxnynXqF(-VS>hlBDE5oYK zgQq)4_6n)ifpMdJnw!TuFtD*pY>X-L=Q|s7k(~v!W(Ef~iVq9b%vRDjiH*|AHtfEh z^xT^R3*C{7lok2ur`uB0U&1CXPzz%N3uR#ogGnC%648(aoB0O=)r&>-Ek%B*Q~f3) zE1c@b1J&{i=s}yT_iUP%1(Nw8)mlNaOkiz16WGYAkBwW4{P&!VE0OIBH2d;P4e}T9 zI>8s%m`VDhV&kaFHq7RLZmqr?SQso8ZY%PeceNGt1(KgS3vUG$W`r%|J;!+#kaR~O z-N3`42VVGcsk0pZoo6tRkA&zCZ#QuHcnJOUmUqg!NhbSjRW%?JCl^L1N+#r0c+T?( zfaC|$439G){j1|cXxW9xi?GKZhPWTr@u1g_R;6@BmPgE6v-xCar`6%uIioQ8yR(zA zl!HVdIeU6#JAtq}9hmO*5#ery(bkkoGnqgzF2c*}(`rd=^SyhxifZ?|>F+yIp@^Bg=+@Lj7Hsk%^2J>ZljfyhgwXeH#e5 zP*AA-g&9dFG!%g$B6Q_C%+8QPttaptQ44?;YEPi5Tnfd-TrE_=GWr=vK3lnY+LcZo z=($&!ry=glU&5k1e7yl?{V;o};)gm3UAjNk zepXA)CYqE;TQ70DjaD}hi!34j9$vCqWi_jZb-nOOD1Aea?xp?-#eW})KXQmEndpmj zH#N$PEtRtIQXNfgydFMxztt)G7O5GQOHK!#V!49ZYU+0Txgh1a$fV-$I`M>!*7Bs= z>8RKrXLw`>Rz?G6MycjDS+cP(oDE&V>!F*qNp}cYTtCnvs{0@9H-(aCsM(3~1EW

Z`v$u0>^p_Q$)Tp(Ngz>jC@O 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�Tg1roaNrK?5 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

_sNfN;^Az`}$9ZT5G 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

L(n4!YLr&T$^!eU?Ig@s*$u&`Hk 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 z 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

iI{0C^dAKE6 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

`nRM~SKL}d{^=&Z$+DjS;_C23AIJ!LPq ziQrokU(5|H|?ktM`VYY)C_HHt`% zbrr|!Dpp&nOV#y?<^YMl1j_suDAc3~FVqP2i$D2hEB@pwQvAtRsQA;;iujbTSm8_P zFC-vUktv3+f{?uGLh>pL$*U?Puaxf=DEU}(7wvN3>^h?~#cpZUGg>P?Un@ReD?VQ< zK3^+7?-ft+R{ns}S;09J%x0XcwP|(STAM+7Y1h5idQ2{`x`h*Wk+dRvo7s*tA$_HI z+AtX@&09-pUO}aKdnwHuET<`bGBw`BeXVd5k|2*hPqjY@>8~xAy*dT83E=rgk~!y= zO}QwKC_0xjPC3+D!icZ-c1M=Otftg>Cz)vKJ1@T4(9!8hR(I&$0MLCkNgq*_8{f65 z%Z+r(#pb*NQtXLnLh_l%^o3Rg`Jt-M z6S~hBdOP=PT<)@~LdIW7&Nu1v6s}fwKddsFs3og|WO90rNuO;`DFjH}#g==a9=24P zFGXo&%QFWB3VKm0XcYKOBMo;a?*;z1OdF-}FPLqV4?AUrPOb{?x_!+Z@W{qJg<@{o z-O~!CLrKpB(zhf|kXi2KJ2IN&A?sEzIyM%)l!*14A~n6Y9hJ$AJ}>seHk|p7UER zMa|=tCRTYRp<_~>Iya$q*}Rjrd8<0=%k4}5u|8pILpP!((<+Fai%4aa!sI7Xnfao9 zq+V^CmaHuzl@$i6Mk*nMcowNFJy?lFs$VWg>e4!Ooi0VG*^<4kPuje5$sU%Mjz@<2 zk(B)Jr?!`bon)?im91F+HLYCJZY0GB*G*O;Po434lL;I4`cm5etu_;d%UeoF-b_Nm zM(<3^zG_~+vu>fqBbZ#rWU?Qf64&yoD>zqthBYz?e;(00K3Y2h9j$k3kaC1v9E8rHG#uo}G?>f)Pe{|0gBQU>9MKLpI-%Hw13xKsnNc87 zNhJF@8%`%|K>DAm>kTa4HzNP?zFyr3@*S=Ck4W}rJNVN4z&xcPfHa6~00C(b`DP2S zU)zWzO>;)EPzUGeHV16A;JFscR9)~pQ+OK%GH$54_E*|jNOd#M`(_rdx|Qc?yRaFbU2BX=KsDxAE;FVR(2X3?;O6-O^5w_CBy^Z}21Y;V1Ce zM4ANtWg^W0@s84YHWob_ywzypP{7Y8(k!r__h<>;eWX|5JuO$^076B<_e-ua13WB| zCV@vL(hP9MNSYZqJK4N(=PljsHIzi>!qt>T-7JNWQU>>Wbm3lLyhN^50<+e3=IgWZu9k(T(} zxz+mYztAuCI$ac8%YdX+f|MD3$eqmBY8UfwEO3q}?FecqrB zOltN^Of%-4q6Qkh&H7Zsh9z~GenETlv>jV+>4K^(CM*dl(!8HBD3R-6sf2+|!ccV}&TeuareC5;| zsR~c^M)q~G^{V5FRowtgsu}sR25vXB*G&ER+vbl1ZV8NaOIO`wmk$2yT2(jvoo-}x zgN?0Oo~;1!yMyGvQ%4qT;5Ek1A$iA!eLcdI?1!+8b5k0Wsx6XL@?$nDlU&_V(w@CT z!BDrx{@8~}Ta58DN3GV#ShZ~!w2@`NmDU9=GdszD<&yO>cD5eetEiwM4oQ}J;@AU7 zsxg=sDm8@@rByEOVQ* zF*?;u?XMg7tYqPo>%6WQg2Q=t}=N1MJ3~Js;vIKuJA;w z!V`;ig(q4So>;6aJkhG~#3B{`>r~+hV5*`V=uRxARI2bqtHO=XRkz$Udo}U?k$GyGOk<_eFr82mBTI};Jv?-Ty-4~mzBv7u=L(_$$RgSTy+YFbE-+#rM`}vNshqeV}UWH zlE+Q8JZ{}$?Qv5rk6YL9xG6mDjDO0Fv~H2qJB+HeHi$FE%8hwte-uU_&KMa<;g7-y#2F(aDg03wfjDDiB!xc;BM@hdjHK{K)zQ4Pbj{XlTe2LB zhF;=$G4V?qNO3To!{cN`LZyN6FS+n^`X-zVUs6WzK}z$^qBJ7WH!Tu{4Do@tTEhpT zZ*P_MO{+BDic0ehrZnF+O7qR2G;eXGc?(LXm*X%`CA}TS-MXWVyLD5;xLY^fmW?a- zgE-E{qRV=&wUEkeAirw?1+U+^dp57;L823suW~`7Gvo}RFjFWpghXd4Gla&xA!i7M z=|hnrB&HEr2ny4)wV_)2E+Q0qYLOizI&P^SG{%5j6coC4DFuxIp(qRzy}uNL#_&*x zLBd;0F=&hdg%~8FMJWc25up%+L_jIUpfNraVvvYAr5H3ui$V+%VW|{@#@JDaK_YUM zV$c{#3Nc6ow^9rm<4Pe0iJ_(x^L;YB;An5p(gj~;T~T8wn4TubVPhj{(7ELN7*3TF z{9&U-PErrI;ZHfi=ovY^X5ke_3n_zWRvr`5_tB}PU&%j+mLbmzt_8nS6=_(Pre?-pL~6aKlyqUfAaM!{^aXf{K?n1 z_>-@vPy2eyQoz?&NWPvz^7Rvvua}T~eT3xeAtbNAki4F){*3PLHDA+s0M7n5N_`GW zU3K&U2D3}fQ^m=&?J-|halWqNd|k!)x{C946|3VD?Sxbb^@qDtc1M4dR+=|Y*BxPc zADi^AUR2|Sktlqm*o}Adxm#vdX=)YEbLK)Da`R-6{aSw`j~;&EV0G^XSRUu4FbJw zfSD@y_%bEAV`<**sdMlD@}-vg5-a16Q>%>u|0Ma}sk`rGEy-kCwn4pH3CVkvki1g~ zsa;v6UTexwG8OZsL-#5`_j*A0%0Ta>(Z4x;Q+X6Ve}EE1|3=g3Xr}nP2)SBkbIvL(m)}O zVe_|JijG`e_Y<=O(NY6Dg9r9i8iNOQY7YS9M)Z*y*T#>OnspK>bSWY?stCzzBBXX1 zhuEN+vl~P9dIq0v3`oWL4m$s7*1AI)uSbEDCLId{rD&->JA$3p9J<$3eFs&tc_(Z0R-tXEJJoEM*U|Mi%UfLagrnw?_4KQTdYnwJ zZj@Z-xmsRHLqndDYx*uDG_;q$^*py8=_YLr&W=Z8EmkwNx>k#;He1cr&me0m^;#wG zTygcQa{cOW*ReIqUZ);sOfIr4Pdki(>UR|*FDp*mGg6svusDy@XIQ6ug*_vc)hJ_F zr23t=Nc9_Qk;+1x<#nX`jqsNwfohcu^B0_i$O*aZXebZ;& z6bF;j&W=ynYyiF_ktTt(kZ{pFTWrfjNM{=SU?x4+BGp0ZG*WFjMizWs%?#&#NM zd?EQJSQ|Dx`!$f4puv9M+DKQz;M@}FoHn<_RNWF&Gz#5GGxMEEX>=u8PHEuBrdFH+ zzQ{<*2Bw8&!4Dj^c2l;xt*2;qnpfGtxv8gsG`X^YbMsFDaRX%oXP2msghgE+8-(No zgOG5X>=M<{t#A%WFv(kLuQyP}Ws!H>HNq+H;_ssPQ#vs=) z;#=uHqasaX{K_s@Xt`jXGBpFzb;UP;?<{&Y`29u`N#Iu!X%4t+;Q(NsA_>Gdq;>EK z!h~vz1AIC~>?vT&C+?V*U{gT0u{1YA$KFLu03Vq`#~hGwp`zedB(_oDKPJ)~@baPn zFp)+o4{QaUZx?|G{IfLZ%m9fhsv8)O7h3}Hcx3}0pT?mn;9HA+1HLS=jRLPuq$%L` zbfuZJah8TUB9`h)PddoX6ha*gKQf&&%mCk9uz}x`*hYb@?ZrrgbW3@MF1|F(|BLR` z1IE+2-W@poh3?5DODhPbPs)57NS~CU8~EU&dxB3+eQ^fJ97-|+uC}?8mI?dv4bsf!}UVTn6=z`yp zZaI#wwQf3BbYbuXsS8g78GOUG3kil_`TZH-1?R+Nk~T8yVnkS;0}1JXsc0C4V`^S&c0Tip|@b4d9LjE=GK8!++O z+;;cHvbWPOA5rv6@C#DEoC02!NHahNVyzJPKE*f+J}&J`%mDv1ktTtEo=7vm(~Hc& z?@pa}2H5JUYtjUH26#}hwgNM^mU!<6Z>?25de6}98M2iPOem1thy;w;LITo(RT4~Z zm8Ax-Ux$?3=#R$~eH2W`6gJ>X^C%Bw7*{>ObYGDK(vy`9OqUim;Dn9L$_CEk@Qm-= zBDvhY{V&CE3jT2#b!ULP72O@YXX@^gz`YY`26$*82_~S3zCNA^$%hlIJn@9+AZ$Q_ zg0eAU6A6S3$ndOeWsg-XAMHs$*pXs`XKJLEETLs=FaHqtu46Kqkox(=xHE5JLE^=5k)r#Z%EN#nr?n$!8TZ* zo}*7QddnmW`06y^PXo_4k|?)j6Metz)b!T%)-^*d=&y80wF*6v8MV^ri43Gl+p!&L znhr(x&>}u$UF27(_iO+%W`=5zVALGyHUQ}ifse#QG5CVdNQ3WsAnVv5OM;cGTn7Zw z(E=Ymjd57(0p2T3jyC}5g(ACo6N$qT-+CauG?YyErOVd9dyLJEHx=_{Fq2@Fy~cX&9j)7`6~IR)+98W48fIp!e66nP$2{fp9Yo7z zTAPM7;RC0+r|^M4QcP><1atr`gIVOs#drf|ywP&~fTOa6=1&%Z9*ifdER(pC3$Fy@ zTFQrK;R;$t*{+Ji9kGEYZiM0^Qi~kC2&R=}*#i8KvB^RX*b1G@a#X*5@sIh7^3j%M z#A1JdASJ~D%Qyu7W?HO|0%;+Uq=EjdScZc+0FfdSYaJM|X?LZ8X>ZX4n42~a z&jbn;1rroR6xh!LB)ffOMb0M`F;v#HcIz0qIl0o+SEpu<&`e(8=NrsMJ>~KwrM^?P;COS@x zR70_rL;PS07CA{R*M0W%?xPd2JQ<5fKgMIULM{pyb1ujHpAsdK0 zP3#DVA{NQ@U0@NGwk@pGxukm@x;SW9I(9Lw-hPW==`Rw5_K3L^6%nQ8)=4TY)e<7~P57irl#17!9uhJ3G0o-ebfn~D03J~CrFQ6%fCR+4(@b*QUP>04Xi{_I)D+)$6= zf2zq~xfMbIs+YaXe2eW*i4l#1IWMW7_T*HOr|QOiuH2`qw@p@c12E~QJ;|5L3v&Fy zX#KH#)l%!Ly7@ou<-3`MsFoUMk28&aQn*$h3??&xKoz3YYbpB^#Imzx~?NLHt?IHVI>)_ zs1J3GYcQ0{)sJ4cK>Vf5UY)qxoQOl-Kf*FuGO2IR?^j$(D3ZK)gxVoP&Oq3cl`U{I zWa2L*<^e28g;ZKVu})vejJ@G@W?nK*q#FCgl*%E37}Otr^@_oR<>obszIK8gxnsu-424d>E`b=S*eGPWo8k3o|QkRuUYSXRSiLJt? zq!q^urCpbabWyjQw_N(?w#59BP*+&H3o_47noOg?kCG1N5FY}ScrjM75qb{;+LP_)j zE;BpHf8~<(dluwE6HdKw%Lk3HA-VYtk>39+nQZRmY1ao zS!?HR#ifK|$$LkrEg5p%E@wAg>o@HythQg7q~^rdWwB2=z-FVl>_FR|gJdU)G&W2J zl&oKn8LuTP#|tiN72vj)xaX4j_7CV=bS znKPSMB=sz#%9;vz%)3fPQjasLFal3r!bs|`Gb8YpC5)u@_~%%Teq&6kSG2r(TX&i{XqAV#)Ciew^ z&%6YKjsySd-MKBt7D;`|sN$r+>z6Q++W$Q%N8tU86ty9>uX(EE2rS#O`H_6@yGzn_ z8?K`b%LZH5Xs~sy23yx?uyr&T?IH6#;LB1wtOL>>!Ud!;)EMg)Nzood-8vxcA+kUk zBg&GZJ%kZRV?;($m{}NsrNsyN_WScKNYE2>7K3rIq0+#M)_KC|A^wjklo10^MWubv zoboI748pfqVpsFJK=dY1X)ye-5a5p+v2XvBg- z3=-j?6f1j;;_`dlIMpuJ?X~ZL@g2Fw@#v8hG%mY1c0=U^f5K>ylhoceUMMFRJtL>r zto&a0Aexo8b?|0%YUx+<529ts?{!`Jz3zV6?qRl-Iv;rEBB}eNS<1v9s*CWQ&s-ct zY2mzU`+MCeNyDm%D4T{+ny)J9*zhyf_vp9!g>ECAmj=`s;1?2U{l6Ndhl=2CHa*-g zO%vDOXv4)GXq`Hj_$*i1LsfYz$wc<~xh(5)i`h+sC zSRs`a^2jSFa~Q9fki0@d@`?z_D*J0{Sg)3Yt5&=4bDwDiduUoCmh8epOi@J zo?s1~8+;wg?~)pP9q_n36WW~S2B+tl(B@6k=G4zge!dR)N+X54Jiem3nMTE^gnn(B zTA+IS3)GXQ*AtS;)rMy5N?mu5)%}DN@DEM#hivo1)M^(AU&peYm?#PEd>)iW)7W#RX|KTahjR7B$+~U;T>qZ#+ zxEVhOvmPIN{)iPpeyHm6gzhtjUYZ5`b(gn%tksM6_#E>uE6EAmDc7hvvZdUYn)G|^ zDZ3v)s;OT|_El7x*RTKAlqu+Ssi09HJ!rVwxEGk-B(%}G@QtZ(s;aWoDL)@-EuTA0 zpRXx*-5;55^aSZDK=hJM3q-{o+~M=^aO;^?C%p<6Ld!XoHZMC99za9jUxetxQjEZLgia$!8irzpGTVeP}Vs2eVftLCPCT*8<*DO7n(lt%v2K zu6%v8>(;E%ucC#2sB+gr6);Z;G(fZz?SLq(rS{YKMHar4@6CpJN_Ie=%0>e|-GY(Q zz%MkKCTsB`b7?JVXa45hGO}!n*JLZ2@Z_g_A`*?;w>b5aL12SHfq@|a$>ry z?O>5eKlmj7hK9CFTN#4V3#zmy&z8<5|4KFgov8Wh)!U`HSa15wLGAqXGgA;;uAPTQ zUT>X?pw)0ut?f@Sql(@#_64_Y9?K?%MaUxVW-TJxc@QR5_&QUPP$0GUV`Zat^j;+)kf%B~ zV<0Zk2)nDg0horngKXF*sy39kT}QvVBn=r=sM~!ICbijt7Vom^gGADJ83);eQed6~ zE;Bn(WaX0eU@+^WvIpsKN+5@1v1XPZ}0d;$x-sh^5|b&%jFJ|9-w?CAHrdid;$j_=_bgsbjxXu#$M4 z5o0|`y{%*=am^A|QrDNPB(@n*st+tZsmJxDW%yC)Q@t?0Mt_s*LqHr$h7BODBwRpx zns5Q}BjEzl&D4VHl1p8eI^8ps)-XDo=Ck6#>G*Xl$b^9w5 zBk)d%GzB~$k;Z|CCDIfSua6~=!tuofK)gOOlEQJte83wEcVD+iYMW8jTMQmtIQzOq zQb!wA7=c$VVI)QG5Jn(wtN~9^^kC$XeYRLMcr~;%z z#&Su~Bc)v6Z0<%tdT5j-MHdxW;AL4BNSBSWr0Ad`3oQF;jWQuz7>@e7B&;_ZAuJnM@WpX&^E7 zmpAyJF$u^SLSZgYN^7{IfV0!H!QBy5UaxeDQB+ zBvZ<58%Z#x$Sqt3kqfiv=WSicI#IZQ+w4!0+vWulEFOi=&z3`}ueX&PAzw}!iSQfK zw#0fML0sO#r4j!zt;gnpuT7-&z_%sRJdn69n)$hS(9F;G0+Bg3fjz>HQb>w zokJFjo@r_NM)N>sGIDfG8|Ma{p+R%I%+!XRsm(A`*LbF`Ty-&gsfuz%+$mWMfhf!T zqWBXe>ak>0^)*`ZM_;!kfAqCn@<(6aC4claU-Cy^=cQ?H0a;^HbE+pKUnwE^stCzf zKuBI~A$etmv{WVi;)DN(nm@G$)2Is09+slWi7ASV)=^}xuFYIso4L9+b9HUz>e^KI z*mPJxe{|LR*w`5j+jq2IU_p$aCb7~RhdbJl{$i{9tI|B_&@}jwnO=h;Lqem{ycv|{ z?VvPo2&H*TD9vk{(`fl{(^3v>fR8d#y!1(99hrjp?CmUS=FmHPJDcDV9UI3)xokz0 zkCh}mQ4&THlDCnNyorS5EhHpwAR&4C2x+NGq|UGgyuzMb`bNKl%o5P4`<=}O(8+J< z^V}-;cS|0a3msk_(hICf;!j$^ec$7)mdxK6Ih z{Jxb)$=tj2r9<~BK=*n;_sT%mvRnTSb^6HRoi9>IzBD2Ef`rt{U%oF;l`v6fH&JKT z=O#keecRe}56fJR;DA_GZ>M{!D$Q3%1B+(n|V4{lN zcCVG(*if={+x^8>`@c(8sG_l~4U^I@H{Iyc(&vHb_1h18c{IKC%Z{dafW;Ids}2ps zQQAUj1X5y7uOWo|h*jc?z+#w4t>8c{J&805N;`S|gyhxh8Nr|7>MO2^?1%+8Apxna z%)GZ$GjDXKUZa+^$OE&|v$fK*6ig|NkumnEM_A5eTa-Y3M7B`^Dwa<&L6V?|_!=9+ zp_j6X)o+k3>Nh}Td`_A^)RwaA(~@?Eb?M&e*m8!w1P*qjdHotuNPdMVB)>isl3(=+ z$uIDP#B60YImOJ|3|^K=tr~fnuVJ}i=c`)&&DXj7o3DKNH*baVZ{8f`-@IMQzj*_d zfAf|q|K?3r{>|H|U)tMH7ABaELvA4fv*V0oM2@p%Z4Bf%TLgg|XA2iFd)$;ax^Q`0 z3(1>UNZztS@&*->x1*4}`Gn-Hlt+-If5Bo$S-;X%894W~QLK1<+Tk1hiNS2eIjoqC zF;^R7t~SP8ZH&3v7<08T=4xZi)y8Np=jxE6i#O_tizp3Dg@?OmhoAXdYRLbzD1?tl z>|M&B-&n8z-C|Z;n>No6O*^7wsbeVF-pBUcl;&-#G;ds`dFv|8n^$SxzDn~3R+_i4 z((0OJ3Uz${hvOE#j6uK;8!0Xn@t;dlM4$D#i>y~&NM3Ux<>GrI6vOxu&rt!}3(1>a zNZ#^7@`e{ut^2JosNB&kdbmAVN>Z`&%(l=`@*2D20=;duyH>f!r6jq(GvyxXb?(1; z>G?=2<3H>vAIk#%!bq~L*!_A_nE8mX)ow86{a3iW`wFRDS*55tT(DSAq7)PV+fAD?{dqT zY(;4x^%2=d2}|o9Q%L^EG9`R^ap*n|^&3hp>Nikj9%yB9*P-P_LtV{v|7x<-;;LU? zuDRsw{{bWN@~Zr+@?1So$@)Gw8uFA})BD`esH6O?=lk44-3M$-jXNFsj$Y8N_+N{w zWf7lO|GIka;jYe8>k1#wvaZn3zF%GSE8ac-HQx;30s$)wy&pTd$V!PjRgub}`mF6e zMk)ua_;#eWE;COq(hHe?MP5!@IOUF1&Vw0fBeivpcyf_#4)%T{FFPIV{zfV{khn4t zss3(Gqzpd9edkG5(i!z zvsOkhV?fB!_oqPg-ASc=X9|qIF9o9SN`dHmQXu+{6o|eb1!D3B1G^Ll8Ag%|UITcO z&uD4deC&+E*TCl(O-6Mf?kA)AAkHUj-t~m!9ZyK!?SzEW9scpyoqX^AJnS1u7|5;EAiT6=$#1jNJ+_Ni z)>qtXtw&x|^j7dCxwisuPNaDt9at3JvM@*&4vfAN2O{10Pi$rrh`u8SB3=0y>&k&h zzrETzvs``yZp;09%fg;EzU2NrU-$3%x_{66{vAr~*Te6Uhm$P}{rdPvt>1?nZ?ps* zUq=pl{_||*78qY=3FdKu*e@V_$4=z}BO&2PdrK$~CoI81Tp-qwV%|rJz~^H{Ao{=% zh&~DgV%_EE>D~XCqQE?GzuddGEYQ0@TM}=y#GP`#-Ll~OcPN?8e242(C*86@C;e6K zxmy-?vmW~Zdj@geP30pO!}cbL|C6NkFU#t#*5~MO>JH%hUQPg64?H1}rhqevGzom_ z2N9eCJ}r?Zf%I)T-5R843!CrPLZVl*UJ;V-(?X(0A9Qt64ETUVS`R$cNMioe(|Ap1 z7vfn9ByuO8syq2q-N~ou_fDiKAiZBUOo7ZLwVlB9eW@sr;a%Cl^n76h zwt@itUf6)=rX8J0AW=cM`o#nmcEUzPpz{mKcYW2?_kF3m@A)F>d$UUV?yNNW-u-gl zJH1-+*mrugH>DwP;67yi2WQqo@n+UEO7pI(H1E4g^UkX@y!Z9ymr4VlliYU{h;z!< zM}Q1UDhbZMJ6HSeTM2@1M$C`PX?v?c_nj~z(|5Mj2aA#&jv;kIIS{* z@!G&h0=HF0Fw>mCNP=aIGJiozLz&EfJc8h%~OHzHd1I3 z5^wnme6v78LEs}nEYPL{6AA(&31WdVf(ZqIkp!_o8NrXryBa_iL^5Rqvf-hU;46~k z6p*+QN+m&HQCaY@Hno#B0v?}8Q@~9|3fYk$K8YyN>8dOW{C*s&h+;V)yEs~V@OoP+3LEfgdG)w?fy7^DKH#fMJ`!)ue85Xe zJ`&S6;CWNPXBsJ(n8azB5BR2%kHlv(AMpB;kHjxCAMknKM3q_KwTUzZ{Hc+Gb|h~9 ztt1MZ_%@^|;1i7$L`i%$@y!B%nMhN>JAWtU7DP#$XhiV};8PN53dp=Li29+--2yCL zoxT-E$;)EhZjGf6BcHlHn4wSh`+=WIqu@M{fln@`0w0yezJWh2apj` zdi+JzR-(!>TrwO6MiPvN$_Rc)8mH!g42VIN1OuYz49sKWydN8bD5K-Q<|2TMev%t- z?=qKq z&jSh65{G=4-eV~tZQE{VkiP8&-YY(l#MUsj3gGSG?>U27)cQNlo3qq z3ydTPe#!_Y`UOT3gg<2j7jdsXLQ%#>+}pCyFYXb%LeYdTVoV^CAjoJjU^;YQT-t-X z?sqf6_T)0554sh*?60k-X_df5uiM(}b+^R{2(73Up%Krt;YMlTw;D~_YwH3D)=|m` zrqKf<306qT2tFt;J+>~8V5y>vV1}cRBZ*sV%uz=0o_P(ib%6wqri|Wq?vM$qVr zL${(&ar&*Ag7GQ2NeujJJNyncw>j`_>A0ZfQT-ZWPu+OeO-SwPBl6Yv@}Hg+#Sow< zTmw&+(*tFAc_*<&-XXIhy-aA1ak4$NHNZS&vI69;p7`*#jU--O$b!!>n#cmrFWA5r zWj5d^3pP%dSvHFMaA3w|z@Q0T+kp!M>_j^>5$?J_q6E+}oSjmiOEVEUW^pjL!V{OhtTiKZ1 zFnLiL_+8f%|3-nVy(Kpm*i3O$63h%o*nmuNl?}|+udo5%Yg@j`24)&0t9l@tyvhb< zW+XNOUSzW=WdpOHD{MffO3DUirX+;|S)MB!tMYzh_#I`GUNTY-I&ELIYKQ?pjK}eA zNSur>{85Kz;`!VV66Azr`e02NYjle;DV7vbo0V%EwN}%tu6Td zNp=*-lqJZL_&O3Ic9OGo2~miHS7bn&&X3VX~qM``L=pn64a_J}5d3gfZ!n_5!{-Pj-OJgTx}IFOc|d;+q2A&*nT*dtk8{cR`vbZB2YrK=$NB6!^n* z(mw?pPS=*Ffd?eg6p+c0|t~ zcNzt96e3Lkd|J92Fb!m;B~5TaIw;v}d|CqdWl44v$c#*M1O7a5O))upc$#!g0XZuX zK_J@+qOISHl5oKU?o!K3wB5Ww;)AJxQ6RHA;oG`E;xmbF3dodC6ant_b;h?T;QbP5 z+Rq2oLQDv`axR$x*#wgy2xMNUl3;EENKJve7ZW}3zDASWfJ`4n68NCRH4S{Yk%Bo$ zFw2zOfTv`>%?nI9*@{zz!B0;|MpM97C(<+rMyt|+(G>9Bi8KxTKSmPeE?w}GRV}nW zSDIqt`o+8zxOssY>;u1(S`f&jR+`4o74I}6+%k{Vd2QdlBKawsxF^+@D`&6VgU*k$ zR6lfCtQRv)wGH^sia8|sAJQCh63ARqj^@5&e?8u&m6{uviKDPhUl9vE*PhD89uqT1 zrGc3_iYSmtq_VN?baa~c&HE{%NHSx*GI31;nI($1oW4MUX`+bUVj@frwNO7lRGOb4 ziYW6#US|*z@Slpw8Dvy~ z>rorH4)HL1xpt`A04(0OJu98;{jiurF*ka6vAY9iMkLp4fd5#$0SRVDN0bA$=1Zgb z9SGnFi8K%7jR~m(@NJn3$WD>kh`ARNEzy=~*89_RY#x~3r`W)h>X~W!G!MKukxl?I z*$OU5g6Y;!cLI=^SKwRj-0Mzh7IXrTDVXGU(EB3sQxot*)1+e@ z$c!gwLV~%576G15lY$MvO-2&g&n}Q)exkB$F)?LO8hHOSR~QBUgOP$ROduGoRTj+1 z92iM3z^{QPL8 zG(R+H84N@WFv{mg4(0Phhw|OaqBwzFX{{}P;w}qN6gC|TTy9XJXgzTxtwqDunCy90`;%9EeMTg5^PMiDjs2u z8;VH^@LBV(uh~;|=g{mx^Do&C1@day**Lpr%xFXuuVKSWNI1;{)27up zGZ40;gbSCsOFF+A-^U=^LaOa?Mq?{TB!MqTk~2UK!$!JOH!YIj&`Q<_%p!h}PNC9u z$wSjSljFb#8Y!5A1lup72pcTBrLB{3j(Of^vjgE`m*lBw@E&J8XYdv-2I{NQK%IUA zbDuN>kK^z7wg~!xb)?(**VV3u@cB&*A<-KcHpM_dMo{4bvX3ELK!!@;0)91(+cRuY za0DZQz;n|;DeXk!5ozCG2FQ3BDn)`3Q}P3H_!9U?+&=9ljMLZILJ(2l`!W}s3BSms zdNU!N&9HMIl?QTM6VzZ!V2ur@!pD{XgQ=*&SjwVbNUYbdPJ-!Zh9OnBSaoys63S%I zWI-!@jG23;mFLXw8D!-tTnvMGp;-MUM2JN4Kc2*x{OlKPerXXX44WhfG1}2tYOvQF z7zc?6!g$dFF@Zj>IdXUK7~9zmjQ)LSW&9f(9oQF?Sy^jakO>{29~V`Wfsqxa*a^s5Qer-kF;lpJjG3Alm=&k60q<2TKEX%i z#V3&PBxM3WT1Y0GM%hue`tM*Q)dRdwVw(jrD2t~98In~J{F*EYBua`TkP%rW!4qlR zodvS&73TtO%3MIka>*aaLRj+$vkn#u0n6YEo=@ZMC~#8_+?V!U5;Kfzv25D2rd2KP9SrMF}UWNsnK0rNCt9LQQp^9S#2%W}yK$cjDaMuMq|%7R(2OUnXTuPYn) zQMPth2QZi?Jk^J!heqVb+dKWQ>-MrihNtHGDjQ{ZDs5L=^APRD40o{U2Tx`Dw5=@I ze-yUo*x4PYbVBl@@m=G!g1c;y)+68vjS=vIz9S%8$65rKg{8C(aL|Errr1IOv*RnrMnLv^ z#VD69kYLAG_<*cX1K*$nFKg60rA6d4kTE$l1_=gDH367WQ%)~{*IU<8HZa!+rJDio zk`|>>HHOfmY_w5XFe8x^0c7k@HZX&RumMlYLkp0#U@#P`L6(4;9heV6*PT_*;@&=@bG`D(iTBpQA7~%H7SBj1DV2y#wC%%$~17$ z5W;4ZR;^zM`KBF~iQfVVB|#MugT@sm?2;dGWhHZ0_JTN}(P}WCrC%v+x!np^Q&~KX z@lR=0D+)ozgmMvRU1X;;{Tu^6I`^K>EgL+70+GlDIt@&FSA=AdC9wZLNw2+G$gCBmZmVEb&2iq;g%mV zmTBE_I!2sekLr|ktR8*F8l%q@Y4rJz;*=E3uD$$H`9nt*Nqwm}I|XxdOgJArQhk?L zRb+?sHw{(viz{r8RAs^R6)ELmBdND=eM~vQtOz3~sqN`kFDS+cFzXO0<(QFl>$}wh zna-<@V9p#w`nZvF0@1Ip-rYNQv;oRli5qKcG(FutZV$g%uz^EaTOsg9g;6ym8Qg`s+8` z8Ugj?ZyYqBp7+LdB>UTPQVEHVJ!5+U>e*)u6i|mo4zSR8kUiyUrgv7^BIvk+##4JS zZ6W>Ka?&gYG~3Hw{$1S{J2kMnp*&P%Wq4r1*6Jv_nfu9 zV5RwDV)~_)Ifpyq75;vTknnd7S6aSfHzUaK6-n#$!0j(1=)6O-<8msOd}P@{{W*E2?|{eedZJK`=lxM-j)fHm9`u2>`^ck zs%eme4Q)h=u|A1ZK%s>?BtPFTR;u>IomwFO8|ysD`2Q%%6$66`Y_gwu?Yo3bwmIJv zPuC2IqR8_md5Mp}lx2LWJX_wvRhAOpUd};W;mYfzm?%oI&y2O}0x4djC>T{VV)}xY zj-r2KnX^|WEYHi=t%s`l$6L;EtEk7P?cj<8A~;ASGl{fA;p4h@vZ# z)UGntTvJ$m6DH0!1O5H^rRHFx{-J4H~Yh-KabJ zUKb4lE0xt*9XfD=LuPdZ|LM3{TimJBj&`)&jpKn0xyypz|1?S-Q|K!OK!W>tVmk&2 z?$ZgWCRxhLGViYzk-p4^8u2?I8Mb^rO9n|4Vo(g3+PM(iYkMVeYq46Alq?m0wwdfT zc9q#35L>iE5cT;&tWOdA$2*JeZfM11Ywf5jC>c7*?K_0ST({;jT|<9h4PyqrOLQ`vRO(9u*odUB|M#>XpN z-!rB3Yb_Y0{y#y_J|vz4nBXOpJpV4A*F3;N^2QjNCpdHaxkO3@1=MNd+^CTVO|lr{ zGgNz4V%P|hW6fT6G9jy#&t77mRff<~{X+AYM079OraxsxH1YndJRd+}ZzJlI51j7V zZs-%AxC_u8GWQ1(S|WFMIg+3`g1NaXUgq!Z3W>7`+|1QyX~0A_Ve|KOg~SO4PA{k= zN4?TLhuX)R#WH5C$Cw4UW39$Tqhsuzv(mW#{Naq|54b5`sna19oen9sVB59|A;xJo zVH!&00ivu78&@r&?f#}&OGMq{j3~mub0TRokV=W@1&h>*=hNlkLmz$b% zr@Lyg>VlzK&AcmAcHJ zODcG#EVwujpxLi6?RSs0m@Xx&lfP1Olrq&pbjn6~$xqXXNEFb1c!}8!V=0Y(g3Xks zb;0H^&kS3yn2Tv>%5sS(p?}_*4hsOcVxQ zZ~2G^T(EeHC4OXA_4!ewU15Ro_gF3j&+4#TaJ7o~%sR~|nROcCK0~x*c4-lE)h>*@ z>CG+2F!J(F6xko~YNYg$-wcuTkL)U;n%IAMe2%OOyeN{UUT-iB-0@|5k1rG3@( z!caWdMQFGWbckV8j{plWhooWQZ`mfdF$f86{S>n8pj&kG1a7Inp=wOs z@h4kB68C7`{bFaU8tS0yY{B5`9pVye z#h=GLsLG>P@jGTb`c%0Wr(3RM#tUnN&A%X|BB?{*C#?A8NrvvF6+`aP@eJw$PmrO3 zYy$DT>CJH=u)vdB_m9t7E%5l&MTw+EWXE%$3LWe3`u311TQtkx2Ddi#&7inY48Q zWDBF^UXU~f@8>4tJa%grZStdzxx|FQWaZ1Ti%G54%rIpER zQ^gry5nq=l&nJKMmMqpCS-fo)lgtTnY^Qh>XuM1T+T4qK6=mPuJNvCNIvT3J7eWfrDZ13cLzmM5uYB5`ai^B6wTw|gwJSZ`^Wn@wVQ zl3FH>^mnn$Ihy#LvGuDbd@eDG| ztSe)g#n~Gz^W!G5JV`ARiGPY^&QsQPvCLvn(lW0yiRDRZnQ~&*WPfH?>8aZmDetCO z?j-R0Mr!%m9I}5@89C>-GqVg@qTN2J^p`B6%1@J)z+dKknSw10T6TxuyoGp?119wI#ZLvE! z!)ZJ7^2hOH%p6uYHO^#!>+PW z2VVU0oRQQgj4F)4H=LO>x~hicb}KwxtMGKC!qc@1Pxs3TPuD6u-KWC4#|lpasiInD zx=)H)%DH@CUg7D&YuXy;6<*>HOVv=0SbD~UH?W&ygGB* zi9V?fe-(=c9%-bIBdKm;1kMdGk~-@2m?Q8bMhZESnmi*i0{_8CfsxeOS4BqP+l>?$ zN&PG_0w?}D$OcAICmL1S9(dPR=ZvIYWK>}Uu6<3;NNQiB3M23n1B|48oEU+-XHjCJ zPwD`pN{+z1cTLoLSN;|N*JFk!=}-U1BJNl-34CP?mAweFJ}?>k0A4P0YT&Cgho9_| z!pnsbi05iUO;$oouja>-6+iCP{CKio`f;!3$CG{hcuREn9uUtBI+DV9r82-PGe4f} zlfrq05s2r897*B4!U)8310yM%R~UhKZeS#Z^9myn&kc;Ea9&{q;<WLFimAh)x7Hfs)?d4ZXdl_iC$K9XZndwi zu@wyS)^*JZ&7luCvAJI#3e(s1%?srhnS0-_nD-|kKe5e(`~)`%5jL6JWIjrmL_28nSm7lX#^CKH3i(3p!sW3H2lL1OgG#h@|s$;2Qrz~*Am zmsd>&nO8s;GQLK>juhbTu@VYGhH@!G3jwZCaS`mC^T(0eM#D#rQZ!@aw-;81! zY3hS79Mvfvcwv7ObEx}VxMHV}ZXkcS0dB4pr#{Pdv-bH|JBESl)vxn5cyEh4lbhad z@IUN2-kj2a8Oi0Q8{_nSADfkSwe6nVXji5AsM=b}H`;hCm7-){#WVTzaYY-?VxEI6{>cF&{G5Sa zNLYnoRY-nZ6OtdZgv7u?J%vP78CZnm_4H}4cQ(#={j;BZJ+hyCeX^f?y|SNt{j#5Y zJ+q&DeY2l@J$>5OTecj0eTC%fDI{M%A^Ca<$=63nz8*sI`U}bH8KTS}72QvY!QkqQv_V5? zYA@SMxqsY)TkuTbC<)Z>n>qH0Q7QE(_d+dXXp%SK(_=5XjIU!Ma zD(OEVqOCTQ9AMWqvX)%;tGh?ze85^8I(5H~?b<^htT3rvi`<_-ko)Ii?txzB9_Zv( zyZ)Y)@e8}ky+L3ny6S0rlYe%0w4-)j0$yu1eft<>poxtjnKNKl3OcEZY>Iz zr>f0;Q`jgE*=K9Drum75Xgak76(E}3A<-}&jPDc*wTqhOal(xV(fPh{dlsUFdX?8SVbJky~&gybtFBvj^_hjD1~9D=#(KW3{caB|}=l|0u@D@d%k*kJPP z`Lf>gWxeOide4{jo-gY?U)FoRtao*xMJ=h3E~=;!^{6kjK4v>aSK77mBpaa)qrTY; z^O3mWbV%G3Bs(3LylIr?ZKE`A9Hn{dD9xKkY2H3cL(ke3-H(moY7Y2ZBduz-$nuM( zeonN@+_&sjK9N_R9`&jV$!lJDl-MhVQW#Xi7sX>wA$fBO$y-xMrA9kDhJMtV|9ZPJ z$w%cGvlw*D_B}HnbZ>=_{&bOh81BgZ*)jJ(FLU4in(S69XlXMqpU7mWq;Fio)fwQ!DKKsZg(>z6~_}z00}+omwIY5X~fj0a5by50Uv` zey30<810WO`Vf|NxbbmWX$0nPSqNEbSRy~7ns_ZR8+cN44Wd$d25A%=K5n3%a1G{6 z>iw?1GCQDZS6S7B1SC7T|FXoJ;Ur(`^_P~l$ODtobKa=pgcM9Em4P+%z86}~WSf;h zeMGiW0=3SsWb!V{lk1#&yspsDepp?D(S6-W$3t6o8(0tA&cu7(%=`nDWjN=W0+k!S+>QxU?qK5G zfyzlFoHkIo5x`B~K;<^?+vA}R)r{pkyAFZZALj{F_PZH^1JxhK3Dh6khB$YC1C^sR z-1`qyf5;?Iu_)Jd?Z|_Dv$MpvE^_E9JLkK^sYWv6x@4v|*D_t=%qp32T{6?_YnfhO zJAZk7@wn+OtK_!Sx)4X1%Z#h>AOF?P54hSGl5Fvt6p z2F8UoKOFc#+do#mqYUHJO7qUEG+dXqQyTacc4tk?c#C09&nfLsZG>BlgZL@U*@o!~ znjam3V{%FZ(>XLh-!+uacMPTZZlN^aDU?Q+I66BDF>RQ83d-j@iPC%*QJU`{O7nkr z$^0X{CMs|Y^AY91Pqx`3RB8_}ej>9F?ULvGnPQsy2VFM<=Ik=DCY&HK8Hehn4 zgZtiX5Zl-SU4aw>Qt(*_bR28ZWSLUB3g;QrCSwG5@}|*)kNk)V1{_f z4OhD`V*@8wE4Gby3!l4Q#+VnJm5BD;%+$IXwx*ek*?$x0Ln+apk|CcbwTSFHmrQVcKa%l^Zp187NWgsTe}}D1I&l?pa38!!oIQ4dJnpFe0e)B} z3Qh~1+M*`SJZJ5myw)E~0G9*Qz*@h5arP_-_^;!M?=Fy?0U-FD*Ja#Qen)}(5Qc9@C#F@ z@N4)tQ3U0i`Qc}R9{Ve*3z$eJEeIsg$tzlccgY8!xAGE1K(c14{7-&+-CFqnl6J#$wgQpZm2roeX?#3KEPKZEHUtVS7Q8C1 zHa7u@8Et+(YN#w>gAk!KV#KTC60`@*chv|I^nMkk`@cVn5@0&LL?Ix3T}5j>`vmK~ z%u8hC2Buy-OD{e<_TnCpUaSSsjcfh$qS!x=VV-eLHmLxo4mw8%t@XT%vrY?U_?N~8 z4)37x?J9@cGF9F&31q*d^+Xc%UI`08I3x zh&&wlX@urCEJWmepMieVJ&3)smIJ>a_WomlEK}NULV|^gbSEIuqU9qoYUnsEn-ze+ z6~oC!U|i(r$IN2FJWC#U@hcwD<(freQS!F5Z&C|9wTQARg%2S2SKInB45XYlvHWL-LEJ{I1jdDOot7ISaURVVj3edw4x1G%*PZfw zak1q(Ri4M~?OngAFWktzi`Q@X>kfODbhLY2bY}`pXI!|mL(|`1xU)mkk1yPT&@_L0 zJI`2Wo9*#4FP6QI=qLupI%5q>R<7D3+45ydD{CSzx~dt0Nf+4Hm>TS*$wJ--Fd_Nu z$2z;%^ql>+%F15VZyIKlT(ttD&Zc~^x>K-HS>3Qi2Uhc9^U8A7m#SuZg;kf0L$Qfp z)mF`PsmZaqxTKkwWOG$BjXKfu3agn`DywEf2X-lBvdjGZF>z1j~`7XPh=dd?@?`azwzZEw)7LK=d+VkRm+!T-{nk-V^Wb@&-#d+}@ka0u` zXC}%jS4cqS7m}AZsroXjPp;ZT+%QN5R9=5S0+Sl=4Q4#HlSMO@S#0*!j3<&GG|A7~ zRgR9c!@!LRA^AF`H1eN|^26@Xv9&E`DocrXLfCJt7*i{&e-!(PSZo)FCh}PNIxw{x zZ%$h03szdlH2tf^e9N+C?_IRPmTY`g6ddbRoG@ja(EZFu9}#_Zy1bt=#PG;-3sZCf zrYz&#Om+i>t1KnnHDb@GQZEcovA+}5?gA-Z4G%^YHL@>wDZ@X>vSyn`w87x4xGD|@{%tWXx1FMjzE8BBg=e#_{ny?b<&1?~`V)uR0wBk6VA4dAM5)QM|NYkg}}rPN9ZUTaN% z!un*IPtOx**!istyy06i$X*^NMzg?WW+#fQT(Z1PTAbsoc&ttokz}7-*viTmOA|F4 zvohGF{YIEc1+yW`G)}w|xXkQCk(Enwi-^sCxxYdc{y6S@Zqmx;e`>asNop?lwXJ6M zRm8RYJ%A;>4cYzDb~SLB*@+@6m*h5gDs!r=LS=+*8YH=UBJDncq?+w?+ZAV9No>Tv zAXEA!&4%o&)6P0@nc0aVE0-*9-p7x5q$*U+h9ozb#B62d3rRH_i=wnZsjYyNGye;n z6j@eLa&Hj0%L`nbu$7}r{O)Rya!i|nAcfx$y9r`uf|nAdsuSG?&OkB zP_8Z1tChUI;F3I-d`8mFYBk-5hUvt|?J7^>10T2{{1hI5BQh@NVniKz9{ zBI5+`wUN{V-eRO6OX{hIgra4f1^(Pffsxc>&Emoc%uQ4Izbp=pVVI`adswHB1Si*McP8&Dak z4>~H1Hn@)&ycK?+C7zfQPqM^IbE2;A3IA-Gms)LTgY+!{D0t`k=43wb0Ersr zZ-R%0uV!M9FhVW{4bM$_K%pnDYxU4iP(Z<$Sy7O%ZC(^K96b|*giUiXXn0K~1_?*X z#bPWl(7qYJJ))24uQ#XBhKo4%(p@%mqSt)csLJ_u!;i8KsWdS9$SoR?_8PSBsUKN` z{N@#nv|K&%JiIpcrugmZb5jhCyPWX4KB=)738q)7oEYiZuPcls?`;>AXd*mUjXbOz z46l4eMOy;BiijZx`<`KSz-MJbgRku!qiu1rX}p1nLMuzD5Y+$4`Z!%v1L91d;^+>6 zO57pyK=pOeHMM*l-0~fWdStNuz~wWQPYBrqyJUPx)-%9VA`A32ccfNri&?m&PPNvN zqQgTqO|y3GuBe70TQfz#T%`qp=qS1YQCG{v;_s_;W4g150=ec@xX!AgH1KzfCW-)W zjig!Nu*%!9dHJZBE+U&)eNA8j8d*yQ?`LKa&jUU%l4gKMMN$ul!89}QFiS}RSc7YB z6~eaCz|V*#=mDP_Ni#r3R4D*>j&W&bV2oE;%gGlm!UG9dPENUDvj=5v1E#vt%Ypcc zC;_Y)jpdQjflPdR8Uy+oLh&fT@6t<>#i)Tp zuIXR9UDD}@4`Rh(w$MDhF(jZL(uw0^?k`}jffFkY)5~6VGB&mYdgY@a8gSKmfVt-U zqv*IxFyrdgJD{Ujc`+0#M5aIde<4&C+-r-1Yk96OxFpXdw~WR+Oy}+!$Cy&j@dmDem9(!; zQ)DJZU%P&ZcZg{%$#&OR`1AJxQ`8dg_kaF9LzH;0U1jeI_(YpM*Js#;)kwib4F5sbibG*@EP~d8A)AWRLK#z>jQE|Qui{dFai%6 zU?g>TVg%;asqD8cWuBpQY&RWkHvxP`BuxQd6iE|6EG>JqK+LR0pNK~PEwivN0x@%7 zB=yZ`%PAn%3yh>NpyUX|dV!G?1{6l%nfs;Indp7W<4KR}Wm&6F1en?(4spE|*MFVqJtL$G6 zb*aH0aWI@iK2Q$K{bVxw2|ZF6fp~@*VzN(aCU(wAAl*^oI`9S2nqb(SXDNrTMl}nr{}R z`4&)`H@4EeU6tlds5EaarR9}ZET7$OFP=W4VXj&HuWFcf4@>XZxxWz&bNzo=!>kJL zW@z`9a@&k(n4AC08fJBPFvGUsl4(XX%&q@r4by(eJlSKJ=p7cKnc$`SNl=*Qt!qDA z-hR^D!a38uBm;%uo@Ag9(DGY9(1`v?3JR0M>ql_?HD*o9cHUz+x{s}B9u-$M3r83{Al7r?Fxwd*M#yPS-?xS2JD`w!=Ms{i*a|5&#sE0o^tdRh z)+M_m;ySYnq_0-?J~h`5TP9bg%o%3W^+QLX((EV} zsJJwB3RD6+HVRZ9fCH5_!zBWh@QGsws*gT_npM7qyENE<8G$SSZ!Ok^#8+D4 z6FHH@SB)t5wI61|pT-mtE&0p17d89L2Zk!W!>+=0;x!dVtT(~1$>pf!Z3kOY2JH9)cdaHW}ZvfQ%QmzUad4R9xkFl{9M_$nw zNi)FY&-0`C^ZaQ3JYV|r{Am6>U;6WW>Cf|}KhNXOzqBYU_5pt308G~Vroq)Q9M1y} zj-(!t4k38~=?~gU;K#(t$~^FhNSXnj9!c}Sb0TR5$eN>OoD)p}zaB|FU>b#{ zM(ee`ve))T>$SbI*T&l`Y0lIudu^}mwLN<6Gh?rv0)8cu=7Dr*aT#C}JMt8eJ}z9q zMm9kb+>U%eT$jxQS^vq3s2o>&%kA4u z?4eV@$41gTkZv#b0MhI82=MkK2_zDTB=BR23z&xO9&zG3i3_-Qj1yD9eIjWdNK}y$ zfrlh6Ad_ClyFWcZz5^;jAttO#w(?gXuN*7f=`sm}cEh_NQad|Sw zn0Z|y(Gjn;PNcbk={hp$0ph>P#z-`5DyMw-Ty5fpQQJzG&4=xjrTdw$Glkz9dw=&M z22pl>vvrR(8xCCetGoB{)K$?(x{ok;lwC7!JQe?|>{Q%MPtJzHL$-zVVQKq|YYqrY zUJFjVf(5b~<>$7o&-z1D0q$#~hq5s>#G&Mz75;Rx4Fbvr<|?_B%agfLyiN+h=@<=! zR4zs4%J4o%cwdN3HwUEYR9P_bRPqNBT19^#o+(^7=67u(rI~?mi<7G^kO(W1z)Nkt zt&-p?;u^dQ{CXty*kXT0w$=e(kT$%4Ov_}kylrvVnur*#nt)S0pQCRP#I+7?t|swn z*fNQpA(B9Pgq8^=hzlEVOX^ENI*xDw3DlaIkJL&dR1ZT zT;Xtgi>1S^bxEk!rr*Ruk^jAF8Q7$ux&31vK)CA1J% zL^t63)7~5~ZGcRbQD=&%b5mRj&H{I{K%`oL_m9D;J8keDHtkb3FpEN|Cy-g91SjAL zal$wYB$P>&fV*Y!3e3_`UZVh?lcv2)y0a;C|vf%ecwpm~boAG>HZ9@9+80lt# zkBOuy;FBY17Wni?ngYH!l4gPDMbZS2P%M51+%1c(;3vk|+5-|>#j?X9D^XMpL>N6e zPM#-#gj0#sA73O!lI#lH#2uD@h=!Qxvj%zVbKnjMQ+!a`dM3k?JT zF*=Z`jk4WrZ1h*9G0&mHEYt% z*XA-C3vbBA#7CI>;9|Kgh#yLOu-Rogvky2KV?&Gx^mvu6O{2V~?UG<@ET7wFYd`Ks z3`*V4I@iOpzQ~0B)kbT5iUhm9o#q8w`y{x^X;&b#fmWD~Mi**{{;oqyq{q-BS|Z~v zU7{s2rm_fYi6of6%X{zk06*B=xMd{4z$vZ|qzkls^aFgqCF1<|u%V;Vtp8Q95!!O@ zV?mvO-x7)Vv~NqqWmy`wL=wz|wK~V!)R#$aTQ~_;jjc2ZroJi-rWdq~eovt#4*H3{ zfldAfoM@QuY)ZcK`b84>hB`FyhzX9A`T9k+!e|O*TxCYa*wRvdF`{hcGN9|H_iYir zTAF^dDf>RV%EKl&4H^l_OYUPc{onFGlf*;&$3aetsM!5EG-z!@YOPs zI69lCf|+-=j3k(NYL4Kn7jIkiy;vD>GKRG_M-q&0$_OUDw~QoGFkk-2mIZ}vL+5<8 z2_C&#HLP^%ZN=T}-!>l>UmpBhmJgqp`7rqTaXK~yyuCbFX1xp-_^cWL{P%J4H3dxG zdraxQ$5gy`TlC&n#wFYo5P#J|!MLmF2D~`)T<|;Ms%Q#`kIHMBhk59Ot7JFmkxg@VBZNUn{}uNg_UA%w+WUTO=CI)Nj3(1| z;M*c;Bar!@^!G~_N#v73@I#~jZw9hL5!sI~lGxduqHEsX7DBhUC|g&6iR3b#0#mox zSayqzm2UA@v0LExc^|km1^P=D=>@pAG7j@^Jh(M9iD#M9t1uWJ9&0uOcO?F@_Ll5` zxOJ-viP{Qe`95BpCA^3DvwAkZKH%O1{>&=+1DP*_aZG93Q_T-|mY?BL_=+|K&cY%z zcY=?^c-aNw3avd!EY~ljYIsE+M-Qu4v<_Euk zbceK9exjeIqn+~KR@z>99<(7Xq;>*x&G~o6KGM!;yL(HcY5Qe=dJJgr3qyBE>&ZI> zXf!&RkbtzKgyy%#v3&&{?j0A5+Tr%U;7-%w?$?Zs>w^{&r)6)OPnhr_6TSUE7MqOw zu}qSJkqX`J&-OYlW{h!m=sA~K*mk6#m5(!@3WsJ zqW5&en3VZ=BXK{sa!Yl5ykgd@KedpjKe0Tw5yWbK+g?Z*@ZaM7wRo`dP#b-P%a^fM z2;|Sr)O!A2Qa5DJvpuEdN$ND=T9f29teA~`RPKHX$(OmR(Fc&COkBZK4(1gv#Hg&f z^pg}RJMO;-BPw8KA^8p6N_J|I^1jYUlATJI@|D;0gRROOJeKIj-AfL>3aKU;XvlMx zSvoT?Wf_k(vt=h-WhwEbEgMzB(Ym$xW4A8sphZU@S8)0e4|kFx=S-2!17K{DL0 zLWT`w3D0R8s~4ALGeEXtTRyLhelCZM*w2&xjm#@!cEzrZN+Q8xR8ABiadDI^KFpw9 zAW5sw=lq`dw+O9!{RhG04w{t!i!~I4e>#u6{S4oCtb_-;OY~>M+@OTEY ztipv6^|=uuB(h6Ozy;q9k``6YGpqcgU1h}wq_Sf4t?_vP>R4GBj(6T=nIKtl;_=Qu z6ikI`YIE4oMzk2~lSl;=>r)-G`1uR5QX8?+Ph(SW^adI0RAx*i(^$jZS+2U2;FMgN z=M=SC)r`QTp1Rd0ZX95e-oai8JJFQ^Q6lfxXx{(f##|H@0;qjuMYCX$i=c7n!Jt=EG z)^nb+zMn|KIPj-sPTSARlhiVi__J6*kFw~7mDPxr`Am~op5%A~|CzDGV&oiaPO%?{ zFsTs^ur<_?aV-_^xBtWzca_mbB@Zhkr`gO*lGO=%DLG1+YHLmzaMoMan=DW{d0bh7 zsk%byhrvS@aE^q^wL*JMCw!tgpv1i^rcT z-Ca$>1`Q*zjKGy$7Uj(ULPH|Uj!u1ZXO!$DZ!%e?A|hFyk1ZYys8n7JjwA==gss%J z*t2PmFIX7jE{ZH27@WC@QrSPLOUCE3lVmXhskZW0Y<5LiqMWordCR8AuHD+Q%=WWu zeuC>887-8Y{vB^Bdv59HfX14GN?kou6Pc13Cp^>Yaqdv1gKFjdj2c(@GU8Q5UScj^ z;F^M|Hr;B**=9toN{kdR)*=cy`3a|dEOyx_a@#f)uuX>HEv_g%mv}nmC_d*U{ z>IKtY6HV{&@vv8Lu3hDr8*uMWgrCA|TSy&ZRAB@@`_nlislPC)FapmRU?lbC#0Y%q zWqFRIjy0;}2)yB|Ib*GYo>o?Px>n)oep%t^T7{?kWre3}6`t->;WxwzPXno<+IqTA zidxF195AnN_3h5}b6egD+fA2dn!rrAL|2&tVk=dDqGGuTEcg8AJySr;7i39cL7CeC zF`x82U_V-N0=Rp0k|`kO3?-1lqEZ6zrJ37J^hy2eXM&GOxxjaRA-UZ|pVW_yDvZFr zugn=q-P5ST2z>DXBdK!|Bk+PR<~fqO!KjiWaP60JMpAb(sxSgC8ek;#t;7iY{s1GX z)2|BUNRGfuj1&w`>OF}O*nB0|k<@NRl^lUT8DJ!Jfc1W11YVv6go!?>s}dtH??}~m zSFf-m(Q%HBy<`$dZ;=@cFz+~%vExj{UeW{7FVv)yG34Okk|PlJ4veJmaA5@E-hq)6 z9xjYP+&eImx-vR@4~R1cMpF2rg~nzdJJH)CU=+f<|YVdi+am`;pv zGs3XazzgPVvh98AB5sB^DWmrxrFnNz8lLf4^Ncn_BH61flC?zd8Y=C*Kxw|gmFC-4 zX}%ei=37i@zHyZ1+o6?phLuntU-)vwEV%jN{%=t(~Q$>X-kxs6BBPU(q(bgI}5xmk~ZyPi&)~h2+kUb zU@atK^Eq+3yon%lX(0N7Lt#ALh?~fNIqf-iD*R4m1{eHUhgatdHu7Wd_A(Ce0{Q?e7&-teEqVY zd_A+De0{T@wo~oK*IQOFzP>{8^%Rn?pOAdLgyicZBwr69dHt2k{n4XxGP+-DKBv7D zocym#eO?`VNtX_DO(acz&Ac;t=_Iv2HSQfv0{_8CvJ$!`PRrkE`!_A|w{4^3;c>%c z5;$R`mM^c6VvR{uj7rG&GxwktsID|G+ccM%USEqU7e^KKE>`-x*evjQtNwU%NQ|;% zrZnH&O7rclG~eJ#^DVA4-{dV{Qma7d`Sch>=77f;X;ri3(ydYX#b|-KFIc-Ur<3~! zv<@>UA$iR!cNqq?LM2QmQ3j8}azgTk6Oy-^kjlb$D6W0LS{46Z)11)W?nb|7nQ@?F z4m=e4V1-G2q{uzoOegnKWA1@o<{s$eSG(S9WxO@E_XO}g(N#~|qiouV(v%b40cE$? z`=5|%Y6RM$Rw5;HO2C&6-KzlI>jB*>1AQrt{>bSg_fmY3Lh_{v$rq$t)xYN!B}|vu zO_$kK4}4I#4sC51aR)p}qXJRq3iK8tcTR0EZDITQ6jWrd521}ZaAb`k^CZyp9J>v6m^P?@+g zFb1mM*$Y&^aTlm;2e8NwRKLp+sNObf+SreEvYSu0Ewb^O-9TF6ZpO%&-dfA_*4izS z@{2?3;vbKOc8PjxzY} zM7rGI)rs_TgX`iw+gaeQku+_d#W_CpsnB*>cW`o! zIh*Xr)DzF51IeYi&-PP*ECqxviezQf(+7BRdn=VU ztmpLYVjAkaY@i2omBk8>Ie?5Rz%#Q^0DO_rL=wmVA$=TJ>zzy=v^BvDAX2EGNhllR z$4S}v0Y2AgQY!G>85{Vci4FMoS@#Ej*l1EMAdx{L0`O;+L;Bnd11bk9FUD*Y48bHhjS+OBhCMM6 zaA!xu^W)uxE|3+0luCTuBOAxS`=#LyIBFPuR2MS0B>9@iLyu~`Y767JO3!5Kvh2(p%km$g4ARz&X z_nJQepAlE{xBkL(PDsEjvmpS?Y)^UwaPR2D3&0JL)C1CgB!3{?M>PX84-_`we%X`| zd{~+i0_hkc2|PWM1k-OM7%*{U5+Ra)4k08zArw+MA6%e=JT7*I1t8NxDG|6O>qg+S zqht@rOi_vi{veYC)7!)ynX~*;JTtujWJ6oH{47QrfN9KgVwYS1GRKq(0N<3jfOK5R z-%o!;e?N-eRlgRX9hLu;fsV`73oRo}_oaVo6~PbxWSW+OPc)irPyw&c*ueW+XB9T! z5n2BQGhz!HkS?u?_&%*P->H>GukOULbqcsC8-l@%*itI+&}<+De<=>Z^T1k+I4+wz z&RHa2kX7Zt45?BokOOwg2By;s8<4S5*}#l~vegWv$0{3L_Q$dRPXX!0%0@S4fu}U^ z^Wyq%3b@=AgD6LTzcvn=Q@|e>NecxtR!Z%GzmxT3F!4dyfUF=jH!#6K*nq?UWg`f@ z*n)u4z^{mbVSbyz4`*!P6fEWm7DNNh4NN!?MSz3@WdoxCN#8$(!7n7<_)4WUwfs+3K`IY7vW}8 z{!2;8f6Xyr?ipFE0>3T341Bqf+Ji8)XhmpxV_Y6=1~T0lZ%%2z%xzR- z{D%;ww5!dRYOyH~L{#hB!HuTET=;Sb8`9U_!X<8s{-5Uyn`&b|kwEdQBK!5)O% zO`qm)0hnZA_gi+>ICsGNV$jpSusgM|3- zMkKo}_B+C_WRd3GNl2r3k@ThW&fBT+M+&~Z-A0uD^m%c;vXg?(*!d#`AE{n!c>`me z&9?o?(oF`tl4-1AQ--T{Yj)&Woyl~_i>_)$VA6F|-_&4NR`#OGnGZ3%rBocV?TahCekY**`9||J~a%dtg+k3rspz4p`QlZ7^p&@>Ft_4JhyP zv%R_8aeZEzevCRErX2d4FpgF!a!Bc_T1s&QCNm&qa5dW*j>(}s#p8~UYA8!9?&=$ATe=$AT`{as(@&EK8H zPO8s&*6Lr3PSksV^$fOnmI%#AzTYI<7>w-iSRO5rdgs@VnbeiB+%7PwQVAv}TYt*^ zB+pl|JgHZ3l`Ya#xJqBr{9bOkog4dd7l;~5M&N(4`q3vO0^>qA#jTw#kj>0WpVtUn zW;RD=NwP57Si=T1tyAkXO>+-xGq@^B$O~6TVJbUQ&9GmxtOx_u@f zQ6DmGITRwR4aRSLuCf3b>wMSbI5<*a%IE&D<(qzZDLdNQhgcRCL|_4c0s8*56>C?k4} ztMs3yl2?sO!i3%V|42OX(*>qZQ!1;;3k)lN*!`pZmo+AOWAm@jC;dXOXu0`7ggy3(M~3(>wFQl z?@&RTwH6> zt;SW0Zg;WhMjxwGd%PKmsqC3$d7ag8ajofI<0|uNn*D8&ODw4BJ`&R-?an_(2sEk` zc5h0qnpUH6$-Bj@%#HDELKlb{l}@2r9cvONBuQYBEC#8uD3_Run=qRF-R+>(gY7CK zQhAPIi9rfkPNqtel(I9N!Qiw~wc&DA?i*F<0?|$fDPP2rgOp{%fgLHga%4se&i{0h zTR!YkybHV1SStA*E0fbd!d4zOFWN!FGZGw}lT|Bsz(2^Gr2M9!N+Q8wV7Z&eIUwdb zl^c9IjDOp-)jD=YNeAo4dsz zwW;jdjkhXSvim2KU0!y&8Hr9DOOqBO6BMd6VpVRyx{_F}|H-=)I6?G3VqF*f*&cB^ zj?Hk@fPA%0s%}V6D)>UyHG02bv?4~xPJ6_+HlqR8r8GImav7dmO%wU5NP`rIe#9>xZ#s0NEFX%Yc2ra8%awTc&BBj27dh)HF!47zt~mYYX>~<+VE5T zMK-OJ`-jZ-`%;jUxiRp~M$*it`=maZ7=d^FN-A}_PwKu#l^lT&8(<{$xWov&Zh(>0 zj}jv=uWjFlBdNDt z6N?6Z)JQ>XQlCwX!2L3JpXif%x=}?(;423hNu8A#fw@0T6f5TKd(R+8=}VLNQtmjD z6~~#xaqtT@#$=^)O_p9ViQ*wlEO8F5r|&|MpC$jFaq(5z(@+$5Jq6`C6&+J$g*fG zZAWG<@mY)5L=T-Q;s~_2(!jS^(2z&QZe7F?Xf|b}v1ksZZLz-mI=i;Q*IDAbIng(l zO8b^knr{T9d3!6(n^tMwib~5Y(n1LUOMI+m2Ml)Gju`B=#frghTfDOvEWbDHjX5lR zwfP@GD18(K3Z9w0R2>qIntz)D8V;K>gu-B#We5p}&NGBYP)iv?A+}{1LSiILLQt4j zq|eGgp*LmOL85cz`axshOhrKnHVHGYc2+jc$$epqCe+i z(1@;?7$mxUE(VQQn~6apD&%6&h`gB?Bmze+28}qJi9sSX=VHD~$QM+a#-2W-OUzlQ z)%XO)^X0jPy*e>aKh{F1a)N2cz)9+_ZGNGgVDt=}UbFUH$o2@-D4OLDz|a@bsit4f zKZ=$)zt?r}t0=>?J=Sb1b$;5peNr!uGvet{R2ShR&+U(*v~WJ@++vW?l`cwd%^I7s zX&5z|uPW&^sG4H>H*J>q)l%#NPrQ+?-}VgzRE0k6Fu?zID_7F zq&X6Oqqcq)9g$p{X3D~oI`sF_+>_ul>+duBdH5a?iAM(ze-%d z_0|tM%?rl*B%TmE$Hv3Vsd~}5y7=39k+c!`6(hB;T)=SqTEA+EkF>5b6T8et;0Z=* z`MjmHA7B$|Ca>S$g9f8NE6vL`&2^?1&RDs3)fsqwFP1pO1ob7*Pc@7wl!h%RKxx#Q zNrTe7$0*IajMBW%D9u+VgaoK)`uW&qbHJ|}DZH%Tt1p|bXn&#AOh|`WW1+l|XtESJ z=Dg~)4O|p#uV{%qC9ahE^w0-u3YRygki0F0RO&N&BzvG)38&}lBq;M2W*q341Gj}f zP~Vt1sK`BhorK)!IlNT9%zb-bGwE3gyT?YH0PbTX{hCkNuoI=#CcOU*`EW_!%B2u$ z;0uE8OM~uv5gy=2591Z|CzO z#ASM!bXFjmN$>!oWQbVwk=r9Y)gWE2U?UM!_sZqhA)HQBO#<{J-LAE5ov{SY@OpCo1qx%SDdh0h4xf-g@F~ zWf_&nh_sZx1maLck%02|*bzVJEp89za%H}G$#?OmDLwosc$md1&v$ZCh3Y8UDr}0d+ z{!`;B%~o;^GsxPJN?u=Z4Xd*Mya+piZ0+gp`b?iSFdJ%t%JLp33{)n6Y;6Uq-^~hC zW`zuBf$I070+r=7i{wD{P7^-=K`pW^`P&vNTkT8i9FLsot+h;Vt!+owKmU;i!WeaHV32$H8UCz%ZMcKE!n==rwsof zquuZ8q5N~~s@Z+a@TW5x_&cM>lZ=GVXl~wPgw4B)knoi~vY#6*|UFxZPzH`(nsx)6yrBPL8dP<|Nr)Ha%U^+@= z>tGl6nxi@c|{$MICnE`)|>o1>582;{l_uN00IWFsoV6HO01L>sVdBC~Mrr_rq zO(cQ1o0u7hxwW}*vk%68-38LWMG|r>J%?$kc%mcwEM?YEs)|>{nkn^oTyh2n1 z;viZm9x|5&e(>{*Cbh& zCb}`$e82`G6$P_VFE#ZZC?ujXUZ;{^JW$whK!UT7fWv&U@-YH+Hw|BdUf2n1Qv7hw z%uT?!kQ5ChI>|H__|{AkOem6-7;w0Q^-2d@@D3(r;$K^0^Y?)GjhYk3sky|HVzgNR z()>~Yt$$G=R8k~qYhGKT;shv=0K(ZeBr9wSl@nEWB0V!;W-2Z6);tY#omvVqh@2M@l7F*Pc(HQg#>4Cw-#9!zp6qB|d`H+( zc244}WEo2sH?&T*fVnfRnV0V)!>Bgh6W?63CfU?puzE+>R6L}A?j7ZupE-BMzB$(U zj%^Myv6nF$+ve9_7#lNt**0ee!+cjrxx^qwp2dO`158=Qbpx|I;VMh1O|S+^g& zF+0`m0IN8&DzPY|8gti5i;s228m7}+wQI5*CL~uxS2ZIrsb(jbX3WuLR6-+05FvRJ zjdk8=a?InGxRdfeWBZI<#XFhm{#|T=!eN8oAp6lM+Xa%HdV|le<{_&8w=A0<#_Xn< zIlVv5r>m-zlHF3WxZkMTt>t!1d?bVRuUS_l|Jb~kIg+rkLo-BDS87PGUKbCavqcay z)QZ!qBoa)EWj4q%iX1Apy|l4TH*HzMi8oLdtFDTJ=!adAp!bR|`&m)dz4cX&;lHL~ zBk@hK=f{of^>H_Aj;&@QiuNS7npwptt+v%%*;hu(zf8NsQk{XnwT+(79!} z;wh#(T$PQu%=Ab6o;fs|(-luz-N3N{>_m~3OO{(13uP6GF;NfAA~_u^Fp8v_?FVk#RGg71JAPmD&-P9NvOkV7dJ2eR zjF_D$vU16$VW(wP?C?D*@$5tbJ|~ivcoKA{Wv2#i9uwZa_c*)Cxgy|OpBjFu-_vY` zYyVhAm&XjJfrmUcWjNg@^(>>x01w>baXBNY`xsRifwM>CjHLd|sKN+*(*Pr>cPB<* z=cqhKQuj2f|vwv-Zp7&2r>3R?;z@Th3%Dc~+oOieP;CpBYKDHnL{ z03)e?ON_u1o|NZEYMW6dN8o`^&KXI~8dVs9xi{?KL!aggm&E=v3CtUQveNLAwT7Rp zH2fqD{|B+*=Yce}w$x;w)Jd`7CxNuKOzeQPwjARI($pb4Qna=(0%>i@4oFi6SyHsN zFal}nz(|VL7DgaV9T-W`+QJB=sRJV^T3Z-_dBazpDD@jp@nCy z*DDQ7;?A^V zo8d|gDJzPqA7V^@eUuxN(sPP_Y|0)^!I^R7{24lEIC9Dm3QdwrK_g@(DJTqCnHrF2 zx;#T@3}7ikC`=mC^OsNvbXif5=vOH_D0o1w1T>;wl7d3N%Zh?TH_S7HMo7%WAkkBE zF=zzHObinKmWx3nTxMdB=-as%G=gU)28no@i+Rr+>vY0CkNkj_e#g93LkJjWl+E2+ z#$#;#rp*VG6HHwLC#eaW04rzDF#1(P)!5jw^vXBV=^SWO(=O*c_y^P8{%ZW{543+A$q$iPeeB-Q1+Eza67V56D$Te`P=8sY=67 zPmOEg9`LP3Qno7$@0Jav4>J5vyNc<6c)g0AY(Kv^6TQgr>oXddky*0yj(0|2H`Wez$Mf%mg^P|%Gw*8++uE}ZlN~7FD}TO;Er*OIhD>B? z!yTCLucpUWuO1)Q`v&Eo9fvM|XkxcLDcd^%zc$WVyFh}zgo|M@zh^a3(}7Qnwd(@^ z&mV@0OKw0$2$du(aFxLjhzltjn5&o%cfx$ic8o=XG=Ow{P!K(CbAyGD@UP?JmS_+7 zwrofNGd~nbMiho&ApuXw1{6QY2%8^cv;qWE-DIsveBdj;L|cT@QV@~@Z}jBn2}9#^U+F3<=|A@?Z0bt*YfoKs#Te%??{BI z`H!D~sBv(htFn;>%oIetwsfjFoCtqZ6ST+o9EmiePquj9^16;Lu6u|sq215Q>G0{V+D1E%^Q?PWMYD#v~n~$ zB;HIoGLAEc^p7>1;>#h?A`B#&-?#;ALch{{&;N~v1HQgC>zmG7rv?`wqb@Dc(NZGu!{iQ8vwemJ#@+`uTw?I zC$uxpvgAOg5GxF76?Sc3ZtY&C@vq7G093e z^97a6ZP+Z?n&vZ>>y>uJM_*z+5S=R33E5p>f`4mQVFXe~iI+@#Qg%fM4O^BMFP$B# zX4#=-B_gYKD1s!FLz`2qlF+?@bQnZ7s|=+MtxVj3Z8Lp>c?TOy+QeV7(ThzbrTJk@ zX-o|1EK2jEl+ye#rL=Mb;E;Z~+Ak-Kxh2q@v0ID{92~}}(C}f|t1j7j&8y8x?##iK zaOrU8lc~px&F-`?GY2x!rLwmCuFIG=p&45dqcxD)otLgJTg%(WV}i}ju!ea`oGea!n|-;s1vcS3icB1Q zKN&KY__CGJwK8xOUl4R(8gySIbZh<4Y4RU(k{>hJCC?)J#qY zm)T91*)2I^c%7;I4!g>D0ldXXI=X|Yq0qWiiP^xgS-{1X*A=mtNg#bk=Kx>=nb5je zFcni9?P(JZ?2X{$kP9-DL35+>~ZU#s}7~y&wZ=yvP&hor_WW{{JmDCaVMRt(kge- zxogf<*|=-YRcXFT`mN7OCC)HKGljjRo;w+D&z|V{6AZxSf;a#M$nS`fAGaNZ4Fj-! znpZkbfJAd@w!L!!UeEEGZY7hPl-jkt@lm^*#!U;ZHM8oxWVGD8CS#4ib0(Yc|F+Z- zCoiq(r=Jn-XWVF48N-0jepUD>oFgK2hEatP`1IH2jHFI5sxSgyHo!>g+{6ex=$t%9 zQb!n7as=M`rkt@>K~F0yJYB2sbib_dbgjbE{j$Q-wF*!7sqopc!qY&isJ5Q&lcJU~ zUjXJ6-r*;TylpD!(p(dm>#@-jW`WpDt1(fr-~<-@QIZ8>#vn@yOUk?vh#BRU3GmEl z(Fx!$B54+gSwjh=u&k5-oXOm8qEG7d^MbEQxxjZCDdb4%%ESnK!}-ZiC;FtmY*fh+ z_~QXaQtK~>MFTe*DHKiWxWowDGQddceTfmc@9T0MN$qb`(Ghs~03)eaCPv`Z1B|48 zlo)}lFU*T3^$ep*(ZDwhFp_#tVgw$4QJy2IEk>0bfxG=p&PeL_j4F)4FaFP*k<>Lt z6-Hp*MR)KqucAYi-EorcnD@QOO5dBL@6ol?b0#a%YqIQUJv!ROu~$t3>0L5S0n)WX z38d&<@;n!it`!(b(Yqu^AZ{BNN#VJ|2*hmzBPl#r7=gHLU?hd-3L_A=4UDAlTww&_ zwtxZXBw7cl;ZHoXI;z2N`%rd{sCZPgZV#B<&JIX$CAT&5SS zm*bCzK0u~-_v=HVQ{>MvL8Eh|452V#$})sR=gBjKMpRE3LLs%``MeJJ$YEIUYa<7pfNCJVvvZJxfnEt$xI9qAvG6+ z#-N#rK_cqrV$c{uGcia6=3ERK18OD)iP)WsL1TE$#2^vgb1`TPwwV|tMu1!l8bfa; z28q!)7juEAk*`gT=SSCxO!uVgg2kvAEkoAxo~!WrBw?LU{W1iOAU{f%XNa_+TkYTpqoF})*p;d zW6j3b8R;i+X)*<5*b%P7Y)Q|UB9AfpAw)<%gbK-rMXvY)&$ zvY$#LcuLvAJ#P$I`FSJg^3pbmm_7)}S6WEEvO@Be6q2u;kbI?tgWxeOidKXVS=?aiqQX{#o zx(8LF9!m2zlMnP+RZfmon($16Cu^GKd1jc?;vC?RICPMmo`vzIQJS}n(!6n$=B=YN zZyu$2`zX!pPa3`765Dv12EBi*{WRMAcHAhO25vBtY(Ct!NUlp=DGe)Sfe|C!i2~!x z%qG}|^Klj0G|^bb;};F zFXGgM_chtw_pT-+?`cA+S5~RNG-W86iuux^dljI2J)nDKpf9D-o@N2`k=tUvNFn*s zgyahnQoW1~MG4bocGG2cOP*WNVlFo`{?M*cI*^*^2Ryx1mFBCbG_R1-yhcj%HB12z zg}xRQ>H_zRPIlrx;DZvaTPk^CRPygEu#7h+7KJy>pIR;{k`yOh^$CLTcd}_lhn5%v zq;JbD!tYp?bUY#XJ}0D+;vU29?ORHy-n}gf5kNKCGs<9JrPW3m->6MPZkZ8Era&oK zXOKddESRH;kh~^Bs+TeEswUwD*&E~9VFJkhhm1ro*;>!AY4*1)A7fWJsRX1n=}_43 zHSsRr43K(M$2&U3!MAHES~ssW!Om+A-P=QbhYF|Gnkmg>oEfP1aG;9zpqi%NkcX z3n{r|ui($uFY$_*)s>RVJoU4nXvkG^jh_XDhW7H?kk5j$3(d|?Wl7j+JX3ALRpTnn zR&ot9$l8%gUSDtxt1@r5@eZSDcF@@p*Slad{a@HBf{o}vJ;GuxGwwiT`HPzdD(fTm z3If&do(3w*bv!jt>H7riK=oUjf$DcI1J!R#2CCn83{=0<6R3WBE>OK4*F-C3x9u({ z;?){C$INSVLKYHR!d9TrX^1R~ZCgKkcKDsvX(JT7@?7QJ@{hD55=Y0=6AOQC@Kn3D zqEE8mcXSMb3%B+|5x3b@ay$O@fpoH6ThXst;yXF(r&Wff0glp_lynPqs=!al5cYvg=zjX5*K%F z_BSWcGQDFcje9P1Y^C{btu)`MmFBy&(tL+jn(xj^^PO2~zAG!ucVwl}jqz%wfs=># zfOxvHfnRD4F2)1m`^pBU9|#+8@si*~$_73<_Kdl*v-IdUTqUztxc(y~-+P4Q`;L%^ zRhL`064G@BA8P$aNWedcq#p3Sku(SVn31$b;Lj(vvR@XvRqJKw4u7d%Znsaa87p2r zmwMyzS#JarA7x+oj_Hk4^v2ZBrvAI1O^w#irbg>$Q}na*vwl`~s;ROEO_e=ps_a2i zbeHeM9yE2LIXlCWb_6g(lsMgJ{bj1`FU4()T`F(d%Ev0jOcjpG_IkmoCrm82C)CCu zCKOula&(!%+kYL02&NrcHDV_d45fLi%UjU&J!tZ+R*CW*`ICRM>4InX>G3uWvVRc< zib0+wGo!sP%5GbO-w|(sc7aS$B-#UslUgq@S8-q9d$Z6A<|?ad;8%?;%nL2?u*@03 zTt(LVr)uIIR225}CL#H0laQDprPnXt0Y5p$ z_AW46sk~&XA1PGU4;4xyVx-wt56D&7{E(tFhL;~CRe=9Dl6t_q#_85P@Bxw31^!+n z%>$X%NlODa8JB7SW`vXX3j!Ia5Za zxZipHQs53~AZQb)znleX@GUV=_kg!X(hP9jUx#evzAtdmNNQ&A2NN6chKw!ZSI6AO z4R#`F26&H1>H*I(l9mbnTw(+MG-Ctr{hC-N@HZoA2KbOj>H*(mBrOwsU19_7a%RY1 z*}w-FO{@kyFp_3~heuKm_`xhQ@U4jrxbL=9CiwA2lQMySo3VjE9ND_S>m#WL-0iGX z0QgX&Ndds~GdA!$B3l=DRV4L*zsw}T_qUN-3IH;~t8u{$;z|QE%TyYp_-^rDQ4hGU zk(3S0mZFsE2YY2JNBf>1?UilVaDSO?btoU0W2aIokmIJx24+h{*qA9C8@I}4fXsG< zi^;=fk*fz}=Tq}9CltlA6v|f4DB=SWua39Q4(@}w_a}NV%lKu+20kc0;nM>iZX_)f z{FKNx1LQ`c6u_M2lE~Eq-k4t>g#s*#$AAsrs?-EH};N2pr2i!Z7W`OsK zq#p30M$!s^=Mx+7+>8zU{=^34fSl&Wlykl91uG4_f7~DL0Ur`cUEqPB{AR|otg$-=~NfEN9I9- zk8=;Q3w%{327XTx1HRX(Y2c@w>H;5{`I%sRP+4jLKF$5iF7U!k6O3Cb+u*=^W!@qf zA5BG@@|9=;&&)Kzc&fS(Yd>G_zHJwHYo-at zpOsDw$U>FIX-ir)Z#TKI-RQ=OCSL4CwuL7YFyH-)o~?)tb8NqxIksTDSy2Vz%xZH7 z-^KmY?mZ3S>#7*eE}pG&rEA;8wf%|(p<>`4cnmYZjZSre+u0c()db@xOWz;;WEErO z?XpZ2%!vU}Eq!NyLjLs3DF(mTI5`;vOzw0Sclw(y)dk)o^LD|w)lvz-=}ZjVbEnuJ zwf|Hm2F8b$OabwsB?90QcYvpXc-SI^gFWR^UEr%SHyQlSB!5(x1Fd4<{oG6L z9%K-wTE)O8x!4R4KU>AXZ*Z{=5Z_zU0ldqngKBWxuh1zVH6bu zvu`L70N?o6yTEQH27a20b%3AF#K7N3 zV!%Co#k&g}Hj9wtGKqMjYfb{0IEWN*nXfzdfXo*}is`}`CZ&Z2=0uN-prb64t12@H zR=BlrE#$T@V|Re8aEla> zfBM7ynRn9vGjkJG>TO~k-EhwHlRhSZ;NuA zKogf9$WmUms`uY%RS$@#EM7}I3MDb(n9vgo1^nAIF#^8csU8q#S``0>U(J<~)Q<(P zMx^M&IbA3e-7k)|P?af4v^vQ)vy}}#D=OYyG1jgaPBOKGzG?QwK@&N|MZd+iwopkv zBJ?@CBDn{qApIB%y9^>##^U?vC;0kcacsPK4zkrE+W+TAWja8jm*@RgfJw@D>y#BS zV&!74G@6YTH0Nz3&6TqXQBxcAeXTXJfg%+frz}Dhn(a!%jzW5WoCVJ=i_}lKdO4)Z z&}ZXD(gI9D`W_BPi$to7#n(tVdQiz1RYtTYTJohy1BqTOYB3c41KmLB`v@dr0-gCm`#Eu)GGV1X>v|bDx1EZ9j1yk zjbVOHH+cmlPjnnrxCwWwZEnZQ2BjSsQCt9Lp%A z_vc#h?4(Qml$$<7stkQL=OuZ-6r}Idb9_{!%2;Y!OEJk*8PUEbRT+@zwUvQMidIjI zc%dr0%!1}Xfuy;ztr!!it?Ub?$;nu$S=I*6^PR@ZSXOcTWAJ)^XPXRgd{(NAO|v`u zHq?+RL!T45l00Av(huiyc0#1eSZb%?Vv?ycqP>@&o9F))7xNmomBJII%82&usmg#vudNJBQnY$v#0yo~M=WTL&`6ru`@SUYeaAA@ zR`z|<`VSmCgOADc^UJXwhLk zr}D&^Q0$(vA|g}_PVQ~PbY|hzq7=&{&$YcU?V|(Fb*gx>SJqo%#FB}j3wd`eUJXi2 zjAHW^uf~Uz?sKiG|G-uOM_Te5Yf&m(b*G4@Bh!*)@e0i)tr3K@$M}$y{`Gd1Dz2Ql ziS1>qU0Ibag~nQ(wdAVF=K)PN#wtD1lGX@9n*0Oo;3fw+CHb7_;rPB#F=cX^T6!-1 z(|>hwAN`{(Jn5WxNS^B;>9)7@MBihYiQ7q{S$Pvn?X4?p*RxeG<#4R6JhPfD)Q@dR zI4unq{r9%=1{?jWWa8k>P4FPV?4R)^8prsO@g>{0wwKo%v4+M~OXsc@+5Nn9y1X`k z2ehR!9%5_3Tve0f2V9k-3|Ph&RV;?3ccIt?W$9ftvt$pTru?2Gr7ddecl)S+u7mVi zXjFb>NRiiWZ(1mY{%?MMxE~X-)RQnoNWW7#K6p(*c^+mqZu^UkmS;g`DN3%IQ8tq4 z_A7(-Qh69bk3np9yd%V+O>rJLv=}m_0aVTZYkmk1&CBi;N8otKTd1hnZXwTu^_5;M39Vqms(hEJrs3}PGY1MK zEOw|!OC@-1HG7*T?NW-d@;H1s?pV|uA8enZ%hsF}5O9&fN%)(uS)QV- zy*DOiL7!nq^B=ulM$y!e*m}eurC#utIdNF2s;1b`Wv@~y3%JPOBq5uxxvbbK?Uh+5 zEMAKZP0lb%y^o-&#TNfA-?!NS+;6|;Drf(JR~Xg2;E%g6BthVsKW}s!LGGSztb_BGPt`I!HCJYMs+QrYIWqh* z&+rtGENT(ThdnK$*zYlwsg)~7Sy~69i3#6np@e`GDoA7db7)I7zW01mY zbh)Ga^mW`;SQMT#YT(&kjuXHQPIZC1Wwtai#~np1!2u~^i4=J3JYDYI^Jvco@LHpq zY;gDEBnX^6I2r839Ct4=Ry#3+=N*y@a`!=FMG*Lw$L50EU2d!h0&kcn$lalj^UMHG zGOEc8ch@CB;2wwOQFHeQVe#hcSvE5KC+Xxx*Mm5SZKLV!xdEH5;_)fYlkt=&&v2 zz(2D-TTXReu%12^6H&ov7|KN(P>%lU80(}40Y)28!C2kO#hO(vR-$sTzLbkqqg<>7 zzTe@styZ-mBY`5IEGru1k)AYSZx>>A0`^XU#jUoRK z9vqD#1qg#rk_8Awqsaq=qmxbn!q8V|0Yc%hBqbPJ!t}*V7+P!=929O%o<2AlccunK z@1IfuL+j52gkvbk0))cz$<^R+fig8H`juP_4u>dHgQ9ihYH$ognHm(GQ?3Tbpp>aW z(SPM?a12qI8Wdezt_H_|m8n6|3+8HY3}2ZV6dh%*2FGBQsX@_~=4x;ZZJ8Pr-EFQ0 z$3T~IZO_$wRBv}Xs??BzK9RpUMqZak=Q22~Qgjr-{;$z9vrMwJAU z)+S8uUNe!xe7#|EQy**Qk2a&^B7f|JPK)%{@{-GslA~OHbMsYxr*c@%PO&DWPK0YyAk>`G}da_E+F!-W{^|EbA!bi1f(@uW)MZo6Y!BBUnp89PW)^ zE%2pAjkiu~Q8;g~bt>g3igSDcX)SQhC>4u;+SJJvIgnF|tH=WBRxawcTCcK1y~)#C zd^oMy3QnqLSONUK`we^ha6@qV$s_WJJE>f>0OiOx9-VU029%3Npj@;9nDA+s{{O@9XS;}*w@+z2Ls zXS+?Gw5qJ{iL!DhqE*VOcr;3(>X}t`TZ@sH$ykgXJ|+P^rUO1E27aLwJtU+@Hj!eK zLdDpGiV+D_k7IQa!&DjER2kfm!^f@Gm#t36EpS<~1yZu(t&?_*rK()ao^mlk%Ec5Z z7ju~UN)q%vOPGuxKzf|<*7{w+^ghCM%0#ZOByxQbd8>7f<^DBZ3Po?8K7*-gKg&`_ zqm~{7NXn!G1Cq$wzl+R=?yVw0I?A?MkF&mwZdr#L`ekZOx!5r;G%OJwy_#%b0<(c< z5Ow9BQhOS05}1t}q^~}1RDu@@mn8+)A#3}(m(l^^xDQ^6P!F)xB45Y)z}f+(So*P0 z#R!R*SSkZ+)B2udArq{$JRtcI-AW9#Bu7oUPt@ONP62!jL+yrGv;2*enU7m5PKz4l z7b3@6rBvwxl~S^}@z8}otT{@U`e9AvxJs!0VNEzvFTV}>u;zZY$iY%oWoFuLMWI@= zs!5f_R!R*kkmV!Ayt0rQmSsL|77a$8&9b6k-CVcGrsr6_U`e`R<1%!^G8M&08y3Hn zHH?OhtF{eGBg4IFSUhw_%!Z9istp@gOdFOZ3l<_8HZDOmY+N^N*jQe&lj0?o?ccq2 zJ?rAx9$f9anpN!!YT6g9k8!W4d~hpy08v`+soQ7Tu@0Snf~jvYeJXoR&af z{plnIWcH_Gn8UT+<#vI;FiOS16r7|3_!`^CRIv|m@ZvFkhTuGdud}O)ZD$=LHYlNC zfcNv2vhINfu}P7N1}RjuMxml93Ki{8sAz;jVSzW=!3d##WDuhi3XA+{$20j$gRE+b zRJ2f`Vx_NKw23q!U?@u-o)*%<$xvoXLzx|BC^f5Cn$<0MH{;}RF7WZ07#Q;qG2n1Z znH^>+t9>nG&pGh1#z_po(=st|GP2py$Yx6;n=OrOwluQY(#U2@BbzOaY_>GA+0w{n zOCy^tjcm3wvf?SHZS-Ln`y1Z>Ahnn)rg-lIk|}~$`-<=kFj;3G>#X%zHB%gJJpEzD zg9T{Ug9$9L&P6%!&ffR*qgkj}vH{C>)C_PRr~1GjIW+@J zU2UIMaJKCnO1gll_wCa#Zc0+XpE}hC{@SS-VA~GG%4`7mCr-@(ALdja_%x?xfX{cT z54_l^8Q_PE(&`0Ym&AZybE*$y^Hho=w%u*L^)J5d)3y)I+BTSmJebaHiKe`Rxd(#) z549T_^?>ANm?NQOH%bd11o*$9jSn(1N-G}-(#~nMT3le7*g%j*MvEGdv?&aBKyrh{ z=GOatGu440HyEo5Mr+;9KkBTXN#Eq3y7!eo?Dm+|&R@J4cfF}3{b?P{Ru^|^-`5B`K&Idl7)yj4G}dH*Z}nxv4v=*s2_MKhkt7!Qn5@qLvvefo z2xRFwbY?J8*lZsSg(FHGP+ zN(lkqkPYSFce-W=$cV3H8wPx}JVyN0w!$#5M#Ftyb?5+};nXB>U8V_sWfEgly4b}| zYh7;mbI$EK2gco%7e)Y+OS|VBcz5@F`@jb~H60z4L9XoQDHrPidrtL%#~P(>bX*hB z(4reH7RU?}Z(3>;h&Qd8V1{WCizA{Ob(3Wg87-TC^=EyUi3gf$xcM#pf~;GR+cKWs};3lIE2AG_BA8U4l38THT4Mt$%{lO~YSUo;u@AD4~#;A4E$?*or@ zY8rUDQ+?pCvcSL(*}qxIlsJJ$8KwCI|Fw(tfzNYl8hC+Iec-l_NGqk_gN>6ofq#{W zfzNfZKJYxJrh#vEst>%^pQSj#Gsa1rz!zp>;J3P1AGp&aQ)uA(8z*fBc$iZ?;K`XL z`0re-4?N;gDFX1Z#z_RgS31=LUY%)zKb^#Y|Kij%u$9~{ARd`kI~ZR}Iq+~Ns~0^g zDR8`}}NxH=O9bNWz{hOhXsObq<>6dLe*PE7-EcB&8LB%>w`&-1|adTmIH_geE1c>8FLbI0e5+G^;5(d}2ENa!KJZ$jG*95~ zxL6PPAUhJEV&DV)#6TZ-tW!PU`!h}O$6TxrOvev;(b3m9@$?^(J#hyb2vJxK85eGjq^e1=m!;OS2Ff!ky|mEgPiPGt{xpi>>- zWtk@U$1c_bK0Nd5!FcXsYCu-)w7_xVZ^=}_xapz_tSuU`qO8Gz@wG)2h-0l{V3z(w z42XBEVqn~3=@)>wz$z9UPW=jvyPXMj)7oL4Z8tqjp`v!B11iGhD^oEQf17fwwBw@J<` z@C>8WF2Fdp66aO+^A9pH@UrBV0+%~A4g4dg`oLEjrE!9BY$eXm+t2@#iGlB)oLAsI zoSFvi?NlH5YNIqxFz&B-pf}jhw`O8s++QhM;8~g53&z)#(5|(g_s^VH@Z;Q5?E?>Y zY8rT|Q+?nKSzzEFxmbs#(37)8N$^YD6`la%{;G?}s^l*+Rqz9RRdOwmrAS#aJ##%b zHJ5p#>-K@T&RPv%7A9q09}g<3Sm|2(yL0fTMH7hcteRk)XAuMLlALBB{7`jX653VqjGrCxXQ|bk51k-5Kmg81>;hyDy}rXvT|VjW@$9I&8yvQ?gMd=RkP+J z;}7HTsvI~uzkQtFV>9O$jC(5y0p6I2fpKr8B?0f@UgugM9{HHW2|44|3{gAX@^17`S00V61gC zkZHw0jvICYv;e@&I|hR63%tc6?f}^ykeLFIDTxLL=ERF+49GNP5Cb>NQ&bmxu&?+Z z4P@@pWY>OYJ3x{Ge7>u8fXrt!1~5ziq6r)}-O)49ga6wL92#r=#+JeN$^3ROKD~b4 z{%`H~&t!67oO+eR4@*v$I49<8vF4~5iXM@2(f!fzSX#uvQ8_Rkj>^^C7@7!YqEr`* zZL2N@eAjH_2#n>a943MZXn0gT^{iZ+OErr;%CaYz8uDI#z=C}#jfoftT{&W4=Br#Z z3*}<$cY@GE863=jE3eH2(dC%_)tp|uSr5_eMNz-(vB>v}#k?#?Bi zqDq4C8b$KM<8$1dmo32#^Cj==j^hN7o$f(1#lhT^H#uy@6>m~gjXR0Is2unWzDha) z#Enxi);eoW8$N-`fzuk=#4z_D+25XXU|Nfm=?&vPywJ~UB(xKHUgf~_!lM1fakuGY ztF;xI3Cwp`7bc&EZvN6PBusg2J`Bw+r9WEs+caKzz;SA0)8a=m;aq;PhK$wbVf-$P zH;;cyT5%rk=9+UJ1}45(qt3&?!x(1Tx%?V#^Sdx-iFQj`u>`ld#w*45$yeT%zWB$l9I-E+9K}x6EN?W2kq3+A zfFa98JXS1J&48n(jI|>^R6JN~_n809QXq3BAc@iM6~@%mzG2uT@80Ii;^&G}9o znxfQa$|9cOq$R$-`Wm&Aj22qdRD*i5rGsNVQm^Ha(xR$0q*G1vWnRrw93VKvS3Qd+ z&=@hgXS!|&NN{SfF}#J1_=6S@lQ78!em4unLQ#xNnT8lK0e;9+I0L-xaSSSAiw4JW zD<-32#N_|O>FmiM-s~SW;?2`+jJQ1QJyRC(Og!PckP(j-T9*6)RD=32t0YdCNxd=? zqfu3=daQl0?U->2O%}>x@OnRCVjM%0p1nK-QdoKl34KnsNi@I|q#tr)BT%HuSp297 z3-OgCH%%-h`)J=6>;Q>gUMjT$Oj5Lw#fWPSPosIR1)Vl6`?M{VA}R@No@n}e(`A=a zOqSi3C#B6AAmf=Xm&U)b_UA1uc1J6bs?xVjjO|gW$Z~ICNJXO0j;lliOhNkQE5{K< zs*J^VFgV&%Nir2lv_G`u%a9KwdaXz>NzqD+5ieAcE!*y8msnE6!v^f+RT8Kbd4JR7 zq@^@8G#E&#ZEwc(IE`5eUaRa_6XQ6hR2f@1oMT$R(NgWC&k0Y72AG2M!=7x2i&Pm) z?ch`sN&1&6BiYZk&OC8rA(W)_a4(_w_n;G zv+lEf>|P5@D@wJt_dN@XQv#JpwS0bIVjL5YS}S+ThO|BOIe8$_08@~D2!Ug{B2~sx z+mB0C*dQvBXg6B&r6PeuuN4UmPIl~p(-M;A%5H2-pjPDmrpbw8X=qH@ zF7p+&!g(8OKg0AmmApWeJDlMXHRYw)?wK zMH1~!mVBv5Akk|@f=P;2T8wz1ihO_t&8ZpMGCFRHgh4cLE!dl>~{Om%l9QU<@Tbv53M5^WZBopHdn21%5AJoe6MW)Gf z6jF}yltR@WKWKV9Yf%x>gniS*c(OvoINZSLg^H$@<4(5k$$2#?MGiH*){jLL>nmfe zy-km2dFAoWm~Z*AwxlgS#49Umx2%iLA`VD47umubkVT-nS*%(1fFExukrx*Oc_LWW zwqi^|5fASZ5(-GvmHL+$?QLCaEy5H_2gp^PN+#mR`4Nc@aM}-7)ZTGX+UFp<`f+ku znvy^LM+?CIp1g2!c#lfydp|hcAdlLUbgmlbXG~*pni?k>|LSq} zd4t$d{?3tNSx@8qrfDorQ{!y4(Aj8V3`P>-JKp161ElDyBTm!T{=|aXB0)B(B&fSu zf^_k0f5V4(BBz0rTXl_XknLrrv;_hjZxIM7l2LLHN%qfJ>)saQ5Fz;*rKpUxI5_h@ z&smQx)cyRdOOLZ5Y(|RL!bUhJJ8BEm8U+D=I7RSp1~~&>@qsjXs?8a0(j?g}kH5^6 zM_7t?1XjvvX-ogRed8E!k?$Kl{}s3GllJ3Kp^6hAsh{_!MfL{s6*vqkJvs*uIL|25 zJT8KuO6F~Zx+9ODuUa39cmWX8#;T8^Xdvn!O`qq}EP&8?+k?-p*;L@q)r{CubkxN;l z1m+o@D$YMhuKlRv!X+^BFGW#JP*mS`bD0EEUYdo8O6g5ddUu^}>P${S0V%>pmpe*P zCY3;nP;`M`@zR_C9^!^F2|PBlsEIl5C};@|NI^@az@N{0f`NDLCA*uL<8IPe2@d#- zelEz}WyXpi@alPj+*~p-2G<~1n&6cJZkPf zY^+2L{OmkI?mACt)I|_@l2J{?bN7NI2>i3B=23IE+E@t@c<++ASa+}3z=JI{OP86d4mi+GK9RHytSzmXc7?q2K0KLey)G=kjy-y{g6T{ME+?dmo? z1EgIvg51$GBr`zTMI*=^O+y5Mw2MZNyGd^)Gr$Y8R=Y6Q8X4T&I-2Gj_0M;j7BVBVb86wPUDTviGf9H9Qx zng)$PXQwm-h8N|)54GkXOAQySry*ePDj2O?xoFVJMY~Wgnt*b#wv~$&t6Z!@VXEqId>FoP*m?w6FbxFIqXEKcr}5MQr+hf3tJktre~3wD!6G$*ud& z!O#L$woZ~?#A&?m925;=8+F9&H#izb3J?ZYEDI2dhLZ;fhhvrkguy?{0))a(OG+^K z!z%{a(hS4UYO~;=@aFRL!O_4oH7JI&lnNNeuS^MwK`f684xcYmgJMX_)!^^|Gc_m% zxLgeme=t*nVz|rI;P4JJH7Gi{Tn&y=v)ns0W(vBq8HEA;21tLH7G{ZTrKu7V@sBd z=g)f5!(44WjSd)KIud!-gkOSu9DXn zCO68(T*!A>=M7?`Bj)>xmjbbDa3U~0bq)0dQ0Qc#S{E|&DY08xo8d?x& zG6j%nW^CX>k;8?rODr)dH(k^x2ObHb;LBpKSlcAl=bhQfrCo}Gl$xFPsb!d6X zvU}wFW?!1?mHF1vhp3z zQt`g-I5ED$%IK@sd(lnlWs7mG%@ST@SV!ci`kj&iYhl#At~T$#zlkoLd!?y3(w z)2QYP>SXAAFPHukZ5am-RQ7se(uImC7b-ZdUTe=VZ ztX2O8yDpPi;)+}E?j~@Gl^8rJy2*wu_eZ=JrXtnZ1kawnoy$_joo%F6Myl)T4VjKs{>%46*CF$wT79q=(R@F@lJ(QzR? zvh5Y46e`9hRE$Wd`5|-n;v$BrGPtQSxFN^mTdiFz&_~-<{81n!D|NbSELG)V_LPeW zQZA-QIr79c^_3*(KAxZskRC^l&hHAQ_YtmBCUSixk?V`d;T3|Nt zr0SZFO6_U1Nnkc^kiPo3F+XJPt`GT@=Re5Wr9KPk09h=l9 z#R!R*SSkZ+)B66_LMGTO2J$1il^953ekBkz={`|E$eaTBTvxH$4Z2zWM#{_*%Sc)` zDBr3z)+(h+7pRny^@GD!t{kMcaF~{k6y{R-S}1XIl~A9*v6%?Y!I66TZ6m)8;?4eP zw^(6jwV^T}Ern{$swPzyTPZcHK$aKDtHr#skQ$cd%6L zmNf(1{f1@kjhQwqo-6C54I7t68KEVW^2e7Ic=%Y>bgv|;0FX2ZtC%7$eDgJp?^ zjjKNm8<*@FHkRSC7NwYN$Gvv_NMaWhVk`W8<=V%jQ9cI8{Xj|^f}9D>rAgCIk4#ID zU}{?qXveD7S!=9l<)~)%0+id|HoDmaP!9ZL-vsZz-QZP;nzRk=zx0jmF7OzqrrAKi zv^4x^Sjt7S8l=Mp4U9^;Xi~~WgHkS&x*RzM{$ZGX53|R_wR*7eWYE3RpnIi3_ez8Al?L6zpfN@%bRf1^ zx65KlY#aZO`6zbTTGkpgZTe{}bN9?L!O1fFx4&ifvCPy*_p!{Q+*td6SeDrzW|?%k z;@<`f2Ze`Ss}L8?_A$^Cd7gm7`0<+LZf!Ha7-Y%?H&?#V8lceY+v2!jPqA zUYgYmnCg%@H}H;{Y2oB+iQ}0qwq~~1;+J+azI183y+Zo$cDs2YDb+IUnm*diVp!wb zH9CXuhnMQq^FDUEPg4(mj30pQ0P*U@Z35!ns3w@J40b@q8WjU`m5v8^VrFk(uCn9- zd~xa=fS9005JxddZtNU{it`MiI7EA*&mU%E&mdB9VjxtU7YG&SnnJ}{flzT$AXJ6-Ebr~YLN)N_R`F0x@H8Ak|eXQ&NY0;udSIda1 znqM`IG|A}#xvCg`;)WCf@V}hu0@L)U&rp4MHa>%Aj9XwBXGlLgi2<2ANwotR^reb{ zxT_)s95%}A&~!H17ttSW3O90%lzakttNNoa^?sC|^XXa72|mYr&Mxq!PE7;fH<%9Y8vDUAg;6R7 z-Z{BMz+If020qxSF7S;;X<*>5x!4SFS>`x_cTX-6@QaRNU+IZnVQI5iDC#;Gpwx-2j-u9vu1 zz?S<>Gr&DE_X>F6jjoz#;;G?^sgZ*5#iTfZxMC^>#ut;&fOuCbR=QU+xL0SJ$E9N6 zw3A{1$%fK5wf-Z2mdp{i@E}H}f#ADNBfcJIHE)XwH zG=WcWscGP}Q(fTmotgpS>S>f<96QN55XVlU1mcRRCioHVNOgd?XQBywyi0X~cx)mC z?77r5@OY=XKwLagWOeX0E;R$Z!6;2D_(m7&0CDAQh7r|<8wHAC>*-CyhgZ*gh{ILuv?bl@$X?(27bU~(2` zN@sCqn6t*elC;ru&<~b=^;ktaBFv>919~1a>}uA!N8;(BNLsOa`a~OP0E4kr{oa@x~W=UFswyu za2PjNd3f^!<8ErntkSM4O+dA4?V3FRt`g$=$8W>baDsL-tkv$aX)8uzeEfH6&6uv7 zF!ir|j$*>O{Qp*4&hxk>jXDpPUUW;Cb{_8Lu6^WTjAH3`!j4C|E?f3HwRO5{c{l=y z`465n7{%^I_kXLoOV+lel}ktaPp{b0UH65rxD8$R)F=i$);`7-tk_wUIY-U7PcbpJ z7BkCWuSBStX2YS+Zly#6OhNj_B@1jKh5e;iYMV1LVjk8a+V#Ft*8vi}Jdd^lOj7i# zD>34QzQpnx3z{8PNpodOEGAG-P5!fKvUVs=a&*~*Dm2^wZhEXQE>LB+niva=Qa|O| z!;mUNpEXN~2AG2MtIE}3-MUfWS?9X&*O5LP=#juG}B`*y^?=bI>*G=OqVJvcjJas8T#zcOEka~ zq;J);q%2ZpEWYQ*LQy5jR2k9!jU`_ufIyQ;@!3&+!S7 zDr2dwxoHyfKdOvq@99hX9U#$bm4Qi$R!@w0p(@+Qf@Wty(p*^|&J(9tt8A@laxz4k zRAu+Q&}_fV^f*1TK$X4T#5g`8RmN%}`@}-U;L|<&oJf&qfGJ2noWdrGNR_eFHl|ZV z4Wi13_7Y3Jv@#&kYn6dXidIjIc%dr0%7W$;jifnFwJT++RrVdzWPeDS6qD}@(|UEx z+F1Kew#~yHQYCm*+TFz1M3VX`S6+uy8T#x?Ni@I|q;FAiU`V9OSZeFLF<(>}(LU5S zvpPVc*D3>(6s?{Z@j_MhI18G+G)Z%12OuU;tL&Mk$yq3AWvq?N`Z|7PEW60`I4`w8 zmA%u%I4~tu#>zEE5{9-i^f`ki(EwABejJN!Ly;( z6s?{Z@j_MhFBUZC#w5+Dm&7G zX7^aqT-m0I3DhcEXPTV;lUBwlqsx4os?y3XH$6@WR;o-*_VXsjezl0P<;A8}MN<>@ zAEwE+wipiER8L9UR7IBB53qf0w!SODYr+mQF}A}+tg^jT1TVx1rpa+eDKQQwjro>Q zrNrh;k0Xx@B<#Z`#s<8Iv3th;SVdDy?0crkKE0G!Wfv@FY1!bTd`oOkpT`j7csj>~ z*y<39T?}?Ggc>!t*)q71?{il@9pG3G;T zq}{exDGYuad~>dmy2rvv;qb%oa}Yc=Bozr{vqY?^vWs|%rJY9xq~KYUXU$S-8+cOM zf&-rB)FhBaPEm}zLPD{L#Ris8>`k%OUMsb{%)G=NV&!!^70z$JB%-nCSNO;rSKR{P zIf_)&MZd75HG+_KSkJRU#4NR?Wby0Ikt1X`a)xYNZmF*Ufza>+h z<*fu<2bs1lIi5I_=y<%9_YMdJ%w9pjgUGaDkt!cVo@I-P?In2{sG1hYXdA8iPou$&a zYBo48z{0r%7Hfm=)opgXBgCQg-D+*K9eKi7W#pn>F;=12ie@uIC~TDLLI!!d75?xD zViAnDs4VJO`jIF!*7}A8K2)eO?8cC^H0Eq{=D9pECQhp`j8^Qc-9nyc_oPk7;FHjo z+NngQ^4N4QXo@v+XQo@Z1e$lY!qa6#aGjcNqB`)U#dZvRh>ZX?Lu6OENz183(6a(AbH_NamX zU{n(#cmI|Ifw#^Rdd5FLl8`Xr!-M=J3;EnSHx%*)f1g^e1&oy`NGFEa8{MRF~8af@=SZfXNSa0Yvz`UWa@rHh#H}o|? zIy4#gfHZiC0Z40CQ(QC09SvS|fwcBUkUJW@2m)!ujUab4PZ0#tHXA|iXr3Ynq-{2W z+|fKm5J=l>1i7PmiXf1-*$8q+^AtfKZL<;Nj^-(XK-y*_$Q{j71c9{8Mvyz2rw9UR zn~flMG*1oD8ZB+J5#)~MDS|-SW+TWQ%~J${w9Q74yQg_eodKSlHR&~T+|iCDL?Dg0 z36VS6u?PZb#El?#v||wj(uf;D?r6s%2+W&w?IUT8LodzNLA?pz%f?|EqWbONzp{2J zN4Gw|juuK2Q^DAUUA_>sP{tCHM3Ae*j&nZh<oIA(eUzjz^ zZqg{lF3QvA&W%#wBKcS6&W%#kBKi1peaUUHhTVKhmcpc|Y7-zoiw&_kCKDS_dB4H@ zwrzdqZWgzU)il}Jf1j;q;}HoJ9U!5ib0ZWU2!4Z5(M=F4x&T7O;a#XWbPE-SWufAb zD^wg#g^EL^P;nR(Dh_c%#oG4%k1|`+4ho#?uS0%b?cH+c4F;1*cFQbw%Pe-wEOyH*cFQbw%Pbb>ZgiD{ zERlTuf-UJM(evSm5Vpn7fY^p*NqNCG?($-tcAU zoBmYo%@)V&yuK%ZfA6NcZdF;e6J=3OL<5wUzC`;Q`l;G|EkYyL>k_MV^)kj_OvRSTx0(NEQuiCkYvWbIS6a~9d# zQ>3Ja>-#nhk=ZtoNZ0){%uym|9bgUYOo# zp%IAi=+$IC2h7G8sxDqj?P;`0U^YmQzHE>nc|ygooBO{D`IW~V$=Y99cP6nNOD)=#B4X=F?@}hUIlRJ^VULB#9R48k+>MbSzXk zx9pa4%WgSq?6T6ra^?c(mFH(DW+t0a#7t#B#mr?t#Y|>D#mr_u#Y|^E#mr|v#X`t_ ziiMH=G`|39cd<}pni2~`s8|R>#moy8Gc8oitWYtNLdDDp6*DDNOn%;Dr{BoXTeAri zIGJ&WOkUz=emj>LOqSg%^W7`+-7E9mEA!ne^W7`+-7E85bd=h_$t5|``4d?pAN4ua zKihKl^>&rMDivOXl1tItUttCFX>0O2woozzr(7%=41-kfv*>TtM$~7 z9+^eOD20l#2^Aw+aQgMaB8I6lxT!L@q0_HjEYO4OD$_3@B`bBhYb;geV)m4a2~sYm zNV!;wsjnnK_wfXEfb=*z{Q}eb==7^hB5(gLG9S#hiUc*&ujg9tL$|EM4gE5;rd;fp7aEobk6ul;(=3W?;E7epeW^W- zHVMqe4boR1Hwbh7_1z)A;vJB+ulgoN2Z$>_boxcU{>FlqAb}~-{aC1CghWg%m4UTs zeJ`?*2{wy?{D^KPhWUAK?g#`;x=++E9bx)~ZkE3hG80qtBlBQ)^G9O3>rx&DZ z*<|NPlu+zIr5(X;xg*$RN05w(BKuSv3KEK4JeI74is_8^G2PjGB&I+6Ddr>lDds2p zDdsEtDdsQxDdsc#Ddso(DdscY$9&6_CFWPCm`|Z%{)CG85-R3LsF)9-V)})O>CC%Y zek1L_%4SXAWPcsJ*7Ak$jyFk$w_g zpCTP+W8}$p-Q*PMu~r7h`avVvCNfN5IwHhUP%ajOaaXiWggXgqX+OC;nVMg$*YgO5?d&o`iSw=u*kQxBqisaxoD zj7_9sL<`PY?rFtF&csnGgPSUY8!~742TSzl?J6@CAhGBSgN#r~%EhRai*YNLN~l51 zPKf&59(4yu7oc+!Fx`R9P0Hld<{^_-T(fR~#i~+14>`ipLA%oyZ4_yc?gU73ws7y1 z#=+ly+|BG_8*BJ+KbL zh4j*qVqUs@xyzAK3HA9Ko6V>>IPxgJZREFDmSe47+Nudhy~kVo6zQr-)k>rct5zaW zYIuo6>G4(>vz7>QT=NovBag#Nq!c!@m3j@om~zV@N6u~sQnE0HKQJg-rDp}Zo;HP0&?sUMctZDRvj@SbDKJSL`Z zB$%G#2bz=mZRgHEntpJ)RHGl7O><>DW*7t>77 zv4X_e-$s@*=jL8p{!{{OK{;B&&34{I zxm8y-N!ZJ-%7G6yPEOmLWAJr}+V6vn=0SE9Dc~VaO&@NM)~Dgewx?WddCJ9hr(A4x z%EdOPTx@a5#r8Ik3$0DL*w&PbEls)D&XkL-OgY-vE+0$Py{lm!I#4-U-Lvd9OUi+t z=dWIx0iKhIfnSxxfUk9`2i)P>lo0SejFS~V;6<4j_}ou4sv>r=!AqU$0l($c%-D5} z0$qtH(vNTz>XG(r*R5vpCJK15Q$65a>`i#0_%MULL;;`aR1f$$6azo3mweo>r(;(BoJIK5!{cT1&Ush}7E%%Kc zpS5!^?OYZ>ww6{dNr`P-sMx}Vx}$w>>2bb0O6!)AAEj-J7%lt#*`OX9v53VcEYv6s zSj1?yT!o5_S16k9vwaBfGK9Y?Q9m=dG8>!^Gd$&Ec(>NNQ=@Irj`LorG8R1XC_Vq) z{P*fW{n@MmgK5Chqg?)59jK4Uym#7ba@A*OuYcx#`OF>db@A;LCtC**Fs*M;<=il| zsvwy5HxT58AyoxqV;l%FymD1Rnka*)a^P%0y?h-lk?SDD*d|q*MoCK?B*&T{t#Y8u z4Fj(VGVbCuD>uIxwOXqpvFUqjw)n+=Vbfv_9ZYkRndj{_Uebs(TAW;3k#cu5qtuLQ zQ(2mcmKFH?G$94jaOB0pz`JDQ6qsfs6%NGUH3IB>m?iF{mM6B=ULo_Wc3a<3#bqU-;;NERaZyRAxTYjj zTv8G$t|$o=7nBAS9$ih5qa8AS2?eA%N__)qj%vnW=8dBHerwF1&l>f24By+=iY9@F z8Kt3tk94sP@Oe&ku-2*3xk;hdhH(7p3p}(*Afx&q58T{fa#~PeyzYS@t~W!ybSaGV2e=2` z0pgsC6c9gMV*ul@ODO@FnTwSGhxOk(H$7|fimB0X(?chX2DO*8^|KtmM}PTPlH)-Z z7*F2%*0g>IzScN7X8`rI7@R|RZ;sjr8oWu$ITqXwoNfHCz?9?=H$vc_| zBrxD#8>P(y{H`Ph{6;1QZe2=fJ>XM~($K)Cx>yJJI;Z-;Ph^_lAG=r&cto~U1%9$| z(wc!UajFk|ccuycvWxYAI3k(@Fb;-tV4Mx*@cMW2b7_4byT~e5dLaGifv8yNg7ixl zq>l^2=9p^YgPdUVYvsTf_?){3yvV6O@NG`@fY)S!fq&y-ec%#5`7{k=ja{s*bcOo3 zLc99|8GRtz-kSc>J?cmINX1J3sE>biXY+tm4EzY=BxyjNELXAUM=6IRwY_~HMLF=E z{?U{k@BpX!z(bsx2KJrm1D|7*h7V>BUIty9w|6B?An)E#O&q$F{_Tf8kQbAw7*5`2 zvo|qAUpX|Z1{o?^{>11f%`f&4Lrc9K5)t?O$PY1BnJF=CIjA&%R0sI4PW6G?+qSV7B#_-Cu?=Pb`(_)> z;Erqdfv?WQz;AM~9`K`1b$}Z)P4NBGv>sSn5tubGEnF~j63G>i8HtL4SwN7SGCR3B z3k}RPMTRBdOg8TTpJALt3Oqj(1E*O`516mo)g~UyF_vAI+zK#r2zj|6@QO?fyy}x! zn>FT9OlLElGaU< zU*2$yw_f6$Njl^5b-;F8?>BE1=eO_vlfNeBF*_Kv+|u9VqVVq1zTv0LOV|2v>|XTj zxWR|mRmH$haIqe6wNul;W1Q*%zmf$8-qF42S>Pj$(m26Kx>yf*lvC5d=Qz~^elH6Q zd{1|*XMu-hE;;x(7wZ9!cWN5A&Z!>oXIWt22f9l>3!Khea_~tm)&rjG)HLwrPW6B{ zXMurn>ZJn%?(QD>EN~)o+`;(y(x(Bxnu&pN>Lmi;D)-1|fse`@cQAgwL;(C)CI-f- zmk59?TS~VC>}E@oU|fG$NC2`LDRBaSooRxZAV?PkWUe4_GF4z+AXGUmn2pl{kt$~e zy*MjSgOAe!(PUPD+pl6^{C>$35FcN~z+Z5Ge-?;yuVT@=SFUvLXL0WzZXUmifqOm+ z=mAe~Y8H5UrU_=UAX$iW1&x5o!ff_daxh0`MYWti%rbvilFc8$+xh%q8h9V4dO)TW zQVDTJArun|W(=BIFjEFe8E|zrF90)VkkEik6*M&F3bl#B&wT#S12RF7(13UHIlwfK z>4PMV*~430Y8J?hL6ZSavxiXjDQ$1j9KJiQ!n+3kZD9t(eN*C(^nM6o3fcW^T3Eta%?+y^ZUo?S_a;a(H z5l;1huXJh_h}W-Cg7NXCuz^2u%?=QEUp4XfPsrSHFb=(h27G;TOH{ z+b>eU8?q?DH@W64kePs{7R;*#gP)#NUS7eg{@Hf-! z1ju|rRl)z6#DHvZNOFPfaHuBu5k8%m1u}(DF)(uo$rbRI*{lP+(L?J1pX|FGv%uO! z*)p5i#Jf8IA5GG>K(OvBZ3od)ak| z)`@Q$TXxW4_rLp|57CFc;n~N%vmP2+cE1(N?l({D{bmWNuJ@XwF3z@ck3myzl#D)f zM3Jz`qa1rYl!J0H|H_q9y7E!1c6(WRB38;EI&@mL(N$9p>wAFZML96})HGmgU&`db zSeVL1Gt%%V2g*P>s)_4e0E@r2+d5%u{x$7m-`W@h9;n6`T~Ots52{>rLY0eNsB**H zP#)n>Iq+(Kc4Gp_KKfwC4}TR;Q+2_tHp|2Oz)>Ah{6r0In4^eOsB+OOR1UWg=T5ok z-3{c-9~{*o!>>|Z94kB~<#3s5ZU}?2%7Gc6rKlL68Szw%A+I)Yr8d6jTy0^APMfQI z(&UZf-hzhpuNpHu$XrGHP4Yz|>sxv9LM7=xYTYNDp~`{jA0*4bVSVLY7vIDhjj_R* z>gM0AxS`Rivv)jA5<#91=Ew<6+ zVQk5!mxnt_8!X=SF>0G!x>N_d=HK6gHYm&6qm65mceJ>)T}LacwKyr!+q!FO^vnx!Q4Jh-pGWj)~?=TVjR7Z7p-7@?BNSl(`-2OIn5){08@~D zu!n5|kt$=U9U6!cH*cG1wTSjAOTNs#fJ85^{8<4eDdVkER>X*vt3`8z1%0DO(T^^2 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

`RTRd1FW-@v2DO{e+P)SBxB}VobMUF|&%r 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 H?=<=^c@Y62 literal 5265424 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⪙Hwern*UR?DnDQUe!AXz_FnoU-M(r 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%0A~)r!Ts73(aND8dv~|7Nbb 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

Oj)vf%T&wEhlV5Oo@q99A42$y1Cj)WH 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

b@*X2)0Ie3pHfu;UpyWg}f?{6@A`vkA};!ltVV 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

R=@>kOARjzlXTppkJfY_Pj1Xt6Ln#7DW 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

yhL? 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%0Dg#{v5YU0lF#FuQ| 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 z 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%0DBWtfui26n$y?7Q-*B7W@*8dk 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⩔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�+GYBr*F<5E16LErjGZErdk4VzWU= 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 b{6FD+(ey3(4^u diff --git a/ivy/engines/XLA/rust_api/python_frontend/xla_core.cpython-310-x86_64-linux-gnu.so b/ivy/engines/XLA/rust_api/python_frontend/xla_core.cpython-310-x86_64-linux-gnu.so old mode 100644 new mode 100755 index a60b73585945558db318921677c65468f0ae64e3..a8df816cecdc493acd6ffe98e25653fe59bd7847 GIT binary patch literal 5308608 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