aisp 0.2.0__py3-none-any.whl → 0.3.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/ina/_base.py ADDED
@@ -0,0 +1,124 @@
1
+ """Base Class for Network Theory Algorithms."""
2
+
3
+ from abc import ABC
4
+ from typing import Optional
5
+
6
+ from numpy import typing as npt
7
+
8
+ import numpy as np
9
+
10
+ from ..base import BaseClusterer
11
+ from ..exceptions import FeatureDimensionMismatch
12
+ from ..utils.types import FeatureType
13
+
14
+
15
+ class BaseAiNet(BaseClusterer, ABC):
16
+ """Abstract base class for AINet-based clustering algorithms."""
17
+
18
+ @staticmethod
19
+ def _check_and_raise_exceptions_fit(
20
+ X: npt.NDArray
21
+ ):
22
+ """
23
+ Verify the fit parameters and throw exceptions if the verification is not successful.
24
+
25
+ Parameters
26
+ ----------
27
+ X : npt.NDArray
28
+ Training array, containing the samples and their characteristics,
29
+ [``N samples`` (rows)][``N features`` (columns)].
30
+
31
+ Raises
32
+ ------
33
+ TypeError
34
+ If X is not an ndarray or list.
35
+ """
36
+ if not isinstance(X, np.ndarray) and not isinstance(X, list):
37
+ raise TypeError("X is not an ndarray or list.")
38
+
39
+ @staticmethod
40
+ def _check_and_raise_exceptions_predict(
41
+ X: npt.NDArray,
42
+ expected: int = 0,
43
+ feature_type: FeatureType = "continuous-features"
44
+ ) -> None:
45
+ """
46
+ Verify the predict parameters and throw exceptions if the verification is not successful.
47
+
48
+ Parameters
49
+ ----------
50
+ X : npt.NDArray
51
+ Input array for prediction, containing the samples and their characteristics,
52
+ [``N samples`` (rows)][``N features`` (columns)].
53
+ expected : int, default=0
54
+ Expected number of features per sample (columns in X).
55
+ feature_type : FeatureType, default="continuous-features"
56
+ Specifies the type of features: "continuous-features", "binary-features",
57
+ or "ranged-features".
58
+
59
+
60
+ Raises
61
+ ------
62
+ TypeError
63
+ If X is not a ndarray or list.
64
+ FeatureDimensionMismatch
65
+ If the number of features in X does not match the expected number.
66
+ ValueError
67
+ If feature_type is "binary-features" and X contains values other than 0 and 1.
68
+ """
69
+ if not isinstance(X, (np.ndarray, list)):
70
+ raise TypeError("X is not an ndarray or list")
71
+ if expected != len(X[0]):
72
+ raise FeatureDimensionMismatch(
73
+ expected,
74
+ len(X[0]),
75
+ "X"
76
+ )
77
+
78
+ if feature_type != "binary-features":
79
+ return
80
+
81
+ # Checks if matrix X contains only binary samples. Otherwise, raises an exception.
82
+ if not np.isin(X, [0, 1]).all():
83
+ raise ValueError(
84
+ "The array X contains values that are not composed only of 0 and 1."
85
+ )
86
+
87
+ @staticmethod
88
+ def _generate_random_antibodies(
89
+ n_samples: int,
90
+ n_features: int,
91
+ feature_type: FeatureType = "continuous-features",
92
+ bounds: Optional[npt.NDArray[np.float64]] = None
93
+ ) -> npt.NDArray:
94
+ """
95
+ Generate a random antibody population.
96
+
97
+ Parameters
98
+ ----------
99
+ n_samples : int
100
+ Number of antibodies (samples) to generate.
101
+ n_features : int
102
+ Number of features (dimensions) for each antibody.
103
+ feature_type : FeatureType, default="continuous-features"
104
+ Specifies the type of features: "continuous-features", "binary-features",
105
+ or "ranged-features".
106
+ bounds : np.ndarray
107
+ Array (n_features, 2) with min and max per dimension.
108
+
109
+ Returns
110
+ -------
111
+ npt.NDArray
112
+ Array of shape (n_samples, n_features) containing the generated antibodies.
113
+ Data type depends on the feature_type type (float for continuous/ranged, bool for
114
+ binary).
115
+ """
116
+ if n_features <= 0:
117
+ raise ValueError("Number of features must be greater than zero.")
118
+
119
+ if feature_type == "binary-features":
120
+ return np.random.randint(0, 2, size=(n_samples, n_features)).astype(np.bool_)
121
+ if feature_type == "ranged-features" and bounds is not None:
122
+ return np.random.uniform(low=bounds[0], high=bounds[1], size=(n_samples, n_features))
123
+
124
+ return np.random.random_sample(size=(n_samples, n_features))
aisp/nsa/_base.py CHANGED
@@ -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 = None,
24
- y: npt.NDArray = None,
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 = None,
70
+ X: npt.NDArray,
71
71
  expected: int = 0,
