# Path: core/kover/learning/common/models.py
# class BaseModel(object):
# class CARTModel(BaseModel):
# class SCMModel(BaseModel):
# class ConjunctionModel(SCMModel):
# class DisjunctionModel(SCMModel):
#     def __init__(self):
#     def predict(self, X):
# 	def predict_proba(self, X):
#     def learner(self):
#     def __str__(self):
#     def __init__(self, class_tags=None):
#     def predict(self, X):
#     def predict_proba(self, X):
#     def learner(self):
#     def _to_string(self, node=None,depth=0):
#     def __len__(self):
#     def depth(self):
#     def __init__(self):
#     def add(self, rule):
#     def predict(self, X):
#     def predict_proba(self, X):
#     def remove(self, index):
#     def example_dependencies(self):
#     def learner(self):
#     def type(self):
#     def _to_string(self, separator=" "):
#     def __eq__(self, other):
#     def __iter__(self):
#     def __len__(self):
#     def predict_proba(self, X):
#     def type(self):
#     def __str__(self):
#     def predict_proba(self, X):
#     def type(self):
#     def __str__(self):
# 
# Path: core/kover/learning/common/tree.py
# class ProbabilisticTreeNode(TreeNode):
# 	def predict(self, X):
# 		"""
# 		Multi-class predictions using the current node's rule
# 
# 		"""
# 		X = np.ascontiguousarray(X)
# 
# 		# Get probabilistic predictions
# 		class_probabilities = self.predict_proba(X)
# 
# 		# Find class with the max probability
# 		predictions = np.argmax(class_probabilities, axis=0)
# 		return predictions
# 
# 
# 	def predict_proba(self, X):
# 		"""
# 		Probabilistic class predictions using the current node's rule
# 
# 		"""
# 		X = np.ascontiguousarray(X)
# 
# 		class_probabilities = np.zeros((len(self.class_examples_idx.keys()), X.shape[0]))
# 
# 		# Push each example down the tree (an example is a row of X)
# 		for i, x in enumerate(X):
# 			x = x.reshape(1, -1)
# 			current_node = self
# 
# 			# While we are not at a leaf
# 			while not current_node.is_leaf:
# 			 # If the rule of the current node returns TRUE, branch left
# 			 if current_node.rule.classify(x):
# 				 current_node = current_node.left_child
# 			 # Otherwise, branch right
# 			 else:
# 				 current_node = current_node.right_child
# 
# 			# A leaf has been reached. Use the leaf class proportions as the the class probabilities.
# 			for c in self.class_examples_idx.iterkeys():
# 			  class_probabilities[c][i] = current_node.breiman_info.p_j_given_t[c]
# 
# 		return class_probabilities


import logging
import numpy as np
from collections import defaultdict, deque
from math import ceil
from copy import deepcopy
from sys import setrecursionlimit; setrecursionlimit(1500)
from ..common.models import cart, CARTModel
from ..common.tree import ProbabilisticTreeNode
			# genome (left child) and those that have the k-mer in their genome (right child).
			# XXX: We keep only the first half of the rule list and classifications, since sum_rows returns the counts
			# XXX: for presence and absence rules, which we don't need here.
			# TODO: We could have a parameter called return_absence=True, which avoid using twice the RAM.

			last_presence_rule_idx = int(1.0 * len(rules) / 2)  # Just because sum_rows returns pres/abs rules

			left_count = {c:np.asarray(rule_classifications.sum_rows(example_idx[c])[: last_presence_rule_idx],dtype=np.float) \
						for c in example_idx.keys() if example_idx[c].size}
			right_count = {c:np.asarray(example_idx[c].shape[0] - left_count[c], dtype=np.float) for c in left_count.keys()}

			# Left child:
			cross_entropy = _cross_entropy(left_count, multiply_by_node_proba=True)
			# Right child:
			cross_entropy += _cross_entropy(right_count, multiply_by_node_proba=True)

			# Don't consider rules that lead to empty nodes
			cross_entropy[sum(left_count.values()) == 0] = np.infty
			cross_entropy[sum(right_count.values())  == 0] = np.infty

			return cross_entropy

		if self.criterion == "gini":
			get_criterion = _gini_impurity
			score_rules = _gini_rule_score
			choice_func = min
		elif self.criterion == "cross-entropy":
			get_criterion = _cross_entropy
			score_rules = _cross_entropy_rule_score
			choice_func = min

