aisp 0.2.0__tar.gz → 0.2.1__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.
- {aisp-0.2.0 → aisp-0.2.1}/PKG-INFO +6 -1
- {aisp-0.2.0 → aisp-0.2.1}/aisp/base/_classifier.py +6 -2
- {aisp-0.2.0 → aisp-0.2.1}/aisp/base/mutation.py +44 -0
- {aisp-0.2.0 → aisp-0.2.1}/aisp/csa/_ai_immune_recognition_sys.py +37 -45
- {aisp-0.2.0 → aisp-0.2.1}/aisp/csa/_base.py +10 -25
- aisp-0.2.1/aisp/csa/_cell.py +61 -0
- {aisp-0.2.0 → aisp-0.2.1}/aisp/exceptions.py +17 -1
- {aisp-0.2.0 → aisp-0.2.1}/aisp/nsa/_base.py +3 -3
- {aisp-0.2.0 → aisp-0.2.1}/aisp/nsa/_negative_selection.py +43 -45
- aisp-0.2.1/aisp/utils/types.py +31 -0
- aisp-0.2.1/aisp/utils/validation.py +47 -0
- {aisp-0.2.0 → aisp-0.2.1}/aisp.egg-info/PKG-INFO +6 -1
- {aisp-0.2.0 → aisp-0.2.1}/aisp.egg-info/SOURCES.txt +3 -1
- aisp-0.2.1/aisp.egg-info/requires.txt +10 -0
- {aisp-0.2.0 → aisp-0.2.1}/aisp.egg-info/top_level.txt +1 -0
- {aisp-0.2.0 → aisp-0.2.1}/pyproject.toml +9 -1
- aisp-0.2.0/aisp/csa/_cell.py +0 -47
- aisp-0.2.0/aisp.egg-info/requires.txt +0 -4
- {aisp-0.2.0 → aisp-0.2.1}/LICENSE +0 -0
- {aisp-0.2.0 → aisp-0.2.1}/README.md +0 -0
- {aisp-0.2.0 → aisp-0.2.1}/aisp/__init__.py +0 -0
- {aisp-0.2.0 → aisp-0.2.1}/aisp/base/__init__.py +0 -0
- {aisp-0.2.0 → aisp-0.2.1}/aisp/csa/__init__.py +0 -0
- {aisp-0.2.0 → aisp-0.2.1}/aisp/nsa/__init__.py +0 -0
- {aisp-0.2.0 → aisp-0.2.1}/aisp/nsa/_ns_core.py +0 -0
- {aisp-0.2.0 → aisp-0.2.1}/aisp/utils/__init__.py +0 -0
- {aisp-0.2.0 → aisp-0.2.1}/aisp/utils/_multiclass.py +0 -0
- {aisp-0.2.0 → aisp-0.2.1}/aisp/utils/distance.py +0 -0
- {aisp-0.2.0 → aisp-0.2.1}/aisp/utils/metrics.py +0 -0
- {aisp-0.2.0 → aisp-0.2.1}/aisp/utils/sanitizers.py +0 -0
- {aisp-0.2.0 → aisp-0.2.1}/aisp.egg-info/dependency_links.txt +0 -0
- {aisp-0.2.0 → aisp-0.2.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: aisp
|
3
|
-
Version: 0.2.
|
3
|
+
Version: 0.2.1
|
4
4
|
Summary: Package with techniques of artificial immune systems.
|
5
5
|
Author-email: João Paulo da Silva Barros <jpsilvabarr@gmail.com>
|
6
6
|
Maintainer-email: Alison Zille Lopes <alisonzille@gmail.com>
|
@@ -26,6 +26,11 @@ Requires-Dist: numpy>=1.22.4
|
|
26
26
|
Requires-Dist: numba>=0.59.0
|
27
27
|
Requires-Dist: scipy>=1.8.1
|
28
28
|
Requires-Dist: tqdm>=4.64.1
|
29
|
+
Provides-Extra: dev
|
30
|
+
Requires-Dist: build>=1.2.2.post1; extra == "dev"
|
31
|
+
Requires-Dist: ipykernel>=6.29.5; extra == "dev"
|
32
|
+
Requires-Dist: twine>=5.1.1; extra == "dev"
|
33
|
+
Requires-Dist: pytest>=8.3.5; extra == "dev"
|
29
34
|
Dynamic: license-file
|
30
35
|
|
31
36
|
<div align = center>
|
@@ -16,10 +16,10 @@ class BaseClassifier(ABC):
|
|
16
16
|
``get_params`` method.
|
17
17
|
"""
|
18
18
|
|
19
|
-
classes:
|
19
|
+
classes: Union[npt.NDArray, list] = []
|
20
20
|
|
21
21
|
@abstractmethod
|
22
|
-
def fit(self, X: npt.NDArray, y: npt.NDArray, verbose: bool = True):
|
22
|
+
def fit(self, X: npt.NDArray, y: npt.NDArray, verbose: bool = True) -> "BaseClassifier":
|
23
23
|
"""
|
24
24
|
Train the model using the input data X and corresponding labels y.
|
25
25
|
|
@@ -83,6 +83,10 @@ class BaseClassifier(ABC):
|
|
83
83
|
if len(y) == 0:
|
84
84
|
return 0
|
85
85
|
y_pred = self.predict(X)
|
86
|
+
|
87
|
+
if y_pred is None:
|
88
|
+
return 0
|
89
|
+
|
86
90
|
return accuracy_score(y, y_pred)
|
87
91
|
|
88
92
|
def _slice_index_list_by_class(self, y: npt.NDArray) -> dict:
|
@@ -84,3 +84,47 @@ def clone_and_mutate_binary(
|
|
84
84
|
clone_set[i] = clone
|
85
85
|
|
86
86
|
return clone_set
|
87
|
+
|
88
|
+
|
89
|
+
@njit([(types.float64[:], types.int64, types.float64[:, :])], cache=True)
|
90
|
+
def clone_and_mutate_ranged(
|
91
|
+
vector: npt.NDArray[np.float64],
|
92
|
+
n: int,
|
93
|
+
bounds: npt.NDArray[np.float64]
|
94
|
+
) -> npt.NDArray[np.float64]:
|
95
|
+
"""
|
96
|
+
Generate a set of mutated clones from a cell represented by custom ranges per dimension.
|
97
|
+
|
98
|
+
This function creates `n` clones of the input vector and applies random mutations to each of
|
99
|
+
them, simulating the process of clonal expansion in artificial immune systems. Each clone
|
100
|
+
will have a random number of mutations applied in distinct positions of the original vector.
|
101
|
+
|
102
|
+
Parameters
|
103
|
+
----------
|
104
|
+
vector : npt.NDArray[np.bool_]
|
105
|
+
The original immune cell with binary values to be cloned and mutated.
|
106
|
+
n : int
|
107
|
+
The number of mutated clones to be generated.
|
108
|
+
bounds : np.ndarray
|
109
|
+
Array (n_features, 2) with min and max per dimension.
|
110
|
+
|
111
|
+
Returns
|
112
|
+
-------
|
113
|
+
clone_set : npt.NDArray
|
114
|
+
An Array(n, len(vector)) containing the `n` mutated clones of the original vector.
|
115
|
+
"""
|
116
|
+
n_features = vector.shape[0]
|
117
|
+
clone_set = np.empty((n, n_features), dtype=np.float64)
|
118
|
+
|
119
|
+
for i in range(n):
|
120
|
+
n_mutations = np.random.randint(1, n_features)
|
121
|
+
clone = vector.copy()
|
122
|
+
position_mutations = np.random.permutation(n_features)[:n_mutations]
|
123
|
+
for j in range(n_mutations):
|
124
|
+
idx = position_mutations[j]
|
125
|
+
min_limit = bounds[idx, 0]
|
126
|
+
max_limit = bounds[idx, 1]
|
127
|
+
clone[idx] = np.random.uniform(min_limit, max_limit)
|
128
|
+
clone_set[i] = clone
|
129
|
+
|
130
|
+
return clone_set
|
@@ -4,16 +4,19 @@ import random
|
|
4
4
|
from collections import Counter
|
5
5
|
from heapq import nlargest
|
6
6
|
from operator import attrgetter
|
7
|
-
from typing import List,
|
7
|
+
from typing import List, Optional, Dict
|
8
8
|
|
9
9
|
import numpy as np
|
10
10
|
import numpy.typing as npt
|
11
11
|
from scipy.spatial.distance import pdist
|
12
12
|
from tqdm import tqdm
|
13
13
|
|
14
|
+
|
14
15
|
from ._cell import Cell
|
15
16
|
from ..utils.sanitizers import sanitize_param, sanitize_seed, sanitize_choice
|
16
17
|
from ..utils.distance import hamming, compute_metric_distance, get_metric_code
|
18
|
+
from ..utils.types import FeatureType, MetricType
|
19
|
+
from ..utils.validation import detect_vector_data_type
|
17
20
|
from ._base import BaseAIRS
|
18
21
|
|
19
22
|
|
@@ -114,20 +117,12 @@ class AIRS(BaseAIRS):
|
|
114
117
|
* ``'manhattan'`` ➜ The calculation of the distance is given by the expression:
|
115
118
|
( |x₁ – x₂| + |y₁ – y₂| + ... + |yn – yn|).
|
116
119
|
|
117
|
-
algorithm : Literal["continuous-features", "binary-features"], default="continuous-features"
|
118
|
-
Specifies the type of algorithm to use based on the nature of the input features:
|
119
|
-
|
120
|
-
* ``continuous-features``: selects an algorithm designed for continuous data, which should
|
121
|
-
be normalized within the range [0, 1].
|
122
|
-
|
123
|
-
* ``binary-features``: selects an algorithm specialized for handling binary variables.
|
124
|
-
|
125
120
|
seed : int
|
126
121
|
Seed for the random generation of detector values. Defaults to None.
|
127
122
|
|
128
123
|
**kwargs
|
129
124
|
p : float
|
130
|
-
This parameter stores the value of ``p`` used in the
|
125
|
+
This parameter stores the value of ``p`` used in the Minkowski distance. The default
|
131
126
|
is ``2``, which represents normalized Euclidean distance.\
|
132
127
|
Different values of p lead to different variants of the Minkowski Distance.
|
133
128
|
|
@@ -160,11 +155,8 @@ class AIRS(BaseAIRS):
|
|
160
155
|
k: int = 3,
|
161
156
|
max_iters: int = 100,
|
162
157
|
resource_amplified: float = 1.0,
|
163
|
-
metric:
|
164
|
-
|
165
|
-
"continuous-features", "binary-features"
|
166
|
-
] = "continuous-features",
|
167
|
-
seed: int = None,
|
158
|
+
metric: MetricType = "euclidean",
|
159
|
+
seed: Optional[int] = None,
|
168
160
|
**kwargs,
|
169
161
|
) -> None:
|
170
162
|
self.n_resources: float = sanitize_param(n_resources, 10, lambda x: x >= 1)
|
@@ -183,35 +175,29 @@ class AIRS(BaseAIRS):
|
|
183
175
|
)
|
184
176
|
self.k: int = sanitize_param(k, 3, lambda x: x > 3)
|
185
177
|
self.max_iters: int = sanitize_param(max_iters, 100, lambda x: x > 0)
|
186
|
-
self.seed: int = sanitize_seed(seed)
|
178
|
+
self.seed: Optional[int] = sanitize_seed(seed)
|
187
179
|
if self.seed is not None:
|
188
180
|
np.random.seed(self.seed)
|
189
181
|
|
190
|
-
self.
|
191
|
-
sanitize_param(
|
192
|
-
algorithm, "continuous-features", lambda x: x == "binary-features"
|
193
|
-
)
|
194
|
-
)
|
182
|
+
self._feature_type: FeatureType = "continuous-features"
|
195
183
|
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
self.metric: str = sanitize_choice(
|
200
|
-
metric, ["manhattan", "minkowski"], "euclidean"
|
201
|
-
)
|
184
|
+
self.metric = sanitize_choice(
|
185
|
+
metric, ["manhattan", "minkowski"], "euclidean"
|
186
|
+
)
|
202
187
|
|
203
188
|
self.p: np.float64 = np.float64(kwargs.get("p", 2.0))
|
204
189
|
|
205
190
|
self._cells_memory = None
|
206
191
|
self.affinity_threshold = 0.0
|
207
|
-
self.classes =
|
192
|
+
self.classes = []
|
193
|
+
self._bounds: Optional[npt.NDArray[np.float64]] = None
|
208
194
|
|
209
195
|
@property
|
210
|
-
def cells_memory(self) -> Dict[str, list[Cell]]:
|
196
|
+
def cells_memory(self) -> Optional[Dict[str, list[Cell]]]:
|
211
197
|
"""Returns the trained cells memory, organized by class."""
|
212
198
|
return self._cells_memory
|
213
199
|
|
214
|
-
def fit(self, X: npt.NDArray, y: npt.NDArray, verbose: bool = True):
|
200
|
+
def fit(self, X: npt.NDArray, y: npt.NDArray, verbose: bool = True) -> "AIRS":
|
215
201
|
"""
|
216
202
|
Fit the model to the training data using the AIRS.
|
217
203
|
|
@@ -235,10 +221,16 @@ class AIRS(BaseAIRS):
|
|
235
221
|
"""
|
236
222
|
progress = None
|
237
223
|
|
238
|
-
|
224
|
+
self._feature_type = detect_vector_data_type(X)
|
239
225
|
|
240
|
-
|
241
|
-
|
226
|
+
super()._check_and_raise_exceptions_fit(X, y)
|
227
|
+
|
228
|
+
match self._feature_type:
|
229
|
+
case "binary-features":
|
230
|
+
X = X.astype(np.bool_)
|
231
|
+
self.metric = "hamming"
|
232
|
+
case "ranged-features":
|
233
|
+
self._bounds = np.vstack([np.min(X, axis=0), np.max(X, axis=0)])
|
242
234
|
|
243
235
|
self.classes = np.unique(y)
|
244
236
|
sample_index = self._slice_index_list_by_class(y)
|
@@ -250,7 +242,7 @@ class AIRS(BaseAIRS):
|
|
250
242
|
)
|
251
243
|
pool_cells_classes = {}
|
252
244
|
for _class_ in self.classes:
|
253
|
-
if verbose:
|
245
|
+
if verbose and progress is not None:
|
254
246
|
progress.set_description_str(
|
255
247
|
f"Generating the memory cells for the {_class_} class:"
|
256
248
|
)
|
@@ -267,7 +259,7 @@ class AIRS(BaseAIRS):
|
|
267
259
|
for ai in x_class:
|
268
260
|
# Calculating the stimulation of memory cells with aᵢ and selecting the largest
|
269
261
|
# stimulation from the memory set.
|
270
|
-
c_match =
|
262
|
+
c_match = pool_c[0]
|
271
263
|
match_stimulation = -1
|
272
264
|
for cell in pool_c:
|
273
265
|
stimulation = self._affinity(cell.vector, ai)
|
@@ -284,7 +276,7 @@ class AIRS(BaseAIRS):
|
|
284
276
|
|
285
277
|
set_clones: npt.NDArray = c_match.hyper_clonal_mutate(
|
286
278
|
int(self.rate_hypermutation * self.rate_clonal * match_stimulation),
|
287
|
-
self.
|
279
|
+
self._feature_type
|
288
280
|
)
|
289
281
|
|
290
282
|
for clone in set_clones:
|
@@ -302,11 +294,11 @@ class AIRS(BaseAIRS):
|
|
302
294
|
if self._affinity(c_candidate.vector, c_match.vector) < sufficiently_similar:
|
303
295
|
pool_c.remove(c_match)
|
304
296
|
|
305
|
-
if verbose:
|
297
|
+
if verbose and progress is not None:
|
306
298
|
progress.update(1)
|
307
299
|
pool_cells_classes[_class_] = pool_c
|
308
300
|
|
309
|
-
if verbose:
|
301
|
+
if verbose and progress is not None:
|
310
302
|
progress.set_description(
|
311
303
|
f"\033[92m✔ Set of memory cells for classes ({', '.join(map(str, self.classes))}) "
|
312
304
|
f"successfully generated\033[0m"
|
@@ -337,7 +329,7 @@ class AIRS(BaseAIRS):
|
|
337
329
|
return None
|
338
330
|
|
339
331
|
super()._check_and_raise_exceptions_predict(
|
340
|
-
X, len(self._cells_memory[self.classes[0]][0].vector), self.
|
332
|
+
X, len(self._cells_memory[self.classes[0]][0].vector), self._feature_type
|
341
333
|
)
|
342
334
|
|
343
335
|
c: list = []
|
@@ -417,7 +409,7 @@ class AIRS(BaseAIRS):
|
|
417
409
|
random_index = random.randint(0, len(arb_list) - 1)
|
418
410
|
clone_arb = arb_list[random_index].hyper_clonal_mutate(
|
419
411
|
int(self.rate_clonal * c_match_stimulation),
|
420
|
-
self.
|
412
|
+
self._feature_type
|
421
413
|
)
|
422
414
|
|
423
415
|
arb_list = [
|
@@ -446,12 +438,12 @@ class AIRS(BaseAIRS):
|
|
446
438
|
antigens_list : npt.NDArray
|
447
439
|
List of training antigens.
|
448
440
|
"""
|
449
|
-
if self.
|
441
|
+
if self._feature_type == "binary-features":
|
450
442
|
distances = pdist(antigens_list, metric="hamming")
|
451
|
-
elif self.metric == "minkowski":
|
452
|
-
distances = pdist(antigens_list, metric="minkowski", p=self.p)
|
453
443
|
else:
|
454
|
-
|
444
|
+
metric_kwargs = {'p': self.p} if self.metric == 'minkowski' else {}
|
445
|
+
distances = pdist(antigens_list, metric=self.metric, **metric_kwargs)
|
446
|
+
|
455
447
|
n = antigens_list.shape[0]
|
456
448
|
sum_affinity = np.sum(1.0 - (distances / (1.0 + distances)))
|
457
449
|
self.affinity_threshold = 1.0 - (sum_affinity / ((n * (n - 1)) / 2))
|
@@ -473,7 +465,7 @@ class AIRS(BaseAIRS):
|
|
473
465
|
The stimulus rate between the vectors.
|
474
466
|
"""
|
475
467
|
distance: float
|
476
|
-
if self.
|
468
|
+
if self._feature_type == "binary-features":
|
477
469
|
distance = hamming(u, v)
|
478
470
|
else:
|
479
471
|
distance = compute_metric_distance(
|
@@ -1,12 +1,12 @@
|
|
1
1
|
"""Base Class for Clonal Selection Algorithm."""
|
2
2
|
|
3
3
|
from abc import ABC
|
4
|
-
from typing import Literal
|
5
4
|
|
6
5
|
import numpy as np
|
7
6
|
import numpy.typing as npt
|
8
7
|
|
9
|
-
from
|
8
|
+
from ..exceptions import FeatureDimensionMismatch
|
9
|
+
from ..utils.types import FeatureType
|
10
10
|
from ..base import BaseClassifier
|
11
11
|
|
12
12
|
|
@@ -20,11 +20,8 @@ class BaseAIRS(BaseClassifier, ABC):
|
|
20
20
|
|
21
21
|
@staticmethod
|
22
22
|
def _check_and_raise_exceptions_fit(
|
23
|
-
X: npt.NDArray
|
24
|
-
y: npt.NDArray
|
25
|
-
algorithm: Literal[
|
26
|
-
"continuous-features", "binary-features"
|
27
|
-
] = "continuous-features"
|
23
|
+
X: npt.NDArray,
|
24
|
+
y: npt.NDArray
|
28
25
|
):
|
29
26
|
"""
|
30
27
|
Verify the fit parameters and throw exceptions if the verification is not successful.
|
@@ -36,17 +33,11 @@ class BaseAIRS(BaseClassifier, ABC):
|
|
36
33
|
[``N samples`` (rows)][``N features`` (columns)].
|
37
34
|
y : npt.NDArray
|
38
35
|
Array of target classes of ``X`` with [``N samples`` (lines)].
|
39
|
-
algorithm : Literal["continuous-features", "binary-features"], default="continuous-features"
|
40
|
-
Specifies the type of algorithm to use, depending on whether the input data has
|
41
|
-
continuous or binary features.
|
42
36
|
|
43
37
|
Raises
|
44
38
|
------
|
45
39
|
TypeError:
|
46
40
|
If X or y are not ndarrays or have incompatible shapes.
|
47
|
-
ValueError
|
48
|
-
If algorithm is binary-features and X contains values that are not composed only
|
49
|
-
of 0 and 1.
|
50
41
|
"""
|
51
42
|
if not isinstance(X, np.ndarray):
|
52
43
|
if isinstance(X, list):
|
@@ -63,18 +54,12 @@ class BaseAIRS(BaseClassifier, ABC):
|
|
63
54
|
"X does not have the same amount of sample for the output classes in y."
|
64
55
|
)
|
65
56
|
|
66
|
-
if algorithm == "binary-features" and not np.isin(X, [0, 1]).all():
|
67
|
-
raise ValueError(
|
68
|
-
"The array X contains values that are not composed only of 0 and 1."
|
69
|
-
)
|
70
57
|
|
71
58
|
@staticmethod
|
72
59
|
def _check_and_raise_exceptions_predict(
|
73
|
-
X: npt.NDArray
|
60
|
+
X: npt.NDArray,
|
74
61
|
expected: int = 0,
|
75
|
-
|
76
|
-
"continuous-features", "binary-features"
|
77
|
-
] = "continuous-features"
|
62
|
+
feature_type: FeatureType = "continuous-features"
|
78
63
|
) -> None:
|
79
64
|
"""
|
80
65
|
Verify the predict parameters and throw exceptions if the verification is not successful.
|
@@ -86,8 +71,8 @@ class BaseAIRS(BaseClassifier, ABC):
|
|
86
71
|
[``N samples`` (rows)][``N features`` (columns)].
|
87
72
|
expected : int, default=0
|
88
73
|
Expected number of features per sample (columns in X).
|
89
|
-
|
90
|
-
Specifies the type of
|
74
|
+
feature_type : FeatureType, default="continuous-features"
|
75
|
+
Specifies the type of feature_type to use, depending on whether the input data has
|
91
76
|
continuous or binary features.
|
92
77
|
|
93
78
|
Raises
|
@@ -97,7 +82,7 @@ class BaseAIRS(BaseClassifier, ABC):
|
|
97
82
|
FeatureDimensionMismatch
|
98
83
|
If the number of features in X does not match the expected number.
|
99
84
|
ValueError
|
100
|
-
If
|
85
|
+
If feature_type is binary-features and X contains values that are not composed only
|
101
86
|
of 0 and 1.
|
102
87
|
"""
|
103
88
|
if not isinstance(X, (np.ndarray, list)):
|
@@ -109,7 +94,7 @@ class BaseAIRS(BaseClassifier, ABC):
|
|
109
94
|
"X"
|
110
95
|
)
|
111
96
|
|
112
|
-
if
|
97
|
+
if feature_type != "binary-features":
|
113
98
|
return
|
114
99
|
|
115
100
|
# Checks if matrix X contains only binary samples. Otherwise, raises an exception.
|
@@ -0,0 +1,61 @@
|
|
1
|
+
"""Represents a memory B-cell."""
|
2
|
+
|
3
|
+
from dataclasses import dataclass
|
4
|
+
from typing import Optional
|
5
|
+
|
6
|
+
import numpy as np
|
7
|
+
import numpy.typing as npt
|
8
|
+
|
9
|
+
from ..base.mutation import (
|
10
|
+
clone_and_mutate_continuous,
|
11
|
+
clone_and_mutate_binary,
|
12
|
+
clone_and_mutate_ranged
|
13
|
+
)
|
14
|
+
from ..utils.types import FeatureType
|
15
|
+
|
16
|
+
|
17
|
+
@dataclass(slots=True)
|
18
|
+
class Cell:
|
19
|
+
"""
|
20
|
+
Represents a memory B-cell.
|
21
|
+
|
22
|
+
Attributes
|
23
|
+
----------
|
24
|
+
vector : npt.NDArray
|
25
|
+
A vector of cell features.
|
26
|
+
"""
|
27
|
+
|
28
|
+
vector: np.ndarray
|
29
|
+
|
30
|
+
def hyper_clonal_mutate(
|
31
|
+
self,
|
32
|
+
n: int,
|
33
|
+
feature_type: FeatureType = "continuous-features",
|
34
|
+
bounds: Optional[npt.NDArray[np.float64]] = None
|
35
|
+
) -> npt.NDArray:
|
36
|
+
"""
|
37
|
+
Clones N features from a cell's features, generating a set of mutated vectors.
|
38
|
+
|
39
|
+
Parameters
|
40
|
+
----------
|
41
|
+
n : int
|
42
|
+
Number of clones to be generated from mutations of the original cell.
|
43
|
+
feature_type : Literal["binary-features", "continuous-features", "ranged-features"]
|
44
|
+
Specifies the type of feature_type to use based on the nature of the input features
|
45
|
+
bounds : np.ndarray
|
46
|
+
Array (n_features, 2) with min and max per dimension.
|
47
|
+
|
48
|
+
Returns
|
49
|
+
-------
|
50
|
+
npt.NDArray
|
51
|
+
An array containing N mutated vectors from the original cell.
|
52
|
+
"""
|
53
|
+
if feature_type == "binary-features":
|
54
|
+
return clone_and_mutate_binary(self.vector, n)
|
55
|
+
if feature_type == "ranged-features" and bounds is not None:
|
56
|
+
clone_and_mutate_ranged(self.vector, n, bounds)
|
57
|
+
return clone_and_mutate_continuous(self.vector, n)
|
58
|
+
|
59
|
+
def __eq__(self, other):
|
60
|
+
"""Check if two cells are equal."""
|
61
|
+
return np.array_equal(self.vector, other.vector)
|
@@ -1,5 +1,7 @@
|
|
1
1
|
"""Custom warnings and errors."""
|
2
2
|
|
3
|
+
from typing import Optional
|
4
|
+
|
3
5
|
|
4
6
|
class MaxDiscardsReachedError(Exception):
|
5
7
|
"""Exception thrown when the maximum number of detector discards is reached."""
|
@@ -27,7 +29,7 @@ class FeatureDimensionMismatch(Exception):
|
|
27
29
|
self,
|
28
30
|
expected: int,
|
29
31
|
received: int,
|
30
|
-
variable_name: str = None
|
32
|
+
variable_name: Optional[str] = None
|
31
33
|
):
|
32
34
|
parts = []
|
33
35
|
if variable_name:
|
@@ -41,3 +43,17 @@ class FeatureDimensionMismatch(Exception):
|
|
41
43
|
"and matches the expected shape for the model."
|
42
44
|
)
|
43
45
|
super().__init__(message)
|
46
|
+
|
47
|
+
|
48
|
+
class UnsupportedTypeError(Exception):
|
49
|
+
"""
|
50
|
+
Exception raised when the input vector type is not supported.
|
51
|
+
|
52
|
+
This exception is thrown when the vector data type does not match any of the supported.
|
53
|
+
"""
|
54
|
+
|
55
|
+
def __init__(self, message=None):
|
56
|
+
if message is None:
|
57
|
+
message = ("Type is not supported. Provide a binary, normalized, or bounded "
|
58
|
+
"continuous vector.")
|
59
|
+
super().__init__(message)
|
@@ -20,8 +20,8 @@ class BaseNSA(BaseClassifier, ABC):
|
|
20
20
|
|
21
21
|
@staticmethod
|
22
22
|
def _check_and_raise_exceptions_fit(
|
23
|
-
X: npt.NDArray
|
24
|
-
y: npt.NDArray
|
23
|
+
X: npt.NDArray,
|
24
|
+
y: npt.NDArray,
|
25
25
|
_class_: Literal["RNSA", "BNSA"] = "RNSA",
|
26
26
|
) -> None:
|
27
27
|
"""Verify fit function parameters.
|
@@ -67,7 +67,7 @@ class BaseNSA(BaseClassifier, ABC):
|
|
67
67
|
|
68
68
|
@staticmethod
|
69
69
|
def _check_and_raise_exceptions_predict(
|
70
|
-
X: npt.NDArray
|
70
|
+
X: npt.NDArray,
|
71
71
|
expected: int = 0,
|
72
72
|
_class_: Literal["RNSA", "BNSA"] = "RNSA",
|
73
73
|
) -> None:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
"""Negative Selection Algorithm."""
|
2
2
|
|
3
|
-
from typing import Dict, Literal, Optional, Union
|
3
|
+
from typing import Any, Dict, Literal, Optional, Union
|
4
4
|
from tqdm import tqdm
|
5
5
|
|
6
6
|
import numpy as np
|
@@ -90,12 +90,12 @@ class RNSA(BaseNSA):
|
|
90
90
|
k: int = 1,
|
91
91
|
metric: Literal["manhattan", "minkowski", "euclidean"] = "euclidean",
|
92
92
|
max_discards: int = 1000,
|
93
|
-
seed: int = None,
|
93
|
+
seed: Optional[int] = None,
|
94
94
|
algorithm: Literal["default-NSA", "V-detector"] = "default-NSA",
|
95
|
-
**kwargs:
|
95
|
+
**kwargs: Any,
|
96
96
|
):
|
97
|
-
self.metric = sanitize_choice(metric, ["manhattan", "minkowski"], "euclidean")
|
98
|
-
self.seed = sanitize_seed(seed)
|
97
|
+
self.metric: str = sanitize_choice(metric, ["manhattan", "minkowski"], "euclidean")
|
98
|
+
self.seed: Optional[int] = sanitize_seed(seed)
|
99
99
|
if self.seed is not None:
|
100
100
|
np.random.seed(seed)
|
101
101
|
self.k: int = sanitize_param(k, 1, lambda x: x > 1)
|
@@ -108,20 +108,20 @@ class RNSA(BaseNSA):
|
|
108
108
|
self.max_discards: int = sanitize_param(max_discards, 1000, lambda x: x > 0)
|
109
109
|
|
110
110
|
# Retrieves the variables from kwargs.
|
111
|
-
self.p:
|
112
|
-
self.cell_bounds: bool = kwargs.get("cell_bounds", False)
|
113
|
-
self.non_self_label: str = kwargs.get("non_self_label", "non-self")
|
111
|
+
self.p: np.float64 = np.float64(kwargs.get("p", 2))
|
112
|
+
self.cell_bounds: bool = bool(kwargs.get("cell_bounds", False))
|
113
|
+
self.non_self_label: str = str(kwargs.get("non_self_label", "non-self"))
|
114
114
|
|
115
115
|
# Initializes the other class variables as None.
|
116
116
|
self._detectors: Union[dict, None] = None
|
117
|
-
self.classes: npt.NDArray =
|
117
|
+
self.classes: Union[npt.NDArray, list] = []
|
118
118
|
|
119
119
|
@property
|
120
|
-
def detectors(self) -> Dict[str, list[Detector]]:
|
120
|
+
def detectors(self) -> Optional[Dict[str, list[Detector]]]:
|
121
121
|
"""Returns the trained detectors, organized by class."""
|
122
122
|
return self._detectors
|
123
123
|
|
124
|
-
def fit(self, X: npt.NDArray, y: npt.NDArray, verbose: bool = True):
|
124
|
+
def fit(self, X: npt.NDArray, y: npt.NDArray, verbose: bool = True) -> "RNSA":
|
125
125
|
"""
|
126
126
|
Perform training according to X and y, using the negative selection method (NegativeSelect).
|
127
127
|
|
@@ -170,7 +170,7 @@ class RNSA(BaseNSA):
|
|
170
170
|
discard_count = 0
|
171
171
|
x_class = X[sample_index[_class_]]
|
172
172
|
# Indicating which class the algorithm is currently processing for the progress bar.
|
173
|
-
if verbose:
|
173
|
+
if verbose and progress is not None:
|
174
174
|
progress.set_description_str(
|
175
175
|
f"Generating the detectors for the {_class_} class:"
|
176
176
|
)
|
@@ -183,11 +183,12 @@ class RNSA(BaseNSA):
|
|
183
183
|
# If the detector is valid, add it to the list of valid detectors.
|
184
184
|
if valid_detector is not False:
|
185
185
|
discard_count = 0
|
186
|
-
|
187
|
-
valid_detector[1]
|
188
|
-
|
186
|
+
if self.algorithm == "V-detector" and isinstance(valid_detector, tuple):
|
187
|
+
radius = valid_detector[1]
|
188
|
+
else:
|
189
|
+
radius = None
|
189
190
|
valid_detectors_set.append(Detector(vector_x, radius))
|
190
|
-
if verbose:
|
191
|
+
if verbose and progress is not None:
|
191
192
|
progress.update(1)
|
192
193
|
else:
|
193
194
|
discard_count += 1
|
@@ -197,7 +198,7 @@ class RNSA(BaseNSA):
|
|
197
198
|
# Add detectors, with classes as keys in the dictionary.
|
198
199
|
list_detectors_by_class[_class_] = valid_detectors_set
|
199
200
|
# Notify completion of detector generation for the classes.
|
200
|
-
if verbose:
|
201
|
+
if verbose and progress is not None:
|
201
202
|
progress.set_description(
|
202
203
|
f"\033[92m✔ Non-self detectors for classes ({', '.join(map(str, self.classes))}) "
|
203
204
|
f"successfully generated\033[0m"
|
@@ -258,9 +259,7 @@ class RNSA(BaseNSA):
|
|
258
259
|
elif not class_found:
|
259
260
|
average_distance: dict = {}
|
260
261
|
for _class_ in self.classes:
|
261
|
-
detectores =
|
262
|
-
map(lambda x: x.position, self._detectors[_class_])
|
263
|
-
)
|
262
|
+
detectores = [x.position for x in self._detectors[_class_]]
|
264
263
|
average_distance[_class_] = np.average(
|
265
264
|
[self.__distance(detector, line) for detector in detectores]
|
266
265
|
)
|
@@ -291,17 +290,17 @@ class RNSA(BaseNSA):
|
|
291
290
|
# If self.k > 1, uses the k nearest neighbors (kNN); otherwise, checks the detector
|
292
291
|
# without considering kNN.
|
293
292
|
if self.k > 1:
|
294
|
-
knn_list = []
|
293
|
+
knn_list: list = []
|
295
294
|
for x in x_class:
|
296
295
|
# Calculates the distance between the two vectors and adds it to the kNN list if
|
297
296
|
# the distance is smaller than the largest distance in the list.
|
298
|
-
|
297
|
+
self.__compare_knearest_neighbors_list(
|
299
298
|
knn_list, self.__distance(x, vector_x)
|
300
299
|
)
|
301
300
|
# If the average of the distances in the kNN list is less than the radius, Returns true.
|
302
301
|
distance_mean = np.mean(knn_list)
|
303
302
|
if self.algorithm == "V-detector":
|
304
|
-
return self.__detector_is_valid_to_vdetector(distance_mean, vector_x)
|
303
|
+
return self.__detector_is_valid_to_vdetector(float(distance_mean), vector_x)
|
305
304
|
if distance_mean > (self.r + self.r_s):
|
306
305
|
return True
|
307
306
|
else:
|
@@ -323,8 +322,8 @@ class RNSA(BaseNSA):
|
|
323
322
|
return False # Detector is not valid!
|
324
323
|
|
325
324
|
def __compare_knearest_neighbors_list(
|
326
|
-
self, knn:
|
327
|
-
) ->
|
325
|
+
self, knn: list, distance: float
|
326
|
+
) -> None:
|
328
327
|
"""
|
329
328
|
Compare the k-nearest neighbor distance at position k=1 in the list knn.
|
330
329
|
|
@@ -336,17 +335,11 @@ class RNSA(BaseNSA):
|
|
336
335
|
List of k-nearest neighbor distances.
|
337
336
|
distance : float
|
338
337
|
Distance to check.
|
339
|
-
|
340
|
-
Returns
|
341
|
-
-------
|
342
|
-
knn : npt.NDArray
|
343
|
-
Updated and sorted nearest neighbor list.
|
344
338
|
"""
|
345
339
|
# If the number of distances in kNN is less than k, adds the distance.
|
346
340
|
if len(knn) < self.k:
|
347
|
-
knn
|
341
|
+
knn.append(distance)
|
348
342
|
knn.sort()
|
349
|
-
return knn
|
350
343
|
|
351
344
|
# Otherwise, add the distance if the new distance is smaller than the largest
|
352
345
|
# distance in the list.
|
@@ -354,7 +347,6 @@ class RNSA(BaseNSA):
|
|
354
347
|
knn[self.k - 1] = distance
|
355
348
|
knn.sort()
|
356
349
|
|
357
|
-
return knn
|
358
350
|
|
359
351
|
def __compare_sample_to_detectors(self, line: npt.NDArray) -> Optional[str]:
|
360
352
|
"""
|
@@ -371,6 +363,9 @@ class RNSA(BaseNSA):
|
|
371
363
|
Returns the predicted class with the detectors or None if the sample does not qualify
|
372
364
|
for any class.
|
373
365
|
"""
|
366
|
+
if self._detectors is None:
|
367
|
+
return None
|
368
|
+
|
374
369
|
# List to store the classes and the average distance between the detectors and the sample.
|
375
370
|
possible_classes = []
|
376
371
|
for _class_ in self.classes:
|
@@ -491,7 +486,7 @@ class BNSA(BaseNSA):
|
|
491
486
|
N: int = 100,
|
492
487
|
aff_thresh: float = 0.1,
|
493
488
|
max_discards: int = 1000,
|
494
|
-
seed: int = None,
|
489
|
+
seed: Optional[int] = None,
|
495
490
|
no_label_sample_selection: Literal[
|
496
491
|
"max_average_difference", "max_nearest_difference"
|
497
492
|
] = "max_average_difference",
|
@@ -500,27 +495,27 @@ class BNSA(BaseNSA):
|
|
500
495
|
self.aff_thresh: float = sanitize_param(aff_thresh, 0.1, lambda x: 0 < x < 1)
|
501
496
|
self.max_discards: float = sanitize_param(max_discards, 1000, lambda x: x > 0)
|
502
497
|
|
503
|
-
self.seed = sanitize_seed(seed)
|
498
|
+
self.seed: Optional[int] = sanitize_seed(seed)
|
504
499
|
|
505
500
|
if self.seed is not None:
|
506
501
|
np.random.seed(seed)
|
507
502
|
|
508
|
-
self.no_label_sample_selection:
|
503
|
+
self.no_label_sample_selection: str = sanitize_param(
|
509
504
|
no_label_sample_selection,
|
510
505
|
"max_average_difference",
|
511
506
|
lambda x: x == "nearest_difference",
|
512
507
|
)
|
513
508
|
|
514
|
-
self.classes: npt.NDArray =
|
509
|
+
self.classes: Union[npt.NDArray, list] = []
|
515
510
|
self._detectors: Optional[dict] = None
|
516
|
-
self._detectors_stack: npt.NDArray = None
|
511
|
+
self._detectors_stack: Optional[npt.NDArray] = None
|
517
512
|
|
518
513
|
@property
|
519
|
-
def detectors(self) -> Dict[str, npt.NDArray[np.bool_]]:
|
514
|
+
def detectors(self) -> Optional[Dict[str, npt.NDArray[np.bool_]]]:
|
520
515
|
"""Returns the trained detectors, organized by class."""
|
521
516
|
return self._detectors
|
522
517
|
|
523
|
-
def fit(self, X: npt.NDArray, y: npt.NDArray, verbose: bool = True):
|
518
|
+
def fit(self, X: npt.NDArray, y: npt.NDArray, verbose: bool = True) -> "BNSA":
|
524
519
|
"""Training according to X and y, using the method negative selection method.
|
525
520
|
|
526
521
|
Parameters
|
@@ -539,7 +534,7 @@ class BNSA(BaseNSA):
|
|
539
534
|
Returns the instance it self.
|
540
535
|
"""
|
541
536
|
super()._check_and_raise_exceptions_fit(X, y, "BNSA")
|
542
|
-
|
537
|
+
progress = None
|
543
538
|
# Converts the entire array X to boolean
|
544
539
|
X = X.astype(np.bool_)
|
545
540
|
|
@@ -562,7 +557,7 @@ class BNSA(BaseNSA):
|
|
562
557
|
valid_detectors_set: list = []
|
563
558
|
discard_count: int = 0
|
564
559
|
# Updating the progress bar with the current class the algorithm is processing.
|
565
|
-
if verbose:
|
560
|
+
if verbose and progress is not None:
|
566
561
|
progress.set_description_str(
|
567
562
|
f"Generating the detectors for the {_class_} class:"
|
568
563
|
)
|
@@ -574,7 +569,7 @@ class BNSA(BaseNSA):
|
|
574
569
|
if check_detector_bnsa_validity(x_class, vector_x, self.aff_thresh):
|
575
570
|
discard_count = 0
|
576
571
|
valid_detectors_set.append(vector_x)
|
577
|
-
if verbose:
|
572
|
+
if verbose and progress is not None:
|
578
573
|
progress.update(1)
|
579
574
|
else:
|
580
575
|
discard_count += 1
|
@@ -585,7 +580,7 @@ class BNSA(BaseNSA):
|
|
585
580
|
list_detectors_by_class[_class_] = np.array(valid_detectors_set)
|
586
581
|
|
587
582
|
# Notify the completion of detector generation for the classes.
|
588
|
-
if verbose:
|
583
|
+
if verbose and progress is not None:
|
589
584
|
progress.set_description(
|
590
585
|
f"\033[92m✔ Non-self detectors for classes ({', '.join(map(str, self.classes))}) "
|
591
586
|
f"successfully generated\033[0m"
|
@@ -613,7 +608,7 @@ class BNSA(BaseNSA):
|
|
613
608
|
``X``. Returns``None``: If there are no detectors for the prediction.
|
614
609
|
"""
|
615
610
|
# If there are no detectors, Returns None.
|
616
|
-
if self._detectors is None:
|
611
|
+
if self._detectors is None or self._detectors_stack is None:
|
617
612
|
return None
|
618
613
|
|
619
614
|
super()._check_and_raise_exceptions_predict(
|
@@ -664,6 +659,9 @@ class BNSA(BaseNSA):
|
|
664
659
|
c : list
|
665
660
|
List of predictions to be updated with the new classification.
|
666
661
|
"""
|
662
|
+
if self._detectors is None:
|
663
|
+
raise ValueError("Detectors is not initialized.")
|
664
|
+
|
667
665
|
class_differences: dict = {}
|
668
666
|
for _class_ in self.classes:
|
669
667
|
distances = np.sum(line != self._detectors[_class_]) / self.N
|
@@ -0,0 +1,31 @@
|
|
1
|
+
"""
|
2
|
+
Defines type aliases used throughout the project to improve readability.
|
3
|
+
|
4
|
+
Type Aliases
|
5
|
+
------------
|
6
|
+
FeatureType : Literal["binary-features", "continuous-features", "ranged-features"]
|
7
|
+
Specifies the type of features in the input data. Can be one of:
|
8
|
+
- "binary-features": Features with binary values (e.g., 0 or 1).
|
9
|
+
- "continuous-features": Features with continuous numeric values.
|
10
|
+
- "ranged-features": Features represented by ranges or intervals.
|
11
|
+
|
12
|
+
MetricType : Literal["manhattan", "minkowski", "euclidean"]
|
13
|
+
Specifies the distance metric to use for calculations. Possible values:
|
14
|
+
- "manhattan": The calculation of the distance is given by the expression:
|
15
|
+
√( (x₁ – x₂)² + (y₁ – y₂)² + ... + (yn – yn)²).
|
16
|
+
- "minkowski": The calculation of the distance is given by the expression:
|
17
|
+
( |X₁ – Y₁|p + |X₂ – Y₂|p + ... + |Xn – Yn|p) ¹/ₚ.
|
18
|
+
- "euclidean": The calculation of the distance is given by the expression:
|
19
|
+
( |x₁ – x₂| + |y₁ – y₂| + ... + |yn – yn|).
|
20
|
+
"""
|
21
|
+
|
22
|
+
|
23
|
+
from typing import Literal, TypeAlias
|
24
|
+
|
25
|
+
|
26
|
+
FeatureType: TypeAlias = Literal[
|
27
|
+
"binary-features",
|
28
|
+
"continuous-features",
|
29
|
+
"ranged-features"
|
30
|
+
]
|
31
|
+
MetricType: TypeAlias = Literal["manhattan", "minkowski", "euclidean"]
|
@@ -0,0 +1,47 @@
|
|
1
|
+
"""Contains functions responsible for validating data types."""
|
2
|
+
|
3
|
+
import numpy as np
|
4
|
+
import numpy.typing as npt
|
5
|
+
|
6
|
+
from .types import FeatureType
|
7
|
+
from ..exceptions import UnsupportedTypeError
|
8
|
+
|
9
|
+
|
10
|
+
def detect_vector_data_type(
|
11
|
+
vector: npt.NDArray
|
12
|
+
) -> FeatureType:
|
13
|
+
"""
|
14
|
+
Detect the type of data in a vector.
|
15
|
+
|
16
|
+
The function detects if the vector contains data of type:
|
17
|
+
- "binary": binary data (boolean True/False or integer 0/1)
|
18
|
+
- "continuous": continuous data between 0.0 and 1.0 (float)
|
19
|
+
- "ranged": numerical data with values outside the normalized range (float)
|
20
|
+
|
21
|
+
Parameters
|
22
|
+
----------
|
23
|
+
vector: npt.NDArray
|
24
|
+
An array containing the data to be classified.
|
25
|
+
|
26
|
+
Returns
|
27
|
+
-------
|
28
|
+
Literal["binary-features", "continuous-features", "ranged-features"]
|
29
|
+
The classified data type of the vector.
|
30
|
+
|
31
|
+
Raises
|
32
|
+
------
|
33
|
+
UnsupportedDataTypeError
|
34
|
+
If the data type of the vector is not supported by the function.
|
35
|
+
"""
|
36
|
+
if vector.dtype == np.bool_:
|
37
|
+
return "binary-features"
|
38
|
+
|
39
|
+
if np.issubdtype(vector.dtype, np.integer) and np.isin(vector, [0, 1]).all():
|
40
|
+
return "binary-features"
|
41
|
+
|
42
|
+
if np.issubdtype(vector.dtype, np.floating):
|
43
|
+
if np.all(vector >= 0.0) and np.all(vector <= 1.0):
|
44
|
+
return "continuous-features"
|
45
|
+
return "ranged-features"
|
46
|
+
|
47
|
+
raise UnsupportedTypeError()
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: aisp
|
3
|
-
Version: 0.2.
|
3
|
+
Version: 0.2.1
|
4
4
|
Summary: Package with techniques of artificial immune systems.
|
5
5
|
Author-email: João Paulo da Silva Barros <jpsilvabarr@gmail.com>
|
6
6
|
Maintainer-email: Alison Zille Lopes <alisonzille@gmail.com>
|
@@ -26,6 +26,11 @@ Requires-Dist: numpy>=1.22.4
|
|
26
26
|
Requires-Dist: numba>=0.59.0
|
27
27
|
Requires-Dist: scipy>=1.8.1
|
28
28
|
Requires-Dist: tqdm>=4.64.1
|
29
|
+
Provides-Extra: dev
|
30
|
+
Requires-Dist: build>=1.2.2.post1; extra == "dev"
|
31
|
+
Requires-Dist: ipykernel>=6.29.5; extra == "dev"
|
32
|
+
Requires-Dist: twine>=5.1.1; extra == "dev"
|
33
|
+
Requires-Dist: pytest>=8.3.5; extra == "dev"
|
29
34
|
Dynamic: license-file
|
30
35
|
|
31
36
|
<div align = center>
|
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
|
|
5
5
|
|
6
6
|
[project]
|
7
7
|
name = "aisp"
|
8
|
-
version = "0.2.
|
8
|
+
version = "0.2.1"
|
9
9
|
authors = [
|
10
10
|
{ name="João Paulo da Silva Barros", email="jpsilvabarr@gmail.com" },
|
11
11
|
]
|
@@ -56,6 +56,14 @@ Documentation = "https://ais-package.github.io/docs/intro"
|
|
56
56
|
"Source Code" = "https://github.com/AIS-Package/aisp"
|
57
57
|
Tracker = "https://github.com/AIS-Package/aisp/issues"
|
58
58
|
|
59
|
+
[project.optional-dependencies]
|
60
|
+
dev = [
|
61
|
+
"build>=1.2.2.post1",
|
62
|
+
"ipykernel>=6.29.5",
|
63
|
+
"twine>=5.1.1",
|
64
|
+
"pytest>=8.3.5",
|
65
|
+
]
|
66
|
+
|
59
67
|
[tool.setuptools]
|
60
68
|
packages = { find = { exclude = [
|
61
69
|
"*test*",
|
aisp-0.2.0/aisp/csa/_cell.py
DELETED
@@ -1,47 +0,0 @@
|
|
1
|
-
"""Represents a memory B-cell."""
|
2
|
-
|
3
|
-
from dataclasses import dataclass
|
4
|
-
from typing import Literal
|
5
|
-
|
6
|
-
import numpy as np
|
7
|
-
import numpy.typing as npt
|
8
|
-
|
9
|
-
from ..base.mutation import clone_and_mutate_continuous, clone_and_mutate_binary
|
10
|
-
|
11
|
-
|
12
|
-
@dataclass(slots=True)
|
13
|
-
class Cell:
|
14
|
-
"""
|
15
|
-
Represents a memory B-cell.
|
16
|
-
|
17
|
-
Attributes
|
18
|
-
----------
|
19
|
-
vector : npt.NDArray
|
20
|
-
A vector of cell features.
|
21
|
-
"""
|
22
|
-
|
23
|
-
vector: np.ndarray
|
24
|
-
|
25
|
-
def hyper_clonal_mutate(
|
26
|
-
self,
|
27
|
-
n: int,
|
28
|
-
algorithm: Literal["continuous-features", "binary-features"] = "continuous-features"
|
29
|
-
) -> npt.NDArray:
|
30
|
-
"""
|
31
|
-
Clones N features from a cell's features, generating a set of mutated vectors.
|
32
|
-
|
33
|
-
Parameters
|
34
|
-
----------
|
35
|
-
n : int
|
36
|
-
Number of clones to be generated from mutations of the original cell.
|
37
|
-
algorithm : Literal["continuous-features", "binary-features"], default="continuous-features"
|
38
|
-
Specifies the type of algorithm to use based on the nature of the input features
|
39
|
-
|
40
|
-
Returns
|
41
|
-
-------
|
42
|
-
npt.NDArray
|
43
|
-
An array containing N mutated vectors from the original cell.
|
44
|
-
"""
|
45
|
-
if algorithm == "binary-features":
|
46
|
-
return clone_and_mutate_binary(self.vector, n)
|
47
|
-
return clone_and_mutate_continuous(self.vector, n)
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|