pyecsago 0.1.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 (57) hide show
  1. pyecsago-0.1.0/PKG-INFO +182 -0
  2. pyecsago-0.1.0/README.md +160 -0
  3. pyecsago-0.1.0/pyproject.toml +63 -0
  4. pyecsago-0.1.0/src/pyecsago/__init__.py +31 -0
  5. pyecsago-0.1.0/src/pyecsago/core/__init__.py +16 -0
  6. pyecsago-0.1.0/src/pyecsago/core/algorithm.py +46 -0
  7. pyecsago-0.1.0/src/pyecsago/core/context.py +80 -0
  8. pyecsago-0.1.0/src/pyecsago/core/exceptions.py +21 -0
  9. pyecsago-0.1.0/src/pyecsago/core/individual.py +33 -0
  10. pyecsago-0.1.0/src/pyecsago/core/population.py +17 -0
  11. pyecsago-0.1.0/src/pyecsago/implementations/__init__.py +0 -0
  12. pyecsago-0.1.0/src/pyecsago/implementations/ecsago/__init__.py +11 -0
  13. pyecsago-0.1.0/src/pyecsago/implementations/ecsago/algorithm.py +233 -0
  14. pyecsago-0.1.0/src/pyecsago/implementations/ecsago/context.py +184 -0
  15. pyecsago-0.1.0/src/pyecsago/implementations/ecsago/individual.py +129 -0
  16. pyecsago-0.1.0/src/pyecsago/implementations/ecsago/population.py +161 -0
  17. pyecsago-0.1.0/src/pyecsago/strategies/__init__.py +0 -0
  18. pyecsago-0.1.0/src/pyecsago/strategies/context/__init__.py +0 -0
  19. pyecsago-0.1.0/src/pyecsago/strategies/context/factory.py +33 -0
  20. pyecsago-0.1.0/src/pyecsago/strategies/evolution/__init__.py +0 -0
  21. pyecsago-0.1.0/src/pyecsago/strategies/evolution/base.py +20 -0
  22. pyecsago-0.1.0/src/pyecsago/strategies/evolution/ecsago.py +132 -0
  23. pyecsago-0.1.0/src/pyecsago/strategies/evolution/factory.py +78 -0
  24. pyecsago-0.1.0/src/pyecsago/strategies/extraction/__init__.py +0 -0
  25. pyecsago-0.1.0/src/pyecsago/strategies/extraction/base.py +28 -0
  26. pyecsago-0.1.0/src/pyecsago/strategies/extraction/ecsago.py +173 -0
  27. pyecsago-0.1.0/src/pyecsago/strategies/extraction/factory.py +76 -0
  28. pyecsago-0.1.0/src/pyecsago/strategies/fitness/__init__.py +0 -0
  29. pyecsago-0.1.0/src/pyecsago/strategies/fitness/base.py +15 -0
  30. pyecsago-0.1.0/src/pyecsago/strategies/fitness/ecsago.py +289 -0
  31. pyecsago-0.1.0/src/pyecsago/strategies/niching/__init__.py +0 -0
  32. pyecsago-0.1.0/src/pyecsago/strategies/niching/base.py +34 -0
  33. pyecsago-0.1.0/src/pyecsago/strategies/niching/deterministic_crowding.py +67 -0
  34. pyecsago-0.1.0/src/pyecsago/strategies/operators/__init__.py +0 -0
  35. pyecsago-0.1.0/src/pyecsago/strategies/operators/base.py +49 -0
  36. pyecsago-0.1.0/src/pyecsago/strategies/operators/crossover.py +45 -0
  37. pyecsago-0.1.0/src/pyecsago/strategies/operators/genetic_operators.py +58 -0
  38. pyecsago-0.1.0/src/pyecsago/strategies/operators/haea.py +119 -0
  39. pyecsago-0.1.0/src/pyecsago/strategies/operators/mutation.py +68 -0
  40. pyecsago-0.1.0/src/pyecsago/strategies/refinement/__init__.py +0 -0
  41. pyecsago-0.1.0/src/pyecsago/strategies/refinement/base.py +62 -0
  42. pyecsago-0.1.0/src/pyecsago/strategies/refinement/cuda_mde.py +100 -0
  43. pyecsago-0.1.0/src/pyecsago/strategies/refinement/factory.py +34 -0
  44. pyecsago-0.1.0/src/pyecsago/strategies/refinement/mde.py +146 -0
  45. pyecsago-0.1.0/src/pyecsago/utils/__init__.py +0 -0
  46. pyecsago-0.1.0/src/pyecsago/utils/compat.py +45 -0
  47. pyecsago-0.1.0/src/pyecsago/utils/cuda/__init__.py +0 -0
  48. pyecsago-0.1.0/src/pyecsago/utils/cuda/compiler.py +35 -0
  49. pyecsago-0.1.0/src/pyecsago/utils/cuda/config.py +94 -0
  50. pyecsago-0.1.0/src/pyecsago/utils/cuda/context.py +84 -0
  51. pyecsago-0.1.0/src/pyecsago/utils/cuda/kernels.py +254 -0
  52. pyecsago-0.1.0/src/pyecsago/utils/data.py +36 -0
  53. pyecsago-0.1.0/src/pyecsago/utils/data_types.py +187 -0
  54. pyecsago-0.1.0/src/pyecsago/utils/dataviz.py +55 -0
  55. pyecsago-0.1.0/src/pyecsago/utils/funcs.py +85 -0
  56. pyecsago-0.1.0/src/pyecsago/utils/metrics.py +6 -0
  57. pyecsago-0.1.0/src/pyecsago/utils/validators.py +48 -0
