ecabc 3.0.1__tar.gz → 3.0.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ecabc-3.0.1 → ecabc-3.0.2}/LICENSE +1 -1
- {ecabc-3.0.1 → ecabc-3.0.2}/PKG-INFO +33 -18
- {ecabc-3.0.1 → ecabc-3.0.2}/README.md +13 -15
- ecabc-3.0.2/pyproject.toml +71 -0
- ecabc-3.0.2/src/ecabc/__init__.py +19 -0
- ecabc-3.0.2/src/ecabc/abc.py +305 -0
- {ecabc-3.0.1 → ecabc-3.0.2/src}/ecabc/bee.py +17 -15
- {ecabc-3.0.1 → ecabc-3.0.2/src}/ecabc/parameter.py +24 -22
- {ecabc-3.0.1 → ecabc-3.0.2/src}/ecabc/utils.py +27 -18
- {ecabc-3.0.1 → ecabc-3.0.2/src}/ecabc.egg-info/PKG-INFO +33 -18
- ecabc-3.0.2/src/ecabc.egg-info/SOURCES.txt +15 -0
- ecabc-3.0.2/src/ecabc.egg-info/requires.txt +14 -0
- ecabc-3.0.2/src/ecabc.egg-info/top_level.txt +1 -0
- {ecabc-3.0.1 → ecabc-3.0.2}/tests/test_all.py +14 -18
- ecabc-3.0.2/tests/test_api_contract.py +305 -0
- ecabc-3.0.1/ecabc/__init__.py +0 -5
- ecabc-3.0.1/ecabc/abc.py +0 -315
- ecabc-3.0.1/ecabc.egg-info/SOURCES.txt +0 -14
- ecabc-3.0.1/ecabc.egg-info/top_level.txt +0 -5
- ecabc-3.0.1/examples/minimize_integers.py +0 -52
- ecabc-3.0.1/pyproject.toml +0 -27
- {ecabc-3.0.1 → ecabc-3.0.2}/setup.cfg +0 -0
- {ecabc-3.0.1 → ecabc-3.0.2/src}/ecabc.egg-info/dependency_links.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2018 Sanskriti Sharma, Hernan Gelaf-Romer
|
|
3
|
+
Copyright (c) 2018-2026 Sanskriti Sharma, Hernan Gelaf-Romer, Travis Kessler
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
|
@@ -1,23 +1,41 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: ecabc
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.2
|
|
4
4
|
Summary: Artificial bee colony for function parameter optimization
|
|
5
5
|
Author-email: Sanskriti Sharma <Sanskriti_Sharma@student.uml.edu>, Hernan Gelaf-Romer <Hernan_Gelafromer@student.uml.edu>, Travis Kessler <travis.j.kessler@gmail.com>
|
|
6
|
+
License: MIT
|
|
6
7
|
Project-URL: Homepage, https://github.com/ecrl/ecabc
|
|
7
8
|
Project-URL: Bug Tracker, https://github.com/ecrl/ecabc/issues
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
8
10
|
Classifier: Programming Language :: Python :: 3.11
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
9
13
|
Classifier: License :: OSI Approved :: MIT License
|
|
10
14
|
Classifier: Operating System :: OS Independent
|
|
11
|
-
Requires-Python: >=3.
|
|
15
|
+
Requires-Python: >=3.10
|
|
12
16
|
Description-Content-Type: text/markdown
|
|
13
17
|
License-File: LICENSE
|
|
18
|
+
Provides-Extra: dev
|
|
19
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
20
|
+
Requires-Dist: pytest-cov>=5; extra == "dev"
|
|
21
|
+
Requires-Dist: ruff>=0.8; extra == "dev"
|
|
22
|
+
Requires-Dist: mypy>=1.8; extra == "dev"
|
|
23
|
+
Requires-Dist: pre-commit>=4; extra == "dev"
|
|
24
|
+
Requires-Dist: build>=1.2; extra == "dev"
|
|
25
|
+
Provides-Extra: docs
|
|
26
|
+
Requires-Dist: sphinx>=7; extra == "docs"
|
|
27
|
+
Requires-Dist: furo>=2024.1; extra == "docs"
|
|
28
|
+
Requires-Dist: sphinx-autodoc-typehints>=2; extra == "docs"
|
|
29
|
+
Requires-Dist: sphinx-copybutton>=0.5; extra == "docs"
|
|
30
|
+
Dynamic: license-file
|
|
14
31
|
|
|
15
32
|
[](http://faculty.uml.edu/Hunter_Mack/)
|
|
16
33
|
|
|
17
|
-
# ECabc: optimization algorithm for tuning user-defined parametric functions
|
|
34
|
+
# ECabc: optimization algorithm for tuning user-defined parametric functions
|
|
18
35
|
[](https://badge.fury.io/gh/ECRL%2FECabc)
|
|
19
36
|
[](https://badge.fury.io/py/ecabc)
|
|
20
37
|
[](https://github.com/ECRL/ecabc/blob/master/LICENSE)
|
|
38
|
+
[](https://github.com/ecrl/ecabc/actions/workflows/run_tests.yml)
|
|
21
39
|
[](https://doi.org/10.21105/joss.01420)
|
|
22
40
|
|
|
23
41
|
**ECabc** is an open source Python package used to tune parameters for user-supplied functions based on the [Artificial Bee Colony by D. Karaboğa](http://scholarpedia.org/article/Artificial_bee_colony_algorithm). ECabc optimizes user supplied functions, or **fitness function**s, using a set of variables that exist within a search space. The bee colony consists of three types of bees: employers, onlookers and scouts. An **employer bee** exploits a solution comprised of a permutation of the variables in the search space, and evaluates the viability of the solution. An **onlooker bee** chooses an employer bee with an optimal solution and searches for new solutions near them. The **scout bee**, a variant of the employer bee, will search for a new solution if it has stayed too long at its current solution.
|
|
@@ -31,38 +49,35 @@ While it has several applications, ECabc has been successfully used by the Energ
|
|
|
31
49
|
|
|
32
50
|
# Installation
|
|
33
51
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
- Have the ability to install python packages
|
|
52
|
+
**Requires Python 3.10 or newer.** ECabc has no runtime third-party dependencies
|
|
53
|
+
(stdlib only). A virtual environment is recommended.
|
|
37
54
|
|
|
38
55
|
### Method 1: pip
|
|
39
|
-
If you are working in a Linux/Mac environment:
|
|
40
|
-
```
|
|
41
|
-
sudo pip install ecabc
|
|
42
|
-
```
|
|
43
56
|
|
|
44
|
-
Alternatively, in a Windows or virtualenv environment:
|
|
45
57
|
```
|
|
46
58
|
pip install ecabc
|
|
47
59
|
```
|
|
48
60
|
|
|
49
|
-
To
|
|
61
|
+
To upgrade to the latest release:
|
|
62
|
+
|
|
50
63
|
```
|
|
51
64
|
pip install --upgrade ecabc
|
|
52
65
|
```
|
|
53
66
|
|
|
54
|
-
Note: if multiple Python releases are installed on your system (e.g. 2.7 and 3.7), you may need to execute the correct version of pip. For Python 3.X, change **"pip install ecabc"** to **"pip3 install ecabc"**.
|
|
55
|
-
|
|
56
67
|
### Method 2: From source
|
|
57
|
-
|
|
68
|
+
|
|
69
|
+
Clone or download this repository, then from the repository root:
|
|
70
|
+
|
|
58
71
|
```
|
|
59
72
|
pip install .
|
|
60
73
|
```
|
|
61
74
|
|
|
62
|
-
There are currently no additional dependencies for ECabc.
|
|
63
|
-
|
|
64
75
|
# Usage
|
|
65
76
|
|
|
77
|
+
API stability for the public surface is documented in
|
|
78
|
+
[API_STABILITY.md](API_STABILITY.md) (also the Sphinx *API stability policy*
|
|
79
|
+
page under `docs/source/stability.rst`).
|
|
80
|
+
|
|
66
81
|
To start using ECabc, you need a couple items:
|
|
67
82
|
- a fitness function (cost function) to optimize
|
|
68
83
|
- parameters used by the fitness function
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
[](http://faculty.uml.edu/Hunter_Mack/)
|
|
2
2
|
|
|
3
|
-
# ECabc: optimization algorithm for tuning user-defined parametric functions
|
|
3
|
+
# ECabc: optimization algorithm for tuning user-defined parametric functions
|
|
4
4
|
[](https://badge.fury.io/gh/ECRL%2FECabc)
|
|
5
5
|
[](https://badge.fury.io/py/ecabc)
|
|
6
6
|
[](https://github.com/ECRL/ecabc/blob/master/LICENSE)
|
|
7
|
+
[](https://github.com/ecrl/ecabc/actions/workflows/run_tests.yml)
|
|
7
8
|
[](https://doi.org/10.21105/joss.01420)
|
|
8
9
|
|
|
9
10
|
**ECabc** is an open source Python package used to tune parameters for user-supplied functions based on the [Artificial Bee Colony by D. Karaboğa](http://scholarpedia.org/article/Artificial_bee_colony_algorithm). ECabc optimizes user supplied functions, or **fitness function**s, using a set of variables that exist within a search space. The bee colony consists of three types of bees: employers, onlookers and scouts. An **employer bee** exploits a solution comprised of a permutation of the variables in the search space, and evaluates the viability of the solution. An **onlooker bee** chooses an employer bee with an optimal solution and searches for new solutions near them. The **scout bee**, a variant of the employer bee, will search for a new solution if it has stayed too long at its current solution.
|
|
@@ -17,38 +18,35 @@ While it has several applications, ECabc has been successfully used by the Energ
|
|
|
17
18
|
|
|
18
19
|
# Installation
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
- Have the ability to install python packages
|
|
21
|
+
**Requires Python 3.10 or newer.** ECabc has no runtime third-party dependencies
|
|
22
|
+
(stdlib only). A virtual environment is recommended.
|
|
23
23
|
|
|
24
24
|
### Method 1: pip
|
|
25
|
-
If you are working in a Linux/Mac environment:
|
|
26
|
-
```
|
|
27
|
-
sudo pip install ecabc
|
|
28
|
-
```
|
|
29
25
|
|
|
30
|
-
Alternatively, in a Windows or virtualenv environment:
|
|
31
26
|
```
|
|
32
27
|
pip install ecabc
|
|
33
28
|
```
|
|
34
29
|
|
|
35
|
-
To
|
|
30
|
+
To upgrade to the latest release:
|
|
31
|
+
|
|
36
32
|
```
|
|
37
33
|
pip install --upgrade ecabc
|
|
38
34
|
```
|
|
39
35
|
|
|
40
|
-
Note: if multiple Python releases are installed on your system (e.g. 2.7 and 3.7), you may need to execute the correct version of pip. For Python 3.X, change **"pip install ecabc"** to **"pip3 install ecabc"**.
|
|
41
|
-
|
|
42
36
|
### Method 2: From source
|
|
43
|
-
|
|
37
|
+
|
|
38
|
+
Clone or download this repository, then from the repository root:
|
|
39
|
+
|
|
44
40
|
```
|
|
45
41
|
pip install .
|
|
46
42
|
```
|
|
47
43
|
|
|
48
|
-
There are currently no additional dependencies for ECabc.
|
|
49
|
-
|
|
50
44
|
# Usage
|
|
51
45
|
|
|
46
|
+
API stability for the public surface is documented in
|
|
47
|
+
[API_STABILITY.md](API_STABILITY.md) (also the Sphinx *API stability policy*
|
|
48
|
+
page under `docs/source/stability.rst`).
|
|
49
|
+
|
|
52
50
|
To start using ECabc, you need a couple items:
|
|
53
51
|
- a fitness function (cost function) to optimize
|
|
54
52
|
- parameters used by the fitness function
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[tool.setuptools.packages.find]
|
|
6
|
+
where = ["src"]
|
|
7
|
+
exclude = ["paper*"]
|
|
8
|
+
|
|
9
|
+
[project]
|
|
10
|
+
name = "ecabc"
|
|
11
|
+
version = "3.0.2"
|
|
12
|
+
authors = [
|
|
13
|
+
{ name="Sanskriti Sharma", email="Sanskriti_Sharma@student.uml.edu" },
|
|
14
|
+
{ name="Hernan Gelaf-Romer", email="Hernan_Gelafromer@student.uml.edu" },
|
|
15
|
+
{ name="Travis Kessler", email="travis.j.kessler@gmail.com" },
|
|
16
|
+
]
|
|
17
|
+
description = "Artificial bee colony for function parameter optimization"
|
|
18
|
+
readme = "README.md"
|
|
19
|
+
requires-python = ">=3.10"
|
|
20
|
+
license = { text = "MIT" }
|
|
21
|
+
classifiers = [
|
|
22
|
+
"Programming Language :: Python :: 3.10",
|
|
23
|
+
"Programming Language :: Python :: 3.11",
|
|
24
|
+
"Programming Language :: Python :: 3.12",
|
|
25
|
+
"Programming Language :: Python :: 3.13",
|
|
26
|
+
"License :: OSI Approved :: MIT License",
|
|
27
|
+
"Operating System :: OS Independent",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[project.urls]
|
|
31
|
+
"Homepage" = "https://github.com/ecrl/ecabc"
|
|
32
|
+
"Bug Tracker" = "https://github.com/ecrl/ecabc/issues"
|
|
33
|
+
|
|
34
|
+
[project.optional-dependencies]
|
|
35
|
+
dev = [
|
|
36
|
+
"pytest>=8",
|
|
37
|
+
"pytest-cov>=5",
|
|
38
|
+
"ruff>=0.8",
|
|
39
|
+
"mypy>=1.8",
|
|
40
|
+
"pre-commit>=4",
|
|
41
|
+
"build>=1.2",
|
|
42
|
+
]
|
|
43
|
+
docs = [
|
|
44
|
+
"sphinx>=7",
|
|
45
|
+
"furo>=2024.1",
|
|
46
|
+
"sphinx-autodoc-typehints>=2",
|
|
47
|
+
"sphinx-copybutton>=0.5",
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
[tool.ruff]
|
|
51
|
+
line-length = 88
|
|
52
|
+
src = ["src", "tests"]
|
|
53
|
+
|
|
54
|
+
[tool.ruff.lint]
|
|
55
|
+
select = ["E", "F", "I", "UP"]
|
|
56
|
+
|
|
57
|
+
[tool.pytest.ini_options]
|
|
58
|
+
addopts = "--cov=ecabc --cov-report=term-missing --cov-fail-under=95"
|
|
59
|
+
testpaths = ["tests"]
|
|
60
|
+
|
|
61
|
+
[tool.coverage.run]
|
|
62
|
+
source = ["ecabc"]
|
|
63
|
+
branch = false
|
|
64
|
+
|
|
65
|
+
[tool.mypy]
|
|
66
|
+
python_version = "3.10"
|
|
67
|
+
files = ["src/ecabc"]
|
|
68
|
+
warn_return_any = true
|
|
69
|
+
warn_unused_configs = true
|
|
70
|
+
check_untyped_defs = true
|
|
71
|
+
disallow_untyped_defs = false
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
2
|
+
|
|
3
|
+
from ecabc.abc import ABC
|
|
4
|
+
from ecabc.bee import Bee
|
|
5
|
+
from ecabc.parameter import Parameter
|
|
6
|
+
|
|
7
|
+
from . import utils as utils
|
|
8
|
+
|
|
9
|
+
__all__ = ["ABC", "Bee", "Parameter", "__version__", "utils"]
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _resolve_version() -> str:
|
|
13
|
+
try:
|
|
14
|
+
return version("ecabc")
|
|
15
|
+
except PackageNotFoundError:
|
|
16
|
+
return "0.0.0+unknown"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
__version__ = _resolve_version()
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
#
|
|
3
|
+
# ecabc/abc.py
|
|
4
|
+
# v.3.0.0
|
|
5
|
+
#
|
|
6
|
+
# Developed in 2019 by Sanskriti Sharma <sanskriti_sharma@student.uml.edu>,
|
|
7
|
+
# Hernan Gelaf-Romer <hernan_gelafromer@student.uml.edu>, and Travis Kessler
|
|
8
|
+
# <Travis_Kessler@student.uml.edu>
|
|
9
|
+
#
|
|
10
|
+
# abc.py: contains ABC object, handling the initialization and runtime of the
|
|
11
|
+
# artificial bee colony
|
|
12
|
+
#
|
|
13
|
+
|
|
14
|
+
# Stdlib. imports
|
|
15
|
+
from collections.abc import Callable
|
|
16
|
+
from multiprocessing import Pool
|
|
17
|
+
from typing import Any
|
|
18
|
+
from warnings import warn
|
|
19
|
+
|
|
20
|
+
# ECabc imports
|
|
21
|
+
from ecabc.bee import Bee
|
|
22
|
+
from ecabc.parameter import Parameter
|
|
23
|
+
from ecabc.utils import apply_mutation, call_obj_fn, choose_bee, determine_best_bee
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ABC:
|
|
27
|
+
def __init__(
|
|
28
|
+
self,
|
|
29
|
+
num_employers: int,
|
|
30
|
+
objective_fn: Callable[[list], float],
|
|
31
|
+
obj_fn_args: dict = {},
|
|
32
|
+
num_processes: int = 1,
|
|
33
|
+
):
|
|
34
|
+
"""ABC object: optimizes parameters for user-supplied function
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
num_employers (int): number of employer bees in the colony
|
|
38
|
+
objective_fn (callable): function to optimize; accepts list of
|
|
39
|
+
ints/floats, returns float (fitness)
|
|
40
|
+
obj_fn_args (dict): non-tunable kwargs to pass to objective_fn
|
|
41
|
+
num_processes (int): number of concurrent processes to utilize
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
if not callable(objective_fn):
|
|
45
|
+
raise ReferenceError("Supplied objective function not callable")
|
|
46
|
+
self._obj_fn = objective_fn
|
|
47
|
+
# Copy so the shared mutable default (and caller-owned dicts) are not aliased.
|
|
48
|
+
self._obj_fn_args = dict(obj_fn_args)
|
|
49
|
+
self._num_employers = num_employers
|
|
50
|
+
self._num_processes = num_processes
|
|
51
|
+
self._params: list[Parameter] = []
|
|
52
|
+
self._bees: list[Bee] = []
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def best_fitness(self) -> float:
|
|
56
|
+
"""Returns fitness score from best-performing bee"""
|
|
57
|
+
|
|
58
|
+
return determine_best_bee(self._bees)[0]
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
def best_ret_val(self) -> int | float | None:
|
|
62
|
+
"""Returns objective_fn return value from best-performing bee"""
|
|
63
|
+
|
|
64
|
+
return determine_best_bee(self._bees)[1]
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def best_params(self) -> dict:
|
|
68
|
+
"""Returns parameters from best-performing bee"""
|
|
69
|
+
|
|
70
|
+
vals = determine_best_bee(self._bees)[2]
|
|
71
|
+
ret_val: dict[str, int | float] = {}
|
|
72
|
+
if vals is None:
|
|
73
|
+
return ret_val
|
|
74
|
+
for idx, param in enumerate(self._params):
|
|
75
|
+
if param._name is None:
|
|
76
|
+
ret_val[f"P{idx}"] = vals[idx]
|
|
77
|
+
else:
|
|
78
|
+
ret_val[param._name] = vals[idx]
|
|
79
|
+
return ret_val
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def average_fitness(self) -> float | None:
|
|
83
|
+
"""Returns average fitness score for bee population"""
|
|
84
|
+
|
|
85
|
+
if len(self._bees) == 0:
|
|
86
|
+
return None
|
|
87
|
+
return sum(b._fitness_score for b in self._bees) / len(self._bees)
|
|
88
|
+
|
|
89
|
+
@property
|
|
90
|
+
def average_ret_val(self) -> int | float | None:
|
|
91
|
+
"""Returns average objective_fn return value for bee population"""
|
|
92
|
+
|
|
93
|
+
if len(self._bees) == 0:
|
|
94
|
+
return None
|
|
95
|
+
return sum(b._obj_fn_val for b in self._bees) / len(self._bees)
|
|
96
|
+
|
|
97
|
+
def add_param(
|
|
98
|
+
self,
|
|
99
|
+
min_val: int | float,
|
|
100
|
+
max_val: int | float,
|
|
101
|
+
restrict: bool = True,
|
|
102
|
+
name: str | None = None,
|
|
103
|
+
):
|
|
104
|
+
"""ABC.add_param: adds a parameter to be processed by the user-
|
|
105
|
+
supplied objective function
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
min_val (int, float): minimum value allowed for the parameter's
|
|
109
|
+
initialization
|
|
110
|
+
max_val (int, float): maximum value allowed for the parameter's
|
|
111
|
+
initialization
|
|
112
|
+
restrict (bool): if `True`, parameter mutations must be within
|
|
113
|
+
[min_val, max_val], `False` allows out-of-bounds mutation
|
|
114
|
+
name (str): name of parameter, optional
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
if len(self._bees) > 0:
|
|
118
|
+
raise RuntimeError("Cannot add another parameter after bee initialization")
|
|
119
|
+
self._params.append(Parameter(min_val, max_val, restrict, name))
|
|
120
|
+
|
|
121
|
+
def initialize(self):
|
|
122
|
+
"""ABC.initialize: creates `num_employers` employer bees and
|
|
123
|
+
`num_employers` onlooker bees
|
|
124
|
+
"""
|
|
125
|
+
|
|
126
|
+
if len(self._bees) > 0:
|
|
127
|
+
warn(
|
|
128
|
+
"initialize() called again: overwriting bee population", RuntimeWarning
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
self._bees = []
|
|
132
|
+
|
|
133
|
+
if self._num_processes > 1:
|
|
134
|
+
employer_pool = Pool(processes=self._num_processes)
|
|
135
|
+
employer_results: list[Any] = []
|
|
136
|
+
|
|
137
|
+
for _ in range(self._num_employers):
|
|
138
|
+
params = [p.rand_val for p in self._params]
|
|
139
|
+
if self._num_processes > 1:
|
|
140
|
+
employer_results.append(
|
|
141
|
+
employer_pool.apply_async(
|
|
142
|
+
call_obj_fn, [params, self._obj_fn, self._obj_fn_args]
|
|
143
|
+
)
|
|
144
|
+
)
|
|
145
|
+
else:
|
|
146
|
+
employer_results.append(
|
|
147
|
+
call_obj_fn(params, self._obj_fn, self._obj_fn_args)
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
if self._num_processes > 1:
|
|
151
|
+
employer_pool.close()
|
|
152
|
+
employer_pool.join()
|
|
153
|
+
employer_results = [r.get() for r in employer_results]
|
|
154
|
+
|
|
155
|
+
for result in employer_results:
|
|
156
|
+
self._bees.append(
|
|
157
|
+
Bee(result[0], result[1], len(self._params) * self._num_employers, True)
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
if self._num_processes > 1:
|
|
161
|
+
onlooker_pool = Pool(processes=self._num_processes)
|
|
162
|
+
onlooker_results: list[Any] = []
|
|
163
|
+
|
|
164
|
+
for _ in range(self._num_employers):
|
|
165
|
+
chosen_employer = choose_bee(self._bees)
|
|
166
|
+
neighbor_food = apply_mutation(chosen_employer._params, self._params)
|
|
167
|
+
|
|
168
|
+
if self._num_processes > 1:
|
|
169
|
+
onlooker_results.append(
|
|
170
|
+
onlooker_pool.apply_async(
|
|
171
|
+
call_obj_fn, [neighbor_food, self._obj_fn, self._obj_fn_args]
|
|
172
|
+
)
|
|
173
|
+
)
|
|
174
|
+
else:
|
|
175
|
+
onlooker_results.append(
|
|
176
|
+
call_obj_fn(neighbor_food, self._obj_fn, self._obj_fn_args)
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
if self._num_processes > 1:
|
|
180
|
+
onlooker_pool.close()
|
|
181
|
+
onlooker_pool.join()
|
|
182
|
+
onlooker_results = [r.get() for r in onlooker_results]
|
|
183
|
+
|
|
184
|
+
for result in onlooker_results:
|
|
185
|
+
self._bees.append(
|
|
186
|
+
Bee(
|
|
187
|
+
result[0],
|
|
188
|
+
result[1],
|
|
189
|
+
len(self._params) * self._num_employers,
|
|
190
|
+
)
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
def search(self):
|
|
194
|
+
"""Perform one search cycle for all bees in the colony.
|
|
195
|
+
|
|
196
|
+
For each bee: if it has exhausted its current food source, an employer
|
|
197
|
+
finds a new random food source and an onlooker travels near a
|
|
198
|
+
well-performing bee; otherwise one parameter is mutated and the bee
|
|
199
|
+
moves only if the neighbor food is better. The colony is then replaced
|
|
200
|
+
with the updated bee states.
|
|
201
|
+
|
|
202
|
+
Exhaustion is the maximum number of search cycles a bee may stay at its
|
|
203
|
+
current food source, ``EX = NE * D``, where ``NE`` is the number of
|
|
204
|
+
employers and ``D`` is the number of parameters.
|
|
205
|
+
"""
|
|
206
|
+
|
|
207
|
+
if len(self._bees) == 0:
|
|
208
|
+
raise RuntimeError("search() cannot be called before initialize()")
|
|
209
|
+
|
|
210
|
+
if self._num_processes > 1:
|
|
211
|
+
bee_pool = Pool(processes=self._num_processes)
|
|
212
|
+
new_employer_results: list[Any] = []
|
|
213
|
+
new_onlooker_results: list[Any] = []
|
|
214
|
+
new_position_results: list[Any] = []
|
|
215
|
+
current_positions: list[Bee] = []
|
|
216
|
+
|
|
217
|
+
for bee in self._bees:
|
|
218
|
+
if bee.abandon:
|
|
219
|
+
if bee._is_employer:
|
|
220
|
+
new_params = [p.rand_val for p in self._params]
|
|
221
|
+
if self._num_processes > 1:
|
|
222
|
+
new_employer_results.append(
|
|
223
|
+
bee_pool.apply_async(
|
|
224
|
+
call_obj_fn,
|
|
225
|
+
[new_params, self._obj_fn, self._obj_fn_args],
|
|
226
|
+
)
|
|
227
|
+
)
|
|
228
|
+
else:
|
|
229
|
+
new_employer_results.append(
|
|
230
|
+
call_obj_fn(new_params, self._obj_fn, self._obj_fn_args)
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
else:
|
|
234
|
+
chosen_bee = choose_bee(self._bees)
|
|
235
|
+
neighbor_food = apply_mutation(chosen_bee._params, self._params)
|
|
236
|
+
if self._num_processes > 1:
|
|
237
|
+
new_onlooker_results.append(
|
|
238
|
+
bee_pool.apply_async(
|
|
239
|
+
call_obj_fn,
|
|
240
|
+
[neighbor_food, self._obj_fn, self._obj_fn_args],
|
|
241
|
+
)
|
|
242
|
+
)
|
|
243
|
+
else:
|
|
244
|
+
new_onlooker_results.append(
|
|
245
|
+
call_obj_fn(neighbor_food, self._obj_fn, self._obj_fn_args)
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
else:
|
|
249
|
+
current_positions.append(bee)
|
|
250
|
+
neighbor_food = apply_mutation(bee._params, self._params)
|
|
251
|
+
if self._num_processes > 1:
|
|
252
|
+
new_position_results.append(
|
|
253
|
+
bee_pool.apply_async(
|
|
254
|
+
call_obj_fn,
|
|
255
|
+
[neighbor_food, self._obj_fn, self._obj_fn_args],
|
|
256
|
+
)
|
|
257
|
+
)
|
|
258
|
+
else:
|
|
259
|
+
new_position_results.append(
|
|
260
|
+
call_obj_fn(neighbor_food, self._obj_fn, self._obj_fn_args)
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
if self._num_processes > 1:
|
|
264
|
+
bee_pool.close()
|
|
265
|
+
bee_pool.join()
|
|
266
|
+
new_employer_results = [r.get() for r in new_employer_results]
|
|
267
|
+
new_onlooker_results = [r.get() for r in new_onlooker_results]
|
|
268
|
+
new_position_results = [r.get() for r in new_position_results]
|
|
269
|
+
|
|
270
|
+
next_bee_generation = []
|
|
271
|
+
|
|
272
|
+
for result in new_employer_results:
|
|
273
|
+
next_bee_generation.append(
|
|
274
|
+
Bee(result[0], result[1], len(self._params) * self._num_employers, True)
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
for result in new_onlooker_results:
|
|
278
|
+
next_bee_generation.append(
|
|
279
|
+
Bee(result[0], result[1], len(self._params) * self._num_employers)
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
for idx, result in enumerate(new_position_results):
|
|
283
|
+
bee_to_compare = current_positions[idx]
|
|
284
|
+
if bee_to_compare.is_better_food(result[1]):
|
|
285
|
+
if bee_to_compare._is_employer:
|
|
286
|
+
next_bee_generation.append(
|
|
287
|
+
Bee(
|
|
288
|
+
result[0],
|
|
289
|
+
result[1],
|
|
290
|
+
len(self._params) * self._num_employers,
|
|
291
|
+
True,
|
|
292
|
+
)
|
|
293
|
+
)
|
|
294
|
+
else:
|
|
295
|
+
next_bee_generation.append(
|
|
296
|
+
Bee(
|
|
297
|
+
result[0],
|
|
298
|
+
result[1],
|
|
299
|
+
len(self._params) * self._num_employers,
|
|
300
|
+
)
|
|
301
|
+
)
|
|
302
|
+
else:
|
|
303
|
+
next_bee_generation.append(bee_to_compare)
|
|
304
|
+
|
|
305
|
+
self._bees = next_bee_generation
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env python
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
2
|
#
|
|
4
3
|
# ecabc/bee.py
|
|
5
4
|
# v.3.0.0
|
|
@@ -13,14 +12,17 @@
|
|
|
13
12
|
#
|
|
14
13
|
|
|
15
14
|
# Stdlib. imports
|
|
16
|
-
from typing import Union
|
|
17
15
|
|
|
18
16
|
|
|
19
17
|
class Bee:
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
params: list,
|
|
21
|
+
obj_fn_val: int | float,
|
|
22
|
+
stay_limit: int,
|
|
23
|
+
is_employer: bool = False,
|
|
24
|
+
):
|
|
25
|
+
"""Bee object: houses bee-specific information such as its current
|
|
24
26
|
set of parameters, the objective function value resulting from its,
|
|
25
27
|
current parameters, maximum number of cycles it can stay at its
|
|
26
28
|
current food source, and whether it is an employer bee or not
|
|
@@ -32,7 +34,7 @@ class Bee:
|
|
|
32
34
|
stay_limit (int): maximum number of cycles the bee can stay at its
|
|
33
35
|
current food source
|
|
34
36
|
is_employer (bool): `True` if employer, `False` if onlooker
|
|
35
|
-
|
|
37
|
+
"""
|
|
36
38
|
|
|
37
39
|
self._params = params
|
|
38
40
|
self._obj_fn_val = obj_fn_val
|
|
@@ -43,9 +45,9 @@ class Bee:
|
|
|
43
45
|
|
|
44
46
|
@property
|
|
45
47
|
def abandon(self) -> bool:
|
|
46
|
-
|
|
48
|
+
"""When called, increment how many times the bee has stayed at its
|
|
47
49
|
current food source; if reached stay limit, return `True` else `False`
|
|
48
|
-
|
|
50
|
+
"""
|
|
49
51
|
|
|
50
52
|
self._stay_count += 1
|
|
51
53
|
if self._stay_count >= self._stay_limit:
|
|
@@ -54,8 +56,8 @@ class Bee:
|
|
|
54
56
|
return False
|
|
55
57
|
|
|
56
58
|
@staticmethod
|
|
57
|
-
def calc_fitness(obj_fn_val:
|
|
58
|
-
|
|
59
|
+
def calc_fitness(obj_fn_val: int | float) -> float:
|
|
60
|
+
"""Static method: Bee.calc_fitness: Calculates fitness score based on
|
|
59
61
|
objective function value, using the equation:
|
|
60
62
|
|
|
61
63
|
fitness = 1 / (1 + ofv) if ofv >= 0
|
|
@@ -69,15 +71,15 @@ class Bee:
|
|
|
69
71
|
|
|
70
72
|
Returns:
|
|
71
73
|
float: resulting fitness score
|
|
72
|
-
|
|
74
|
+
"""
|
|
73
75
|
|
|
74
76
|
if obj_fn_val >= 0:
|
|
75
77
|
return 1 / (obj_fn_val + 1)
|
|
76
78
|
else:
|
|
77
79
|
return 1 + abs(obj_fn_val)
|
|
78
80
|
|
|
79
|
-
def is_better_food(self, obj_fn_val:
|
|
80
|
-
|
|
81
|
+
def is_better_food(self, obj_fn_val: int | float) -> bool:
|
|
82
|
+
"""Bee.is_better_food: determines if objective function value
|
|
81
83
|
resulting from a new food source is better than the bee's current food
|
|
82
84
|
source
|
|
83
85
|
|
|
@@ -87,7 +89,7 @@ class Bee:
|
|
|
87
89
|
|
|
88
90
|
Returns:
|
|
89
91
|
bool: `True` if better food source else `False`
|
|
90
|
-
|
|
92
|
+
"""
|
|
91
93
|
|
|
92
94
|
if self.calc_fitness(obj_fn_val) > self._fitness_score:
|
|
93
95
|
return True
|