72
72
  _class_: Literal["RNSA", "BNSA"] = "RNSA",
73
73
  ) -> None:
@@ -1,11 +1,12 @@
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
7
7
  import numpy.typing as npt
8
8
 
9
+ from ..base import set_seed_numba
9
10
  from ._ns_core import (
10
11
  check_detector_bnsa_validity,
11
12
  bnsa_class_prediction,
@@ -90,14 +91,15 @@ class RNSA(BaseNSA):
90
91
  k: int = 1,
91
92
  metric: Literal["manhattan", "minkowski", "euclidean"] = "euclidean",
92
93
  max_discards: int = 1000,
93
- seed: int = None,
94
+ seed: Optional[int] = None,
94
95
  algorithm: Literal["default-NSA", "V-detector"] = "default-NSA",
95
- **kwargs: Dict[str, Union[bool, str, float]],
96
+ **kwargs: Any,
96
97
  ):
97
- self.metric = sanitize_choice(metric, ["manhattan", "minkowski"], "euclidean")
98
- self.seed = sanitize_seed(seed)
98
+ self.metric: str = sanitize_choice(metric, ["manhattan", "minkowski"], "euclidean")
99
+ self.seed: Optional[int] = sanitize_seed(seed)
99
100
  if self.seed is not None:
100
101
  np.random.seed(seed)
102
+ set_seed_numba(self.seed)
101
103
  self.k: int = sanitize_param(k, 1, lambda x: x > 1)
102
104
  self.N: int = sanitize_param(N, 100, lambda x: x >= 1)
103
105
  self.r: float = sanitize_param(r, 0.05, lambda x: x > 0)
@@ -108,20 +110,20 @@ class RNSA(BaseNSA):
108
110
  self.max_discards: int = sanitize_param(max_discards, 1000, lambda x: x > 0)
109
111
 
110
112
  # Retrieves the variables from kwargs.
111
- self.p: float = kwargs.get("p", 2)
112
- self.cell_bounds: bool = kwargs.get("cell_bounds", False)
113
- self.non_self_label: str = kwargs.get("non_self_label", "non-self")
113
+ self.p: np.float64 = np.float64(kwargs.get("p", 2))
114
+ self.cell_bounds: bool = bool(kwargs.get("cell_bounds", False))
115
+ self.non_self_label: str = str(kwargs.get("non_self_label", "non-self"))
114
116
 
115
117
  # Initializes the other class variables as None.
116
118
  self._detectors: Union[dict, None] = None
117
- self.classes: npt.NDArray = None
119
+ self.classes: Union[npt.NDArray, list] = []
118
120
 
119
121
  @property
120
- def detectors(self) -> Dict[str, list[Detector]]:
122
+ def detectors(self) -> Optional[Dict[str, list[Detector]]]:
121
123
  """Returns the trained detectors, organized by class."""
122
124
  return self._detectors
123
125
 