@@ -0,0 +1,182 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyecsago
3
+ Version: 0.1.0
4
+ Summary: Python implementation of the evolutionary clustering ECSAGO - Evolutionary Clustering with Self Adaptive Genetic Operators
5
+ Author: Joan Sebastian Tamayo Rivera
6
+ License-Expression: MIT
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Programming Language :: Python :: 3.12
9
+ Classifier: Programming Language :: Python :: 3.13
10
+ Classifier: Programming Language :: Python :: 3.14
11
+ Requires-Dist: numpy>=2.0,<3
12
+ Requires-Dist: matplotlib>=3.9,<4
13
+ Requires-Dist: scikit-learn>=1.5,<2
14
+ Requires-Dist: pandas>=2.2,<3
15
+ Requires-Dist: seaborn>=0.13,<1
16
+ Requires-Dist: scipy>=1.14,<2
17
+ Requires-Dist: tqdm>=4.67,<5
18
+ Requires-Dist: cupy-cuda12x>=13.3.0,<14 ; extra == 'cuda'
19
+ Requires-Python: >=3.12, <4
20
+ Provides-Extra: cuda
21
+ Description-Content-Type: text/markdown
22
+
23
+ # pyecsago
24
+
25
+ Python implementation of **ECSAGO** (Evolutionary Clustering with Self-Adaptive Genetic Operators), a robust evolutionary clustering algorithm that automatically discovers the number of clusters in data while being resistant to noise.
26
+
27
+ ![ECSAGO vs K-Means](docs/ecsago_vs_kmeans.png)
28
+
29
+ ## Features
30
+
31
+ - **Automatic cluster detection** — no need to specify *k* in advance
32
+ - **Noise-robust** density-based fitness function (RBF kernel weights)
33
+ - **Self-adaptive operators** via HAEA (Hybrid Adaptive Evolutionary Algorithm)
34
+ - **Deterministic Crowding** for niche maintenance
35
+ - **MDE refinement** (Maximal Density Estimator) for prototype center/spread convergence
36
+ - **GPU acceleration** — optional CUDA support via CuPy
37
+
38
+ ## Installation
39
+
40
+ CPU only:
41
+
42
+ ```bash
43
+ pip install pyecsago
44
+ ```
45
+
46
+ With CUDA support:
47
+
48
+ ```bash
49
+ pip install pyecsago[cuda]
50
+ ```
51
+
52
+ ### From source
53
+
54
+ ```bash
55
+ git clone https://github.com/pwnaoj/pyecsago
56
+ cd pyecsago
57
+ pip install .
58
+ ```
59
+
60
+ ## Quick start
61
+
62
+ ```python
63
+ import numpy as np
64
+ from pyecsago import ECSAGO
65
+
66
+ # Sample data: 3 Gaussian clusters
67
+ data = np.vstack([
68
+ np.random.randn(100, 2) + [0, 0],
69
+ np.random.randn(100, 2) + [5, 5],
70
+ np.random.randn(100, 2) + [10, 0],
71
+ ])
72
+
73
+ config = {
74
+ "population_size": 100,
75
+ "weight_threshold": 0.3,
76
+ "max_generations": 30,
77
+ "iterations": 10,
78
+ "extraction_type": {2: 0.25}, # PROPORTION_MAX with 25% threshold
79
+ "k": 13.8,
80
+ "use_cuda": False,
81
+ }
82
+
83
+ ecsago = ECSAGO(config)
84
+ results = ecsago.run(data)
85
+
86
+ prototypes = results["refined_prototypes"]
87
+ labels = results["cluster_assignments"]
88
+
89
+ print(f"Clusters found: {len(prototypes)}")
90
+ ```
91
+
92
+ ## Step-by-step usage
93
+
94
+ For finer control you can invoke each stage independently:
95
+
96
+ ```python
97
+ ecsago = ECSAGO(config)
98
+
99
+ # 1. Load data
100
+ ecsago.context.set_data(data)
101
+
102
+ # 2. Evolve the population
103
+ ecsago.evolve()
104
+
105
+ # 3. Extract prototypes
106
+ prototypes = ecsago.extract_prototypes(
107
+ extraction_type={2: 0.25},
108
+ k=13.8,
109
+ )
110
+
111
+ # 4. Refine with MDE
112
+ refined = ecsago.refine_prototypes(
113
+ prototypes=prototypes,
114
+ iterations=10,
115
+ k=13.8,
116
+ )
117
+ ```
118
+
119
+ ## Configuration
120
+
121
+ All parameters are passed as a dictionary to `ECSAGO(config)`.
122
+
123
+ | Parameter | Type | Description |
124
+ |---|---|---|
125
+ | `population_size` | `int` | Number of individuals in the evolutionary population |
126
+ | `weight_threshold` | `float` | Threshold for weight binarization (0–1) |
127
+ | `max_generations` | `int` | Maximum number of evolutionary generations |
128
+ | `iterations` | `int` | Number of MDE refinement iterations |
129
+ | `extraction_type` | `dict` | Extraction method — key is the type (0–4), value is the threshold |
130
+ | `k` | `float` | Chi-squared factor for minimum inter-prototype distance |
131
+ | `use_cuda` | `bool` | Enable CUDA/GPU acceleration via CuPy |
132
+
133
+ ### Extraction types
134
+
135
+ | Key | Method | Threshold meaning |
136
+ |---|---|---|
137
+ | 0 | `ABSOLUTE_VALUE` | Absolute fitness threshold (auto-calculated) |
138
+ | 1 | `PROPORTION_AVG` | Proportion of average fitness |
139
+ | 2 | `PROPORTION_MAX` | Proportion of maximum fitness |
140
+ | 3 | `PROPORTION_MEDIAN` | Proportion of median fitness |
141
+ | 4 | `MINIMUM_DENSITY` | Based on minimum density |
142
+
143
+ ## Output
144
+
145
+ `ecsago.run(data)` returns a dictionary:
146
+
147
+ | Key | Description |
148
+ |---|---|
149
+ | `final_population` | Full evolved population |
150
+ | `prototypes` | Extracted prototypes (before refinement) |
151
+ | `refined_prototypes` | Refined prototypes (after MDE) |
152
+ | `cluster_assignments` | Cluster label for each data point |
153
+
154
+ Each prototype is an `ECSAGOIndividual` with attributes `genome` (center), `sigma2` (spread), and `fitness`.
155
+
156
+ ## Architecture
157
+
158
+ ```
159
+ pyecsago/
160
+ ├── core/ # Abstract base classes and exceptions
161
+ ├── implementations/
162
+ │ └── ecsago/ # ECSAGO algorithm, context, individual, population
163
+ ├── strategies/
164
+ │ ├── evolution/ # Evolution strategy (HAEA + Deterministic Crowding)
165
+ │ ├── extraction/ # Prototype extraction (fitness, niche, composite)
166
+ │ ├── fitness/ # Fitness calculation (CPU and CUDA)
167
+ │ ├── niching/ # Deterministic Crowding
168
+ │ ├── operators/ # Genetic operators (mutation, crossover, HAEA)
169
+ │ └── refinement/ # MDE refinement (CPU and CUDA)
170
+ └── utils/ # Data type strategies, compatibility, utilities
171
+ ```
172
+
173
+ ## License
174
+
175
+ **pyecsago** was created by Joan Sebastian Tamayo Rivera. It is licensed under the terms of the MIT license.
176
+
177
+ ## References
178
+
179
+ - León, E. *"Scalable and Adaptive Evolutionary Clustering for Noisy and Dynamic Data"*
180
+ - León, E., Nasraoui, O., & Gómez, J. *"ECSAGO: Evolutionary Clustering with Self-Adaptive Genetic Operators"*
181
+ - Gómez, J. *"Self Adaptation of Operator Rates for Multimodal Optimization"*
182
+ - Tamayo, J. *"GPU/CUDA-Based Parallelization of the ECSAGO Evolutionary Algorithm"*
@@ -0,0 +1,160 @@
1
+ # pyecsago
2
+
3
+ Python implementation of **ECSAGO** (Evolutionary Clustering with Self-Adaptive Genetic Operators), a robust evolutionary clustering algorithm that automatically discovers the number of clusters in data while being resistant to noise.
4
+
5
+ ![ECSAGO vs K-Means](docs/ecsago_vs_kmeans.png)
6
+
7
+ ## Features
8
+
9
+ - **Automatic cluster detection** — no need to specify *k* in advance
10
+ - **Noise-robust** density-based fitness function (RBF kernel weights)
11
+ - **Self-adaptive operators** via HAEA (Hybrid Adaptive Evolutionary Algorithm)
12
+ - **Deterministic Crowding** for niche maintenance
13
+ - **MDE refinement** (Maximal Density Estimator) for prototype center/spread convergence
14
+ - **GPU acceleration** — optional CUDA support via CuPy
15
+
16
+ ## Installation
17
+
18
+ CPU only:
19
+
20
+ ```bash
21
+ pip install pyecsago
22
+ ```
23
+
24
+ With CUDA support:
25
+
26
+ ```bash
27
+ pip install pyecsago[cuda]
28
+ ```
29
+
30
+ ### From source
31
+
32
+ ```bash
33
+ git clone https://github.com/pwnaoj/pyecsago
34
+ cd pyecsago
35
+ pip install .
36
+ ```
37
+
38
+ ## Quick start
39
+
40
+ ```python
41
+ import numpy as np
42
+ from pyecsago import ECSAGO
43
+
44
+ # Sample data: 3 Gaussian clusters
45
+ data = np.vstack([
46
+ np.random.randn(100, 2) + [0, 0],
47
+ np.random.randn(100, 2) + [5, 5],
48
+ np.random.randn(100, 2) + [10, 0],
49
+ ])
50
+
51
+ config = {
52
+ "population_size": 100,
53
+ "weight_threshold": 0.3,
54
+ "max_generations": 30,
55
+ "iterations": 10,
56
+ "extraction_type": {2: 0.25}, # PROPORTION_MAX with 25% threshold
57
+ "k": 13.8,
58
+ "use_cuda": False,
59
+ }
60
+
61
+ ecsago = ECSAGO(config)
62
+ results = ecsago.run(data)
63
+
64
+ prototypes = results["refined_prototypes"]
65
+ labels = results["cluster_assignments"]
66
+
67
+ print(f"Clusters found: {len(prototypes)}")
68
+ ```
69
+
70
+ ## Step-by-step usage
71
+
72
+ For finer control you can invoke each stage independently:
73
+
74
+ ```python
75
+ ecsago = ECSAGO(config)
76
+
77
+ # 1. Load data
78
+ ecsago.context.set_data(data)
79
+
80
+ # 2. Evolve the population
81
+ ecsago.evolve()
82
+
83
+ # 3. Extract prototypes
84
+ prototypes = ecsago.extract_prototypes(
85
+ extraction_type={2: 0.25},
86
+ k=13.8,
87
+ )
88
+
89
+ # 4. Refine with MDE
90
+ refined = ecsago.refine_prototypes(
91
+ prototypes=prototypes,
92
+ iterations=10,
93
+ k=13.8,
94
+ )
95
+ ```
96
+
97
+ ## Configuration
98
+
99
+ All parameters are passed as a dictionary to `ECSAGO(config)`.
100
+
101
+ | Parameter | Type | Description |
102
+ |---|---|---|
103
+ | `population_size` | `int` | Number of individuals in the evolutionary population |
104
+ | `weight_threshold` | `float` | Threshold for weight binarization (0–1) |
105
+ | `max_generations` | `int` | Maximum number of evolutionary generations |
106
+ | `iterations` | `int` | Number of MDE refinement iterations |
107
+ | `extraction_type` | `dict` | Extraction method — key is the type (0–4), value is the threshold |
108
+ | `k` | `float` | Chi-squared factor for minimum inter-prototype distance |
109
+ | `use_cuda` | `bool` | Enable CUDA/GPU acceleration via CuPy |
110
+
111
+ ### Extraction types
112
+
113
+ | Key | Method | Threshold meaning |
114
+ |---|---|---|
115
+ | 0 | `ABSOLUTE_VALUE` | Absolute fitness threshold (auto-calculated) |
116
+ | 1 | `PROPORTION_AVG` | Proportion of average fitness |
117
+ | 2 | `PROPORTION_MAX` | Proportion of maximum fitness |
118
+ | 3 | `PROPORTION_MEDIAN` | Proportion of median fitness |
119
+ | 4 | `MINIMUM_DENSITY` | Based on minimum density |
120
+
121
+ ## Output
122
+
123
+ `ecsago.run(data)` returns a dictionary:
124
+
125
+ | Key | Description |
126
+ |---|---|
127
+ | `final_population` | Full evolved population |
128
+ | `prototypes` | Extracted prototypes (before refinement) |
129
+ | `refined_prototypes` | Refined prototypes (after MDE) |
130
+ | `cluster_assignments` | Cluster label for each data point |
131
+
132
+ Each prototype is an `ECSAGOIndividual` with attributes `genome` (center), `sigma2` (spread), and `fitness`.
133
+
134
+ ## Architecture
135
+
136
+ ```
137
+ pyecsago/
138
+ ├── core/ # Abstract base classes and exceptions
139
+ ├── implementations/
140
+ │ └── ecsago/ # ECSAGO algorithm, context, individual, population
141
+ ├── strategies/
142
+ │ ├── evolution/ # Evolution strategy (HAEA + Deterministic Crowding)
143
+ │ ├── extraction/ # Prototype extraction (fitness, niche, composite)
144
+ │ ├── fitness/ # Fitness calculation (CPU and CUDA)
145
+ │ ├── niching/ # Deterministic Crowding
146
+ │ ├── operators/ # Genetic operators (mutation, crossover, HAEA)
147
+ │ └── refinement/ # MDE refinement (CPU and CUDA)
148
+ └── utils/ # Data type strategies, compatibility, utilities
149
+ ```
150
+
151
+ ## License
152
+
153
+ **pyecsago** was created by Joan Sebastian Tamayo Rivera. It is licensed under the terms of the MIT license.
154
+
155
+ ## References
156
+
157
+ - León, E. *"Scalable and Adaptive Evolutionary Clustering for Noisy and Dynamic Data"*
158
+ - León, E., Nasraoui, O., & Gómez, J. *"ECSAGO: Evolutionary Clustering with Self-Adaptive Genetic Operators"*
159
+ - Gómez, J. *"Self Adaptation of Operator Rates for Multimodal Optimization"*
160
+ - Tamayo, J. *"GPU/CUDA-Based Parallelization of the ECSAGO Evolutionary Algorithm"*
@@ -0,0 +1,63 @@
1
+ [project]
2
+ name = "pyecsago"
3
+ version = "0.1.0"
4
+ description = "Python implementation of the evolutionary clustering ECSAGO - Evolutionary Clustering with Self Adaptive Genetic Operators"
5
+ authors = [{ name = "Joan Sebastian Tamayo Rivera" }]
6
+ requires-python = ">=3.12,<4"
7
+ readme = "README.md"
8
+ license = "MIT"
9
+ classifiers = [
10
+ "Programming Language :: Python :: 3",
11
+ "Programming Language :: Python :: 3.12",
12
+ "Programming Language :: Python :: 3.13",
13
+ "Programming Language :: Python :: 3.14",
14
+ ]
15
+ dependencies = [
16
+ "numpy>=2.0,<3",
17
+ "matplotlib>=3.9,<4",
18
+ "scikit-learn>=1.5,<2",
19
+ "pandas>=2.2,<3",
20
+ "seaborn>=0.13,<1",
21
+ "scipy>=1.14,<2",
22
+ "tqdm>=4.67,<5",
23
+ ]
24
+
25
+ [project.optional-dependencies]
26
+ cuda = ["cupy-cuda12x>=13.3.0,<14"]
27
+
28
+ [dependency-groups]
29
+ dev = [
30
+ "pytest>=8.0,<9",
31
+ "pytest-cov>=6.0,<7",
32
+ ]
33
+ docs = [
34
+ "mkdocs-material>=9.6,<10",
35
+ "mkdocstrings[python]>=0.29,<1",
36
+ "mkdocs-jupyter>=0.25,<1",
37
+ "mkdocs-include-markdown-plugin>=7.0,<8",
38
+ ]
39
+
40
+ [tool.uv]
41
+ default-groups = "all"
42
+
43
+ [tool.pytest.ini_options]
44
+ testpaths = ["tests"]
45
+
46
+ [tool.coverage.run]
47
+ source = ["pyecsago"]
48
+ omit = [
49
+ "src/pyecsago/utils/cuda/*",
50
+ "src/pyecsago/strategies/refinement/cuda_mde.py",
51
+ ]
52
+
53
+ [tool.coverage.report]
54
+ exclude_also = [
55
+ "if TYPE_CHECKING:",
56
+ "@abstractmethod",
57
+ "pass$",
58
+ "raise NotImplementedError",
59
+ ]
60
+
61
+ [build-system]
62
+ requires = ["uv_build>=0.9.21,<0.10.0"]
63
+ build-backend = "uv_build"
@@ -0,0 +1,31 @@
1
+ # read version from installed package
2
+ from importlib.metadata import version
3
+ __version__ = version("pyecsago")
4
+
5
+ # import features
6
+ from .implementations.ecsago.algorithm import ECSAGO
7
+ from .core.algorithm import EvolutionaryAlgorithm
8
+ from .strategies.evolution.factory import ECSAGOStrategyFactory
9
+ from .strategies.evolution.ecsago import ECSAGOStrategy
10
+ from .implementations.ecsago.population import ECSAGOPopulation
11
+ from .implementations.ecsago.individual import ECSAGOIndividual
12
+ from .strategies.fitness.ecsago import ECSAGOFitnessCalculator, CUDAECSAGOFitnessCalculator
13
+ from .strategies.operators.crossover import LinearCrossoverPerDimension
14
+ from .strategies.operators.mutation import AdaptiveGaussianMutation, GaussianMutation
15
+ from .strategies.niching.deterministic_crowding import DeterministicCrowding
16
+ from .strategies.operators.haea import HAEA
17
+ __all__ = [
18
+ 'ECSAGO',
19
+ 'EvolutionaryAlgorithm',
20
+ 'ECSAGOStrategyFactory',
21
+ 'ECSAGOStrategy',
22
+ 'ECSAGOPopulation',
23
+ 'ECSAGOIndividual',
24
+ 'ECSAGOFitnessCalculator',
25
+ 'CUDAECSAGOFitnessCalculator',
26
+ 'LinearCrossoverPerDimension',
27
+ 'AdaptiveGaussianMutation',
28
+ 'GaussianMutation',
29
+ 'DeterministicCrowding',
30
+ 'HAEA'
31
+ ]
@@ -0,0 +1,16 @@
1
+ from .algorithm import EvolutionaryAlgorithm
2
+ from .context import AlgorithmContext
3
+ from .individual import BaseIndividual
4
+ from .population import BasePopulation
5
+ from .exceptions import PyECSAGOError, ValidationError, ConfigurationError, EvolutionError
6
+
7
+ __all__ = [
8
+ 'EvolutionaryAlgorithm',
9
+ 'AlgorithmContext',
10
+ 'BaseIndividual',
11
+ 'BasePopulation',
12
+ 'PyECSAGOError',
13
+ 'ValidationError',
14
+ 'ConfigurationError',
15
+ 'EvolutionError',
16
+ ]
@@ -0,0 +1,46 @@
1
+ """Base class for evolutionary algorithms."""
2
+
3
+ from abc import ABC, abstractmethod
4
+
5
+
6
+ class EvolutionaryAlgorithm(ABC):
7
+ """Abstract base class defining the evolutionary algorithm interface.
8
+
9
+ Subclasses must implement evolve, extract_prototypes, and refine_prototypes.
10
+ """
11
+
12
+ def __init__(self, config: dict):
13
+ """Initializes the algorithm with configuration.
14
+
15
+ Args:
16
+ config: Algorithm configuration dictionary.
17
+ """
18
+ self.config: dict = config
19
+ self.use_cuda: bool = config.get('use_cuda', False)
20
+
21
+ @abstractmethod
22
+ def evolve(self, max_generations: int = 100):
23
+ """Runs the main evolutionary loop.
24
+
25
+ Args:
26
+ max_generations: Maximum number of generations.
27
+ """
28
+ pass
29
+
30
+ @abstractmethod
31
+ def extract_prototypes(self):
32
+ """Extracts final prototypes from the evolved population."""
33
+ pass
34
+
35
+ @abstractmethod
36
+ def refine_prototypes(self, prototypes, iterations: int = 10):
37
+ """Refines extracted prototypes.
38
+
39
+ Args:
40
+ prototypes: Prototypes to refine.
41
+ iterations: Number of refinement iterations.
42
+
43
+ Returns:
44
+ List of refined prototypes.
45
+ """
46
+ pass
@@ -0,0 +1,80 @@
1
+ """Abstract context interface for evolutionary algorithms."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from abc import ABC, abstractmethod
6
+ from collections.abc import Callable
7
+ from typing import Any, TYPE_CHECKING
8
+
9
+ if TYPE_CHECKING:
10
+ from pyecsago.utils.data_types import DataTypeStrategy
11
+
12
+
13
+ class AlgorithmContext(ABC):
14
+ """Abstract context managing data, type strategy, and CUDA resources."""
15
+
16
+ @property
17
+ @abstractmethod
18
+ def dtype_strategy(self) -> DataTypeStrategy:
19
+ """Returns the data type strategy (NumPy/CuPy)."""
20
+ pass
21
+
22
+ @property
23
+ @abstractmethod
24
+ def use_cuda(self) -> bool:
25
+ """Returns whether CUDA is enabled."""
26
+ pass
27
+
28
+ @property
29
+ @abstractmethod
30
+ def cuda_context(self) -> Any | None:
31
+ """Returns the CUDA context, or None if not available."""
32
+ pass
33
+
34
+ @abstractmethod
35
+ def _initialize_sigma_limits(self) -> None:
36
+ """Initializes sigma2 bounds based on current data."""
37
+ pass
38
+
39
+ @abstractmethod
40
+ def get_sigma2_max(self) -> Any:
41
+ """Returns the maximum allowed sigma2 value."""
42
+ pass
43
+
44
+ @abstractmethod
45
+ def get_sigma2_min(self) -> Any:
46
+ """Returns the minimum allowed sigma2 value."""
47
+ pass
48
+
49
+ @abstractmethod
50
+ def get_sigma2_initial(self) -> Any:
51
+ """Returns the initial sigma2 value for new individuals."""
52
+ pass
53
+
54
+ @abstractmethod
55
+ def set_data(self, data: Any) -> None:
56
+ """Sets the dataset in the context.
57
+
58
+ Args:
59
+ data: Dataset to set.
60
+ """
61
+ pass
62
+
63
+ @abstractmethod
64
+ def get_data(self) -> Any:
65
+ """Returns the current dataset."""
66
+ pass
67
+
68
+ @abstractmethod
69
+ def execute(self, func: Callable, *args: Any, **kwargs: Any) -> Any:
70
+ """Executes a function in the appropriate context (CPU/GPU).
71
+
72
+ Args:
73
+ func: Function to execute.
74
+ *args: Positional arguments.
75
+ **kwargs: Keyword arguments.
76
+
77
+ Returns:
78
+ Function result.
79
+ """
80
+ pass
@@ -0,0 +1,21 @@
1
+ """Custom exception hierarchy for PyECSAGO."""
2
+
3
+
4
+ class PyECSAGOError(Exception):
5
+ """Base exception for the PyECSAGO project."""
6
+ pass
7
+
8
+
9
+ class ValidationError(PyECSAGOError, ValueError):
10
+ """Raised when data or parameter validation fails."""
11
+ pass
12
+
13
+
14
+ class ConfigurationError(PyECSAGOError, ValueError):
15
+ """Raised when algorithm configuration is invalid."""
16
+ pass
17
+
18
+
19
+ class EvolutionError(PyECSAGOError, RuntimeError):
20
+ """Raised when an error occurs during the evolutionary process."""
21
+ pass
@@ -0,0 +1,33 @@
1
+ """Base class for population individuals."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import numpy as np
6
+
7
+ from abc import ABC, abstractmethod
8
+ from typing import Any, TYPE_CHECKING
9
+
10
+ if TYPE_CHECKING:
11
+ import cupy as cp
12
+
13
+
14
+ class BaseIndividual(ABC):
15
+ """Abstract base class for a population individual."""
16
+
17
+ def __init__(self, genome: np.ndarray | cp.ndarray, sigma2: np.ndarray | cp.ndarray, **kwargs: Any) -> None:
18
+ self.genome = genome
19
+ self.sigma2 = sigma2
20
+ self.fitness = 0.0
21
+
22
+ @abstractmethod
23
+ def calculate_fitness(self, data: np.ndarray, **kwargs: Any) -> None:
24
+ """Calculates the individual's fitness."""
25
+ pass
26
+
27
+ def clone(self) -> BaseIndividual:
28
+ """Creates a copy of the individual.
29
+
30
+ Raises:
31
+ NotImplementedError: Subclasses must override this method.
32
+ """
33
+ raise NotImplementedError("Subclasses must override clone()")
@@ -0,0 +1,17 @@
1
+ """Base class for populations."""
2
+
3
+ from abc import ABC, abstractmethod
4
+
5
+
6
+ class BasePopulation(ABC):
7
+ """Abstract base class for a population of individuals."""
8
+
9
+ @abstractmethod
10
+ def evaluate_population(self):
11
+ """Evaluates fitness for all individuals in the population."""
12
+ pass
13
+
14
+ @abstractmethod
15
+ def evolve(self):
16
+ """Evolves the population to the next generation."""
17
+ pass
@@ -0,0 +1,11 @@
1
+ from .algorithm import ECSAGO
2
+ from .context import StandardAlgorithmContext
3
+ from .individual import ECSAGOIndividual
4
+ from .population import ECSAGOPopulation
5
+
6
+ __all__ = [
7
+ 'ECSAGO',
8
+ 'StandardAlgorithmContext',
9
+ 'ECSAGOIndividual',
10
+ 'ECSAGOPopulation',
11
+ ]