# Path: core/kover/learning/common/models.py
# class BaseModel(object):
# class CARTModel(BaseModel):
# class SCMModel(BaseModel):
# class ConjunctionModel(SCMModel):
# class DisjunctionModel(SCMModel):
#     def __init__(self):
#     def predict(self, X):
# 	def predict_proba(self, X):
#     def learner(self):
#     def __str__(self):
#     def __init__(self, class_tags=None):
#     def predict(self, X):
#     def predict_proba(self, X):
#     def learner(self):
#     def _to_string(self, node=None,depth=0):
#     def __len__(self):
#     def depth(self):
#     def __init__(self):
#     def add(self, rule):
#     def predict(self, X):
#     def predict_proba(self, X):
#     def remove(self, index):
#     def example_dependencies(self):
#     def learner(self):
#     def type(self):
#     def _to_string(self, separator=" "):
#     def __eq__(self, other):
#     def __iter__(self):
#     def __len__(self):
#     def predict_proba(self, X):
#     def type(self):
#     def __str__(self):
#     def predict_proba(self, X):
#     def type(self):
#     def __str__(self):
# 
# Path: core/kover/learning/common/tree.py
# class ProbabilisticTreeNode(TreeNode):
# 	def predict(self, X):
# 		"""
# 		Multi-class predictions using the current node's rule
# 
# 		"""
# 		X = np.ascontiguousarray(X)
# 
# 		# Get probabilistic predictions
# 		class_probabilities = self.predict_proba(X)
# 
# 		# Find class with the max probability
# 		predictions = np.argmax(class_probabilities, axis=0)
# 		return predictions
# 
# 
# 	def predict_proba(self, X):
# 		"""
# 		Probabilistic class predictions using the current node's rule
# 
# 		"""
# 		X = np.ascontiguousarray(X)
# 
# 		class_probabilities = np.zeros((len(self.class_examples_idx.keys()), X.shape[0]))
# 
# 		# Push each example down the tree (an example is a row of X)
# 		for i, x in enumerate(X):
# 			x = x.reshape(1, -1)
# 			current_node = self
# 
# 			# While we are not at a leaf
# 			while not current_node.is_leaf:
# 			 # If the rule of the current node returns TRUE, branch left
# 			 if current_node.rule.classify(x):
# 				 current_node = current_node.left_child
# 			 # Otherwise, branch right
# 			 else:
# 				 current_node = current_node.right_child
# 
# 			# A leaf has been reached. Use the leaf class proportions as the the class probabilities.
# 			for c in self.class_examples_idx.iterkeys():
# 			  class_probabilities[c][i] = current_node.breiman_info.p_j_given_t[c]
# 
# 		return class_probabilities


# Path: core/kover/learning/learners/cart.py
import logging
import numpy as np
from collections import defaultdict, deque
from math import ceil
from copy import deepcopy
from sys import setrecursionlimit; setrecursionlimit(1500)
from ..common.models import cart, CARTModel
from ..common.tree import ProbabilisticTreeNode

			# genome (left child) and those that have the k-mer in their genome (right child).
			# XXX: We keep only the first half of the rule list and classifications, since sum_rows returns the counts
			# XXX: for presence and absence rules, which we don't need here.
			# TODO: We could have a parameter called return_absence=True, which avoid using twice the RAM.

			last_presence_rule_idx = int(1.0 * len(rules) / 2)  # Just because sum_rows returns pres/abs rules

			left_count = {c:np.asarray(rule_classifications.sum_rows(example_idx[c])[: last_presence_rule_idx],dtype=np.float) \
						for c in example_idx.keys() if example_idx[c].size}
			right_count = {c:np.asarray(example_idx[c].shape[0] - left_count[c], dtype=np.float) for c in left_count.keys()}

			# Left child:
			cross_entropy = _cross_entropy(left_count, multiply_by_node_proba=True)
			# Right child:
			cross_entropy += _cross_entropy(right_count, multiply_by_node_proba=True)

			# Don't consider rules that lead to empty nodes
			cross_entropy[sum(left_count.values()) == 0] = np.infty
			cross_entropy[sum(right_count.values())  == 0] = np.infty

			return cross_entropy

		if self.criterion == "gini":
			get_criterion = _gini_impurity
			score_rules = _gini_rule_score
			choice_func = min
		elif self.criterion == "cross-entropy":
			get_criterion = _cross_entropy
			score_rules = _cross_entropy_rule_score
			choice_func = min

		node_type = ProbabilisticTreeNode