124
- def fit(self, X: npt.NDArray, y: npt.NDArray, verbose: bool = True):
126
+ def fit(self, X: npt.NDArray, y: npt.NDArray, verbose: bool = True) -> "RNSA":
125
127
  """
126
128
  Perform training according to X and y, using the negative selection method (NegativeSelect).
127
129
 
@@ -148,7 +150,6 @@ class RNSA(BaseNSA):
148
150
  self : RNSA
149
151
  Returns the instance itself.
150
152
  """
151
- progress = None
152
153
  super()._check_and_raise_exceptions_fit(X, y)
153
154
 
154
155
  # Identifying the possible classes within the output array `y`.
@@ -158,22 +159,21 @@ class RNSA(BaseNSA):
158
159
  # Separates the classes for training.
159
160
  sample_index = self._slice_index_list_by_class(y)
160
161
  # Progress bar for generating all detectors.
161
- if verbose:
162
- progress = tqdm(
163
- total=int(self.N * (len(self.classes))),
164
- bar_format="{desc} ┇{bar}┇ {n}/{total} detectors",
165
- postfix="\n",
166
- )
162
+ progress = tqdm(
163
+ total=int(self.N * (len(self.classes))),
164
+ bar_format="{desc} ┇{bar}┇ {n}/{total} detectors",
165
+ postfix="\n",
166
+ disable=not verbose
167
+ )
167
168
  for _class_ in self.classes:
168
169
  # Initializes the empty set that will contain the valid detectors.
169
170
  valid_detectors_set = []
170
171
  discard_count = 0
171
172
  x_class = X[sample_index[_class_]]
172
173
  # Indicating which class the algorithm is currently processing for the progress bar.
173
- if verbose:
174
- progress.set_description_str(
175
- f"Generating the detectors for the {_class_} class:"
176
- )
174
+ progress.set_description_str(
175
+ f"Generating the detectors for the {_class_} class:"
176
+ )
177
177
  while len(valid_detectors_set) < self.N:
178
178
  # Generates a candidate detector vector randomly with values between 0 and 1.
179
179
  vector_x = np.random.random_sample(size=X.shape[1])
