epicon 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. epicon-0.3.0/LICENSE +21 -0
  2. epicon-0.3.0/PKG-INFO +113 -0
  3. epicon-0.3.0/README.md +80 -0
  4. epicon-0.3.0/epicon/__init__.py +78 -0
  5. epicon-0.3.0/epicon/_jit.py +250 -0
  6. epicon-0.3.0/epicon/activations/__init__.py +8 -0
  7. epicon-0.3.0/epicon/activations/base.py +21 -0
  8. epicon-0.3.0/epicon/activations/leaky_relu.py +65 -0
  9. epicon-0.3.0/epicon/activations/relu.py +37 -0
  10. epicon-0.3.0/epicon/activations/sigmoid.py +37 -0
  11. epicon-0.3.0/epicon/activations/softmax.py +94 -0
  12. epicon-0.3.0/epicon/activations/tanh.py +80 -0
  13. epicon-0.3.0/epicon/datasets/__init__.py +4 -0
  14. epicon-0.3.0/epicon/datasets/generator.py +90 -0
  15. epicon-0.3.0/epicon/datasets/loader.py +305 -0
  16. epicon-0.3.0/epicon/ensemble/__init__.py +4 -0
  17. epicon-0.3.0/epicon/ensemble/random_forest_classifier.py +210 -0
  18. epicon-0.3.0/epicon/ensemble/random_forest_regressor.py +170 -0
  19. epicon-0.3.0/epicon/layers/__init__.py +6 -0
  20. epicon-0.3.0/epicon/layers/base.py +30 -0
  21. epicon-0.3.0/epicon/layers/conv1d.py +80 -0
  22. epicon-0.3.0/epicon/layers/dense.py +96 -0
  23. epicon-0.3.0/epicon/layers/dropout.py +64 -0
  24. epicon-0.3.0/epicon/linear_model/__init__.py +6 -0
  25. epicon-0.3.0/epicon/linear_model/lasso.py +180 -0
  26. epicon-0.3.0/epicon/linear_model/linear_regression.py +193 -0
  27. epicon-0.3.0/epicon/linear_model/logistic_regression.py +192 -0
  28. epicon-0.3.0/epicon/linear_model/ridge.py +128 -0
  29. epicon-0.3.0/epicon/losses/__init__.py +6 -0
  30. epicon-0.3.0/epicon/losses/base.py +41 -0
  31. epicon-0.3.0/epicon/losses/binary_cross_entropy.py +100 -0
  32. epicon-0.3.0/epicon/losses/categorical_cross_entropy.py +50 -0
  33. epicon-0.3.0/epicon/losses/mse.py +42 -0
  34. epicon-0.3.0/epicon/metrics/__init__.py +19 -0
  35. epicon-0.3.0/epicon/metrics/classification.py +185 -0
  36. epicon-0.3.0/epicon/metrics/regression.py +69 -0
  37. epicon-0.3.0/epicon/models/__init__.py +4 -0
  38. epicon-0.3.0/epicon/models/model.py +549 -0
  39. epicon-0.3.0/epicon/models/sequential.py +146 -0
  40. epicon-0.3.0/epicon/naive_bayes/__init__.py +3 -0
  41. epicon-0.3.0/epicon/naive_bayes/gaussian_nb.py +168 -0
  42. epicon-0.3.0/epicon/neighbors/__init__.py +4 -0
  43. epicon-0.3.0/epicon/neighbors/knn_classifier.py +171 -0
  44. epicon-0.3.0/epicon/neighbors/knn_regressor.py +125 -0
  45. epicon-0.3.0/epicon/optimizers/__init__.py +6 -0
  46. epicon-0.3.0/epicon/optimizers/adam.py +125 -0
  47. epicon-0.3.0/epicon/optimizers/base.py +45 -0
  48. epicon-0.3.0/epicon/optimizers/gradient_descent.py +43 -0
  49. epicon-0.3.0/epicon/optimizers/momentum.py +74 -0
  50. epicon-0.3.0/epicon/preprocessing/__init__.py +5 -0
  51. epicon-0.3.0/epicon/preprocessing/encoder.py +187 -0
  52. epicon-0.3.0/epicon/preprocessing/scaler.py +214 -0
  53. epicon-0.3.0/epicon/preprocessing/split.py +60 -0
  54. epicon-0.3.0/epicon/tree/__init__.py +4 -0
  55. epicon-0.3.0/epicon/tree/decision_tree_classifier.py +329 -0
  56. epicon-0.3.0/epicon/tree/decision_tree_regressor.py +286 -0
  57. epicon-0.3.0/epicon/utils/__init__.py +4 -0
  58. epicon-0.3.0/epicon/utils/layer_config.py +18 -0
  59. epicon-0.3.0/epicon/utils/model_builder.py +54 -0
  60. epicon-0.3.0/epicon.egg-info/PKG-INFO +113 -0
  61. epicon-0.3.0/epicon.egg-info/SOURCES.txt +65 -0
  62. epicon-0.3.0/epicon.egg-info/dependency_links.txt +1 -0
  63. epicon-0.3.0/epicon.egg-info/requires.txt +7 -0
  64. epicon-0.3.0/epicon.egg-info/top_level.txt +1 -0
  65. epicon-0.3.0/pyproject.toml +81 -0
  66. epicon-0.3.0/setup.cfg +4 -0
  67. epicon-0.3.0/tests/test_model_builder.py +40 -0
