neonet 0.1.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.
neonet-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ocedev112
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.
neonet-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,32 @@
1
+ Metadata-Version: 2.4
2
+ Name: neonet
3
+ Version: 0.1.0
4
+ Summary: Neonet is a deep learning tool for building simple to medium sized neural networks, it supports multiple configurations and arguments for training
5
+ Author: Olewuenyi Emmanuel
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/ocedev112
8
+ Project-URL: Repository, https://github.com/ocedev112/Neural_Network_from_scratch_numpy
9
+ Keywords: neural network,deep learning,numpy,machine learning
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
17
+ Requires-Python: >=3.11
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ Requires-Dist: numpy>=2.4.6
21
+ Requires-Dist: pydantic>=2.13.4
22
+ Requires-Dist: joblib>=1.5.3
23
+ Requires-Dist: typing_extensions>=4.15.0
24
+ Provides-Extra: dev
25
+ Requires-Dist: pytest>=7.4; extra == "dev"
26
+ Requires-Dist: pytest-cov>=4.1; extra == "dev"
27
+ Requires-Dist: ruff>=0.4; extra == "dev"
28
+ Requires-Dist: mypy>=1.8; extra == "dev"
29
+ Requires-Dist: scikit-learn>=1.9.0; extra == "dev"
30
+ Requires-Dist: scipy>=1.17.1; extra == "dev"
31
+ Requires-Dist: narwhals>=2.22.1; extra == "dev"
32
+ Dynamic: license-file
neonet-0.1.0/README.md ADDED
File without changes
@@ -0,0 +1,66 @@
1
+ [build-system]
2
+ requires = ["setuptools>=82.0", "wheel>=0.47.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "neonet"
7
+ description = "Neonet is a deep learning tool for building simple to medium sized neural networks, it supports multiple configurations and arguments for training"
8
+ version = "0.1.0"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ license-files = ["LICENSE"]
12
+ requires-python = ">=3.11"
13
+ authors = [
14
+ { name = "Olewuenyi Emmanuel" }
15
+ ]
16
+ keywords = ["neural network", "deep learning", "numpy", "machine learning"]
17
+ classifiers = [
18
+ "Development Status :: 3 - Alpha",
19
+ "Intended Audience :: Developers",
20
+ "Intended Audience :: Science/Research",
21
+ "Programming Language :: Python :: 3",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
25
+ ]
26
+ dependencies = [
27
+ "numpy>=2.4.6",
28
+ "pydantic>=2.13.4",
29
+ "joblib>=1.5.3",
30
+ "typing_extensions>=4.15.0",
31
+ ]
32
+
33
+ [project.optional-dependencies]
34
+ dev = [
35
+ "pytest>=7.4",
36
+ "pytest-cov>=4.1",
37
+ "ruff>=0.4",
38
+ "mypy>=1.8",
39
+ "scikit-learn>=1.9.0",
40
+ "scipy>=1.17.1",
41
+ "narwhals>=2.22.1",
42
+ ]
43
+
44
+ [project.urls]
45
+ Homepage = "https://github.com/ocedev112"
46
+ Repository = "https://github.com/ocedev112/Neural_Network_from_scratch_numpy"
47
+
48
+ [tool.setuptools.packages.find]
49
+ where = ["src"]
50
+
51
+ [tool.pytest.ini_options]
52
+ testpaths = ["tests"]
53
+ addopts = "--cov=src --cov-report=term-missing"
54
+
55
+ [tool.ruff]
56
+ line-length = 88
57
+ target-version = "py311"
58
+
59
+ [tool.ruff.lint]
60
+ select = ["E", "F", "I", "UP"]
61
+ ignore = ["E501"]
62
+
63
+ [tool.mypy]
64
+ python_version = "3.11"
65
+ strict = true
66
+ ignore_missing_imports = true
neonet-0.1.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
File without changes
@@ -0,0 +1,356 @@
1
+ import joblib
2
+ import numpy as np
3
+ from typing import Optional
4
+ from pydantic import BaseModel, model_validator
5
+ from enum import Enum
6
+
7
+
8
+ class Optimizer(str, Enum):
9
+ SGD = "SGD"
10
+ GRADIENT_DESCENT = "GD"
11
+ ADAMS_LOSS = "adams_loss"
12
+
13
+
14
+ class Regularization(str, Enum):
15
+ L1 = "L1"
16
+ L2 = "L2"
17
+ class Loss(str, Enum):
18
+ MSE = "MSE"
19
+ MAE = "MAE"
20
+ BCE = "BCE"
21
+ HUBER = "Huber"
22
+
23
+ class TrainArg(BaseModel):
24
+ batch_size: Optional[int] = None
25
+ learning_rate: float
26
+ optimizer: Optional[Optimizer] = None
27
+ regularization: Optional[Regularization] = None
28
+ alpha: Optional[float] = None
29
+ lasso: Optional[float] = None
30
+ b1_coefficient: float = 0.99
31
+ b2_coefficient: float = 0.999
32
+ epsilon: float = 1e-8
33
+ loss: Optional[Loss] = None
34
+ epochs: Optional[int] = None
35
+ use_decay: Optional[bool] = False
36
+ logging_steps: Optional[int] = 1
37
+
38
+ @model_validator(mode="after")
39
+ def validate_dependency(self):
40
+ if self.batch_size is not None and self.batch_size < 1:
41
+ raise ValueError("Batch size must be equal to ot greater than 1 when set")
42
+ if self.regularization is not None and (self.alpha is None and self.lasso is None):
43
+ raise ValueError("You need to set alpha or lasso coefficient when using regularization")
44
+ if self.regularization == "L1" and self.lasso == None:
45
+ raise ValueError("You need to use lasso coefficient with L1, alpha is for L2")
46
+ if self.regularization == "L2" and self.alpha == None:
47
+ raise ValueError("You need to use alpha coefficient with L2, lasso is for L1")
48
+ if self.optimizer == "adams_loss" and (self.epsilon <= 0):
49
+ raise ValueError("You need to use epsilon value for adams loss above 0")
50
+ if self.epochs is not None and (self.epochs < 1 or self.logging_steps < 1):
51
+ raise ValueError("Epochs and logging steps must be greater than 1")
52
+ if self.epochs is not None and self.logging_steps > self.epochs:
53
+ raise ValueError("Logging steps must be smaller than or equal to the epochs value")
54
+ return self
55
+
56
+
57
+
58
+ class NeuralNetwork():
59
+
60
+ def optimize_weights_activation(self, activation_sign, input_size, output_size):
61
+ He = ["ReLU", "LeakyReLU", "ELU", "Swish"]
62
+ Xavier = ["Tanh", "Sigmoid", "Softmax"]
63
+ if activation_sign in He:
64
+ return np.sqrt(2/(input_size))
65
+ if activation_sign in Xavier:
66
+ return np.sqrt(2/(input_size + output_size))
67
+ if activation_sign == "None":
68
+ return 1
69
+
70
+ def __init__(self, input_size,layers, optimize_weights_activation=True):
71
+ self.layers = layers
72
+ self.weight_matrix = []
73
+ self.bias_matrix = []
74
+ for idx,layer in enumerate(layers):
75
+ if idx==0:
76
+ weights_balancer = self.optimize_weights_activation(layer[1], input_size, layer[0]) if optimize_weights_activation else 1
77
+ self.weight_matrix.append(np.random.randn(input_size, layer[0]) * weights_balancer)
78
+ self.bias_matrix.append(np.zeros(layer[0]))
79
+ else:
80
+ weights_balancer = self.optimize_weights_activation(layer[1], layers[idx-1][0], layer[0]) if optimize_weights_activation else 1
81
+ self.weight_matrix.append(np.random.randn(layers[idx-1][0] ,layer[0]) * weights_balancer)
82
+ self.bias_matrix.append(np.zeros(layer[0]))
83
+
84
+ def activation(self, sign, z):
85
+ if sign == "ReLU":
86
+ return np.atleast_1d(np.clip(z, a_min=0, a_max=None))
87
+ if sign == "LeakyReLU":
88
+ return np.atleast_1d(np.where(z > 0, z, 0.01 * z))
89
+ if sign == "ELU":
90
+ z = np.clip(z, -500, 500)
91
+ return np.atleast_1d(np.where(z > 0, z, 0.01 * (np.exp(z) - 1)))
92
+ if sign == "Sigmoid":
93
+ z = np.clip(z, -500, 500)
94
+ return np.atleast_1d(1 / (1 + np.exp(-z)))
95
+ if sign == "Softmax":
96
+ e = np.exp(z - np.max(z))
97
+ return np.atleast_1d(e / e.sum())
98
+ if sign == "Tanh":
99
+ return np.atleast_1d(np.tanh(z))
100
+ if sign == "Swish":
101
+ return np.atleast_1d(z * (1 / (1 + np.exp(-z))))
102
+ if sign == "None":
103
+ return np.atleast_1d(z)
104
+
105
+ def __forward(self,input, weight_matrix, bias_matrix,idx,isTraining, training_layers):
106
+ layer_dict = {}
107
+ weight = weight_matrix[idx-1]
108
+ bias = bias_matrix[idx-1]
109
+ layer = np.matmul(input, weight)
110
+ layer = (layer + bias)
111
+ activation_sign = self.layers[idx-1][1]
112
+ activated_layer = self.activation(activation_sign, layer)
113
+ if isTraining:
114
+ layer_dict["input"] = input
115
+ layer_dict["weight"] = weight
116
+ layer_dict["bias"] = bias
117
+ layer_dict["z_layer"] = layer
118
+ layer_dict["activated_layer"] = activated_layer
119
+ layer_dict["activation_sign"] = activation_sign
120
+ layer_dict["v_layer"] = []
121
+ training_layers.append(layer_dict)
122
+
123
+ if idx == len(weight_matrix):
124
+ if isTraining:
125
+ return activated_layer, training_layers
126
+ return activated_layer
127
+ idx+=1
128
+ return self.__forward(activated_layer, self.weight_matrix, self.bias_matrix, idx, isTraining, training_layers)
129
+
130
+ def __forward_pass(self,inputs, train_mode=False):
131
+ outputs = []
132
+ for input in inputs:
133
+ training_layers = []
134
+ output = self.__forward(input, self.weight_matrix, self.bias_matrix, 1, train_mode, training_layers)
135
+ if output is not None:
136
+ outputs.append(output)
137
+ return outputs
138
+
139
+ def derivate_activation(self,sign, a):
140
+ if sign == "ReLU":
141
+ return np.where(a > 0, 1, 0)
142
+ if sign == "LeakyReLU":
143
+ return np.where(a> 0, 1, 0.01)
144
+ if sign == "ELU":
145
+ return np.where(a > 0, 1.0, a + 0.01)
146
+ if sign == "Sigmoid":
147
+ return a*(1-a)
148
+
149
+ if sign == "Tanh":
150
+ return 1 - a ** 2
151
+ if sign == "Swish":
152
+ s = 1 / (1 + np.exp(-a))
153
+ return s + a * s * (1 - s)
154
+ if sign == "None":
155
+ return 1
156
+ def loss_derivative(self, predicted_output, real_output, loss):
157
+ if loss == "MSE":
158
+ return 2 * (predicted_output - real_output)
159
+ if loss == "MAE":
160
+ return np.sign(predicted_output - real_output)
161
+ if loss == "BCE":
162
+ predicted_output = np.clip(predicted_output, 1e-7, 1 - 1e-7)
163
+ real_output = np.array(real_output)
164
+ diff = -(real_output / predicted_output) + (1 - real_output) / (1 - predicted_output)
165
+ return np.atleast_1d(np.clip(diff, -10, 10)).squeeze()
166
+ if loss == "Huber":
167
+ delta = 1.0
168
+ diff = predicted_output - real_output
169
+ return np.where(np.abs(diff) < delta, diff, delta * np.sign(diff))
170
+
171
+ def __backprop(self, layer_dict, real_output, loss):
172
+ lay_idx = len(layer_dict) - 1
173
+ cost_weight_matrix = []
174
+ cost_bias_matrix = []
175
+ for lay_idx,( matrix, layer) in enumerate(zip(self.weight_matrix[::-1], layer_dict[::-1])):
176
+ real_lay_idx = len(layer_dict) - 1 - lay_idx
177
+ if lay_idx == 0:
178
+ if layer["activation_sign"] == "Softmax":
179
+ v_matrix = layer["activated_layer"] - np.array(real_output)
180
+ else:
181
+ in_v = self.loss_derivative(layer["activated_layer"], real_output, loss)
182
+ dadz = self.derivate_activation(layer["activation_sign"], layer["activated_layer"])
183
+ v_matrix = dadz * in_v
184
+ else:
185
+ prev_weights = layer_dict[real_lay_idx+1]["weight"]
186
+ v_layer = layer_dict[real_lay_idx+1]["v_layer"]
187
+ in_v = np.dot(prev_weights, v_layer)
188
+ dadz = self.derivate_activation(layer["activation_sign"], layer["activated_layer"])
189
+ v_matrix = dadz * in_v
190
+ cost_weight = np.outer(layer["input"], v_matrix)
191
+ cost_bias = v_matrix
192
+
193
+ layer["v_layer"] = v_matrix
194
+ cost_weight_matrix.append(cost_weight)
195
+ cost_bias_matrix.append(cost_bias)
196
+
197
+ return cost_weight_matrix, cost_bias_matrix
198
+
199
+
200
+ def regularization(self,regularizer, matrix, coef):
201
+ if regularizer == None:
202
+ return 0
203
+ if regularizer == "L1":
204
+ matrix = np.sign(matrix)
205
+ return matrix * coef
206
+ elif regularizer == "L2":
207
+ return matrix * coef
208
+
209
+ def compute_loss(self, cost_weight, cost_bias, learning_rate, regularizer, coef):
210
+ for i, (cost_w, cost_b) in enumerate(zip(cost_weight, cost_bias)):
211
+ reg_w = self.regularization(regularizer, self.weight_matrix[-(i+1)], coef)
212
+ reg_b = self.regularization(regularizer, self.bias_matrix[-(i+1)], coef)
213
+ self.weight_matrix[-(i+1)] -= (cost_w + reg_w) * learning_rate
214
+
215
+ self.bias_matrix[-(i+1)] -= (cost_b + reg_b) * learning_rate
216
+
217
+ def compute_loss_adams(self, momentum_w, energy_w, momentum_b, energy_b, learning_rate, regularizer, coef, ep, t, b1,b2):
218
+ for i, (m_w,v_w, m_b, v_b ) in enumerate(zip(momentum_w, energy_w, momentum_b, energy_b)):
219
+ reg_w = self.regularization(regularizer, self.weight_matrix[-(i+1)], coef)
220
+ reg_b = self.regularization(regularizer, self.bias_matrix[-(i+1)], coef)
221
+ m_w_hat = m_w / (1 - b1 ** t)
222
+ v_w_hat = v_w / (1 - b2 ** t)
223
+ m_b_hat = m_b / (1 - b1 ** t)
224
+ v_b_hat = v_b / (1 - b2 ** t)
225
+
226
+
227
+ self.weight_matrix[-(i+1)] -= (m_w_hat / (np.sqrt(v_w_hat) + ep) + reg_w) * learning_rate
228
+ self.bias_matrix[-(i+1)] -= (m_b_hat / (np.sqrt(v_b_hat) + ep) + reg_b) * learning_rate
229
+
230
+ def batch_data(self, outputs, batch_size):
231
+ if batch_size is None:
232
+ batch_size = len(outputs)
233
+ for i in range(0, len(outputs), batch_size):
234
+ yield outputs[i:i+batch_size]
235
+
236
+ def train(self, inputs, real_output, training_args=None, eval_dataset=None, check_loss=False):
237
+ if len(inputs) != len(real_output):
238
+ raise ValueError("input and output dataset must match")
239
+ regularizer = None
240
+ coef = 0.0
241
+ batch_size = None
242
+ loss = "MSE"
243
+ epochs = 1
244
+ if training_args == None:
245
+ learning_rate = 0.04
246
+ else:
247
+ learning_rate = training_args.learning_rate
248
+ regularizer = training_args.regularization
249
+ if regularizer is not None and regularizer == "L1":
250
+ coef = training_args.lasso
251
+ else:
252
+ coef = training_args.alpha
253
+ if training_args.batch_size is not None:
254
+ batch_size = training_args.batch_size
255
+ if training_args.loss is not None:
256
+ loss = training_args.loss
257
+ if training_args.epochs is not None:
258
+ epochs = training_args.epochs
259
+
260
+
261
+
262
+
263
+
264
+
265
+ m_w,v_w = None,None
266
+ m_b, v_b = None, None
267
+ g_m_w, g_v_w = None, None
268
+ g_m_b, g_v_b = None, None
269
+ t = 0
270
+ base_lr = learning_rate
271
+ for epoch in range(epochs):
272
+ outputs = self.__forward_pass(inputs, True)
273
+ cost_weight_list = []
274
+ cost_bias_list = []
275
+ if training_args is not None and training_args.use_decay:
276
+ decay = 1 / (1 + 0.001 * epoch)
277
+ learning_rate = base_lr * decay
278
+ global_idx = 0
279
+ for outputs_batch in self.batch_data(outputs, batch_size):
280
+ for idx, (output, layer_dict) in enumerate(outputs_batch):
281
+ label = real_output[global_idx]
282
+ global_idx += 1
283
+ if len(output) != len(label):
284
+ raise ValueError("Y value must be the same length as predicted value")
285
+ cost_weight_matrix, cost_bias_matrix = self.__backprop(layer_dict, label, loss)
286
+ if training_args == None or training_args.optimizer == "GD":
287
+ cost_weight_list.append(cost_weight_matrix)
288
+ cost_bias_list.append(cost_bias_matrix)
289
+ if training_args is not None and training_args.optimizer == "SGD":
290
+ self.compute_loss(cost_weight_matrix, cost_bias_matrix, learning_rate, regularizer, coef)
291
+
292
+ if training_args is not None and training_args.optimizer == "adams_loss":
293
+ b1 = training_args.b1_coefficient
294
+ b2 = training_args.b2_coefficient
295
+ ep = training_args.epsilon
296
+ g_m_w = cost_weight_matrix
297
+ g_v_w = [cw**2 for cw in cost_weight_matrix]
298
+ g_m_b = cost_bias_matrix
299
+ g_v_b = [cb**2 for cb in cost_bias_matrix]
300
+
301
+ if m_w is None:
302
+ m_w = [np.zeros_like(w) for w in cost_weight_matrix]
303
+ v_w = [np.zeros_like(w) for w in cost_weight_matrix]
304
+ m_b = [np.zeros_like(b) for b in cost_bias_matrix]
305
+ v_b = [np.zeros_like(b) for b in cost_bias_matrix]
306
+
307
+ mv_w = [(b1*mw + (1-b1)*gm, b2*vw + (1-b2)*gv) for mw, gm, vw, gv in zip(m_w, g_m_w, v_w, g_v_w)]
308
+ mv_b = [(b1*mb + (1-b1)*gm, b2*vb + (1-b2)*gv) for mb, gm, vb, gv in zip(m_b, g_m_b, v_b, g_v_b)]
309
+
310
+ m_w, v_w = zip(*mv_w)
311
+ m_b, v_b = zip(*mv_b)
312
+ t+=1
313
+ self.compute_loss_adams(m_w,v_w,m_b,v_b, learning_rate, regularizer, coef,ep,t, b1, b2)
314
+
315
+ if training_args == None or training_args.optimizer == "GD":
316
+ cost_weight_mean = [np.stack(arrays).mean(axis=0) for arrays in zip(*cost_weight_list)]
317
+ cost_bias_mean = [np.stack(arrays).mean(axis=0) for arrays in zip(*cost_bias_list)]
318
+ self.compute_loss(cost_weight_mean, cost_bias_mean, learning_rate, regularizer, coef)
319
+
320
+ if check_loss:
321
+ if training_args is not None and training_args.logging_steps is not None:
322
+ if epoch % training_args.logging_steps == 0:
323
+ train_sample = len(inputs) // 4
324
+ train_ouputs = self.predict(inputs[:train_sample])
325
+ train_loss = sum([np.sum((np.array(o) - np.array(y))**2) for o, y in zip(train_ouputs, real_output[:train_sample])])
326
+ print(f"Epoch {epoch} Loss: {round(train_loss, 4)}")
327
+ if eval_dataset is not None:
328
+ test_sample = len(inputs) // 4
329
+ test_outputs = self.predict(eval_dataset[0][:test_sample])
330
+ test_loss = sum([np.sum((np.array(o) - np.array(y))**2) for o, y in zip(test_outputs, eval_dataset[1][:test_sample])])
331
+ print(f"Epoch {epoch} Val Loss: {round(test_loss, 4)}")
332
+
333
+
334
+ def predict(self, inputs):
335
+ outputs = self.__forward_pass(inputs, False)
336
+ return outputs
337
+
338
+ def save(self, path):
339
+ joblib.dump({
340
+ "weight_matrix" : self.weight_matrix,
341
+ "bias_matrix" : self.bias_matrix,
342
+ "layers" :self.layers
343
+ },path)
344
+
345
+
346
+ @classmethod
347
+ def load(cls, path):
348
+ obj = cls.__new__(cls)
349
+ loaded = joblib.load(path)
350
+ obj.weight_matrix = loaded["weight_matrix"]
351
+ obj.bias_matrix = loaded["bias_matrix"]
352
+ obj.layers = loaded["layers"]
353
+ return obj
354
+
355
+
356
+
@@ -0,0 +1,32 @@
1
+ Metadata-Version: 2.4
2
+ Name: neonet
3
+ Version: 0.1.0
4
+ Summary: Neonet is a deep learning tool for building simple to medium sized neural networks, it supports multiple configurations and arguments for training
5
+ Author: Olewuenyi Emmanuel
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/ocedev112
8
+ Project-URL: Repository, https://github.com/ocedev112/Neural_Network_from_scratch_numpy
9
+ Keywords: neural network,deep learning,numpy,machine learning
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
17
+ Requires-Python: >=3.11
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ Requires-Dist: numpy>=2.4.6
21
+ Requires-Dist: pydantic>=2.13.4
22
+ Requires-Dist: joblib>=1.5.3
23
+ Requires-Dist: typing_extensions>=4.15.0
24
+ Provides-Extra: dev
25
+ Requires-Dist: pytest>=7.4; extra == "dev"
26
+ Requires-Dist: pytest-cov>=4.1; extra == "dev"
27
+ Requires-Dist: ruff>=0.4; extra == "dev"
28
+ Requires-Dist: mypy>=1.8; extra == "dev"
29
+ Requires-Dist: scikit-learn>=1.9.0; extra == "dev"
30
+ Requires-Dist: scipy>=1.17.1; extra == "dev"
31
+ Requires-Dist: narwhals>=2.22.1; extra == "dev"
32
+ Dynamic: license-file
@@ -0,0 +1,10 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ src/neonet/__init__.py
5
+ src/neonet/nn.py
6
+ src/neonet.egg-info/PKG-INFO
7
+ src/neonet.egg-info/SOURCES.txt
8
+ src/neonet.egg-info/dependency_links.txt
9
+ src/neonet.egg-info/requires.txt
10
+ src/neonet.egg-info/top_level.txt
@@ -0,0 +1,13 @@
1
+ numpy>=2.4.6
2
+ pydantic>=2.13.4
3
+ joblib>=1.5.3
4
+ typing_extensions>=4.15.0
5
+
6
+ [dev]
7
+ pytest>=7.4
8
+ pytest-cov>=4.1
9
+ ruff>=0.4
10
+ mypy>=1.8
11
+ scikit-learn>=1.9.0
12
+ scipy>=1.17.1
13
+ narwhals>=2.22.1
@@ -0,0 +1 @@
1
+ neonet