@@ -183,12 +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
- radius = (
187
- valid_detector[1] if self.algorithm == "V-detector" else None
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
- progress.update(1)
191
+ progress.update(1)
192
192
  else:
193
193
  discard_count += 1
194
194
  if discard_count == self.max_discards:
@@ -197,11 +197,11 @@ class RNSA(BaseNSA):
197
197
  # Add detectors, with classes as keys in the dictionary.
198
198
  list_detectors_by_class[_class_] = valid_detectors_set
199
199
  # Notify completion of detector generation for the classes.
200
- if verbose:
201
- progress.set_description(
202
- f"\033[92m✔ Non-self detectors for classes ({', '.join(map(str, self.classes))}) "
203
- f"successfully generated\033[0m"
204
- )
200
+ progress.set_description(
201
+ f"\033[92m✔ Non-self detectors for classes ({', '.join(map(str, self.classes))}) "
202
+ f"successfully generated\033[0m"
203
+ )
204
+ progress.close()
205
205
  # Saves the found detectors in the attribute for the non-self detectors of the trained model
206
206
  self._detectors = list_detectors_by_class
207
207
  return self
@@ -258,9 +258,7 @@ class RNSA(BaseNSA):
258
258
  elif not class_found:
259
259
  average_distance: dict = {}
260
260
  for _class_ in self.classes:
261
- detectores = list(
262
- map(lambda x: x.position, self._detectors[_class_])
263
- )
261
+ detectores = [x.position for x in self._detectors[_class_]]
264
262
  average_distance[_class_] = np.average(
265
263
  [self.__distance(detector, line) for detector in detectores]
266
264
  )
@@ -291,17 +289,17 @@ class RNSA(BaseNSA):
291
289
  # If self.k > 1, uses the k nearest neighbors (kNN); otherwise, checks the detector
292
290
  # without considering kNN.
293
291
  if self.k > 1:
294
- knn_list = []
292
+ knn_list: list = []
295
293
  for x in x_class:
296
294
  # Calculates the distance between the two vectors and adds it to the kNN list if
297
295
  # the distance is smaller than the largest distance in the list.
298
- knn_list = self.__compare_knearest_neighbors_list(
296
+ self.__compare_knearest_neighbors_list(
299
297
  knn_list, self.__distance(x, vector_x)
300
298
  )
301
299
  # If the average of the distances in the kNN list is less than the radius, Returns true.
302
300
  distance_mean = np.mean(knn_list)
303
301
  if self.algorithm == "V-detector":
304
- return self.__detector_is_valid_to_vdetector(distance_mean, vector_x)
302
+ return self.__detector_is_valid_to_vdetector(float(distance_mean), vector_x)
305
303
  if distance_mean > (self.r + self.r_s):
306
304
  return True
307
305
  else:
@@ -323,8 +321,8 @@ class RNSA(BaseNSA):
323
321
  return False # Detector is not valid!
324
322
 
325
323
  def __compare_knearest_neighbors_list(
326
- self, knn: npt.NDArray, distance: float
327
- ) -> npt.NDArray:
324
+ self, knn: list, distance: float
325
+ ) -> None:
328
326
  """
329
327
  Compare the k-nearest neighbor distance at position k=1 in the list knn.
330
328
 
@@ -336,17 +334,11 @@ class RNSA(BaseNSA):
336
334
  List of k-nearest neighbor distances.
337
335
  distance : float
338
336
  Distance to check.
339
-
340
- Returns
341
- -------
342
- knn : npt.NDArray
343
- Updated and sorted nearest neighbor list.
344
337
  """
345
338
  # If the number of distances in kNN is less than k, adds the distance.
346
339
  if len(knn) < self.k:
347
- knn = np.append(knn, distance)
340
+ knn.append(distance)
348
341
  knn.sort()
349
- return knn
350
342
 
351
343
  # Otherwise, add the distance if the new distance is smaller than the largest
352
344
  # distance in the list.
@@ -354,8 +346,6 @@ class RNSA(BaseNSA):
354
346
  knn[self.k - 1] = distance
355
347
  knn.sort()
356
348
 
357
- return knn
358
-
359
349
  def __compare_sample_to_detectors(self, line: npt.NDArray) -> Optional[str]:
360
350
  """
361
351
  Compare a sample with the detectors, verifying if the sample is proper.
@@ -371,6 +361,9 @@ class RNSA(BaseNSA):
371
361
  Returns the predicted class with the detectors or None if the sample does not qualify
372
362
  for any class.
373
363
  """
364
+ if self._detectors is None:
365
+ return None
366
+
374
367
  # List to store the classes and the average distance between the detectors and the sample.
375
368
  possible_classes = []
376
369
  for _class_ in self.classes:
@@ -453,7 +446,7 @@ class RNSA(BaseNSA):
453
446
  if (p - new_detector_r) < 0 or (p + new_detector_r) > 1:
454
447
  return False
455
448
 
456
- return (True, new_detector_r)
449
+ return True, new_detector_r
457
450
 
458
451
 
459
452
  class BNSA(BaseNSA):
@@ -491,7 +484,7 @@ class BNSA(BaseNSA):
491
484
  N: int = 100,
492
485
  aff_thresh: float = 0.1,
493
486
  max_discards: int = 1000,
494
- seed: int = None,
487
+ seed: Optional[int] = None,
495
488
  no_label_sample_selection: Literal[
496
489
  "max_average_difference", "max_nearest_difference"
497
490
  ] = "max_average_difference",
@@ -500,27 +493,27 @@ class BNSA(BaseNSA):
500
493
  self.aff_thresh: float = sanitize_param(aff_thresh, 0.1, lambda x: 0 < x < 1)
501
494
  self.max_discards: float = sanitize_param(max_discards, 1000, lambda x: x > 0)
502
495
 
503
- self.seed = sanitize_seed(seed)
496
+ self.seed: Optional[int] = sanitize_seed(seed)
504
497
 
505
498
  if self.seed is not None:
506
499
  np.random.seed(seed)
507
500
 
508
- self.no_label_sample_selection: float = sanitize_param(
501
+ self.no_label_sample_selection: str = sanitize_param(
509
502
  no_label_sample_selection,
510
503
  "max_average_difference",
511
504
  lambda x: x == "nearest_difference",
512
505
  )
513
506
 
514
- self.classes: npt.NDArray = None
507
+ self.classes: Union[npt.NDArray, list] = []
515
508
  self._detectors: Optional[dict] = None
516
- self._detectors_stack: npt.NDArray = None
509
+ self._detectors_stack: Optional[npt.NDArray] = None
517
510
 
518
511
  @property
519
- def detectors(self) -> Dict[str, npt.NDArray[np.bool_]]:
512
+ def detectors(self) -> Optional[Dict[str, npt.NDArray[np.bool_]]]:
520
513
  """Returns the trained detectors, organized by class."""
521
514
  return self._detectors
522
515
 
523
- def fit(self, X: npt.NDArray, y: npt.NDArray, verbose: bool = True):
516
+ def fit(self, X: npt.NDArray, y: npt.NDArray, verbose: bool = True) -> "BNSA":
524
517
  """Training according to X and y, using the method negative selection method.
525
518
 
526
519
  Parameters
@@ -539,7 +532,6 @@ class BNSA(BaseNSA):
539
532
  Returns the instance it self.
540
533
  """
541
534
  super()._check_and_raise_exceptions_fit(X, y, "BNSA")
542
-
543
535
  # Converts the entire array X to boolean
544
536
  X = X.astype(np.bool_)
545
537
 
@@ -550,22 +542,22 @@ class BNSA(BaseNSA):
550
542
  # Separates the classes for training.
551
543
  sample_index: dict = self._slice_index_list_by_class(y)
552
544
  # Progress bar for generating all detectors.
553
- if verbose:
554
- progress = tqdm(
555
- total=int(self.N * (len(self.classes))),
556
- bar_format="{desc} ┇{bar}┇ {n}/{total} detectors",
557
- postfix="\n",
558
- )
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
+ )
559
552
 
560
553
  for _class_ in self.classes:
561
554
  # Initializes the empty set that will contain the valid detectors.
562
555
  valid_detectors_set: list = []
563
556
  discard_count: int = 0
564
557
  # Updating the progress bar with the current class the algorithm is processing.
565
- if verbose:
566
- progress.set_description_str(
567
- f"Generating the detectors for the {_class_} class:"
568
- )
558
+ progress.set_description_str(
559
+ f"Generating the detectors for the {_class_} class:"
560
+ )
569
561
  x_class = X[sample_index[_class_]]
570
562
  while len(valid_detectors_set) < self.N:
571
563
  # Generates a candidate detector vector randomly with values 0 and 1.
@@ -574,8 +566,7 @@ class BNSA(BaseNSA):
574
566
  if check_detector_bnsa_validity(x_class, vector_x, self.aff_thresh):
575
567
  discard_count = 0
576
568
  valid_detectors_set.append(vector_x)
577
- if verbose:
578
- progress.update(1)
569
+ progress.update(1)
579
570
  else:
580
571
  discard_count += 1
581
572
  if discard_count == self.max_discards:
@@ -585,11 +576,11 @@ class BNSA(BaseNSA):
585
576
  list_detectors_by_class[_class_] = np.array(valid_detectors_set)
586
577
 
587
578
  # Notify the completion of detector generation for the classes.
588
- if verbose:
589
- progress.set_description(
590
- f"\033[92m✔ Non-self detectors for classes ({', '.join(map(str, self.classes))}) "
591
- f"successfully generated\033[0m"
592
- )
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()
593
584
  # Saves the found detectors in the attribute for the class detectors.
594
585
  self._detectors = list_detectors_by_class
595
586
  self._detectors_stack = np.array(
@@ -613,7 +604,7 @@ class BNSA(BaseNSA):
613
604
  ``X``. Returns``None``: If there are no detectors for the prediction.
614
605
  """
615
606
  # If there are no detectors, Returns None.
616
- if self._detectors is None:
607
+ if self._detectors is None or self._detectors_stack is None:
617
608
  return None
618
609
 
619
610
  super()._check_and_raise_exceptions_predict(
@@ -664,6 +655,9 @@ class BNSA(BaseNSA):
664
655
  c : list
665
656
  List of predictions to be updated with the new classification.
666
657
  """
658
+ if self._detectors is None:
659
+ raise ValueError("Detectors is not initialized.")
660
+
667
661
  class_differences: dict = {}
668
662
  for _class_ in self.classes:
669
663
  distances = np.sum(line != self._detectors[_class_]) / self.N
aisp/utils/distance.py CHANGED
@@ -5,10 +5,10 @@ import numpy.typing as npt
5
5
  from numba import njit, types
6
6
  from numpy import float64
7
7
 
8
- EUCLIDEAN = 0
9
- MANHATTAN = 1
10
- MINKOWSKI = 2
11
- HAMMING = 3
8
+ EUCLIDEAN: int = 0
9
+ MANHATTAN: int = 1
10
+ MINKOWSKI: int = 2
11
+ HAMMING: int = 3
12
12
 
13
13
 
14
14
  @njit([(types.boolean[:], types.boolean[:])], cache=True)
aisp/utils/types.py ADDED
@@ -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 features: boolean values or integers restricted to 0 and 1.
18
+ - Continuous features: floating-point values in the normalized range [0.0, 1.0].
19
+ - Ranged features: floating-point values outside the normalized range.
20
+
21
+ Parameters
22
+ ----------
23
+ vector: npt.NDArray
24
+ An array containing the data to be classified.
25
+
26
+ Returns
27
+ -------
28
+ str
29
+ The data type of the vector: "binary-features", "continuous-features", or "ranged-features".
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.0
3
+ Version: 0.3.0
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>
@@ -79,10 +84,11 @@ Artificial Immune Systems (AIS) are inspired by the vertebrate immune system, cr
79
84
  ##### Algorithms implemented:
80
85
 
81
86
  > - [x] [**Negative Selection.**](https://ais-package.github.io/docs/aisp-techniques/Negative%20Selection/)
82
- > - [x] **Clonal Selection Algorithms.**
83
- > * [AIRS - Artificial Immune Recognition System](https://ais-package.github.io/docs/aisp-techniques/Clonal%20Selection%20Algorithms/)
84
- > - [ ] *Dendritic Cells.*
85
- > - [ ] *Immune Network Theory.*
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/)
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)
86
92
 
87
93
  </section>
88
94
 
@@ -0,0 +1,30 @@
1
+ aisp/__init__.py,sha256=GUbFSuDKAfnn80xxK4giPZxuAakRl5dHC5gdp_WTJU0,111
2
+ aisp/exceptions.py,sha256=I9JaQx6p8Jo7qjwwcrqnuewQgyBdUnOSSZofPoBeDNE,1954
3
+ aisp/base/__init__.py,sha256=cDDN6YcYqSU080AvEankhUtIALkSTVmm6XTa48htHjU,211
4
+ aisp/base/_base.py,sha256=uTVh__hQJGe8RCOzCet4ZV3vQbwgj5fXAt4Jdf0x1r0,1792
5
+ aisp/base/_classifier.py,sha256=yGxRGhJxN8jIpr6S7_fsaEOCMPFws8rNCF4E-hYs37E,3339
6
+ aisp/base/_clusterer.py,sha256=VKwhX8oHMc7ylsSu2jnbw3uar3GHc2AMSNPMEmwrPo0,2432
7
+ aisp/base/mutation.py,sha256=A_AlGp8S4ooFEMW3Jgv0n0Y6tbhfusaMMWFsoH4HmD8,4762
8
+ aisp/csa/__init__.py,sha256=708jwpqia10bqmh-4-_srwwNuBh7jf2Zix-u8Hfbzmk,348
9
+ aisp/csa/_ai_recognition_sys.py,sha256=_niEark6HNsu9ognXussura16KeCw4mi3xU4Xm18hQo,18760
10
+ aisp/csa/_base.py,sha256=jR1IIhGINn7DLo8V5iJinDn-wW-t6etcE39bAZnQylw,3595
11
+ aisp/csa/_cell.py,sha256=GUxnzvPyIbBm1YYkMhSx0tcV_oyDhJ7wAo5gtr_1CoY,1845
12
+ aisp/ina/__init__.py,sha256=cOnxGcxrBdg6lLv2w2sdlToMahKMh_Gw57AfUUPQjMo,329
13
+ aisp/ina/_ai_network.py,sha256=aNXNWdFvgmjqki7-lWApLZWq5w1OVUuZpgxsnluiqNE,21053
14
+ aisp/ina/_base.py,sha256=x9eFUKiAcXSfwqVyBVmS54FDeIcApEtFPGruZvwQOwQ,4404
15
+ aisp/nsa/__init__.py,sha256=3cXuBmO-_Dp3-8ZG3Eu8e_bD1JDb-RH4Wu0UDNVD1bs,385
16
+ aisp/nsa/_base.py,sha256=3YKlZzA3yhP2uQHfhyKswbHUutlxkOR4wn6N10nSO-w,4119
17
+ aisp/nsa/_negative_selection.py,sha256=4FA0fwGVHpSsParsUUdNnnv0FYtJS4_olZBWWiPODk8,28153
18
+ aisp/nsa/_ns_core.py,sha256=SXkZL-p2VQygU4Pf6J5AP_yPzU4cR6aU6wx-e_vlm-c,5021
19
+ aisp/utils/__init__.py,sha256=RzpKhkg8nCZi4G0C4il97f3ESYs7Bbxq6EjTeOQQUGk,195
20
+ aisp/utils/_multiclass.py,sha256=nWd58ayVfxgdopBQc9b_xywkolJ2fGW3AN-JoD2A9Fw,1134
21
+ aisp/utils/distance.py,sha256=pY23YGZpu6qVCCkZfhaEpRazUULfVUy2piyzYuAryN0,6576
22
+ aisp/utils/metrics.py,sha256=zDAScDbHRnfu24alRcZ6fEIUaWNoCD-QCtOCFBWPPo8,1277
23
+ aisp/utils/sanitizers.py,sha256=u1GizdJ-RKfPWJLnuFiM09lpItZMhDR_EvK8YdVHwDk,1858
24
+ aisp/utils/types.py,sha256=KELzr1kSBT7hHdsABoIS1xmEBGj6gRSH5A5YNG36I_c,1324
25
+ aisp/utils/validation.py,sha256=RqcS2VdFXkNcOH_7Y3yPi7FBoGWR_ReLBPDBx0UMCqI,1431
26
+ aisp-0.3.0.dist-info/licenses/LICENSE,sha256=fTqV5eBpeAZO0_jit8j4Ref9ikBSlHJ8xwj5TLg7gFk,7817
27
+ aisp-0.3.0.dist-info/METADATA,sha256=hYtREi4OT36M5B0LgIKnnrzdQ1SZKwzYNdpIT-O7Hwg,5173
28
+ aisp-0.3.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
29
+ aisp-0.3.0.dist-info/top_level.txt,sha256=Q5aJi_rAVT5UNS1As0ZafoyS5dwNibnoyOYV7RWUB9s,5
30
+ aisp-0.3.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.8.0)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5