aisp 0.3.0__tar.gz → 0.3.2__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 (37) hide show
  1. {aisp-0.3.0 → aisp-0.3.2}/PKG-INFO +6 -6
  2. {aisp-0.3.0 → aisp-0.3.2}/README.md +5 -5
  3. aisp-0.3.2/aisp/__init__.py +26 -0
  4. {aisp-0.3.0 → aisp-0.3.2}/aisp/base/__init__.py +1 -1
  5. {aisp-0.3.0 → aisp-0.3.2}/aisp/base/_classifier.py +3 -1
  6. {aisp-0.3.0 → aisp-0.3.2}/aisp/base/_clusterer.py +3 -1
  7. {aisp-0.3.0 → aisp-0.3.2}/aisp/csa/_ai_recognition_sys.py +14 -12
  8. {aisp-0.3.0 → aisp-0.3.2}/aisp/csa/_base.py +1 -2
  9. {aisp-0.3.0 → aisp-0.3.2}/aisp/ina/_ai_network.py +9 -9
  10. {aisp-0.3.0 → aisp-0.3.2}/aisp/ina/_base.py +1 -2
  11. {aisp-0.3.0 → aisp-0.3.2}/aisp/nsa/__init__.py +2 -1
  12. aisp-0.3.2/aisp/nsa/_binary_negative_selection.py +240 -0
  13. {aisp-0.3.0 → aisp-0.3.2}/aisp/nsa/_negative_selection.py +7 -233
  14. {aisp-0.3.0 → aisp-0.3.2}/aisp/utils/_multiclass.py +1 -0
  15. {aisp-0.3.0 → aisp-0.3.2}/aisp.egg-info/PKG-INFO +6 -6
  16. {aisp-0.3.0 → aisp-0.3.2}/aisp.egg-info/SOURCES.txt +1 -0
  17. {aisp-0.3.0 → aisp-0.3.2}/pyproject.toml +1 -1
  18. aisp-0.3.0/aisp/__init__.py +0 -4
  19. {aisp-0.3.0 → aisp-0.3.2}/LICENSE +0 -0
  20. {aisp-0.3.0 → aisp-0.3.2}/aisp/base/_base.py +0 -0
  21. {aisp-0.3.0 → aisp-0.3.2}/aisp/base/mutation.py +0 -0
  22. {aisp-0.3.0 → aisp-0.3.2}/aisp/csa/__init__.py +0 -0
  23. {aisp-0.3.0 → aisp-0.3.2}/aisp/csa/_cell.py +0 -0
  24. {aisp-0.3.0 → aisp-0.3.2}/aisp/exceptions.py +0 -0
  25. {aisp-0.3.0 → aisp-0.3.2}/aisp/ina/__init__.py +0 -0
  26. {aisp-0.3.0 → aisp-0.3.2}/aisp/nsa/_base.py +0 -0
  27. {aisp-0.3.0 → aisp-0.3.2}/aisp/nsa/_ns_core.py +0 -0
  28. {aisp-0.3.0 → aisp-0.3.2}/aisp/utils/__init__.py +0 -0
  29. {aisp-0.3.0 → aisp-0.3.2}/aisp/utils/distance.py +0 -0
  30. {aisp-0.3.0 → aisp-0.3.2}/aisp/utils/metrics.py +0 -0
  31. {aisp-0.3.0 → aisp-0.3.2}/aisp/utils/sanitizers.py +0 -0
  32. {aisp-0.3.0 → aisp-0.3.2}/aisp/utils/types.py +0 -0
  33. {aisp-0.3.0 → aisp-0.3.2}/aisp/utils/validation.py +0 -0
  34. {aisp-0.3.0 → aisp-0.3.2}/aisp.egg-info/dependency_links.txt +0 -0
  35. {aisp-0.3.0 → aisp-0.3.2}/aisp.egg-info/requires.txt +0 -0
  36. {aisp-0.3.0 → aisp-0.3.2}/aisp.egg-info/top_level.txt +0 -0
  37. {aisp-0.3.0 → aisp-0.3.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aisp
3
- Version: 0.3.0
3
+ Version: 0.3.2
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>
@@ -83,12 +83,12 @@ Artificial Immune Systems (AIS) are inspired by the vertebrate immune system, cr
83
83
 
84
84
  ##### Algorithms implemented:
85
85
 
86
- > - [x] [**Negative Selection.**](https://ais-package.github.io/docs/aisp-techniques/Negative%20Selection/)
87
- > - [x] [**Clonal Selection Algorithms.**](https://ais-package.github.io/docs/aisp-techniques/Clonal%20Selection%20Algorithms/)
88
- > * [AIRS - Artificial Immune Recognition System](https://ais-package.github.io/docs/aisp-techniques/Clonal%20Selection%20Algorithms/airs/)
86
+ > - [x] [**Negative Selection.**](https://ais-package.github.io/docs/aisp-techniques/negative-selection/)
87
+ > - [x] [**Clonal Selection Algorithms.**](https://ais-package.github.io/docs/aisp-techniques/clonal-selection-algorithms/)
88
+ > * [AIRS - Artificial Immune Recognition System](https://ais-package.github.io/docs/aisp-techniques/clonal-selection-algorithms/airs/)
89
89
  > - [ ] *Danger Theory.*
90
- > - [x] [*Immune Network Theory.*](https://ais-package.github.io/docs/aisp-techniques/Immune%20Network%20Theory)
91
- > - [AiNet - Artificial Immune Network para Clustering and Compression](https://ais-package.github.io/docs/aisp-techniques/Immune%20Network%20Theory/ainet)
90
+ > - [x] [*Immune Network Theory.*](https://ais-package.github.io/docs/aisp-techniques/immune-network-theory/)
91
+ > - [AiNet - Artificial Immune Network para Clustering and Compression](https://ais-package.github.io/docs/aisp-techniques/immune-network-theory/ainet)
92
92
 
93
93
  </section>
94
94
 
@@ -48,12 +48,12 @@ Artificial Immune Systems (AIS) are inspired by the vertebrate immune system, cr
48
48
 
49
49
  ##### Algorithms implemented:
50
50
 
51
- > - [x] [**Negative Selection.**](https://ais-package.github.io/docs/aisp-techniques/Negative%20Selection/)
52
- > - [x] [**Clonal Selection Algorithms.**](https://ais-package.github.io/docs/aisp-techniques/Clonal%20Selection%20Algorithms/)
53
- > * [AIRS - Artificial Immune Recognition System](https://ais-package.github.io/docs/aisp-techniques/Clonal%20Selection%20Algorithms/airs/)
51
+ > - [x] [**Negative Selection.**](https://ais-package.github.io/docs/aisp-techniques/negative-selection/)
52
+ > - [x] [**Clonal Selection Algorithms.**](https://ais-package.github.io/docs/aisp-techniques/clonal-selection-algorithms/)
53
+ > * [AIRS - Artificial Immune Recognition System](https://ais-package.github.io/docs/aisp-techniques/clonal-selection-algorithms/airs/)
54
54
  > - [ ] *Danger Theory.*
55
- > - [x] [*Immune Network Theory.*](https://ais-package.github.io/docs/aisp-techniques/Immune%20Network%20Theory)
56
- > - [AiNet - Artificial Immune Network para Clustering and Compression](https://ais-package.github.io/docs/aisp-techniques/Immune%20Network%20Theory/ainet)
55
+ > - [x] [*Immune Network Theory.*](https://ais-package.github.io/docs/aisp-techniques/immune-network-theory/)
56
+ > - [AiNet - Artificial Immune Network para Clustering and Compression](https://ais-package.github.io/docs/aisp-techniques/immune-network-theory/ainet)
57
57
 
58
58
  </section>
59
59
 
@@ -0,0 +1,26 @@
1
+ """AISP - Artificial Immune Systems Package.
2
+
3
+ AISP is a Python package of immunoinspired techniques that apply metaphors from the vertebrate
4
+ immune system to pattern recognition and optimization tasks.
5
+
6
+ The package is organized into specialized modules, each dedicated to a family of Artificial
7
+ Immune Systems algorithms:
8
+ - csa: Clonal Selection Algorithms
9
+ - nsa: Negative Selection Algorithms
10
+ - ina: Immune Network Algorithms
11
+
12
+ For detailed documentation and examples, visit:
13
+ https://ais-package.github.io/docs/intro
14
+ """
15
+
16
+ from . import csa
17
+ from . import ina
18
+ from . import nsa
19
+
20
+ __author__ = "AISP Development Team"
21
+ __version__ = "0.3.2"
22
+ __all__ = [
23
+ 'csa',
24
+ 'nsa',
25
+ 'ina'
26
+ ]
@@ -1,7 +1,7 @@
1
1
  """Base class modules."""
2
2
 
3
+ from ._base import set_seed_numba
3
4
  from ._classifier import BaseClassifier
4
5
  from ._clusterer import BaseClusterer
5
- from ._base import set_seed_numba
6
6
 
7
7
  __all__ = ['BaseClassifier', 'BaseClusterer', 'set_seed_numba']
@@ -1,5 +1,7 @@
1
1
  """Base class for classification algorithm."""
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  from abc import ABC, abstractmethod
4
6
  from typing import Optional, Union
5
7
 
@@ -19,7 +21,7 @@ class BaseClassifier(ABC, Base):
19
21
  classes: Union[npt.NDArray, list] = []
20
22
 
21
23
  @abstractmethod
22
- def fit(self, X: npt.NDArray, y: npt.NDArray, verbose: bool = True) -> "BaseClassifier":
24
+ def fit(self, X: npt.NDArray, y: npt.NDArray, verbose: bool = True) -> BaseClassifier:
23
25
  """
24
26
  Train the model using the input data X and corresponding labels y.
25
27
 
@@ -1,5 +1,7 @@
1
1
  """Base class for clustering algorithms."""
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  from abc import ABC, abstractmethod
4
6
  from typing import Optional
5
7
 
@@ -17,7 +19,7 @@ class BaseClusterer(ABC, Base):
17
19
  """
18
20
 
19
21
  @abstractmethod
20
- def fit(self, X: npt.NDArray, verbose: bool = True) -> "BaseClusterer":
22
+ def fit(self, X: npt.NDArray, verbose: bool = True) -> BaseClusterer:
21
23
  """
22
24
  Train the model using the input data X.
23
25
 
@@ -1,5 +1,7 @@
1
1
  """Artificial Immune Recognition System (AIRS)."""
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  import random
4
6
  from collections import Counter
5
7
  from heapq import nlargest
@@ -11,13 +13,13 @@ import numpy.typing as npt
11
13
  from scipy.spatial.distance import pdist
12
14
  from tqdm import tqdm
13
15
 
14
- from ..base import set_seed_numba
16
+ from ._base import BaseAIRS
15
17
  from ._cell import Cell
16
- from ..utils.sanitizers import sanitize_param, sanitize_seed, sanitize_choice
18
+ from ..base import set_seed_numba
17
19
  from ..utils.distance import hamming, compute_metric_distance, get_metric_code
20
+ from ..utils.sanitizers import sanitize_param, sanitize_seed, sanitize_choice
18
21
  from ..utils.types import FeatureType, MetricType
19
22
  from ..utils.validation import detect_vector_data_type
20
- from ._base import BaseAIRS
21
23
 
22
24
 
23
25
  class _ARB(Cell):
@@ -189,6 +191,7 @@ class AIRS(BaseAIRS):
189
191
  self.p: np.float64 = np.float64(kwargs.get("p", 2.0))
190
192
 
191
193
  self._cells_memory = None
194
+ self._all_class_cell_vectors = None
192
195
  self.affinity_threshold = 0.0
193
196
  self.classes = []
194
197
  self._bounds: Optional[npt.NDArray[np.float64]] = None
@@ -198,7 +201,7 @@ class AIRS(BaseAIRS):
198
201
  """Returns the trained cells memory, organized by class."""
199
202
  return self._cells_memory
200
203
 
201
- def fit(self, X: npt.NDArray, y: npt.NDArray, verbose: bool = True) -> "AIRS":
204
+ def fit(self, X: npt.NDArray, y: npt.NDArray, verbose: bool = True) -> AIRS:
202
205
  """
203
206
  Fit the model to the training data using the AIRS.
204
207
 
@@ -301,6 +304,11 @@ class AIRS(BaseAIRS):
301
304
  )
302
305
  progress.close()
303
306
  self._cells_memory = pool_cells_classes
307
+ self._all_class_cell_vectors = [
308
+ (class_name, cell.vector)
309
+ for class_name in self.classes
310
+ for cell in self._cells_memory[class_name]
311
+ ]
304
312
  return self
305
313
 
306
314
  def predict(self, X: npt.NDArray) -> Optional[npt.NDArray]:
@@ -322,7 +330,7 @@ class AIRS(BaseAIRS):
322
330
  An ndarray of the form ``C`` [``N samples``], containing the predicted classes for
323
331
  ``X``. or ``None``: If there are no detectors for the prediction.
324
332
  """
325
- if self._cells_memory is None:
333
+ if self._all_class_cell_vectors is None:
326
334
  return None
327
335
 
328
336
  super()._check_and_raise_exceptions_predict(
@@ -331,16 +339,10 @@ class AIRS(BaseAIRS):
331
339
 
332
340
  c: list = []
333
341
 
334
- all_cells_memory = [
335
- (class_name, cell.vector)
336
- for class_name in self.classes
337
- for cell in self._cells_memory[class_name]
338
- ]
339
-
340
342
  for line in X:
341
343
  label_stim_list = [
342
344
  (class_name, self._affinity(memory, line))
343
- for class_name, memory in all_cells_memory
345
+ for class_name, memory in self._all_class_cell_vectors
344
346
  ]
345
347
  # Create the list with the k nearest neighbors and select the class with the most votes
346
348
  k_nearest = nlargest(self.k, label_stim_list, key=lambda x: x[1])
@@ -5,9 +5,9 @@ from abc import ABC
5
5
  import numpy as np
6
6
  import numpy.typing as npt
7
7
 
8
+ from ..base import BaseClassifier
8
9
  from ..exceptions import FeatureDimensionMismatch
9
10
  from ..utils.types import FeatureType
10
- from ..base import BaseClassifier
11
11
 
12
12
 
13
13
  class BaseAIRS(BaseClassifier, ABC):
@@ -54,7 +54,6 @@ class BaseAIRS(BaseClassifier, ABC):
54
54
  "X does not have the same amount of sample for the output classes in y."
55
55
  )
56
56
 
57
-
58
57
  @staticmethod
59
58
  def _check_and_raise_exceptions_predict(
60
59
  X: npt.NDArray,
@@ -1,5 +1,7 @@
1
1
  """Artificial Immune Network (AiNet)."""
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  from collections import Counter
4
6
  from heapq import nlargest
5
7
  from typing import Optional
@@ -14,14 +16,14 @@ from ._base import BaseAiNet
14
16
  from ..base import set_seed_numba
15
17
  from ..base.mutation import clone_and_mutate_binary, clone_and_mutate_continuous, \
16
18
  clone_and_mutate_ranged
17
- from ..utils.sanitizers import sanitize_choice, sanitize_param, sanitize_seed
18
19
  from ..utils.distance import hamming, compute_metric_distance, get_metric_code
20
+ from ..utils.sanitizers import sanitize_choice, sanitize_param, sanitize_seed
19
21
  from ..utils.types import FeatureType, MetricType
20
22
  from ..utils.validation import detect_vector_data_type
21
23
 
22
24
 
23
25
  class AiNet(BaseAiNet):
24
- """Artificial Immune Network for Compression and Clustering .
26
+ """Artificial Immune Network for Compression and Clustering.
25
27
 
26
28
  This class implements the aiNet algorithm, an artificial immune network model designed for
27
29
  clustering and data compression tasks. The aiNet algorithm uses principles from immune
@@ -77,13 +79,11 @@ class AiNet(BaseAiNet):
77
79
 
78
80
  References
79
81
  ----------
80
- .. [1] de Castro, L. N., & Von Zuben, F. J. (2001).
81
- *aiNet: An Artificial Immune Network for Data Analysis*.
82
- Draft Chapter XII of the book *Data Mining: A Heuristic Approach*.
83
- Department of Computer and Automation Engineering, University of Campinas.
82
+ .. [1] De Castro, Leandro & José, Fernando & von Zuben, Antonio Augusto. (2001). aiNet: An
83
+ Artificial Immune Network for Data Analysis.
84
84
  Available at:
85
- https://www.dca.fee.unicamp.br/~vonzuben/research/lnunes_dout/
86
- artigos/DMHA.PDF
85
+ https://www.researchgate.net/publication/
86
+ 228378350_aiNet_An_Artificial_Immune_Network_for_Data_Analysis
87
87
  .. [2] SciPy Documentation. *Minimum Spanning Tree*.
88
88
  https://docs.scipy.org/doc/scipy/reference/generated/
89
89
  scipy.sparse.csgraph.minimum_spanning_tree
@@ -175,7 +175,7 @@ class AiNet(BaseAiNet):
175
175
  'std_distance': self._mst_std_distance
176
176
  }
177
177
 
178
- def fit(self, X: npt.NDArray, verbose: bool = True):
178
+ def fit(self, X: npt.NDArray, verbose: bool = True) -> AiNet:
179
179
  """
180
180
  Train the AiNet model on input data.
181
181
 
@@ -3,9 +3,8 @@
3
3
  from abc import ABC
4
4
  from typing import Optional
5
5
 
6
- from numpy import typing as npt
7
-
8
6
  import numpy as np
7
+ from numpy import typing as npt
9
8
 
10
9
  from ..base import BaseClusterer
11
10
  from ..exceptions import FeatureDimensionMismatch
@@ -5,7 +5,8 @@ distinguish between self and non-self. Only T-cells capable of recognizing non-s
5
5
  preserved.
6
6
  """
7
7
 
8
- from ._negative_selection import BNSA, RNSA
8
+ from ._binary_negative_selection import BNSA
9
+ from ._negative_selection import RNSA
9
10
 
10
11
  __author__ = "João Paulo da Silva Barros"
11
12
  __all__ = ["RNSA", "BNSA"]
@@ -0,0 +1,240 @@
1
+ """Negative Selection Algorithm."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Dict, Literal, Optional, Union
6
+
7
+ import numpy as np
8
+ import numpy.typing as npt
9
+ from tqdm import tqdm
10
+
11
+ from ._base import BaseNSA
12
+ from ._ns_core import (
13
+ check_detector_bnsa_validity,
14
+ bnsa_class_prediction
15
+ )
16
+ from ..exceptions import MaxDiscardsReachedError
17
+ from ..utils.sanitizers import sanitize_seed, sanitize_param
18
+
19
+
20
+ class BNSA(BaseNSA):
21
+ """BNSA (Binary Negative Selection Algorithm).
22
+
23
+ Class is for classification and identification purposes of anomalies through the self and not
24
+ self method.
25
+
26
+ Parameters
27
+ ----------
28
+ N : int, default=100
29
+ Number of detectors.
30
+ aff_thresh : float, default=0.1
31
+ The variable represents the percentage of similarity between the T cell and the own
32
+ samples. The default value is 10% (0.1), while a value of 1.0 represents 100% similarity.
33
+ max_discards : int, default=1000
34
+ This parameter indicates the maximum number of detector discards in sequence, which aims
35
+ to avoid a possible infinite loop if a radius is defined that it is not possible to
36
+ generate non-self detectors.
37
+ seed : Optional[int], default=None
38
+ Seed for the random generation of values in the detectors.
39
+ no_label_sample_selection : str, default="max_average_difference"
40
+ Method for selecting labels for samples designated as non-self by all detectors.
41
+ Available method types:
42
+
43
+ - max_average_difference - Selects the class with the highest average difference among the
44
+ detectors.
45
+
46
+ - max_nearest_difference - Selects the class with the highest difference between the
47
+ nearest and farthest detector from the sample.
48
+ """
49
+
50
+ def __init__(
51
+ self,
52
+ N: int = 100,
53
+ aff_thresh: float = 0.1,
54
+ max_discards: int = 1000,
55
+ seed: Optional[int] = None,
56
+ no_label_sample_selection: Literal[
57
+ "max_average_difference", "max_nearest_difference"
58
+ ] = "max_average_difference",
59
+ ):
60
+ self.N: int = sanitize_param(N, 100, lambda x: x > 0)
61
+ self.aff_thresh: float = sanitize_param(aff_thresh, 0.1, lambda x: 0 < x < 1)
62
+ self.max_discards: float = sanitize_param(max_discards, 1000, lambda x: x > 0)
63
+
64
+ self.seed: Optional[int] = sanitize_seed(seed)
65
+
66
+ if self.seed is not None:
67
+ np.random.seed(seed)
68
+
69
+ self.no_label_sample_selection: str = sanitize_param(
70
+ no_label_sample_selection,
71
+ "max_average_difference",
72
+ lambda x: x == "nearest_difference",
73
+ )
74
+
75
+ self.classes: Union[npt.NDArray, list] = []
76
+ self._detectors: Optional[dict] = None
77
+ self._detectors_stack: Optional[npt.NDArray] = None
78
+
79
+ @property
80
+ def detectors(self) -> Optional[Dict[str, npt.NDArray[np.bool_]]]:
81
+ """Returns the trained detectors, organized by class."""
82
+ return self._detectors
83
+
84
+ def fit(self, X: npt.NDArray, y: npt.NDArray, verbose: bool = True) -> BNSA:
85
+ """Training according to X and y, using the method negative selection method.
86
+
87
+ Parameters
88
+ ----------
89
+ X : npt.NDArray
90
+ Training array, containing the samples and their characteristics, [``N samples`` (
91
+ rows)][``N features`` (columns)].
92
+ y : npt.NDArray
93
+ Array of target classes of ``X`` with [``N samples`` (lines)].
94
+ verbose : bool, default=True
95
+ Feedback from detector generation to the user.
96
+
97
+ Returns
98
+ -------
99
+ self : BNSA
100
+ Returns the instance it self.
101
+ """
102
+ super()._check_and_raise_exceptions_fit(X, y, "BNSA")
103
+ # Converts the entire array X to boolean
104
+ X = X.astype(np.bool_)
105
+
106
+ # Identifying the possible classes within the output array `y`.
107
+ self.classes = np.unique(y)
108
+ # Dictionary that will store detectors with classes as keys.
109
+ list_detectors_by_class = {}
110
+ # Separates the classes for training.
111
+ sample_index: dict = self._slice_index_list_by_class(y)
112
+ # Progress bar for generating all detectors.
113
+
114
+ progress = tqdm(
115
+ total=int(self.N * (len(self.classes))),
116
+ bar_format="{desc} ┇{bar}┇ {n}/{total} detectors",
117
+ postfix="\n",
118
+ disable=not verbose
119
+ )
120
+
121
+ for _class_ in self.classes:
122
+ # Initializes the empty set that will contain the valid detectors.
123
+ valid_detectors_set: list = []
124
+ discard_count: int = 0
125
+ # Updating the progress bar with the current class the algorithm is processing.
126
+ progress.set_description_str(
127
+ f"Generating the detectors for the {_class_} class:"
128
+ )
129
+ x_class = X[sample_index[_class_]]
130
+ while len(valid_detectors_set) < self.N:
131
+ # Generates a candidate detector vector randomly with values 0 and 1.
132
+ vector_x = np.random.randint(0, 2, size=X.shape[1]).astype(np.bool_)
133
+ # If the detector is valid, add it to the list of valid detectors.
134
+ if check_detector_bnsa_validity(x_class, vector_x, self.aff_thresh):
135
+ discard_count = 0
136
+ valid_detectors_set.append(vector_x)
137
+ progress.update(1)
138
+ else:
139
+ discard_count += 1
140
+ if discard_count == self.max_discards:
141
+ raise MaxDiscardsReachedError(_class_)
142
+
143
+ # Add detectors to the dictionary with classes as keys.
144
+ list_detectors_by_class[_class_] = np.array(valid_detectors_set)
145
+
146
+ # Notify the completion of detector generation for the classes.
147
+ progress.set_description(
148
+ f"\033[92m✔ Non-self detectors for classes ({', '.join(map(str, self.classes))}) "
149
+ f"successfully generated\033[0m"
150
+ )
151
+ progress.close()
152
+ # Saves the found detectors in the attribute for the class detectors.
153
+ self._detectors = list_detectors_by_class
154
+ self._detectors_stack = np.array(
155
+ [np.stack(self._detectors[class_name]) for class_name in self.classes]
156
+ )
157
+ return self
158
+
159
+ def predict(self, X: npt.NDArray) -> Optional[npt.NDArray]:
160
+ """Prediction of classes based on detectors created after training.
161
+
162
+ Parameters
163
+ ----------
164
+ X : npt.NDArray
165
+ Array with input samples with [``N_samples`` (Lines)] and [``N_characteristics``(
166
+ Columns)]
167
+
168
+ Returns
169
+ -------
170
+ c : Optional[npt.NDArray]
171
+ an ndarray of the form ``C`` [``N samples``], containing the predicted classes for
172
+ ``X``. Returns``None``: If there are no detectors for the prediction.
173
+ """
174
+ # If there are no detectors, Returns None.
175
+ if self._detectors is None or self._detectors_stack is None:
176
+ return None
177
+
178
+ super()._check_and_raise_exceptions_predict(
179
+ X, len(self._detectors[self.classes[0]][0]), "BNSA"
180
+ )
181
+
182
+ # Converts the entire array X to boolean.
183
+ if X.dtype != bool:
184
+ X = X.astype(bool)
185
+
186
+ # Initializes an empty array that will store the predictions.
187
+ c = []
188
+ # For each sample row in X.
189
+ for line in X:
190
+ class_found: bool = True
191
+ # Class prediction based on detectors
192
+ class_index = bnsa_class_prediction(
193
+ line, self._detectors_stack, self.aff_thresh
194
+ )
195
+ # If belonging to one or more classes, adds the class with the greatest
196
+ # average distance
197
+ if class_index > -1:
198
+ c.append(self.classes[class_index])
199
+ class_found = True
200
+ else:
201
+ class_found = False
202
+
203
+ # If there is only one class and the sample is not classified, sets the
204
+ # output as non-self.
205
+ if not class_found and len(self.classes) == 1:
206
+ c.append("non-self")
207
+ # If the class cannot be identified by the detectors
208
+ elif not class_found:
209
+ self.__assign_class_to_non_self_sample(line, c)
210
+
211
+ return np.array(c)
212
+
213
+ def __assign_class_to_non_self_sample(self, line: npt.NDArray, c: list):
214
+ """Determine the class of a sample when all detectors classify it as "non-self".
215
+
216
+ Classification is performed using the ``max_average_difference`` and
217
+ ``max_nearest_difference`` methods.
218
+
219
+ Parameters
220
+ ----------
221
+ line : npt.NDArray
222
+ Sample to be classified.
223
+ c : list
224
+ List of predictions to be updated with the new classification.
225
+ """
226
+ if self._detectors is None:
227
+ raise ValueError("Detectors is not initialized.")
228
+
229
+ class_differences: dict = {}
230
+ for _class_ in self.classes:
231
+ distances = np.sum(line != self._detectors[_class_]) / self.N
232
+ # Assign the label to the class with the greatest distance from
233
+ # the nearest detector.
234
+ if self.no_label_sample_selection == "nearest_difference":
235
+ class_differences[_class_] = distances.min()
236
+ # Or based on the greatest distance from the average distances of the detectors.
237
+ else:
238
+ class_differences[_class_] = distances.sum() / self.N
239
+
240
+ c.append(max(class_differences, key=class_differences.get))
@@ -1,17 +1,16 @@
1
1
  """Negative Selection Algorithm."""
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  from typing import Any, Dict, Literal, Optional, Union
4
- from tqdm import tqdm
5
6
 
6
7
  import numpy as np
7
8
  import numpy.typing as npt
9
+ from tqdm import tqdm
8
10
 
11
+ from ._base import BaseNSA, Detector
12
+ from ._ns_core import check_detector_rnsa_validity
9
13
  from ..base import set_seed_numba
10
- from ._ns_core import (
11
- check_detector_bnsa_validity,
12
- bnsa_class_prediction,
13
- check_detector_rnsa_validity,
14
- )
15
14
  from ..exceptions import MaxDiscardsReachedError
16
15
  from ..utils.distance import (
17
16
  min_distance_to_class_vectors,
@@ -19,7 +18,6 @@ from ..utils.distance import (
19
18
  compute_metric_distance,
20
19
  )
21
20
  from ..utils.sanitizers import sanitize_seed, sanitize_choice, sanitize_param
22
- from ._base import BaseNSA, Detector
23
21
 
24
22
 
25
23
  class RNSA(BaseNSA):
@@ -123,7 +121,7 @@ class RNSA(BaseNSA):
123
121
  """Returns the trained detectors, organized by class."""
124
122
  return self._detectors
125
123
 
126
- def fit(self, X: npt.NDArray, y: npt.NDArray, verbose: bool = True) -> "RNSA":
124
+ def fit(self, X: npt.NDArray, y: npt.NDArray, verbose: bool = True) -> RNSA:
127
125
  """
128
126
  Perform training according to X and y, using the negative selection method (NegativeSelect).
129
127
 
@@ -339,10 +337,9 @@ class RNSA(BaseNSA):
339
337
  if len(knn) < self.k:
340
338
  knn.append(distance)
341
339
  knn.sort()
342
-
343
340
  # Otherwise, add the distance if the new distance is smaller than the largest
344
341
  # distance in the list.
345
- if knn[self.k - 1] > distance:
342
+ elif knn[self.k - 1] > distance:
346
343
  knn[self.k - 1] = distance
347
344
  knn.sort()
348
345
 
@@ -447,226 +444,3 @@ class RNSA(BaseNSA):
447
444
  return False
448
445
 
449
446
  return True, new_detector_r
450
-
451
-
452
- class BNSA(BaseNSA):
453
- """BNSA (Binary Negative Selection Algorithm).
454
-
455
- Class is for classification and identification purposes of anomalies through the self and not
456
- self method.
457
-
458
- Parameters
459
- ----------
460
- N : int, default=100
461
- Number of detectors.
462
- aff_thresh : float, default=0.1
463
- The variable represents the percentage of similarity between the T cell and the own
464
- samples. The default value is 10% (0.1), while a value of 1.0 represents 100% similarity.
465
- max_discards : int, default=1000
466
- This parameter indicates the maximum number of detector discards in sequence, which aims
467
- to avoid a possible infinite loop if a radius is defined that it is not possible to
468
- generate non-self detectors.
469
- seed : Optional[int], default=None
470
- Seed for the random generation of values in the detectors.
471
- no_label_sample_selection : str, default="max_average_difference"
472
- Method for selecting labels for samples designated as non-self by all detectors.
473
- Available method types:
474
-
475
- - max_average_difference - Selects the class with the highest average difference among the
476
- detectors.
477
-
478
- - max_nearest_difference - Selects the class with the highest difference between the
479
- nearest and farthest detector from the sample.
480
- """
481
-
482
- def __init__(
483
- self,
484
- N: int = 100,
485
- aff_thresh: float = 0.1,
486
- max_discards: int = 1000,
487
- seed: Optional[int] = None,
488
- no_label_sample_selection: Literal[
489
- "max_average_difference", "max_nearest_difference"
490
- ] = "max_average_difference",
491
- ):
492
- self.N: int = sanitize_param(N, 100, lambda x: x > 0)
493
- self.aff_thresh: float = sanitize_param(aff_thresh, 0.1, lambda x: 0 < x < 1)
494
- self.max_discards: float = sanitize_param(max_discards, 1000, lambda x: x > 0)
495
-
496
- self.seed: Optional[int] = sanitize_seed(seed)
497
-
498
- if self.seed is not None:
499
- np.random.seed(seed)
500
-
501
- self.no_label_sample_selection: str = sanitize_param(
502
- no_label_sample_selection,
503
- "max_average_difference",
504
- lambda x: x == "nearest_difference",
505
- )
506
-
507
- self.classes: Union[npt.NDArray, list] = []
508
- self._detectors: Optional[dict] = None
509
- self._detectors_stack: Optional[npt.NDArray] = None
510
-
511
- @property
512
- def detectors(self) -> Optional[Dict[str, npt.NDArray[np.bool_]]]:
513
- """Returns the trained detectors, organized by class."""
514
- return self._detectors
515
-
516
- def fit(self, X: npt.NDArray, y: npt.NDArray, verbose: bool = True) -> "BNSA":
517
- """Training according to X and y, using the method negative selection method.
518
-
519
- Parameters
520
- ----------
521
- X : npt.NDArray
522
- Training array, containing the samples and their characteristics, [``N samples`` (
523
- rows)][``N features`` (columns)].
524
- y : npt.NDArray
525
- Array of target classes of ``X`` with [``N samples`` (lines)].
526
- verbose : bool, default=True
527
- Feedback from detector generation to the user.
528
-
529
- Returns
530
- -------
531
- self : BNSA
532
- Returns the instance it self.
533
- """
534
- super()._check_and_raise_exceptions_fit(X, y, "BNSA")
535
- # Converts the entire array X to boolean
536
- X = X.astype(np.bool_)
537
-
538
- # Identifying the possible classes within the output array `y`.
539
- self.classes = np.unique(y)
540
- # Dictionary that will store detectors with classes as keys.
541
- list_detectors_by_class = {}
542
- # Separates the classes for training.
543
- sample_index: dict = self._slice_index_list_by_class(y)
544
- # Progress bar for generating all detectors.
545
-
546
- progress = tqdm(
547
- total=int(self.N * (len(self.classes))),
548
- bar_format="{desc} ┇{bar}┇ {n}/{total} detectors",
549
- postfix="\n",
550
- disable=not verbose
551
- )
552
-
553
- for _class_ in self.classes:
554
- # Initializes the empty set that will contain the valid detectors.
555
- valid_detectors_set: list = []
556
- discard_count: int = 0
557
- # Updating the progress bar with the current class the algorithm is processing.
558
- progress.set_description_str(
559
- f"Generating the detectors for the {_class_} class:"
560
- )
561
- x_class = X[sample_index[_class_]]
562
- while len(valid_detectors_set) < self.N:
563
- # Generates a candidate detector vector randomly with values 0 and 1.
564
- vector_x = np.random.randint(0, 2, size=X.shape[1]).astype(np.bool_)
565
- # If the detector is valid, add it to the list of valid detectors.
566
- if check_detector_bnsa_validity(x_class, vector_x, self.aff_thresh):
567
- discard_count = 0
568
- valid_detectors_set.append(vector_x)
569
- progress.update(1)
570
- else:
571
- discard_count += 1
572
- if discard_count == self.max_discards:
573
- raise MaxDiscardsReachedError(_class_)
574
-
575
- # Add detectors to the dictionary with classes as keys.
576
- list_detectors_by_class[_class_] = np.array(valid_detectors_set)
577
-
578
- # Notify the completion of detector generation for the classes.
579
- progress.set_description(
580
- f"\033[92m✔ Non-self detectors for classes ({', '.join(map(str, self.classes))}) "
581
- f"successfully generated\033[0m"
582
- )
583
- progress.close()
584
- # Saves the found detectors in the attribute for the class detectors.
585
- self._detectors = list_detectors_by_class
586
- self._detectors_stack = np.array(
587
- [np.stack(self._detectors[class_name]) for class_name in self.classes]
588
- )
589
- return self
590
-
591
- def predict(self, X: npt.NDArray) -> Optional[npt.NDArray]:
592
- """Prediction of classes based on detectors created after training.
593
-
594
- Parameters
595
- ----------
596
- X : npt.NDArray
597
- Array with input samples with [``N_samples`` (Lines)] and [``N_characteristics``(
598
- Columns)]
599
-
600
- Returns
601
- -------
602
- c : Optional[npt.NDArray]
603
- an ndarray of the form ``C`` [``N samples``], containing the predicted classes for
604
- ``X``. Returns``None``: If there are no detectors for the prediction.
605
- """
606
- # If there are no detectors, Returns None.
607
- if self._detectors is None or self._detectors_stack is None:
608
- return None
609
-
610
- super()._check_and_raise_exceptions_predict(
611
- X, len(self._detectors[self.classes[0]][0]), "BNSA"
612
- )
613
-
614
- # Converts the entire array X to boolean.
615
- if X.dtype != bool:
616
- X = X.astype(bool)
617
-
618
- # Initializes an empty array that will store the predictions.
619
- c = []
620
- # For each sample row in X.
621
- for line in X:
622
- class_found: bool = True
623
- # Class prediction based on detectors
624
- class_index = bnsa_class_prediction(
625
- line, self._detectors_stack, self.aff_thresh
626
- )
627
- # If belonging to one or more classes, adds the class with the greatest
628
- # average distance
629
- if class_index > -1:
630
- c.append(self.classes[class_index])
631
- class_found = True
632
- else:
633
- class_found = False
634
-
635
- # If there is only one class and the sample is not classified, sets the
636
- # output as non-self.
637
- if not class_found and len(self.classes) == 1:
638
- c.append("non-self")
639
- # If the class cannot be identified by the detectors
640
- elif not class_found:
641
- self.__assign_class_to_non_self_sample(line, c)
642
-
643
- return np.array(c)
644
-
645
- def __assign_class_to_non_self_sample(self, line: npt.NDArray, c: list):
646
- """Determine the class of a sample when all detectors classify it as "non-self".
647
-
648
- Classification is performed using the ``max_average_difference`` and
649
- ``max_nearest_difference`` methods.
650
-
651
- Parameters
652
- ----------
653
- line : npt.NDArray
654
- Sample to be classified.
655
- c : list
656
- List of predictions to be updated with the new classification.
657
- """
658
- if self._detectors is None:
659
- raise ValueError("Detectors is not initialized.")
660
-
661
- class_differences: dict = {}
662
- for _class_ in self.classes:
663
- distances = np.sum(line != self._detectors[_class_]) / self.N
664
- # Assign the label to the class with the greatest distance from
665
- # the nearest detector.
666
- if self.no_label_sample_selection == "nearest_difference":
667
- class_differences[_class_] = distances.min()
668
- # Or based on the greatest distance from the average distances of the detectors.
669
- else:
670
- class_differences[_class_] = distances.sum() / self.N
671
-
672
- c.append(max(class_differences, key=class_differences.get))
@@ -1,6 +1,7 @@
1
1
  """Utility functions for handling classes with multiple categories."""
2
2
 
3
3
  from typing import Union
4
+
4
5
  import numpy as np
5
6
  import numpy.typing as npt
6
7
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aisp
3
- Version: 0.3.0
3
+ Version: 0.3.2
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>
@@ -83,12 +83,12 @@ Artificial Immune Systems (AIS) are inspired by the vertebrate immune system, cr
83
83
 
84
84
  ##### Algorithms implemented:
85
85
 
86
- > - [x] [**Negative Selection.**](https://ais-package.github.io/docs/aisp-techniques/Negative%20Selection/)
87
- > - [x] [**Clonal Selection Algorithms.**](https://ais-package.github.io/docs/aisp-techniques/Clonal%20Selection%20Algorithms/)
88
- > * [AIRS - Artificial Immune Recognition System](https://ais-package.github.io/docs/aisp-techniques/Clonal%20Selection%20Algorithms/airs/)
86
+ > - [x] [**Negative Selection.**](https://ais-package.github.io/docs/aisp-techniques/negative-selection/)
87
+ > - [x] [**Clonal Selection Algorithms.**](https://ais-package.github.io/docs/aisp-techniques/clonal-selection-algorithms/)
88
+ > * [AIRS - Artificial Immune Recognition System](https://ais-package.github.io/docs/aisp-techniques/clonal-selection-algorithms/airs/)
89
89
  > - [ ] *Danger Theory.*
90
- > - [x] [*Immune Network Theory.*](https://ais-package.github.io/docs/aisp-techniques/Immune%20Network%20Theory)
91
- > - [AiNet - Artificial Immune Network para Clustering and Compression](https://ais-package.github.io/docs/aisp-techniques/Immune%20Network%20Theory/ainet)
90
+ > - [x] [*Immune Network Theory.*](https://ais-package.github.io/docs/aisp-techniques/immune-network-theory/)
91
+ > - [AiNet - Artificial Immune Network para Clustering and Compression](https://ais-package.github.io/docs/aisp-techniques/immune-network-theory/ainet)
92
92
 
93
93
  </section>
94
94
 
@@ -22,6 +22,7 @@ aisp/ina/_ai_network.py
22
22
  aisp/ina/_base.py
23
23
  aisp/nsa/__init__.py
24
24
  aisp/nsa/_base.py
25
+ aisp/nsa/_binary_negative_selection.py
25
26
  aisp/nsa/_negative_selection.py
26
27
  aisp/nsa/_ns_core.py
27
28
  aisp/utils/__init__.py
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
5
5
 
6
6
  [project]
7
7
  name = "aisp"
8
- version = "0.3.0"
8
+ version = "0.3.2"
9
9
  authors = [
10
10
  { name="João Paulo da Silva Barros", email="jpsilvabarr@gmail.com" },
11
11
  ]
@@ -1,4 +0,0 @@
1
- """Artificial Immune Systems Package."""
2
-
3
- __author__ = "João Paulo da Silva Barros"
4
- __version__ = "0.3.0"
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
File without changes
File without changes
File without changes
File without changes