epicon-0.3.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Epicon Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
epicon-0.3.0/PKG-INFO ADDED
@@ -0,0 +1,113 @@
1
+ Metadata-Version: 2.4
2
+ Name: epicon
3
+ Version: 0.3.0
4
+ Summary: Lightweight from-scratch ML library — neural nets, tree-based models, linear models, and more
5
+ Author-email: Kebtes <kebtes@pm.me>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/kebtes/epicon
8
+ Project-URL: Repository, https://github.com/kebtes/epicon
9
+ Project-URL: Documentation, https://github.com/kebtes/epicon#readme
10
+ Project-URL: Changelog, https://github.com/kebtes/epicon/blob/main/CHANGELOG.md
11
+ Project-URL: Bug Tracker, https://github.com/kebtes/epicon/issues
12
+ Keywords: machine-learning,neural-networks,decision-trees,numpy,ml-library
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Intended Audience :: Science/Research
16
+ Classifier: Natural Language :: English
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
23
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
+ Requires-Python: >=3.10
25
+ Description-Content-Type: text/markdown
26
+ License-File: LICENSE
27
+ Requires-Dist: numpy>=1.21
28
+ Provides-Extra: numba
29
+ Requires-Dist: numba>=0.56; extra == "numba"
30
+ Provides-Extra: all
31
+ Requires-Dist: epicon[numba]; extra == "all"
32
+ Dynamic: license-file
33
+
34
+ # Epicon
35
+
36
+ A **lightweight, from-scratch** machine learning library built on NumPy with
37
+ optional Numba acceleration. Provides a unified API for neural networks
38
+ **and** traditional ML models.
39
+
40
+ Designed to be minimal yet capable — like **Flask for ML**.
41
+
42
+ ## Quick Start
43
+
44
+ ```python
45
+ import epicon
46
+ from epicon.datasets import load_iris
47
+ from epicon.preprocessing import train_test_split
48
+ from epicon.metrics import accuracy_score
49
+
50
+ # Load data
51
+ X, y = load_iris(return_X_y=True)
52
+ X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)
53
+
54
+ # Train a model
55
+ model = epicon.DecisionTreeClassifier(max_depth=5)
56
+ model.fit(X_train, y_train)
57
+
58
+ # Evaluate
59
+ y_pred = model.predict(X_test)
60
+ print(f"Accuracy: {accuracy_score(y_test, y_pred):.4f}")
61
+ ```
62
+
63
+ ## What's Included
64
+
65
+ ### Neural Networks
66
+ - Layers: `Dense`, `Dropout`, `Conv1D`
67
+ - Activations: `ReLU`, `LeakyReLU`, `Sigmoid`, `Softmax`, `Tanh`
68
+ - Losses: `MSE`, `BinaryCrossEntropy`, `CategoricalCrossEntropy`
69
+ - Optimizers: `GradientDescent`, `Momentum`, `Adam`
70
+ - `Model` — layer-by-layer construction
71
+ - `Sequential` — Keras-style wrapper with string activations
72
+
73
+ ### Traditional ML Models
74
+ - `LinearRegression`, `Ridge`, `Lasso`, `LogisticRegression`
75
+ - `KNeighborsClassifier`, `KNeighborsRegressor`
76
+ - `GaussianNB`
77
+ - `DecisionTreeClassifier`, `DecisionTreeRegressor`
78
+ - `RandomForestClassifier`, `RandomForestRegressor`
79
+
80
+ ### Utilities
81
+ - Preprocessing: `StandardScaler`, `MinMaxScaler`, `LabelEncoder`, `OneHotEncoder`, `train_test_split`
82
+ - Datasets: `load_iris`, `load_mnist`, `make_classification`, `make_regression`
83
+ - Metrics: `accuracy_score`, `precision_score`, `recall_score`, `f1_score`, `confusion_matrix`, `mean_squared_error`, `mean_absolute_error`, `r2_score`
84
+
85
+ ## Installation
86
+
87
+ ```bash
88
+ # Minimal install (NumPy required)
89
+ pip install numpy
90
+
91
+ # Install Epicon from source
92
+ pip install -e .
93
+
94
+ # With Numba (optional, for faster tree/KNN)
95
+ pip install numba
96
+ ```
97
+
98
+ ## Design
99
+
100
+ - **Consistent API**: all models follow `fit(X, y)` / `predict(X)`.
101
+ - **Minimal dependencies**: only NumPy is required.
102
+ - **Optional acceleration**: Numba JIT for tree split search and KNN.
103
+ - **Educational**: readable, fully documented source code.
104
+ - **Tested**: 169+ unit tests with pytest.
105
+
106
+ ## Examples
107
+
108
+ See the [examples/](examples/) directory:
109
+
110
+ - `example_ml_iris.py` — Decision tree on Iris dataset
111
+ - `example_ml_binary.py` — LogisticRegression with L2 penalty
112
+ - `example_ml_forest.py` — RandomForestRegressor on synthetic data
113
+ - `example_nn_sequential.py` — Sequential neural net with Adam on MNIST
epicon-0.3.0/README.md ADDED
@@ -0,0 +1,80 @@
1
+ # Epicon
2
+
3
+ A **lightweight, from-scratch** machine learning library built on NumPy with
4
+ optional Numba acceleration. Provides a unified API for neural networks
5
+ **and** traditional ML models.
6
+
7
+ Designed to be minimal yet capable — like **Flask for ML**.
8
+
9
+ ## Quick Start
10
+
11
+ ```python
12
+ import epicon
13
+ from epicon.datasets import load_iris
14
+ from epicon.preprocessing import train_test_split
15
+ from epicon.metrics import accuracy_score
16
+
17
+ # Load data
18
+ X, y = load_iris(return_X_y=True)
19
+ X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)
20
+
21
+ # Train a model
22
+ model = epicon.DecisionTreeClassifier(max_depth=5)
23
+ model.fit(X_train, y_train)
24
+
25
+ # Evaluate
26
+ y_pred = model.predict(X_test)
27
+ print(f"Accuracy: {accuracy_score(y_test, y_pred):.4f}")
28
+ ```
29
+
30
+ ## What's Included
31
+
32
+ ### Neural Networks
33
+ - Layers: `Dense`, `Dropout`, `Conv1D`
34
+ - Activations: `ReLU`, `LeakyReLU`, `Sigmoid`, `Softmax`, `Tanh`
35
+ - Losses: `MSE`, `BinaryCrossEntropy`, `CategoricalCrossEntropy`
36
+ - Optimizers: `GradientDescent`, `Momentum`, `Adam`
37
+ - `Model` — layer-by-layer construction
38
+ - `Sequential` — Keras-style wrapper with string activations
39
+
40
+ ### Traditional ML Models
41
+ - `LinearRegression`, `Ridge`, `Lasso`, `LogisticRegression`
42
+ - `KNeighborsClassifier`, `KNeighborsRegressor`
43
+ - `GaussianNB`
44
+ - `DecisionTreeClassifier`, `DecisionTreeRegressor`
45
+ - `RandomForestClassifier`, `RandomForestRegressor`
46
+
47
+ ### Utilities
48
+ - Preprocessing: `StandardScaler`, `MinMaxScaler`, `LabelEncoder`, `OneHotEncoder`, `train_test_split`
49
+ - Datasets: `load_iris`, `load_mnist`, `make_classification`, `make_regression`
50
+ - Metrics: `accuracy_score`, `precision_score`, `recall_score`, `f1_score`, `confusion_matrix`, `mean_squared_error`, `mean_absolute_error`, `r2_score`
51
+
52
+ ## Installation
53
+
54
+ ```bash
55
+ # Minimal install (NumPy required)
56
+ pip install numpy
57
+
58
+ # Install Epicon from source
59
+ pip install -e .
60
+
61
+ # With Numba (optional, for faster tree/KNN)
62
+ pip install numba
63
+ ```
64
+
65
+ ## Design
66
+
67
+ - **Consistent API**: all models follow `fit(X, y)` / `predict(X)`.
68
+ - **Minimal dependencies**: only NumPy is required.
69
+ - **Optional acceleration**: Numba JIT for tree split search and KNN.
70
+ - **Educational**: readable, fully documented source code.
71
+ - **Tested**: 169+ unit tests with pytest.
72
+
73
+ ## Examples
74
+
75
+ See the [examples/](examples/) directory:
76
+
77
+ - `example_ml_iris.py` — Decision tree on Iris dataset
78
+ - `example_ml_binary.py` — LogisticRegression with L2 penalty
79
+ - `example_ml_forest.py` — RandomForestRegressor on synthetic data
80
+ - `example_nn_sequential.py` — Sequential neural net with Adam on MNIST
@@ -0,0 +1,78 @@
1
+ """
2
+ ============================================================
3
+ Epicon
4
+ ============================================================
5
+
6
+ A lightweight, from-scratch machine learning library built on
7
+ NumPy with optional Numba acceleration. Provides a unified API
8
+ for neural networks and traditional ML models, designed to be
9
+ a minimal yet capable alternative to PyTorch/TensorFlow for
10
+ everyday ML tasks.
11
+
12
+ Key Design Principles:
13
+ - Simple, consistent API (fit/predict) across all models
14
+ - Minimal dependencies (NumPy required, Numba optional)
15
+ - Educational transparency — readable, documented source
16
+ - Fast execution via vectorized NumPy and optional Numba JIT
17
+
18
+ Quick Start:
19
+ >>> import epicon
20
+ >>> from epicon.datasets import load_iris
21
+ >>> X, y = load_iris(return_X_y=True)
22
+ >>> model = epicon.LogisticRegression()
23
+ >>> model.fit(X, y)
24
+ >>> model.predict(X[:5])
25
+ """
26
+
27
+ # ---------------------------------------------------------------------------
28
+ # Neural Network Components
29
+ # ---------------------------------------------------------------------------
30
+ from epicon.activations import Activation, LeakyReLU, ReLU, Sigmoid, Softmax, Tanh
31
+
32
+ # ---------------------------------------------------------------------------
33
+ # Datasets
34
+ # ---------------------------------------------------------------------------
35
+ from epicon.datasets import load_iris, load_mnist, make_classification, make_regression
36
+ from epicon.ensemble import RandomForestClassifier, RandomForestRegressor
37
+ from epicon.layers import Conv1D, Dense, Dropout, Layer
38
+
39
+ # ---------------------------------------------------------------------------
40
+ # Traditional ML Models
41
+ # ---------------------------------------------------------------------------
42
+ from epicon.linear_model import Lasso, LinearRegression, LogisticRegression, Ridge
43
+ from epicon.losses import MSE, BinaryCrossEntropy, CategoricalCrossEntropy, Loss
44
+
45
+ # ---------------------------------------------------------------------------
46
+ # Metrics
47
+ # ---------------------------------------------------------------------------
48
+ from epicon.metrics import (
49
+ accuracy_score,
50
+ confusion_matrix,
51
+ f1_score,
52
+ mean_absolute_error,
53
+ mean_squared_error,
54
+ precision_score,
55
+ r2_score,
56
+ recall_score,
57
+ )
58
+ from epicon.models import Model, Sequential
59
+ from epicon.naive_bayes import GaussianNB
60
+ from epicon.neighbors import KNeighborsClassifier, KNeighborsRegressor
61
+ from epicon.optimizers import Adam, GradientDescent, Momentum, Optimizer
62
+
63
+ # ---------------------------------------------------------------------------
64
+ # Preprocessing & Utilities
65
+ # ---------------------------------------------------------------------------
66
+ from epicon.preprocessing import (
67
+ LabelEncoder,
68
+ MinMaxScaler,
69
+ OneHotEncoder,
70
+ StandardScaler,
71
+ train_test_split,
72
+ )
73
+ from epicon.tree import DecisionTreeClassifier, DecisionTreeRegressor
74
+
75
+ # ---------------------------------------------------------------------------
76
+ # Version
77
+ # ---------------------------------------------------------------------------
78
+ __version__ = "0.3.0"
@@ -0,0 +1,250 @@
1
+ """
2
+ ---------------------------------------------------------
3
+ NUMBA JIT COMPILED KERNELS FOR PERFORMANCE-CRITICAL LOOPS
4
+ ---------------------------------------------------------
5
+
6
+ Optional Numba acceleration for tree-based models, KNN,
7
+ and other algorithms with non-vectorizable loops.
8
+ If Numba is not installed, pure Python fallbacks are used.
9
+ """
10
+
11
+ import numpy as np
12
+
13
+ try:
14
+ import numba as nb
15
+
16
+ HAS_NUMBA = True
17
+ except ImportError:
18
+ HAS_NUMBA = False
19
+ nb = None
20
+
21
+
22
+ def _jit_if_available(func=None, *, nopython=True, parallel=False, **kwargs):
23
+ """
24
+ Decorator that applies Numba JIT only if Numba is installed.
25
+ Can be used with or without arguments.
26
+
27
+ Usage:
28
+ @_jit_if_available
29
+ def my_func(x): ...
30
+
31
+ @_jit_if_available(nopython=True, parallel=True)
32
+ def my_func(x): ...
33
+ """
34
+
35
+ def decorator(f):
36
+ if HAS_NUMBA:
37
+ return nb.jit(f, nopython=nopython, parallel=parallel, **kwargs)
38
+ return f
39
+
40
+ if func is not None:
41
+ return decorator(func)
42
+ return decorator
43
+
44
+
45
+ # ---------------------------------------------------------------------------
46
+ # Tree split helpers
47
+ # ---------------------------------------------------------------------------
48
+
49
+
50
+ @_jit_if_available
51
+ def _gini_impurity(left_counts, right_counts, total_left, total_right, total_samples):
52
+ """
53
+ Compute weighted Gini impurity for a binary split.
54
+
55
+ Args:
56
+ left_counts (np.ndarray): Class counts in left child.
57
+ right_counts (np.ndarray): Class counts in right child.
58
+ total_left (int): Total samples in left child.
59
+ total_right (int): Total samples in right child.
60
+ total_samples (int): Total samples in parent.
61
+
62
+ Returns:
63
+ float: Weighted Gini impurity of the split.
64
+ """
65
+ if total_left == 0 or total_right == 0:
66
+ return 0.0
67
+
68
+ left_gini = 1.0 - np.sum((left_counts / total_left) ** 2)
69
+ right_gini = 1.0 - np.sum((right_counts / total_right) ** 2)
70
+
71
+ return (total_left / total_samples) * left_gini + (total_right / total_samples) * right_gini
72
+
73
+
74
+ @_jit_if_available
75
+ def _entropy_impurity(left_counts, right_counts, total_left, total_right, total_samples):
76
+ """
77
+ Compute weighted entropy for a binary split.
78
+
79
+ Args:
80
+ left_counts (np.ndarray): Class counts in left child.
81
+ right_counts (np.ndarray): Class counts in right child.
82
+ total_left (int): Total samples in left child.
83
+ total_right (int): Total samples in right child.
84
+ total_samples (int): Total samples in parent.
85
+
86
+ Returns:
87
+ float: Weighted entropy of the split.
88
+ """
89
+ if total_left == 0 or total_right == 0:
90
+ return 0.0
91
+
92
+ def _entropy(counts, total):
93
+ if total == 0:
94
+ return 0.0
95
+ probs = counts / total
96
+ probs = probs[probs > 0]
97
+ return -np.sum(probs * np.log2(probs))
98
+
99
+ left_ent = _entropy(left_counts, total_left)
100
+ right_ent = _entropy(right_counts, total_right)
101
+
102
+ return (total_left / total_samples) * left_ent + (total_right / total_samples) * right_ent
103
+
104
+
105
+ @_jit_if_available
106
+ def _mse_split(left_y, right_y):
107
+ """
108
+ Compute weighted MSE for a binary split.
109
+
110
+ Args:
111
+ left_y (np.ndarray): Target values in left child.
112
+ right_y (np.ndarray): Target values in right child.
113
+
114
+ Returns:
115
+ float: Weighted MSE of the split.
116
+ """
117
+ total = len(left_y) + len(right_y)
118
+ if total == 0:
119
+ return 0.0
120
+
121
+ left_mse = np.var(left_y) * len(left_y) if len(left_y) > 0 else 0.0
122
+ right_mse = np.var(right_y) * len(right_y) if len(right_y) > 0 else 0.0
123
+
124
+ return (left_mse + right_mse) / total
125
+
126
+
127
+ @_jit_if_available
128
+ def _best_split_numeric(X_column, y, num_classes, criterion, min_samples_split, min_samples_leaf):
129
+ """
130
+ Find the best threshold for a numeric feature.
131
+
132
+ Args:
133
+ X_column (np.ndarray): Feature values (sorted).
134
+ y (np.ndarray): Target values.
135
+ num_classes (int): Number of classes (1 for regression).
136
+ criterion (str): 'gini', 'entropy', or 'mse'.
137
+ min_samples_split (int): Minimum samples to split.
138
+ min_samples_leaf (int): Minimum samples per leaf.
139
+
140
+ Returns:
141
+ tuple: (best_threshold, best_impurity) or (None, inf).
142
+ """
143
+ n = len(X_column)
144
+ best_threshold = None
145
+ best_score = np.inf
146
+
147
+ if n < min_samples_split:
148
+ return None, np.inf
149
+
150
+ # Unique thresholds at midpoints between sorted values
151
+ unique_vals = np.unique(X_column)
152
+ if len(unique_vals) == 1:
153
+ return None, np.inf
154
+
155
+ thresholds = (unique_vals[:-1] + unique_vals[1:]) / 2.0
156
+
157
+ for threshold in thresholds:
158
+ left_mask = X_column <= threshold
159
+ right_mask = ~left_mask
160
+
161
+ left_count = np.sum(left_mask)
162
+ right_count = np.sum(right_mask)
163
+
164
+ if left_count < min_samples_leaf or right_count < min_samples_leaf:
165
+ continue
166
+
167
+ if criterion == "mse":
168
+ score = _mse_split(y[left_mask], y[right_mask])
169
+ elif criterion in ("gini", "entropy"):
170
+ left_y = y[left_mask].astype(np.int64)
171
+ right_y = y[right_mask].astype(np.int64)
172
+
173
+ left_counts = np.zeros(num_classes, dtype=np.float64)
174
+ right_counts = np.zeros(num_classes, dtype=np.float64)
175
+
176
+ for c in range(num_classes):
177
+ left_counts[c] = np.sum(left_y == c)
178
+ right_counts[c] = np.sum(right_y == c)
179
+
180
+ if criterion == "gini":
181
+ score = _gini_impurity(left_counts, right_counts, left_count, right_count, n)
182
+ else:
183
+ score = _entropy_impurity(left_counts, right_counts, left_count, right_count, n)
184
+ else:
185
+ raise ValueError(f"Unknown criterion: {criterion}")
186
+
187
+ if score < best_score:
188
+ best_score = score
189
+ best_threshold = threshold
190
+
191
+ return best_threshold, best_score
192
+
193
+
194
+ # ---------------------------------------------------------------------------
195
+ # KNN distance helpers
196
+ # ---------------------------------------------------------------------------
197
+
198
+
199
+ @_jit_if_available
200
+ def _euclidean_distance(x1, x2):
201
+ """
202
+ Compute Euclidean distance between two vectors.
203
+ """
204
+ diff = x1 - x2
205
+ return np.sqrt(np.dot(diff, diff))
206
+
207
+
208
+ @_jit_if_available
209
+ def _manhattan_distance(x1, x2):
210
+ """
211
+ Compute Manhattan distance between two vectors.
212
+ """
213
+ return np.sum(np.abs(x1 - x2))
214
+
215
+
216
+ @_jit_if_available
217
+ def _knn_predict_single(x_test, X_train, y_train, k, metric="euclidean"):
218
+ """
219
+ Predict label for a single test point using KNN.
220
+
221
+ Args:
222
+ x_test (np.ndarray): Single test sample.
223
+ X_train (np.ndarray): Training data.
224
+ y_train (np.ndarray): Training labels.
225
+ k (int): Number of neighbors.
226
+ metric (str): 'euclidean' or 'manhattan'.
227
+
228
+ Returns:
229
+ float: Predicted label (for classification, returns the majority class).
230
+ """
231
+ n_train = X_train.shape[0]
232
+ distances = np.zeros(n_train)
233
+
234
+ for i in range(n_train):
235
+ if metric == "euclidean":
236
+ distances[i] = _euclidean_distance(x_test, X_train[i])
237
+ else:
238
+ distances[i] = _manhattan_distance(x_test, X_train[i])
239
+
240
+ # Get indices of k smallest distances
241
+ indices = np.argsort(distances)[:k]
242
+ k_nearest = y_train[indices]
243
+
244
+ # For regression, return mean; for classification, return mode
245
+ if np.issubdtype(y_train.dtype, np.floating) or len(np.unique(y_train)) > 20:
246
+ return np.mean(k_nearest)
247
+ else:
248
+ # Return mode (most common)
249
+ counts = np.bincount(k_nearest.astype(np.int64))
250
+ return np.argmax(counts)
@@ -0,0 +1,8 @@
1
+ from epicon.activations.base import Activation
2
+ from epicon.activations.leaky_relu import LeakyReLU
3
+ from epicon.activations.relu import ReLU
4
+ from epicon.activations.sigmoid import Sigmoid
5
+ from epicon.activations.softmax import Softmax
6
+ from epicon.activations.tanh import Tanh
7
+
8
+ __all__ = ["Activation", "ReLU", "Softmax", "Sigmoid", "LeakyReLU", "Tanh"]
@@ -0,0 +1,21 @@
1
+ from epicon.layers.base import Layer
2
+
3
+
4
+ class Activation(Layer):
5
+ """
6
+ -----------------------------------
7
+ BASE CLASS FOR ACTIVATION FUNCTIONS
8
+ -----------------------------------
9
+ """
10
+
11
+ def __init__(self):
12
+ super().__init__()
13
+
14
+ def forward(self, inputs):
15
+ raise NotImplementedError
16
+
17
+ def backward(self, dvalues):
18
+ raise NotImplementedError
19
+
20
+ def get_params(self):
21
+ return super().get_params()
@@ -0,0 +1,65 @@
1
+ from typing import override
2
+
3
+ import numpy as np
4
+
5
+ from epicon.activations.base import Activation
6
+
7
+
8
+ class LeakyReLU(Activation):
9
+ """
10
+ -----------------------------------------------
11
+ LEAKY RELU ACTIVATION FUNCTION
12
+ f(x) = x if x > 0, otherwise f(x) = α * x
13
+ This prevents neurons from "dying" by allowing a small,
14
+ non-zero gradient when the unit is not active.
15
+ -----------------------------------------------
16
+ """
17
+
18
+ def __init__(self, alpha=0.01):
19
+ """
20
+ Initialize the LeakyReLU with a given alpha.
21
+
22
+ Parameters:
23
+ alpha (float): the small constant multiplier for negative inputs. Default is 0.01.
24
+ """
25
+ self.alpha = alpha
26
+
27
+ def forward(self, inputs):
28
+ """
29
+ The forward pass applies the LeakyReLU function.
30
+
31
+ Parameters:
32
+ inputs (ndarray): input data to the layer
33
+
34
+ Returns:
35
+ output (ndarray): transformed output of the layer
36
+ """
37
+ self.inputs = inputs
38
+ # For each element, if positive keep it; if negative, multiply by alpha
39
+ self.output = np.where(inputs > 0, inputs, self.alpha * inputs)
40
+ return self.output
41
+
42
+ def backward(self, dvalues):
43
+ """
44
+ The backward pass computes the gradient of the loss with respect to the inputs.
45
+
46
+ Parameters:
47
+ dvalues (ndarray): the gradient from the next layer
48
+
49
+ Returns:
50
+ dinputs (ndarray): the gradient with respect to the inputs of this layer
51
+ """
52
+ # Initialize gradient as a copy of dvalues
53
+ self.dinputs = np.copy(dvalues)
54
+ # For inputs <= 0, multiply the gradient by alpha
55
+ self.dinputs[self.inputs <= 0] *= self.alpha
56
+ return self.dinputs
57
+
58
+ @override
59
+ def get_params(self):
60
+ return {"type": "LeakyReLU", "attrs": {"alpha": self.alpha}}
61
+
62
+ @override
63
+ def set_params(self, params: dict):
64
+ for key, val in params.items():
65
+ setattr(self, key, val)
@@ -0,0 +1,37 @@
1
+ from typing import override
2
+
3
+ import numpy as np
4
+
5
+ from epicon.activations.base import Activation
6
+
7
+
8
+ class ReLU(Activation):
9
+ """
10
+ -----------------------------------------------
11
+ RECTIFIED LINEAR UNIT (ReLU) ACTIVATION FUNCTION
12
+ f(x) = max(0, x)
13
+ -----------------------------------------------
14
+ """
15
+
16
+ def forward(self, inputs):
17
+ """
18
+ The `forward` function takes an input array, applies a ReLU activation function element-wise,
19
+ and returns the resulting output array.
20
+ """
21
+ self.inputs = inputs
22
+ self.output = np.maximum(0, inputs)
23
+ return self.output
24
+
25
+ def backward(self, dvalues):
26
+ """
27
+ The `backward` function calculates the derivative of the inputs with respect to the given values
28
+ and sets the derivative to zero for inputs less than or equal to zero.
29
+ """
30
+
31
+ self.dinputs = dvalues.copy()
32
+ self.dinputs[self.inputs <= 0] = 0
33
+ return self.dinputs
34
+
35
+ @override
36
+ def get_params(self):
37
+ return {"type": "ReLU", "attrs": {}}