aisp 0.3.2__py3-none-any.whl → 0.4.0__py3-none-any.whl
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.
- aisp/__init__.py +17 -4
- aisp/base/__init__.py +21 -2
- aisp/base/_classifier.py +4 -2
- aisp/base/_optimizer.py +188 -0
- aisp/base/mutation.py +86 -18
- aisp/base/populations.py +49 -0
- aisp/csa/__init__.py +12 -1
- aisp/csa/_ai_recognition_sys.py +10 -8
- aisp/csa/_cell.py +2 -2
- aisp/csa/_clonalg.py +369 -0
- aisp/ina/__init__.py +2 -2
- aisp/ina/_ai_network.py +18 -9
- aisp/ina/_base.py +0 -40
- aisp/nsa/__init__.py +7 -0
- aisp/nsa/_binary_negative_selection.py +1 -1
- aisp/nsa/_negative_selection.py +4 -4
- aisp/utils/display.py +185 -0
- aisp/utils/distance.py +4 -4
- aisp/utils/sanitizers.py +53 -1
- aisp/utils/types.py +13 -5
- {aisp-0.3.2.dist-info → aisp-0.4.0.dist-info}/METADATA +2 -1
- aisp-0.4.0.dist-info/RECORD +35 -0
- aisp-0.3.2.dist-info/RECORD +0 -31
- {aisp-0.3.2.dist-info → aisp-0.4.0.dist-info}/WHEEL +0 -0
- {aisp-0.3.2.dist-info → aisp-0.4.0.dist-info}/licenses/LICENSE +0 -0
- {aisp-0.3.2.dist-info → aisp-0.4.0.dist-info}/top_level.txt +0 -0
aisp/csa/_clonalg.py
ADDED
@@ -0,0 +1,369 @@
|
|
1
|
+
"""Clonal Selection Algorithm (CLONALG)."""
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
|
5
|
+
import heapq
|
6
|
+
from typing import Optional, Callable, Dict, Literal
|
7
|
+
|
8
|
+
import numpy as np
|
9
|
+
import numpy.typing as npt
|
10
|
+
|
11
|
+
from ..utils.display import ProgressTable
|
12
|
+
from ..base import BaseOptimizer, set_seed_numba
|
13
|
+
from ..base.mutation import clone_and_mutate_binary, clone_and_mutate_ranged, \
|
14
|
+
clone_and_mutate_continuous, clone_and_mutate_permutation
|
15
|
+
from ..base.populations import generate_random_antibodies
|
16
|
+
from ..utils.sanitizers import sanitize_seed, sanitize_param, sanitize_bounds
|
17
|
+
from ..utils.types import FeatureTypeAll
|
18
|
+
|
19
|
+
|
20
|
+
class Clonalg(BaseOptimizer):
|
21
|
+
"""Clonal Selection Algorithm (CLONALG).
|
22
|
+
|
23
|
+
The Clonal Selection Algorithm (CSA) is an optimization algorithm inspired by the biological
|
24
|
+
process of clonal selection and expansion of antibodies in the immune system [1]_. This
|
25
|
+
implementation of CLONALG has been adapted for the minimization or maximization of cost
|
26
|
+
functions in binary, continuous, ranged-value, and permutation problems.
|
27
|
+
|
28
|
+
|
29
|
+
Parameters
|
30
|
+
----------
|
31
|
+
problem_size : int
|
32
|
+
Dimension of the problem to be minimized.
|
33
|
+
N : int, default=50
|
34
|
+
Number of memory cells (antibodies) in the population.
|
35
|
+
rate_clonal : float, default=10
|
36
|
+
Maximum number of possible clones of a cell. This value is multiplied by
|
37
|
+
cell_affinity to determine the number of clones.
|
38
|
+
rate_hypermutation : float, default=0.75
|
39
|
+
Rate of mutated clones, used as a scalar factor.
|
40
|
+
n_diversity_injection : int, default=5
|
41
|
+
Number of new random memory cells injected to maintain diversity.
|
42
|
+
selection_size : int, default=5
|
43
|
+
Number of the best antibodies selected for cloning.
|
44
|
+
affinity_function : Optional[Callable[..., npt.NDArray]], default=None
|
45
|
+
Objective function to evaluate candidate solutions in minimizing the problem.
|
46
|
+
feature_type : FeatureTypeAll, default='ranged-features'
|
47
|
+
Type of problem samples: binary, continuous, or based on value ranges.
|
48
|
+
Specifies the type of features: "continuous-features", "binary-features",
|
49
|
+
"ranged-features", or "permutation-features".
|
50
|
+
bounds : Optional[Dict], default=None
|
51
|
+
Definition of search limits when ``feature_type='ranged-features'``.
|
52
|
+
Can be provided in two ways:
|
53
|
+
|
54
|
+
* Fixed values: ``{'low': float, 'high': float}``
|
55
|
+
Values are replicated across all dimensions, generating equal limits for each
|
56
|
+
dimension.
|
57
|
+
* Arrays: ``{'low': list, 'high': list}``
|
58
|
+
Each dimension has specific limits. Both arrays must be
|
59
|
+
``problem_size``.
|
60
|
+
|
61
|
+
mode : Literal["min", "max"], default="min"
|
62
|
+
Defines whether the algorithm minimizes or maximizes the cost function.
|
63
|
+
seed : Optional[int], default=None
|
64
|
+
Seed for random generation of detector values. If None, the value is random.
|
65
|
+
|
66
|
+
Notes
|
67
|
+
-----
|
68
|
+
This CLONALG implementation contains some changes based on the AISP context, for general
|
69
|
+
application to various problems, which may produce results different from the standard or
|
70
|
+
specific implementation. This adaptation aims to generalize CLONALG to minimization and
|
71
|
+
maximization tasks, in addition to supporting continuous, discrete, and permutation problems.
|
72
|
+
|
73
|
+
References
|
74
|
+
----------
|
75
|
+
.. [1] BROWNLEE, Jason. Clonal Selection Algorithm. Clever Algorithms: Nature-inspired
|
76
|
+
Programming Recipes., 2011. Available at:
|
77
|
+
https://cleveralgorithms.com/nature-inspired/immune/clonal_selection_algorithm.html
|
78
|
+
"""
|
79
|
+
|
80
|
+
def __init__(
|
81
|
+
self,
|
82
|
+
problem_size: int,
|
83
|
+
N: int = 50,
|
84
|
+
rate_clonal: int = 10,
|
85
|
+
rate_hypermutation: float = 0.75,
|
86
|
+
n_diversity_injection: int = 5,
|
87
|
+
selection_size: int = 5,
|
88
|
+
affinity_function: Optional[Callable[..., npt.NDArray]] = None,
|
89
|
+
feature_type: FeatureTypeAll = 'ranged-features',
|
90
|
+
bounds: Optional[Dict] = None,
|
91
|
+
mode: Literal["min", "max"] = "min",
|
92
|
+
seed: Optional[int] = None
|
93
|
+
):
|
94
|
+
super().__init__()
|
95
|
+
self.problem_size = sanitize_param(problem_size, 1, lambda x: x > 0)
|
96
|
+
self.N: int = sanitize_param(N, 50, lambda x: x > 0)
|
97
|
+
self.rate_clonal: int = sanitize_param(rate_clonal, 10, lambda x: x > 0)
|
98
|
+
self.rate_hypermutation: np.float64 = np.float64(
|
99
|
+
sanitize_param(
|
100
|
+
rate_hypermutation, 0.75, lambda x: x > 0
|
101
|
+
)
|
102
|
+
)
|
103
|
+
self.n_diversity_injection: int = sanitize_param(
|
104
|
+
n_diversity_injection, 5, lambda x: x > 0
|
105
|
+
)
|
106
|
+
self.selection_size: int = sanitize_param(
|
107
|
+
selection_size, 5, lambda x: x > 0
|
108
|
+
)
|
109
|
+
self._affinity_function = affinity_function
|
110
|
+
self.feature_type: FeatureTypeAll = feature_type
|
111
|
+
|
112
|
+
self._bounds = None
|
113
|
+
self._bounds_extend_cache = None
|
114
|
+
self.bounds = bounds
|
115
|
+
|
116
|
+
self.mode: Literal["min", "max"] = sanitize_param(
|
117
|
+
mode,
|
118
|
+
"min",
|
119
|
+
lambda x: x == "max"
|
120
|
+
)
|
121
|
+
|
122
|
+
self.seed: Optional[int] = sanitize_seed(seed)
|
123
|
+
if self.seed is not None:
|
124
|
+
np.random.seed(self.seed)
|
125
|
+
set_seed_numba(self.seed)
|
126
|
+
|
127
|
+
self.population = None
|
128
|
+
|
129
|
+
@property
|
130
|
+
def bounds(self) -> Optional[Dict]:
|
131
|
+
"""Getter for the bounds attribute."""
|
132
|
+
return self._bounds
|
133
|
+
|
134
|
+
@bounds.setter
|
135
|
+
def bounds(self, value: Optional[Dict]):
|
136
|
+
"""Setter for the bounds attribute."""
|
137
|
+
if self.feature_type == 'ranged-features':
|
138
|
+
self._bounds = sanitize_bounds(value, self.problem_size)
|
139
|
+
low_bounds = np.array(self._bounds['low'])
|
140
|
+
high_bounds = np.array(self._bounds['high'])
|
141
|
+
self._bounds_extend_cache = np.array([low_bounds, high_bounds])
|
142
|
+
else:
|
143
|
+
self._bounds = None
|
144
|
+
self._bounds_extend_cache = None
|
145
|
+
|
146
|
+
def optimize(
|
147
|
+
self,
|
148
|
+
max_iters: int = 50,
|
149
|
+
n_iter_no_change=10,
|
150
|
+
verbose: bool = True
|
151
|
+
) -> npt.NDArray:
|
152
|
+
"""Execute the optimization process and return the population.
|
153
|
+
|
154
|
+
Parameters
|
155
|
+
----------
|
156
|
+
max_iters : int, default=50
|
157
|
+
Maximum number of interactions when searching for the best solution using clonalg.
|
158
|
+
n_iter_no_change: int, default=10
|
159
|
+
the maximum number of iterations without updating the best cell
|
160
|
+
verbose : bool, default=True
|
161
|
+
Feedback on interactions, indicating the best antibody.
|
162
|
+
|
163
|
+
Returns
|
164
|
+
-------
|
165
|
+
population : npt.NDArray
|
166
|
+
Antibody population after clonal expansion.
|
167
|
+
"""
|
168
|
+
self.reset()
|
169
|
+
self.population = self._init_population_antibodies()
|
170
|
+
|
171
|
+
t = 1
|
172
|
+
antibodies = [(antibody, self.affinity_function(antibody)) for antibody in self.population]
|
173
|
+
best_cost = None
|
174
|
+
stop = 0
|
175
|
+
progress = ProgressTable(
|
176
|
+
{
|
177
|
+
"Iteration": 11,
|
178
|
+
f"Best Affinity ({self.mode})": 25,
|
179
|
+
"Worse Affinity": 20,
|
180
|
+
"Stagnation": 17},
|
181
|
+
verbose
|
182
|
+
)
|
183
|
+
|
184
|
+
while t <= max_iters:
|
185
|
+
p_select = self._select_top_antibodies(self.selection_size, antibodies)
|
186
|
+
self._record_best(p_select[0][1], p_select[0][0])
|
187
|
+
|
188
|
+
clones = self._clone_and_hypermutation(p_select)
|
189
|
+
|
190
|
+
p_rand = [
|
191
|
+
(antibody, self.affinity_function(antibody))
|
192
|
+
for antibody in self._diversity_introduction()
|
193
|
+
]
|
194
|
+
antibodies = p_select
|
195
|
+
antibodies.extend(clones)
|
196
|
+
antibodies = self._select_top_antibodies(
|
197
|
+
self.N - self.n_diversity_injection, antibodies
|
198
|
+
)
|
199
|
+
antibodies.extend(p_rand)
|
200
|
+
if len(antibodies) > self.N:
|
201
|
+
antibodies = self._select_top_antibodies(self.N, antibodies)
|
202
|
+
if best_cost == self.best_cost:
|
203
|
+
stop += 1
|
204
|
+
else:
|
205
|
+
stop = 0
|
206
|
+
best_cost = self.best_cost
|
207
|
+
progress.update(
|
208
|
+
{
|
209
|
+
"Iteration": t,
|
210
|
+
f"Best Affinity ({self.mode})": f"{self.best_cost:>25.6f}",
|
211
|
+
"Worse Affinity": f"{antibodies[-1][1]:>20.6f}",
|
212
|
+
"Stagnation": stop
|
213
|
+
}
|
214
|
+
)
|
215
|
+
if stop == n_iter_no_change:
|
216
|
+
break
|
217
|
+
|
218
|
+
t += 1
|
219
|
+
progress.finish()
|
220
|
+
self.population = np.array([antibody for antibody, _ in antibodies]).astype(dtype=float)
|
221
|
+
return self.population
|
222
|
+
|
223
|
+
def _select_top_antibodies(self, n: int, antibodies: list[tuple]) -> list[tuple]:
|
224
|
+
"""Select the antibodies with the highest or lowest values, depending on the mode.
|
225
|
+
|
226
|
+
Parameters
|
227
|
+
----------
|
228
|
+
n : int
|
229
|
+
Number of antibodies to select.
|
230
|
+
antibodies : list[tuple]
|
231
|
+
Representing the antibodies and their associated score.
|
232
|
+
|
233
|
+
Returns
|
234
|
+
-------
|
235
|
+
List containing the `n` antibodies selected according to the defined min or max
|
236
|
+
criterion.
|
237
|
+
"""
|
238
|
+
if self.mode == "max":
|
239
|
+
return heapq.nlargest(n, antibodies, key=lambda x: x[1])
|
240
|
+
|
241
|
+
return heapq.nsmallest(n, antibodies, key=lambda x: x[1])
|
242
|
+
|
243
|
+
def affinity_function(self, solution: npt.NDArray) -> np.float64:
|
244
|
+
"""
|
245
|
+
Evaluate the affinity of a candidate cell.
|
246
|
+
|
247
|
+
Parameters
|
248
|
+
----------
|
249
|
+
solution : npt.NDArray
|
250
|
+
Candidate solution to evaluate.
|
251
|
+
|
252
|
+
Returns
|
253
|
+
-------
|
254
|
+
affinity : float
|
255
|
+
Affinity value associated with the given cell.
|
256
|
+
|
257
|
+
Raises
|
258
|
+
------
|
259
|
+
NotImplementedError
|
260
|
+
If no affinity function has been provided.
|
261
|
+
"""
|
262
|
+
if not callable(self._affinity_function):
|
263
|
+
raise NotImplementedError(
|
264
|
+
"No affinity function to evaluate the candidate cell was provided."
|
265
|
+
)
|
266
|
+
return np.float64(self._affinity_function(solution))
|
267
|
+
|
268
|
+
def _init_population_antibodies(self) -> npt.NDArray:
|
269
|
+
"""Initialize the antibody set of the population randomly.
|
270
|
+
|
271
|
+
Returns
|
272
|
+
-------
|
273
|
+
npt.NDArray
|
274
|
+
List of initialized antibodies.
|
275
|
+
"""
|
276
|
+
return generate_random_antibodies(
|
277
|
+
self.N,
|
278
|
+
self.problem_size,
|
279
|
+
self.feature_type,
|
280
|
+
self._bounds_extend_cache
|
281
|
+
)
|
282
|
+
|
283
|
+
def _diversity_introduction(self):
|
284
|
+
"""Introduce diversity into the antibody population.
|
285
|
+
|
286
|
+
Returns
|
287
|
+
-------
|
288
|
+
npt.NDArray
|
289
|
+
Array of new random antibodies for diversity introduction.
|
290
|
+
"""
|
291
|
+
return generate_random_antibodies(
|
292
|
+
self.n_diversity_injection,
|
293
|
+
self.problem_size,
|
294
|
+
self.feature_type,
|
295
|
+
self._bounds_extend_cache
|
296
|
+
)
|
297
|
+
|
298
|
+
def _clone_and_mutate(
|
299
|
+
self,
|
300
|
+
antibody: npt.NDArray,
|
301
|
+
n_clone: int,
|
302
|
+
rate_hypermutation: float
|
303
|
+
) -> npt.NDArray:
|
304
|
+
"""
|
305
|
+
Generate mutated clones from an antibody, based on the feature type.
|
306
|
+
|
307
|
+
Parameters
|
308
|
+
----------
|
309
|
+
antibody : npt.NDArray
|
310
|
+
Original antibody vector to be cloned and mutated.
|
311
|
+
n_clone : int
|
312
|
+
Number of clones to generate.
|
313
|
+
|
314
|
+
Returns
|
315
|
+
-------
|
316
|
+
npt.NDArray
|
317
|
+
Array of shape (n_clone, len(antibody)) containing mutated clones
|
318
|
+
"""
|
319
|
+
if self.feature_type == "binary-features":
|
320
|
+
return clone_and_mutate_binary(antibody, n_clone)
|
321
|
+
if self.feature_type == "ranged-features" and self._bounds_extend_cache is not None:
|
322
|
+
return clone_and_mutate_ranged(
|
323
|
+
antibody, n_clone, self._bounds_extend_cache, rate_hypermutation
|
324
|
+
)
|
325
|
+
if self.feature_type == "permutation-features":
|
326
|
+
return clone_and_mutate_permutation(antibody, n_clone, rate_hypermutation)
|
327
|
+
return clone_and_mutate_continuous(antibody, n_clone, rate_hypermutation)
|
328
|
+
|
329
|
+
def _clone_and_hypermutation(
|
330
|
+
self,
|
331
|
+
population: list[tuple]
|
332
|
+
) -> list:
|
333
|
+
"""Clone and hypermutate the population's antibodies.
|
334
|
+
|
335
|
+
The clone list is returned with the clones and their affinities with respect to the cost
|
336
|
+
function.
|
337
|
+
|
338
|
+
Parameters
|
339
|
+
----------
|
340
|
+
population: list
|
341
|
+
The list of antibodies (solutions) to be evaluated and cloned.
|
342
|
+
|
343
|
+
Returns
|
344
|
+
-------
|
345
|
+
list[npt.NDArray]
|
346
|
+
List of mutated clones.
|
347
|
+
"""
|
348
|
+
clonal_m = []
|
349
|
+
min_affinity = min(item[1] for item in population)
|
350
|
+
max_affinity = max(item[1] for item in population)
|
351
|
+
affinity_range = max_affinity - min_affinity
|
352
|
+
|
353
|
+
for antibody, affinity in population:
|
354
|
+
if affinity_range == 0:
|
355
|
+
normalized_affinity = 1
|
356
|
+
else:
|
357
|
+
normalized_affinity = (affinity - min_affinity) / affinity_range
|
358
|
+
if self.mode == "min":
|
359
|
+
normalized_affinity = max(0.0, 1.0 - normalized_affinity)
|
360
|
+
|
361
|
+
num_clones = max(0, int(self.rate_clonal * normalized_affinity))
|
362
|
+
clones = self._clone_and_mutate(
|
363
|
+
antibody,
|
364
|
+
num_clones,
|
365
|
+
1 - np.exp(-self.rate_hypermutation * normalized_affinity)
|
366
|
+
)
|
367
|
+
clonal_m.extend(clones)
|
368
|
+
|
369
|
+
return [(clone, self.affinity_function(clone)) for clone in clonal_m]
|
aisp/ina/__init__.py
CHANGED
@@ -4,8 +4,8 @@ This module implements algorithms based on Network Theory Algorithms proposed by
|
|
4
4
|
|
5
5
|
Classes
|
6
6
|
-------
|
7
|
-
AiNet
|
8
|
-
|
7
|
+
AiNet : Artificial Immune Network.
|
8
|
+
An unsupervised learning algorithm for clustering, based on the theory of immune networks.
|
9
9
|
"""
|
10
10
|
|
11
11
|
from ._ai_network import AiNet
|
aisp/ina/_ai_network.py
CHANGED
@@ -16,6 +16,7 @@ from ._base import BaseAiNet
|
|
16
16
|
from ..base import set_seed_numba
|
17
17
|
from ..base.mutation import clone_and_mutate_binary, clone_and_mutate_continuous, \
|
18
18
|
clone_and_mutate_ranged
|
19
|
+
from ..base.populations import generate_random_antibodies
|
19
20
|
from ..utils.distance import hamming, compute_metric_distance, get_metric_code
|
20
21
|
from ..utils.sanitizers import sanitize_choice, sanitize_param, sanitize_seed
|
21
22
|
from ..utils.types import FeatureType, MetricType
|
@@ -29,7 +30,7 @@ class AiNet(BaseAiNet):
|
|
29
30
|
clustering and data compression tasks. The aiNet algorithm uses principles from immune
|
30
31
|
network theory, clonal selection, and affinity maturation to compress high-dimensional
|
31
32
|
datasets. [1]_
|
32
|
-
For clustering, the class uses SciPy
|
33
|
+
For clustering, the class uses SciPy's implementation of the **Minimum Spanning Tree**
|
33
34
|
(MST) to remove the most distant nodes and separate the groups. [2]_
|
34
35
|
|
35
36
|
Parameters
|
@@ -58,13 +59,13 @@ class AiNet(BaseAiNet):
|
|
58
59
|
Way to calculate the distance between the detector and the sample:
|
59
60
|
|
60
61
|
* ``'Euclidean'`` ➜ The calculation of the distance is given by the expression:
|
61
|
-
√( (x₁
|
62
|
+
√( (x₁ - x₂)² + (y₁ - y₂)² + ... + (yn - yn)²).
|
62
63
|
|
63
64
|
* ``'minkowski'`` ➜ The calculation of the distance is given by the expression:
|
64
|
-
( |X₁
|
65
|
+
( |X₁ - Y₁|p + |X₂ - Y₂|p + ... + |Xn - Yn|p) ¹/ₚ.
|
65
66
|
|
66
67
|
* ``'manhattan'`` ➜ The calculation of the distance is given by the expression:
|
67
|
-
( |x₁
|
68
|
+
( |x₁ - x₂| + |y₁ - y₂| + ... + |yn - yn|).
|
68
69
|
|
69
70
|
seed : Optional[int]
|
70
71
|
Seed for the random generation of detector values. Defaults to None.
|
@@ -293,7 +294,7 @@ class AiNet(BaseAiNet):
|
|
293
294
|
npt.NDArray
|
294
295
|
List of initialized memories.
|
295
296
|
"""
|
296
|
-
return
|
297
|
+
return generate_random_antibodies(
|
297
298
|
self.N,
|
298
299
|
self._n_features,
|
299
300
|
self._feature_type,
|
@@ -402,7 +403,7 @@ class AiNet(BaseAiNet):
|
|
402
403
|
npt.NDArray
|
403
404
|
Array of new random antibodies for diversity introduction.
|
404
405
|
"""
|
405
|
-
return
|
406
|
+
return generate_random_antibodies(
|
406
407
|
self.n_diversity_injection,
|
407
408
|
self._n_features,
|
408
409
|
self._feature_type,
|
@@ -455,7 +456,7 @@ class AiNet(BaseAiNet):
|
|
455
456
|
"""
|
456
457
|
u = np.reshape(u, (1, -1))
|
457
458
|
v = np.atleast_2d(v)
|
458
|
-
distances = cdist(u, v, metric=self.metric, **self._metric_params)[0]
|
459
|
+
distances = cdist(u, v, metric=self.metric, **self._metric_params)[0] # type: ignore
|
459
460
|
|
460
461
|
return 1 - (distances / (1 + distances))
|
461
462
|
|
@@ -478,8 +479,8 @@ class AiNet(BaseAiNet):
|
|
478
479
|
if self._feature_type == "binary-features":
|
479
480
|
return clone_and_mutate_binary(antibody, n_clone)
|
480
481
|
if self._feature_type == "ranged-features" and self._bounds is not None:
|
481
|
-
return clone_and_mutate_ranged(antibody, n_clone, self._bounds)
|
482
|
-
return clone_and_mutate_continuous(antibody, n_clone)
|
482
|
+
return clone_and_mutate_ranged(antibody, n_clone, self._bounds, np.float64(1.0))
|
483
|
+
return clone_and_mutate_continuous(antibody, n_clone, np.float64(1.0))
|
483
484
|
|
484
485
|
def _build_mst(self):
|
485
486
|
"""Construct the Minimum Spanning Tree (MST) for the antibody population.
|
@@ -523,6 +524,8 @@ class AiNet(BaseAiNet):
|
|
523
524
|
------
|
524
525
|
ValueError
|
525
526
|
If the Minimum Spanning Tree (MST) has not yet been created
|
527
|
+
If Population of antibodies is empty
|
528
|
+
If MST statistics (mean or std) are not available.
|
526
529
|
|
527
530
|
Updates
|
528
531
|
-------
|
@@ -534,6 +537,12 @@ class AiNet(BaseAiNet):
|
|
534
537
|
if self._mst_structure is None:
|
535
538
|
raise ValueError("The Minimum Spanning Tree (MST) has not yet been created.")
|
536
539
|
|
540
|
+
if self._population_antibodies is None or len(self._population_antibodies) == 0:
|
541
|
+
raise ValueError("Population of antibodies is empty")
|
542
|
+
|
543
|
+
if self._mst_mean_distance is None or self._mst_std_distance is None:
|
544
|
+
raise ValueError("MST statistics (mean or std) are not available.")
|
545
|
+
|
537
546
|
if mst_inconsistency_factor is not None:
|
538
547
|
self.mst_inconsistency_factor = mst_inconsistency_factor
|
539
548
|
|
aisp/ina/_base.py
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
"""Base Class for Network Theory Algorithms."""
|
2
2
|
|
3
3
|
from abc import ABC
|
4
|
-
from typing import Optional
|
5
4
|
|
6
5
|
import numpy as np
|
7
6
|
from numpy import typing as npt
|
@@ -82,42 +81,3 @@ class BaseAiNet(BaseClusterer, ABC):
|
|
82
81
|
raise ValueError(
|
83
82
|
"The array X contains values that are not composed only of 0 and 1."
|
84
83
|
)
|
85
|
-
|
86
|
-
@staticmethod
|
87
|
-
def _generate_random_antibodies(
|
88
|
-
n_samples: int,
|
89
|
-
n_features: int,
|
90
|
-
feature_type: FeatureType = "continuous-features",
|
91
|
-
bounds: Optional[npt.NDArray[np.float64]] = None
|
92
|
-
) -> npt.NDArray:
|
93
|
-
"""
|
94
|
-
Generate a random antibody population.
|
95
|
-
|
96
|
-
Parameters
|
97
|
-
----------
|
98
|
-
n_samples : int
|
99
|
-
Number of antibodies (samples) to generate.
|
100
|
-
n_features : int
|
101
|
-
Number of features (dimensions) for each antibody.
|
102
|
-
feature_type : FeatureType, default="continuous-features"
|
103
|
-
Specifies the type of features: "continuous-features", "binary-features",
|
104
|
-
or "ranged-features".
|
105
|
-
bounds : np.ndarray
|
106
|
-
Array (n_features, 2) with min and max per dimension.
|
107
|
-
|
108
|
-
Returns
|
109
|
-
-------
|
110
|
-
npt.NDArray
|
111
|
-
Array of shape (n_samples, n_features) containing the generated antibodies.
|
112
|
-
Data type depends on the feature_type type (float for continuous/ranged, bool for
|
113
|
-
binary).
|
114
|
-
"""
|
115
|
-
if n_features <= 0:
|
116
|
-
raise ValueError("Number of features must be greater than zero.")
|
117
|
-
|
118
|
-
if feature_type == "binary-features":
|
119
|
-
return np.random.randint(0, 2, size=(n_samples, n_features)).astype(np.bool_)
|
120
|
-
if feature_type == "ranged-features" and bounds is not None:
|
121
|
-
return np.random.uniform(low=bounds[0], high=bounds[1], size=(n_samples, n_features))
|
122
|
-
|
123
|
-
return np.random.random_sample(size=(n_samples, n_features))
|
aisp/nsa/__init__.py
CHANGED
@@ -3,6 +3,13 @@
|
|
3
3
|
NSAs simulate the maturation process of T-cells in the immune system, where these cells learn to
|
4
4
|
distinguish between self and non-self. Only T-cells capable of recognizing non-self elements are
|
5
5
|
preserved.
|
6
|
+
|
7
|
+
Classes
|
8
|
+
-------
|
9
|
+
RNSA : Real-valued Negative Selection Algorithm.
|
10
|
+
A supervised learning algorithm for classification that uses real-valued detectors.
|
11
|
+
BNSA : Binary Negative Selection Algorithm.
|
12
|
+
A supervised learning algorithm for classification that uses binary detectors.
|
6
13
|
"""
|
7
14
|
|
8
15
|
from ._binary_negative_selection import BNSA
|
aisp/nsa/_negative_selection.py
CHANGED
@@ -40,11 +40,11 @@ class RNSA(BaseNSA):
|
|
40
40
|
Way to calculate the distance between the detector and the sample:
|
41
41
|
|
42
42
|
+ ``'Euclidean'`` ➜ The calculation of the distance is given by the expression:
|
43
|
-
√( (x₁
|
43
|
+
√( (x₁ - x₂)² + (y₁ - y₂)² + ... + (yn - yn)²).
|
44
44
|
+ ``'minkowski'`` ➜ The calculation of the distance is given by the expression:
|
45
|
-
( |X₁
|
45
|
+
( |X₁ - Y₁|p + |X₂ - Y₂|p + ... + |Xn - Yn|p) ¹/ₚ.
|
46
46
|
+ ``'manhattan'`` ➜ The calculation of the distance is given by the expression:
|
47
|
-
( |x₁
|
47
|
+
( |x₁ - x₂| + |y₁ - y₂| + ... + |yn - yn|) .
|
48
48
|
max_discards : int, default=1000
|
49
49
|
This parameter indicates the maximum number of consecutive detector discards, aimed at
|
50
50
|
preventing a possible infinite loop in case a radius is defined that cannot generate
|
@@ -260,7 +260,7 @@ class RNSA(BaseNSA):
|
|
260
260
|
average_distance[_class_] = np.average(
|
261
261
|
[self.__distance(detector, line) for detector in detectores]
|
262
262
|
)
|
263
|
-
c.append(max(average_distance, key=average_distance.get))
|
263
|
+
c.append(max(average_distance, key=average_distance.get)) # type: ignore
|
264
264
|
return np.array(c)
|
265
265
|
|
266
266
|
def __checks_valid_detector(
|