aisp 0.3.2__tar.gz → 0.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. {aisp-0.3.2 → aisp-0.4.0}/PKG-INFO +2 -1
  2. {aisp-0.3.2 → aisp-0.4.0}/README.md +1 -0
  3. aisp-0.4.0/aisp/__init__.py +39 -0
  4. aisp-0.4.0/aisp/base/__init__.py +26 -0
  5. {aisp-0.3.2 → aisp-0.4.0}/aisp/base/_classifier.py +4 -2
  6. aisp-0.4.0/aisp/base/_optimizer.py +188 -0
  7. {aisp-0.3.2 → aisp-0.4.0}/aisp/base/mutation.py +86 -18
  8. aisp-0.4.0/aisp/base/populations.py +49 -0
  9. aisp-0.4.0/aisp/csa/__init__.py +20 -0
  10. {aisp-0.3.2 → aisp-0.4.0}/aisp/csa/_ai_recognition_sys.py +10 -8
  11. {aisp-0.3.2 → aisp-0.4.0}/aisp/csa/_cell.py +2 -2
  12. aisp-0.4.0/aisp/csa/_clonalg.py +369 -0
  13. {aisp-0.3.2 → aisp-0.4.0}/aisp/ina/__init__.py +2 -2
  14. {aisp-0.3.2 → aisp-0.4.0}/aisp/ina/_ai_network.py +18 -9
  15. {aisp-0.3.2 → aisp-0.4.0}/aisp/ina/_base.py +0 -40
  16. {aisp-0.3.2 → aisp-0.4.0}/aisp/nsa/__init__.py +7 -0
  17. {aisp-0.3.2 → aisp-0.4.0}/aisp/nsa/_binary_negative_selection.py +1 -1
  18. {aisp-0.3.2 → aisp-0.4.0}/aisp/nsa/_negative_selection.py +4 -4
  19. aisp-0.4.0/aisp/utils/display.py +185 -0
  20. {aisp-0.3.2 → aisp-0.4.0}/aisp/utils/distance.py +4 -4
  21. aisp-0.4.0/aisp/utils/sanitizers.py +116 -0
  22. {aisp-0.3.2 → aisp-0.4.0}/aisp/utils/types.py +13 -5
  23. {aisp-0.3.2 → aisp-0.4.0}/aisp.egg-info/PKG-INFO +2 -1
  24. {aisp-0.3.2 → aisp-0.4.0}/aisp.egg-info/SOURCES.txt +4 -0
  25. {aisp-0.3.2 → aisp-0.4.0}/pyproject.toml +1 -1
  26. aisp-0.3.2/aisp/__init__.py +0 -26
  27. aisp-0.3.2/aisp/base/__init__.py +0 -7
  28. aisp-0.3.2/aisp/csa/__init__.py +0 -9
  29. aisp-0.3.2/aisp/utils/sanitizers.py +0 -64
  30. {aisp-0.3.2 → aisp-0.4.0}/LICENSE +0 -0
  31. {aisp-0.3.2 → aisp-0.4.0}/aisp/base/_base.py +0 -0
  32. {aisp-0.3.2 → aisp-0.4.0}/aisp/base/_clusterer.py +0 -0
  33. {aisp-0.3.2 → aisp-0.4.0}/aisp/csa/_base.py +0 -0
  34. {aisp-0.3.2 → aisp-0.4.0}/aisp/exceptions.py +0 -0
  35. {aisp-0.3.2 → aisp-0.4.0}/aisp/nsa/_base.py +0 -0
  36. {aisp-0.3.2 → aisp-0.4.0}/aisp/nsa/_ns_core.py +0 -0
  37. {aisp-0.3.2 → aisp-0.4.0}/aisp/utils/__init__.py +0 -0
  38. {aisp-0.3.2 → aisp-0.4.0}/aisp/utils/_multiclass.py +0 -0
  39. {aisp-0.3.2 → aisp-0.4.0}/aisp/utils/metrics.py +0 -0
  40. {aisp-0.3.2 → aisp-0.4.0}/aisp/utils/validation.py +0 -0
  41. {aisp-0.3.2 → aisp-0.4.0}/aisp.egg-info/dependency_links.txt +0 -0
  42. {aisp-0.3.2 → aisp-0.4.0}/aisp.egg-info/requires.txt +0 -0
  43. {aisp-0.3.2 → aisp-0.4.0}/aisp.egg-info/top_level.txt +0 -0
  44. {aisp-0.3.2 → aisp-0.4.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aisp
3
- Version: 0.3.2
3
+ Version: 0.4.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>
@@ -86,6 +86,7 @@ Artificial Immune Systems (AIS) are inspired by the vertebrate immune system, cr
86
86
  > - [x] [**Negative Selection.**](https://ais-package.github.io/docs/aisp-techniques/negative-selection/)
87
87
  > - [x] [**Clonal Selection Algorithms.**](https://ais-package.github.io/docs/aisp-techniques/clonal-selection-algorithms/)
88
88
  > * [AIRS - Artificial Immune Recognition System](https://ais-package.github.io/docs/aisp-techniques/clonal-selection-algorithms/airs/)
89
+ > * [CLONALG - Clonal Selection Algorithm](https://ais-package.github.io/docs/aisp-techniques/clonal-selection-algorithms/clonalg)
89
90
  > - [ ] *Danger Theory.*
90
91
  > - [x] [*Immune Network Theory.*](https://ais-package.github.io/docs/aisp-techniques/immune-network-theory/)
91
92
  > - [AiNet - Artificial Immune Network para Clustering and Compression](https://ais-package.github.io/docs/aisp-techniques/immune-network-theory/ainet)
@@ -51,6 +51,7 @@ Artificial Immune Systems (AIS) are inspired by the vertebrate immune system, cr
51
51
  > - [x] [**Negative Selection.**](https://ais-package.github.io/docs/aisp-techniques/negative-selection/)
52
52
  > - [x] [**Clonal Selection Algorithms.**](https://ais-package.github.io/docs/aisp-techniques/clonal-selection-algorithms/)
53
53
  > * [AIRS - Artificial Immune Recognition System](https://ais-package.github.io/docs/aisp-techniques/clonal-selection-algorithms/airs/)
54
+ > * [CLONALG - Clonal Selection Algorithm](https://ais-package.github.io/docs/aisp-techniques/clonal-selection-algorithms/clonalg)
54
55
  > - [ ] *Danger Theory.*
55
56
  > - [x] [*Immune Network Theory.*](https://ais-package.github.io/docs/aisp-techniques/immune-network-theory/)
56
57
  > - [AiNet - Artificial Immune Network para Clustering and Compression](https://ais-package.github.io/docs/aisp-techniques/immune-network-theory/ainet)
@@ -0,0 +1,39 @@
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
+
9
+ Modules
10
+ -------
11
+ csa : Clonal Selection Algorithms.
12
+ Inspired by the processes of antibody proliferation and mutation.
13
+ - AIRS: Artificial Immune Recognition System for classification.
14
+ - Clonalg: Clonal Selection Algorithm for optimization.
15
+
16
+ nsa : Negative Selection Algorithms
17
+ Simulates T cell maturation and is capable of detecting non-self cells.
18
+ - RNSA: Real-value Negative Selection Algorithm for classification.
19
+ - BNSA: Binary Negative Selection Algorithm for classification.
20
+
21
+ ina : Immune Network Algorithms
22
+ Based on immune network theory.
23
+ - AiNet: Artificial Immune Network for clustering.
24
+
25
+ For detailed documentation and examples, visit:
26
+ https://ais-package.github.io/docs/intro
27
+ """
28
+
29
+ from . import csa
30
+ from . import ina
31
+ from . import nsa
32
+
33
+ __author__ = "AISP Development Team"
34
+ __version__ = "0.4.0"
35
+ __all__ = [
36
+ 'csa',
37
+ 'nsa',
38
+ 'ina'
39
+ ]
@@ -0,0 +1,26 @@
1
+ """Base Classes and Core Utilities.
2
+
3
+ This module provides the fundamental classes and utilities that serve as the basis for all
4
+ Artificial Immune Systems algorithms implemented in the AISP package.
5
+
6
+ Classes
7
+ -------
8
+ BaseClassifier
9
+ Abstract Base Class for Classification Algorithms.
10
+ BaseClusterer
11
+ Abstract Base Class for Clustering Algorithms.
12
+ BaseOptimizer
13
+ Abstract Base Class for optimization algorithms.
14
+
15
+ Functions
16
+ ---------
17
+ set_seed_numba
18
+ Set Random Seed for Numba JIT Compilation.
19
+ """
20
+
21
+ from ._base import set_seed_numba
22
+ from ._classifier import BaseClassifier
23
+ from ._clusterer import BaseClusterer
24
+ from ._optimizer import BaseOptimizer
25
+
26
+ __all__ = ['BaseClassifier', 'BaseClusterer', 'BaseOptimizer', 'set_seed_numba']
@@ -13,9 +13,11 @@ from ..utils.metrics import accuracy_score
13
13
 
14
14
 
15
15
  class BaseClassifier(ABC, Base):
16
- """Base class for classification algorithms.
16
+ """Abstract base class for classification algorithms.
17
17
 
18
- Defines the abstract methods ``fit`` and ``predict``, and implements the ``score`` method.
18
+ This class defines the core interface for classification models. It enforces the
19
+ implementation of the ``fit`` and ``predict`` methods in all derived classes,
20
+ and provides a default implementation of ``score`` and utility functions.
19
21
  """
20
22
 
21
23
  classes: Union[npt.NDArray, list] = []
@@ -0,0 +1,188 @@
1
+ """Base class for optimization algorithms."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from abc import ABC, abstractmethod
6
+ from typing import Optional, List, Any, Callable
7
+
8
+ from ..utils.display import TableFormatter
9
+
10
+
11
+ class BaseOptimizer(ABC):
12
+ """Abstract base class for optimization algorithms.
13
+
14
+ This class defines the core interface for optimization strategies. It keeps track of cost
15
+ history, evaluated solutions, and the best solution found during the optimization process.
16
+ Subclasses must implement ``optimize`` and ``affinity_function``.
17
+ """
18
+
19
+ def __init__(self) -> None:
20
+ self._cost_history: List[float] = []
21
+ self._solution_history: list = []
22
+ self._best_solution: Optional[Any] = None
23
+ self._best_cost: Optional[float] = None
24
+ self._protected_aliases = [
25
+ "__init__",
26
+ "optimize",
27
+ "register",
28
+ "get_report"
29
+ ]
30
+ self.mode = "min"
31
+
32
+ @property
33
+ def cost_history(self) -> List[float]:
34
+ """Return the history of costs during optimization."""
35
+ return self._cost_history
36
+
37
+ @property
38
+ def solution_history(self) -> List:
39
+ """Returns the history of evaluated solutions."""
40
+ return self._solution_history
41
+
42
+ @property
43
+ def best_solution(self) -> Optional[Any]:
44
+ """Return the best solution found so far, or ``None`` if unavailable."""
45
+ return self._best_solution
46
+
47
+ @property
48
+ def best_cost(self) -> Optional[float]:
49
+ """Return the cost of the best solution found so far, or ``None`` if unavailable."""
50
+ return self._best_cost
51
+
52
+ def _record_best(self, cost: float, best_solution: Any) -> None:
53
+ """Record a new cost value and update the best solution if improved.
54
+
55
+ Parameters
56
+ ----------
57
+ cost : float
58
+ Cost value to be added to the history.
59
+ """
60
+ self._solution_history.append(best_solution)
61
+ self._cost_history.append(cost)
62
+ is_better = (
63
+ self._best_cost is None or
64
+ (self.mode == "min" and cost < self._best_cost) or
65
+ (self.mode == "max" and cost > self._best_cost)
66
+ )
67
+ if is_better:
68
+ self._best_solution = best_solution
69
+ self._best_cost = cost
70
+
71
+ def get_report(self) -> str:
72
+ """Generate a formatted summary report of the optimization process.
73
+
74
+ The report includes the best solution, its associated cost, and the evolution of cost
75
+ values per iteration.
76
+
77
+ Returns
78
+ -------
79
+ report : str
80
+ A formatted string containing the optimization summary.
81
+ """
82
+ if not self._cost_history:
83
+ return "Optimization has not been run. The report is empty."
84
+
85
+ header = "\n" + "=" * 45 + "\n"
86
+ report_parts = [
87
+ header,
88
+ f"{'Optimization Summary':^45}",
89
+ header,
90
+ f"Best cost : {self.best_cost}\n",
91
+ f"Best solution : {self.best_solution}\n",
92
+ "Cost History per Iteration:\n"
93
+ ]
94
+ table_formatter = TableFormatter(
95
+ {
96
+ 'Iteration': 12,
97
+ 'Cost': 28
98
+ }
99
+ )
100
+
101
+ report_parts.extend([table_formatter.get_header()])
102
+
103
+ for i, cost in enumerate(self._cost_history, start=1):
104
+ report_parts.append(
105
+ '\n' + table_formatter.get_row(
106
+ {
107
+ 'Iteration': f"{i:>11} ",
108
+ 'Cost': f"{cost:>27.6f} "
109
+ }
110
+ )
111
+ )
112
+
113
+ report_parts.append(table_formatter.get_bottom(True))
114
+ return "".join(report_parts)
115
+
116
+ @abstractmethod
117
+ def optimize(self, max_iters: int = 50, n_iter_no_change=10, verbose: bool = True) -> Any:
118
+ """Execute the optimization process.
119
+
120
+ This abstract method must be implemented by the subclass, defining
121
+ how the optimization strategy explores the search space.
122
+
123
+ Parameters
124
+ ----------
125
+ max_iters : int
126
+ Maximum number of interactions
127
+ n_iter_no_change: int, default=10
128
+ the maximum number of iterations without updating the best
129
+ verbose : bool, default=True
130
+ Flag to enable or disable detailed output during optimization.
131
+
132
+ Returns
133
+ -------
134
+ best_solution : Any
135
+ The best solution found by the optimization algorithm.
136
+ """
137
+
138
+ @abstractmethod
139
+ def affinity_function(self, solution: Any) -> float:
140
+ """Evaluate the affinity of a candidate solution.
141
+
142
+ This abstract method must be implemented by the subclass to define the problem-specific.
143
+
144
+ Parameters
145
+ ----------
146
+ solution : Any
147
+ Candidate solution to be evaluated.
148
+
149
+ Returns
150
+ -------
151
+ cost : float
152
+ Cost value associated with the given solution.
153
+ """
154
+
155
+ def register(self, alias: str, function: Callable[..., Any]) -> None:
156
+ """Register a function dynamically in the optimizer instance.
157
+
158
+ Parameters
159
+ ----------
160
+ alias : str
161
+ Name used to access the function as an attribute.
162
+ function : Callable[..., Any]
163
+ Callable to be registered.
164
+
165
+ Raises
166
+ ------
167
+ TypeError
168
+ If `function` is not callable.
169
+ AttributeError
170
+ If `alias` is protected and cannot be modified. Or if `alias` does not exist in the
171
+ optimizer class.
172
+ """
173
+ if not callable(function):
174
+ raise TypeError(f"Expected a function for '{alias}', got {type(function).__name__}")
175
+ if alias in self._protected_aliases or alias.startswith("_"):
176
+ raise AttributeError(f"The alias '{alias}' is protected and cannot be modified.")
177
+ if not hasattr(self, alias):
178
+ raise AttributeError(
179
+ f"Alias '{alias}' is not a valid method of {self.__class__.__name__}"
180
+ )
181
+ setattr(self, alias, function)
182
+
183
+ def reset(self):
184
+ """Reset the object's internal state, clearing history and resetting values."""
185
+ self._cost_history: List[float] = []
186
+ self._solution_history: list = []
187
+ self._best_solution: Optional[Any] = None
188
+ self._best_cost: Optional[float] = None
@@ -10,10 +10,11 @@ import numpy.typing as npt
10
10
  from numba import njit, types
11
11
 
12
12
 
13
- @njit([(types.float64[:], types.int64)], cache=True)
13
+ @njit([(types.float64[:], types.int64, types.float64)], cache=True)
14
14
  def clone_and_mutate_continuous(
15
15
  vector: npt.NDArray[np.float64],
16
- n: int
16
+ n: int,
17
+ mutation_rate: float
17
18
  ) -> npt.NDArray[np.float64]:
18
19
  """
19
20
  Generate a set of mutated clones from a cell represented by a continuous vector.
@@ -28,6 +29,10 @@ def clone_and_mutate_continuous(
28
29
  The original immune cell with continuous values to be cloned and mutated.
29
30
  n : int
30
31
  The number of mutated clones to be generated.
32
+ mutation_rate : float, default=1
33
+ If 0 <= mutation_rate < 1: probability of mutating each component.
34
+ If mutation_rate >= 1 or mutation_rate <= 0: the mutation randomizes
35
+ a number of components between 1 and len(vector).
31
36
 
32
37
  Returns
33
38
  -------
@@ -37,13 +42,23 @@ def clone_and_mutate_continuous(
37
42
  n_features = vector.shape[0]
38
43
  clone_set = np.empty((n, n_features), dtype=np.float64)
39
44
  for i in range(n):
40
- n_mutations = np.random.randint(1, n_features)
41
45
  clone = vector.copy()
42
- position_mutations = np.random.permutation(n_features)[:n_mutations]
43
- for j in range(n_mutations):
44
- idx = position_mutations[j]
45
- clone[idx] = np.float64(np.random.random())
46
- clone_set[i] = clone
46
+ if 0 <= mutation_rate < 1:
47
+ any_mutation = False
48
+ for j in range(n_features):
49
+ if np.random.random() < mutation_rate:
50
+ clone[j] = np.random.random()
51
+ any_mutation = True
52
+ if not any_mutation:
53
+ idx = np.random.randint(0, n_features)
54
+ clone[idx] = np.random.random()
55
+ else:
56
+ n_mutations = np.random.randint(1, n_features)
57
+ position_mutations = np.random.permutation(n_features)[:n_mutations]
58
+ for j in range(n_mutations):
59
+ idx = position_mutations[j]
60
+ clone[idx] = np.float64(np.random.random())
61
+ clone_set[i] = clone
47
62
 
48
63
  return clone_set
49
64
 
@@ -75,8 +90,8 @@ def clone_and_mutate_binary(
75
90
  n_features = vector.shape[0]
76
91
  clone_set = np.empty((n, n_features), dtype=np.bool_)
77
92
  for i in range(n):
78
- n_mutations = np.random.randint(1, n_features)
79
93
  clone = vector.copy()
94
+ n_mutations = np.random.randint(1, n_features)
80
95
  position_mutations = np.random.permutation(n_features)[:n_mutations]
81
96
  for j in range(n_mutations):
82
97
  idx = position_mutations[j]
@@ -86,11 +101,12 @@ def clone_and_mutate_binary(
86
101
  return clone_set
87
102
 
88
103
 
89
- @njit([(types.float64[:], types.int64, types.float64[:, :])], cache=True)
104
+ @njit([(types.float64[:], types.int64, types.float64[:, :], types.float64)], cache=True)
90
105
  def clone_and_mutate_ranged(
91
106
  vector: npt.NDArray[np.float64],
92
107
  n: int,
93
- bounds: npt.NDArray[np.float64]
108
+ bounds: npt.NDArray[np.float64],
109
+ mutation_rate: float
94
110
  ) -> npt.NDArray[np.float64]:
95
111
  """
96
112
  Generate a set of mutated clones from a cell represented by custom ranges per dimension.
@@ -107,6 +123,10 @@ def clone_and_mutate_ranged(
107
123
  The number of mutated clones to be generated.
108
124
  bounds : np.ndarray
109
125
  Array (n_features, 2) with min and max per dimension.
126
+ mutation_rate : float, default=1
127
+ If 0 <= mutation_rate < 1: probability of mutating each component.
128
+ If mutation_rate >= 1 or mutation_rate <= 0: the mutation randomizes
129
+ a number of components between 1 and len(vector).
110
130
 
111
131
  Returns
112
132
  -------
@@ -117,14 +137,62 @@ def clone_and_mutate_ranged(
117
137
  clone_set = np.empty((n, n_features), dtype=np.float64)
118
138
 
119
139
  for i in range(n):
120
- n_mutations = np.random.randint(1, n_features)
121
140
  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)
141
+ if 0 <= mutation_rate < 1:
142
+ any_mutation = False
143
+ for j in range(n_features):
144
+ if np.random.random() < mutation_rate:
145
+ clone[j] = np.random.uniform(low=bounds[0][j], high=bounds[1][j])
146
+ any_mutation = True
147
+ if not any_mutation:
148
+ idx = np.random.randint(0, n_features)
149
+ clone[idx] = np.random.uniform(low=bounds[0][idx], high=bounds[1][idx])
150
+ else:
151
+ n_mutations = np.random.randint(1, n_features)
152
+ position_mutations = np.random.permutation(n_features)[:n_mutations]
153
+ for j in range(n_mutations):
154
+ idx = position_mutations[j]
155
+ min_limit = bounds[0][idx]
156
+ max_limit = bounds[1][idx]
157
+ clone[idx] = np.random.uniform(low=min_limit, high=max_limit)
158
+ clone_set[i] = clone
159
+
160
+ return clone_set
161
+
162
+
163
+ @njit([(types.int64[:], types.int64, types.float64)], cache=True)
164
+ def clone_and_mutate_permutation(
165
+ vector: npt.NDArray[np.int64],
166
+ n: int,
167
+ mutation_rate: float
168
+ ) -> npt.NDArray[np.int64]:
169
+ """Generate a set of mutated clones by random permutation.
170
+
171
+ Parameters
172
+ ----------
173
+ vector : npt.NDArray[np.int64]
174
+ The original immune cell with permutation values to be cloned and mutated.
175
+ n : int
176
+ The number of mutated clones to be generated.
177
+ mutation_rate : float
178
+ Probability of mutating each component 0 <= mutation_rate < 1.
179
+
180
+ Returns
181
+ -------
182
+ clone_set : npt.NDArray
183
+ An Array(n, len(vector)) containing the `n` mutated clones of the original vector.
184
+ """
185
+ n_features = vector.shape[0]
186
+ clone_set = np.empty((n, n_features), dtype=np.int64)
187
+
188
+ for i in range(n):
189
+ clone = vector.copy()
190
+ for j in range(n_features):
191
+ if np.random.random() < mutation_rate:
192
+ idx = np.random.randint(0, n_features)
193
+ tmp = clone[j]
194
+ clone[j] = clone[idx]
195
+ clone[idx] = tmp
128
196
  clone_set[i] = clone
129
197
 
130
198
  return clone_set
@@ -0,0 +1,49 @@
1
+ """Provide utility functions for generating antibody populations in immunological algorithms."""
2
+
3
+ from typing import Optional
4
+
5
+ import numpy as np
6
+ import numpy.typing as npt
7
+
8
+ from ..utils.types import FeatureTypeAll
9
+
10
+
11
+ def generate_random_antibodies(
12
+ n_samples: int,
13
+ n_features: int,
14
+ feature_type: FeatureTypeAll = "continuous-features",
15
+ bounds: Optional[npt.NDArray[np.float64]] = None
16
+ ) -> npt.NDArray:
17
+ """
18
+ Generate a random antibody population.
19
+
20
+ Parameters
21
+ ----------
22
+ n_samples : int
23
+ Number of antibodies (samples) to generate.
24
+ n_features : int
25
+ Number of features (dimensions) for each antibody.
26
+ feature_type : FeatureType, default="continuous-features"
27
+ Specifies the type of features: "continuous-features", "binary-features",
28
+ "ranged-features", or "permutation-features".
29
+ bounds : np.ndarray
30
+ Array (n_features, 2) with min and max per dimension.
31
+
32
+ Returns
33
+ -------
34
+ npt.NDArray
35
+ Array of shape (n_samples, n_features) containing the generated antibodies.
36
+ """
37
+ if n_features <= 0:
38
+ raise ValueError("Number of features must be greater than zero.")
39
+
40
+ if feature_type == "binary-features":
41
+ return np.random.randint(0, 2, size=(n_samples, n_features)).astype(np.bool_)
42
+ if feature_type == "ranged-features" and bounds is not None:
43
+ return np.random.uniform(low=bounds[0], high=bounds[1], size=(n_samples, n_features))
44
+ if feature_type == "permutation-features":
45
+ return np.array(
46
+ [np.random.permutation(n_features) for _ in range(n_samples)]
47
+ ).astype(dtype=np.int64)
48
+
49
+ return np.random.random_sample(size=(n_samples, n_features))
@@ -0,0 +1,20 @@
1
+ """Module (CSA) Clonal Selection Algorithm.
2
+
3
+ CSAs are inspired by the process of antibody proliferation upon detecting an antigen, during which
4
+ the generated antibodies undergo mutations in an attempt to enhance pathogen recognition.
5
+
6
+ Classes
7
+ -------
8
+ AIRS : Artificial Immune Recognition System.
9
+ A supervised learning algorithm for classification tasks based on the clonal
10
+ selection principle.
11
+ Clonalg : Clonal Selection Algorithm.
12
+ Implementation of the clonal selection algorithm for optimization, adapted
13
+ for both minimization and maximization of cost functions in binary,
14
+ continuous, and permutation problems.
15
+ """
16
+ from ._ai_recognition_sys import AIRS
17
+ from ._clonalg import Clonalg
18
+
19
+ __author__ = 'João Paulo da Silva Barros'
20
+ __all__ = ['AIRS', 'Clonalg']
@@ -111,13 +111,13 @@ class AIRS(BaseAIRS):
111
111
  Way to calculate the distance between the detector and the sample:
112
112
 
113
113
  * ``'Euclidean'`` ➜ The calculation of the distance is given by the expression:
114
- √( (x₁ x₂)² + (y₁ y₂)² + ... + (yn yn)²).
114
+ √( (x₁ - x₂)² + (y₁ - y₂)² + ... + (yn - yn)²).
115
115
 
116
116
  * ``'minkowski'`` ➜ The calculation of the distance is given by the expression:
117
- ( |X₁ Y₁|p + |X₂ Y₂|p + ... + |Xn Yn|p) ¹/ₚ.
117
+ ( |X₁ - Y₁|p + |X₂ - Y₂|p + ... + |Xn - Yn|p) ¹/ₚ.
118
118
 
119
119
  * ``'manhattan'`` ➜ The calculation of the distance is given by the expression:
120
- ( |x₁ x₂| + |y₁ y₂| + ... + |yn yn|).
120
+ ( |x₁ - x₂| + |y₁ - y₂| + ... + |yn - yn|).
121
121
 
122
122
  seed : int
123
123
  Seed for the random generation of detector values. Defaults to None.
@@ -139,7 +139,7 @@ class AIRS(BaseAIRS):
139
139
 
140
140
  References
141
141
  ----------
142
- .. [1] Brabazon, A., ONeill, M., & McGarraghy, S. (2015). Natural Computing Algorithms. In
142
+ .. [1] Brabazon, A., O'Neill, M., & McGarraghy, S. (2015). Natural Computing Algorithms. In
143
143
  Natural Computing Series. Springer Berlin Heidelberg.
144
144
  https://doi.org/10.1007/978-3-662-43631-8
145
145
 
@@ -195,6 +195,7 @@ class AIRS(BaseAIRS):
195
195
  self.affinity_threshold = 0.0
196
196
  self.classes = []
197
197
  self._bounds: Optional[npt.NDArray[np.float64]] = None
198
+ self._n_features: Optional[int] = None
198
199
 
199
200
  @property
200
201
  def cells_memory(self) -> Optional[Dict[str, list[Cell]]]:
@@ -235,6 +236,7 @@ class AIRS(BaseAIRS):
235
236
  self._bounds = np.vstack([np.min(X, axis=0), np.max(X, axis=0)])
236
237
 
237
238
  self.classes = np.unique(y)
239
+ self._n_features = X.shape[1]
238
240
  sample_index = self._slice_index_list_by_class(y)
239
241
  progress = tqdm(
240
242
  total=len(y),
@@ -330,11 +332,11 @@ class AIRS(BaseAIRS):
330
332
  An ndarray of the form ``C`` [``N samples``], containing the predicted classes for
331
333
  ``X``. or ``None``: If there are no detectors for the prediction.
332
334
  """
333
- if self._all_class_cell_vectors is None:
335
+ if self._all_class_cell_vectors is None or self._n_features is None:
334
336
  return None
335
337
 
336
338
  super()._check_and_raise_exceptions_predict(
337
- X, len(self._cells_memory[self.classes[0]][0].vector), self._feature_type
339
+ X, self._n_features, self._feature_type
338
340
  )
339
341
 
340
342
  c: list = []
@@ -380,7 +382,7 @@ class AIRS(BaseAIRS):
380
382
 
381
383
  References
382
384
  ----------
383
- .. [1] Brabazon, A., ONeill, M., & McGarraghy, S. (2015).
385
+ .. [1] Brabazon, A., O'Neill, M., & McGarraghy, S. (2015).
384
386
  Natural Computing Algorithms. Natural Computing Series.
385
387
  Springer Berlin Heidelberg. https://doi.org/10.1007/978-3-662-43631-8
386
388
  """
@@ -441,7 +443,7 @@ class AIRS(BaseAIRS):
441
443
  distances = pdist(antigens_list, metric="hamming")
442
444
  else:
443
445
  metric_kwargs = {'p': self.p} if self.metric == 'minkowski' else {}
444
- distances = pdist(antigens_list, metric=self.metric, **metric_kwargs)
446
+ distances = pdist(antigens_list, metric=self.metric, **metric_kwargs) # type: ignore
445
447
 
446
448
  n = antigens_list.shape[0]
447
449
  sum_affinity = np.sum(1.0 - (distances / (1.0 + distances)))
@@ -53,8 +53,8 @@ class Cell:
53
53
  if feature_type == "binary-features":
54
54
  return clone_and_mutate_binary(self.vector, n)
55
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)
56
+ clone_and_mutate_ranged(self.vector, n, bounds, np.float64(1.0))
57
+ return clone_and_mutate_continuous(self.vector, n, np.float64(1.0))
58
58
 
59
59
  def __eq__(self, other):
60
60
  """Check if two cells are equal."""