bioopt 0.1.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.
bioopt/__init__.py ADDED
@@ -0,0 +1,37 @@
1
+ """bioopt - Bio-inspired optimization algorithms.
2
+
3
+ A collection of nature-inspired optimization algorithms for
4
+ machine learning and scientific computing.
5
+
6
+ Categories:
7
+ - swarm: Swarm intelligence algorithms (PSO, ACO, ABC, GWO, FA, WOA)
8
+ - evolutionary: Evolutionary algorithms (coming soon)
9
+ - physics: Physics-based algorithms (coming soon)
10
+
11
+ Usage:
12
+ >>> from bioopt.swarm import PSO
13
+ >>> pso = PSO(n_agents=30, bounds=[(-5, 5)] * 10)
14
+ >>> best_pos, best_fit = pso.optimize(objective_fn, iterations=100)
15
+ """
16
+
17
+ __version__ = "0.1.0"
18
+ __author__ = "Rehan Guha"
19
+
20
+ from bioopt.base import BaseOptimizer, BoundsError, OptimizationError
21
+ from bioopt.utils import (
22
+ BenchmarkFunctions,
23
+ flatten_params,
24
+ make_bounds_uniform,
25
+ unflatten_params,
26
+ )
27
+
28
+ __all__ = [
29
+ "__version__",
30
+ "BaseOptimizer",
31
+ "BoundsError",
32
+ "OptimizationError",
33
+ "BenchmarkFunctions",
34
+ "flatten_params",
35
+ "unflatten_params",
36
+ "make_bounds_uniform",
37
+ ]
@@ -0,0 +1,6 @@
1
+ """Adapters for deep learning frameworks.
2
+
3
+ Optional adapters for integrating biopt optimizers with:
4
+ - PyTorch (bioopt.adapters.pytorch)
5
+ - TensorFlow (bioopt.adapters.tensorflow)
6
+ """
@@ -0,0 +1,89 @@
1
+ """PyTorch adapter for biopt optimization algorithms."""
2
+
3
+ from typing import Any, Callable, Dict, List, Optional, Tuple
4
+
5
+ import numpy as np
6
+
7
+ try:
8
+ import torch
9
+ import torch.nn as nn
10
+ from torch.utils.data import DataLoader
11
+ except ImportError:
12
+ raise ImportError("PyTorch is required. Install with: pip install bioopt[pytorch]")
13
+
14
+ from bioopt.base import BaseOptimizer
15
+ from bioopt.utils import flatten_params, unflatten_params
16
+
17
+
18
+ class PyTorchAdapter:
19
+ """Adapter for using biopt optimizers with PyTorch models."""
20
+
21
+ def __init__(self, model: nn.Module, device: Optional[str] = None):
22
+ self.model = model
23
+ self.device = device or next(model.parameters()).device
24
+ self.param_shapes: Dict[str, Tuple[int, ...]] = {}
25
+ self.param_names: List[str] = []
26
+ for name, param in model.named_parameters():
27
+ self.param_shapes[name] = param.shape
28
+ self.param_names.append(name)
29
+ self.n_params = sum(int(np.prod(s)) for s in self.param_shapes.values())
30
+
31
+ def flat_to_model(self, flat_weights: np.ndarray) -> None:
32
+ if len(flat_weights) != self.n_params:
33
+ raise ValueError(f"Expected {self.n_params} parameters, got {len(flat_weights)}")
34
+ shapes = [self.param_shapes[name] for name in self.param_names]
35
+ param_list = unflatten_params(flat_weights, shapes)
36
+ with torch.no_grad():
37
+ for i, (name, param) in enumerate(self.model.named_parameters()):
38
+ param.copy_(torch.from_numpy(param_list[i]).to(dtype=param.dtype, device=self.device))
39
+
40
+ def model_to_flat(self) -> np.ndarray:
41
+ params = [p.detach().cpu().numpy() for _, p in self.model.named_parameters()]
42
+ return flatten_params(params)
43
+
44
+ def get_bounds(self, default_low: float = -5.0, default_high: float = 5.0,
45
+ per_param_bounds: Optional[Dict[str, Tuple[float, float]]] = None) -> List[Tuple[float, float]]:
46
+ bounds = []
47
+ for name in self.param_names:
48
+ n_elements = int(np.prod(self.param_shapes[name]))
49
+ if per_param_bounds and name in per_param_bounds:
50
+ low, high = per_param_bounds[name]
51
+ else:
52
+ low, high = default_low, default_high
53
+ bounds.extend([(low, high)] * n_elements)
54
+ return bounds
55
+
56
+ def evaluate(self, flat_weights: np.ndarray, loss_fn: Callable,
57
+ dataloader: Optional[DataLoader] = None,
58
+ inputs: Optional[torch.Tensor] = None,
59
+ targets: Optional[torch.Tensor] = None, **kwargs) -> float:
60
+ self.flat_to_model(flat_weights)
61
+ self.model.eval()
62
+ with torch.no_grad():
63
+ if dataloader is not None:
64
+ total_loss, n_batches = 0.0, 0
65
+ for batch_inputs, batch_targets in dataloader:
66
+ outputs = self.model(batch_inputs.to(self.device))
67
+ loss = loss_fn(outputs, batch_targets.to(self.device), **kwargs)
68
+ total_loss += loss.item() if isinstance(loss, torch.Tensor) else float(loss)
69
+ n_batches += 1
70
+ return total_loss / n_batches if n_batches > 0 else float("inf")
71
+ elif inputs is not None and targets is not None:
72
+ outputs = self.model(inputs.to(self.device))
73
+ loss = loss_fn(outputs, targets.to(self.device), **kwargs)
74
+ return loss.item() if isinstance(loss, torch.Tensor) else float(loss)
75
+ else:
76
+ loss = loss_fn(self.model, dataloader, **kwargs)
77
+ return loss.item() if isinstance(loss, torch.Tensor) else float(loss)
78
+
79
+ def optimize(self, optimizer: BaseOptimizer, loss_fn: Callable,
80
+ dataloader: Optional[DataLoader] = None,
81
+ inputs: Optional[torch.Tensor] = None,
82
+ targets: Optional[torch.Tensor] = None,
83
+ iterations: int = 100, verbose: bool = False,
84
+ callback: Optional[Callable] = None, **kwargs) -> Tuple[np.ndarray, float]:
85
+ def objective_fn(flat_weights: np.ndarray) -> float:
86
+ return self.evaluate(flat_weights, loss_fn, dataloader, inputs, targets)
87
+ best_weights, best_loss = optimizer.optimize(objective_fn, iterations=iterations, verbose=verbose, callback=callback, **kwargs)
88
+ self.flat_to_model(best_weights)
89
+ return best_weights, best_loss
@@ -0,0 +1,263 @@
1
+ """TensorFlow adapter for bioopt optimization algorithms.
2
+
3
+ Provides integration with TensorFlow/Keras models by converting model parameters
4
+ to flat vectors and back, enabling gradient-free optimization of neural networks.
5
+
6
+ Usage:
7
+ >>> from bioopt.swarm import PSO
8
+ >>> from bioopt.adapters.tensorflow import TensorFlowAdapter
9
+ >>>
10
+ >>> model = tf.keras.Sequential([...])
11
+ >>> adapter = TensorFlowAdapter(model)
12
+ >>>
13
+ >>> pso = PSO(n_agents=30, bounds=adapter.get_bounds())
14
+ >>> best_weights, best_loss = adapter.optimize(
15
+ ... pso, loss_fn, dataset, iterations=50
16
+ ... )
17
+ """
18
+
19
+ from typing import Any, Callable, Dict, List, Optional, Tuple
20
+
21
+ import numpy as np
22
+
23
+ try:
24
+ import tensorflow as tf
25
+ from tensorflow import keras
26
+ except ImportError:
27
+ raise ImportError(
28
+ "TensorFlow is required for this module. Install with: pip install bioopt[tensorflow]"
29
+ )
30
+
31
+ from bioopt.base import BaseOptimizer
32
+ from bioopt.utils import flatten_params, unflatten_params
33
+
34
+
35
+ class TensorFlowAdapter:
36
+ """Adapter for using biopt optimizers with TensorFlow/Keras models.
37
+
38
+ Converts TensorFlow model parameters to/from flat numpy arrays
39
+ so that swarm intelligence algorithms can optimize them.
40
+
41
+ Parameters
42
+ ----------
43
+ model : tf.keras.Model
44
+ TensorFlow/Keras model to optimize.
45
+
46
+ Attributes
47
+ ----------
48
+ model : tf.keras.Model
49
+ Reference to the model being optimized.
50
+ param_shapes : dict
51
+ Dictionary mapping parameter names to their shapes.
52
+ n_params : int
53
+ Total number of parameters in the model.
54
+ """
55
+
56
+ def __init__(self, model: keras.Model):
57
+ self.model = model
58
+ self.param_shapes: Dict[str, Tuple[int, ...]] = {}
59
+ self.param_names: List[str] = []
60
+
61
+ for var in model.trainable_variables:
62
+ self.param_shapes[var.name] = var.shape
63
+ self.param_names.append(var.name)
64
+
65
+ self.n_params = sum(int(np.prod(s)) for s in self.param_shapes.values())
66
+
67
+ def get_weights_list(self) -> List[np.ndarray]:
68
+ """Get model weights as a list of numpy arrays."""
69
+ return [w.numpy() for w in self.model.trainable_variables]
70
+
71
+ def flat_to_model(self, flat_weights: np.ndarray) -> None:
72
+ """Set model parameters from a flat array.
73
+
74
+ Parameters
75
+ ----------
76
+ flat_weights : ndarray of shape (n_params,)
77
+ Flattened parameter vector.
78
+ """
79
+ if len(flat_weights) != self.n_params:
80
+ raise ValueError(
81
+ f"Expected {self.n_params} parameters, got {len(flat_weights)}"
82
+ )
83
+
84
+ shapes = [self.param_shapes[name] for name in self.param_names]
85
+ param_list = unflatten_params(flat_weights, shapes)
86
+
87
+ for i, var in enumerate(self.model.trainable_variables):
88
+ var.assign(tf.constant(param_list[i], dtype=var.dtype))
89
+
90
+ def model_to_flat(self) -> np.ndarray:
91
+ """Get current model parameters as a flat array.
92
+
93
+ Returns
94
+ -------
95
+ flat : ndarray of shape (n_params,)
96
+ Flattened parameter vector.
97
+ """
98
+ weights = self.get_weights_list()
99
+ return flatten_params(weights)
100
+
101
+ def get_bounds(
102
+ self,
103
+ default_low: float = -5.0,
104
+ default_high: float = 5.0,
105
+ per_param_bounds: Optional[Dict[str, Tuple[float, float]]] = None,
106
+ ) -> List[Tuple[float, float]]:
107
+ """Get bounds for all model parameters.
108
+
109
+ Parameters
110
+ ----------
111
+ default_low : float
112
+ Default lower bound.
113
+ default_high : float
114
+ Default upper bound.
115
+ per_param_bounds : dict, optional
116
+ Per-parameter bounds as {name: (low, high)}.
117
+ Parameters not specified use default bounds.
118
+
119
+ Returns
120
+ -------
121
+ bounds : list of tuples
122
+ Bounds for each parameter in flat array.
123
+ """
124
+ bounds = []
125
+ for name in self.param_names:
126
+ shape = self.param_shapes[name]
127
+ n_elements = int(np.prod(shape))
128
+
129
+ if per_param_bounds and name in per_param_bounds:
130
+ low, high = per_param_bounds[name]
131
+ else:
132
+ low, high = default_low, default_high
133
+
134
+ bounds.extend([(low, high)] * n_elements)
135
+
136
+ return bounds
137
+
138
+ def evaluate(
139
+ self,
140
+ flat_weights: np.ndarray,
141
+ loss_fn: Callable,
142
+ dataset: Optional[tf.data.Dataset] = None,
143
+ inputs: Optional[tf.Tensor] = None,
144
+ targets: Optional[tf.Tensor] = None,
145
+ **kwargs
146
+ ) -> float:
147
+ """Evaluate the model with given weights on a loss function.
148
+
149
+ Parameters
150
+ ----------
151
+ flat_weights : ndarray
152
+ Flattened parameter vector.
153
+ loss_fn : callable
154
+ Loss function. Can be:
155
+ - A function that takes (outputs, targets) and returns loss
156
+ - A function that takes (model, dataset) and returns loss
157
+ dataset : tf.data.Dataset, optional
158
+ Dataset for evaluation.
159
+ inputs : tf.Tensor, optional
160
+ Input tensor for direct evaluation.
161
+ targets : tf.Tensor, optional
162
+ Target tensor for direct evaluation.
163
+ **kwargs : dict
164
+ Additional arguments passed to loss_fn.
165
+
166
+ Returns
167
+ -------
168
+ loss : float
169
+ Computed loss value.
170
+ """
171
+ self.flat_to_model(flat_weights)
172
+
173
+ if dataset is not None:
174
+ total_loss = 0.0
175
+ n_batches = 0
176
+ for batch_inputs, batch_targets in dataset:
177
+ outputs = self.model(batch_inputs, training=False)
178
+ loss = loss_fn(outputs, batch_targets, **kwargs)
179
+ if isinstance(loss, tf.Tensor):
180
+ loss = loss.numpy()
181
+ total_loss += float(loss)
182
+ n_batches += 1
183
+ return total_loss / n_batches if n_batches > 0 else float("inf")
184
+
185
+ elif inputs is not None and targets is not None:
186
+ outputs = self.model(inputs, training=False)
187
+ loss = loss_fn(outputs, targets, **kwargs)
188
+ if isinstance(loss, tf.Tensor):
189
+ loss = loss.numpy()
190
+ return float(loss)
191
+
192
+ else:
193
+ loss = loss_fn(self.model, dataset, **kwargs)
194
+ if isinstance(loss, tf.Tensor):
195
+ loss = loss.numpy()
196
+ return float(loss)
197
+
198
+ def optimize(
199
+ self,
200
+ optimizer: BaseOptimizer,
201
+ loss_fn: Callable,
202
+ dataset: Optional[tf.data.Dataset] = None,
203
+ inputs: Optional[tf.Tensor] = None,
204
+ targets: Optional[tf.Tensor] = None,
205
+ iterations: int = 100,
206
+ verbose: bool = False,
207
+ callback: Optional[Callable] = None,
208
+ **kwargs
209
+ ) -> Tuple[np.ndarray, float]:
210
+ """Run optimizer to find best model weights.
211
+
212
+ Parameters
213
+ ----------
214
+ optimizer : BaseOptimizer
215
+ A biopt optimizer (PSO, GWO, etc.).
216
+ loss_fn : callable
217
+ Loss function for evaluation.
218
+ dataset : tf.data.Dataset, optional
219
+ Dataset for evaluation.
220
+ inputs : tf.Tensor, optional
221
+ Input tensor for direct evaluation.
222
+ targets : tf.Tensor, optional
223
+ Target tensor for direct evaluation.
224
+ iterations : int
225
+ Number of optimization iterations.
226
+ verbose : bool
227
+ Print progress.
228
+ callback : callable, optional
229
+ Callback function called with (iteration, best_weights, best_loss).
230
+ **kwargs : dict
231
+ Additional arguments passed to optimizer.optimize().
232
+
233
+ Returns
234
+ -------
235
+ best_weights : ndarray
236
+ Best parameter vector found.
237
+ best_loss : float
238
+ Best loss achieved.
239
+ """
240
+ def objective_fn(flat_weights: np.ndarray) -> float:
241
+ return self.evaluate(flat_weights, loss_fn, dataset, inputs, targets)
242
+
243
+ best_weights, best_loss = optimizer.optimize(
244
+ objective_fn, iterations=iterations, verbose=verbose, callback=callback, **kwargs
245
+ )
246
+
247
+ self.flat_to_model(best_weights)
248
+
249
+ return best_weights, best_loss
250
+
251
+ def get_weight_statistics(self) -> Dict[str, Any]:
252
+ """Get statistics about current model weights."""
253
+ stats = {}
254
+ for var in self.model.trainable_variables:
255
+ w = var.numpy()
256
+ stats[var.name] = {
257
+ "mean": float(np.mean(w)),
258
+ "std": float(np.std(w)),
259
+ "min": float(np.min(w)),
260
+ "max": float(np.max(w)),
261
+ "norm": float(np.linalg.norm(w)),
262
+ }
263
+ return stats
bioopt/base.py ADDED
@@ -0,0 +1,251 @@
1
+ """Base optimizer class for bio-inspired optimization algorithms."""
2
+
3
+ from abc import ABC, abstractmethod
4
+ from typing import Callable, List, Optional, Tuple, Union
5
+
6
+ import numpy as np
7
+
8
+
9
+ class BoundsError(Exception):
10
+ """Raised when bounds are invalid."""
11
+ pass
12
+
13
+
14
+ class OptimizationError(Exception):
15
+ """Raised when optimization fails."""
16
+ pass
17
+
18
+
19
+ class BaseOptimizer(ABC):
20
+ """
21
+ Abstract base class for all bio-inspired optimization algorithms.
22
+
23
+ Provides a common interface with bounds handling, fitness tracking,
24
+ and a standardized optimize() method.
25
+
26
+ Parameters
27
+ ----------
28
+ n_agents : int
29
+ Number of agents/particles/solutions in the population.
30
+ bounds : list of tuple
31
+ Search space bounds as [(min, max), ...] for each dimension.
32
+ seed : int, optional
33
+ Random seed for reproducibility.
34
+
35
+ Attributes
36
+ ----------
37
+ best_position : ndarray
38
+ Best position found during optimization.
39
+ best_fitness : float
40
+ Best fitness value found during optimization.
41
+ fitness_history : list
42
+ History of best fitness values per iteration.
43
+ """
44
+
45
+ def __init__(
46
+ self,
47
+ n_agents: int,
48
+ bounds: Union[List[Tuple[float, float]], np.ndarray],
49
+ seed: Optional[int] = None,
50
+ ):
51
+ self.n_agents = n_agents
52
+ self.bounds = np.array(bounds, dtype=np.float64)
53
+ self.seed = seed
54
+ self.rng = np.random.RandomState(seed)
55
+
56
+ self.best_position: Optional[np.ndarray] = None
57
+ self.best_fitness: float = np.inf
58
+ self.fitness_history: List[float] = []
59
+
60
+ self._validate_bounds()
61
+
62
+ def _validate_bounds(self):
63
+ """Validate that bounds are properly formatted."""
64
+ if self.bounds.ndim != 2 or self.bounds.shape[1] != 2:
65
+ raise BoundsError(
66
+ f"Bounds must be 2D with shape (n_dims, 2), got {self.bounds.shape}"
67
+ )
68
+ if np.any(self.bounds[:, 0] >= self.bounds[:, 1]):
69
+ raise BoundsError("All lower bounds must be less than upper bounds.")
70
+
71
+ @property
72
+ def n_dims(self) -> int:
73
+ """Number of dimensions in the search space."""
74
+ return self.bounds.shape[0]
75
+
76
+ def initialize_population(self) -> np.ndarray:
77
+ """
78
+ Initialize population uniformly within bounds.
79
+
80
+ Returns
81
+ -------
82
+ population : ndarray of shape (n_agents, n_dims)
83
+ """
84
+ return self.rng.uniform(
85
+ self.bounds[:, 0],
86
+ self.bounds[:, 1],
87
+ size=(self.n_agents, self.n_dims)
88
+ )
89
+
90
+ def clip_to_bounds(self, positions: np.ndarray) -> np.ndarray:
91
+ """
92
+ Clip positions to stay within bounds.
93
+
94
+ Parameters
95
+ ----------
96
+ positions : ndarray of shape (n_agents, n_dims)
97
+
98
+ Returns
99
+ -------
100
+ clipped : ndarray of same shape
101
+ """
102
+ return np.clip(positions, self.bounds[:, 0], self.bounds[:, 1])
103
+
104
+ def evaluate(
105
+ self,
106
+ positions: np.ndarray,
107
+ objective_fn: Callable[[np.ndarray], float]
108
+ ) -> np.ndarray:
109
+ """
110
+ Evaluate fitness for all positions.
111
+
112
+ Parameters
113
+ ----------
114
+ positions : ndarray of shape (n_agents, n_dims)
115
+ objective_fn : callable
116
+ Function that takes a 1D array and returns a scalar fitness value.
117
+
118
+ Returns
119
+ -------
120
+ fitness : ndarray of shape (n_agents,)
121
+ """
122
+ return np.array([objective_fn(pos) for pos in positions])
123
+
124
+ def update_best(
125
+ self,
126
+ positions: np.ndarray,
127
+ fitness: np.ndarray
128
+ ) -> None:
129
+ """
130
+ Update global best position and fitness.
131
+
132
+ Parameters
133
+ ----------
134
+ positions : ndarray of shape (n_agents, n_dims)
135
+ fitness : ndarray of shape (n_agents,)
136
+ """
137
+ best_idx = np.argmin(fitness)
138
+ if fitness[best_idx] < self.best_fitness:
139
+ self.best_fitness = float(fitness[best_idx])
140
+ self.best_position = positions[best_idx].copy()
141
+
142
+ @abstractmethod
143
+ def step(
144
+ self,
145
+ positions: np.ndarray,
146
+ fitness: np.ndarray,
147
+ iteration: int,
148
+ **kwargs
149
+ ) -> np.ndarray:
150
+ """
151
+ Perform one iteration step of the algorithm.
152
+
153
+ Parameters
154
+ ----------
155
+ positions : ndarray of shape (n_agents, n_dims)
156
+ Current positions of all agents.
157
+ fitness : ndarray of shape (n_agents,)
158
+ Current fitness values.
159
+ iteration : int
160
+ Current iteration number.
161
+ **kwargs : dict
162
+ Additional algorithm-specific parameters.
163
+
164
+ Returns
165
+ -------
166
+ new_positions : ndarray of shape (n_agents, n_dims)
167
+ """
168
+ pass
169
+
170
+ def optimize(
171
+ self,
172
+ objective_fn: Callable[[np.ndarray], float],
173
+ iterations: int = 100,
174
+ verbose: bool = False,
175
+ callback: Optional[Callable[[int, np.ndarray, float], None]] = None,
176
+ **kwargs
177
+ ) -> Tuple[np.ndarray, float]:
178
+ """
179
+ Run the optimization.
180
+
181
+ Parameters
182
+ ----------
183
+ objective_fn : callable
184
+ The objective function to minimize. Takes a 1D array and returns a scalar.
185
+ iterations : int
186
+ Number of iterations to run.
187
+ verbose : bool
188
+ If True, print progress information.
189
+ callback : callable, optional
190
+ Function called after each iteration with signature
191
+ (iteration, best_position, best_fitness).
192
+ **kwargs : dict
193
+ Additional algorithm-specific parameters.
194
+
195
+ Returns
196
+ -------
197
+ best_position : ndarray
198
+ Best position found.
199
+ best_fitness : float
200
+ Best fitness value found.
201
+ """
202
+ # Initialize
203
+ positions = self.initialize_population()
204
+ fitness = self.evaluate(positions, objective_fn)
205
+ self.update_best(positions, fitness)
206
+ self.fitness_history = [self.best_fitness]
207
+
208
+ if verbose:
209
+ print(f"Iter 0: Best Fitness = {self.best_fitness:.6e}")
210
+
211
+ if callback is not None:
212
+ callback(0, self.best_position, self.best_fitness)
213
+
214
+ # Main loop
215
+ for i in range(1, iterations + 1):
216
+ positions = self.step(positions, fitness, i, **kwargs)
217
+ positions = self.clip_to_bounds(positions)
218
+ fitness = self.evaluate(positions, objective_fn)
219
+ self.update_best(positions, fitness)
220
+ self.fitness_history.append(self.best_fitness)
221
+
222
+ if verbose:
223
+ print(f"Iter {i}: Best Fitness = {self.best_fitness:.6e}")
224
+
225
+ if callback is not None:
226
+ callback(i, self.best_position, self.best_fitness)
227
+
228
+ return self.best_position, self.best_fitness
229
+
230
+ def reset(self) -> None:
231
+ """Reset optimizer state for a fresh optimization run."""
232
+ self.best_position = None
233
+ self.best_fitness = np.inf
234
+ self.fitness_history = []
235
+ self.rng = np.random.RandomState(self.seed)
236
+
237
+ def get_state(self) -> dict:
238
+ """Get optimizer state for checkpointing."""
239
+ return {
240
+ "best_position": self.best_position,
241
+ "best_fitness": self.best_fitness,
242
+ "fitness_history": self.fitness_history,
243
+ "rng_state": self.rng.get_state(),
244
+ }
245
+
246
+ def set_state(self, state: dict) -> None:
247
+ """Restore optimizer state from checkpoint."""
248
+ self.best_position = state["best_position"]
249
+ self.best_fitness = state["best_fitness"]
250
+ self.fitness_history = state["fitness_history"]
251
+ self.rng.set_state(state["rng_state"])
@@ -0,0 +1,31 @@
1
+ """Swarm intelligence algorithms.
2
+
3
+ Available algorithms:
4
+ - PSO: Particle Swarm Optimization
5
+ - ACO: Ant Colony Optimization (Continuous, using KDE)
6
+ - ABC: Artificial Bee Colony
7
+ - GWO: Grey Wolf Optimizer
8
+ - FA: Firefly Algorithm
9
+ - WOA: Whale Optimization Algorithm
10
+
11
+ Usage:
12
+ >>> from bioopt.swarm import PSO, ACO, GWO
13
+ >>> pso = PSO(n_agents=30, bounds=[(-5, 5)] * 10)
14
+ >>> best_pos, best_fit = pso.optimize(objective_fn, iterations=100)
15
+ """
16
+
17
+ from bioopt.swarm.pso import PSO
18
+ from bioopt.swarm.aco import ACO
19
+ from bioopt.swarm.abc import ABC
20
+ from bioopt.swarm.gwo import GWO
21
+ from bioopt.swarm.fa import FA
22
+ from bioopt.swarm.woa import WOA
23
+
24
+ __all__ = [
25
+ "PSO",
26
+ "ACO",
27
+ "ABC",
28
+ "GWO",
29
+ "FA",
30
+ "WOA",
31
+ ]