Skip to content

Commit

Permalink
Merge pull request #55 from ecrl/dev
Browse files Browse the repository at this point in the history
3.0.1 - updated install method, GitHub workflows, unit testing
  • Loading branch information
tjkessler authored Aug 2, 2023
2 parents afc1711 + 123301e commit f6e0c1e
Show file tree
Hide file tree
Showing 13 changed files with 315 additions and 354 deletions.
8 changes: 0 additions & 8 deletions .editorconfig

This file was deleted.

30 changes: 30 additions & 0 deletions .github/workflows/publish_to_pypi.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: Upload new PaDELPy version to PyPI

on:
release:
types: [published]

permissions:
contents: read

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up Python 3.11
uses: actions/setup-python@v3
with:
python-version: '3.11'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install build
- name: Build package
run: python -m build
- name: Publish package to PyPI
uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29
with:
user: __token__
password: ${{ secrets.PYPI_API_TOKEN }}
26 changes: 26 additions & 0 deletions .github/workflows/run_tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Run ECabc tests

on: [push]

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up Python 3.11
uses: actions/setup-python@v3
with:
python-version: '3.11'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install build
pip install pytest pytest-md
- name: Install package
run: python -m pip install .
- name: Run tests
uses: pavelzw/pytest-action@v2
with:
emoji: false
report-title: 'ECabc test report'
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
[![PyPI version](https://badge.fury.io/py/ecabc.svg)](https://badge.fury.io/py/ecabc)
[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/ECRL/ecabc/blob/master/LICENSE)
[![DOI](http://joss.theoj.org/papers/10.21105/joss.01420/status.svg)](https://doi.org/10.21105/joss.01420)
[![Build Status](https://dev.azure.com/uml-ecrl/package-management/_apis/build/status/ECRL.ecabc?branchName=master)](https://dev.azure.com/uml-ecrl/package-management/_build/latest?definitionId=5&branchName=master)

**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.

Expand Down Expand Up @@ -43,7 +42,7 @@ Note: if multiple Python releases are installed on your system (e.g. 2.7 and 3.7
### Method 2: From source
- Download the ECabc repository, navigate to the download location on the command line/terminal, and execute:
```
python setup.py install
pip install .
```

There are currently no additional dependencies for ECabc.
Expand Down
21 changes: 0 additions & 21 deletions azure-pipelines.yml

This file was deleted.

Binary file removed images/state.png
Binary file not shown.
27 changes: 27 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
[build-system]
requires = ["setuptools>=61.0"]
build-backend = "setuptools.build_meta"

[tool.setuptools.packages.find]
exclude = ["paper*"]

[project]
name = "ecabc"
version = "3.0.1"
authors = [
{ name="Sanskriti Sharma", email="[email protected]" },
{ name="Hernan Gelaf-Romer", email="[email protected]" },
{ name="Travis Kessler", email="[email protected]" },
]
description = "Artificial bee colony for function parameter optimization"
readme = "README.md"
requires-python = ">=3.11"
classifiers = [
"Programming Language :: Python :: 3.11",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
]

[project.urls]
"Homepage" = "https://github.com/ecrl/ecabc"
"Bug Tracker" = "https://github.com/ecrl/ecabc/issues"
16 changes: 0 additions & 16 deletions setup.py

This file was deleted.

238 changes: 231 additions & 7 deletions tests/test_all.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,235 @@
import unittest
import pytest

from unit_tests.bee import TestBee
from unit_tests.colony import TestColony
from unit_tests.parameter import TestParameter
from unit_tests.utils import TestUtils
import ecabc.utils as abc_utils
from ecabc import ABC, Bee, Parameter


if __name__ == '__main__':
def _objective_function(params):
return sum(params)

unittest.main()

def _objective_function_kwargs(params, my_kwarg):
return sum(params) + my_kwarg


# utils.py


def test_apply_mutation():
params = [Parameter(0, 10, True) for _ in range(3)]
curr_params = [5, 5, 5]
mut_params = abc_utils.apply_mutation(curr_params, params)
assert curr_params != mut_params


def test_call_obj_fn():
param_vals = [2, 2, 2]
ret_params, result = abc_utils.call_obj_fn(
param_vals, _objective_function, {}
)
assert result == 6
assert ret_params == param_vals
fn_args = {'my_kwarg': 2}
ret_params, result = abc_utils.call_obj_fn(
param_vals, _objective_function_kwargs, fn_args
)
assert result == 8
assert param_vals == ret_params


def test_choose_bee():

bee_1 = Bee([0], 100000000000000000000, 0)
bee_2 = Bee([0], 0, 0)
chosen_bee = abc_utils.choose_bee([bee_1, bee_2])
assert chosen_bee == bee_2


def test_determine_best_bee():

bee_1 = Bee([10], 10, 0)
bee_2 = Bee([0], 0, 0)
bee_2_fitness = bee_2._fitness_score
bee_2_ret_val = bee_2._obj_fn_val
bee_2_params = bee_2._params
_fit, _ret, _param = abc_utils.determine_best_bee([bee_1, bee_2])
assert _fit == bee_2_fitness
assert _ret == bee_2_ret_val
assert _param == bee_2_params


# parameter.py


def test_param_init():
param = Parameter(0, 10, False)
assert param._restrict is False
param = Parameter(0, 10)
assert param._restrict is True
assert param._dtype == int
assert param._min_val == 0
assert param._max_val == 10


def test_rand_val():
for _ in range(100):
param = Parameter(0, 10)
rand_val = param.rand_val
assert rand_val >= 0
assert rand_val <= 10
assert type(rand_val) == int
param = Parameter(0.0, 10.0)
rand_val = param.rand_val
assert type(rand_val) == float


def test_mutate():
param = Parameter(0, 10)
rand_val = param.rand_val
for _ in range(1000):
mutation = param.mutate(rand_val)
if mutation < 0 or mutation > 10:
raise ValueError('Mutation outside min/max bounds')
param = Parameter(0, 3, False)
rand_val = param.rand_val
outside_bounds = False
mutation = param.mutate(rand_val)
while True:
if mutation < 0 or mutation > 3:
outside_bounds = True
break
mutation = param.mutate(mutation)
assert outside_bounds is True


# abc.py


def test_colony_init():
c = ABC(10, _objective_function)
assert c._num_employers == 10
assert c._obj_fn == _objective_function
kwargs = {'my_kwarg': 2}
c = ABC(10, _objective_function_kwargs, kwargs)
assert c._obj_fn_args == kwargs
c = ABC(10, _objective_function, num_processes=8)
assert c._num_processes == 8
with pytest.raises(ReferenceError):
c = ABC(10, None)


def test_no_bees():
c = ABC(10, _objective_function)
assert c.best_fitness == 0
assert c.best_ret_val is None
assert c.best_params == {}
assert c.average_fitness is None
assert c.average_ret_val is None
with pytest.raises(RuntimeError):
c.search()


def test_add_parameter():
c = ABC(10, _objective_function)
c.add_param(0, 1)
c.add_param(2, 3)
c.add_param(4, 5)
assert len(c._params) == 3
assert c._params[0]._min_val == 0
assert c._params[0]._max_val == 1
assert c._params[1]._min_val == 2
assert c._params[1]._max_val == 3
assert c._params[2]._min_val == 4
assert c._params[2]._max_val == 5
assert c._params[0]._dtype == int
c.add_param(0.0, 1.0)
assert c._params[3]._dtype == float
assert c._params[0]._restrict is True
c.add_param(0, 1, False)
assert c._params[4]._restrict is False


def test_initialize():
c = ABC(10, _objective_function)
c.add_param(0, 10)
c.add_param(0, 10)
c.initialize()
assert len(c._bees) == 20


def test_search_and_stats():
c = ABC(10, _objective_function)
c.add_param(0, 0)
c.add_param(0, 0)
c.initialize()
for _ in range(50):
c.search()
assert c.best_fitness == 1
assert c.best_ret_val == 0
assert c.best_params == {'P0': 0, 'P1': 0}


def test_kwargs():
c = ABC(10, _objective_function_kwargs, {'my_kwarg': 2})
c.add_param(0, 0)
c.add_param(0, 0)
c.initialize()
for _ in range(50):
c.search()
assert c.best_ret_val == 2


def test_multiprocessing():
c = ABC(20, _objective_function, num_processes=4)
assert c._num_processes == 4
c.add_param(0, 10)
c.add_param(0, 10)
c.initialize()
c.search()


def test_custom_param_name():
c = ABC(20, _objective_function)
c.add_param(0, 10, name='int1')
c.add_param(0, 10, name='int2')
c.initialize()
for _ in range(50):
c.search()
assert c.best_params == {'int1': 0, 'int2': 0}


# bee.py


def test_bee_init():
bee = Bee([0, 0, 0], 0, 1, True)
assert bee._is_employer is True
bee = Bee([0, 0, 0], 0, 1)
assert bee._is_employer is False
assert bee._params == [0, 0, 0]
assert bee._obj_fn_val == 0
assert bee._fitness_score == 1
assert bee._stay_limit == 1


def test_abandon():
bee = Bee([0, 0, 0], 0, 2)
result = bee.abandon
assert result is False
result = bee.abandon
assert result is True


def test_calc_fitness():
result = Bee.calc_fitness(0)
assert result == 1
result = Bee.calc_fitness(-1)
assert result == 2


def test_is_better_food():
bee = Bee([0, 0, 1], 1, 1)
result = bee.is_better_food(0)
assert result is True
result = bee.is_better_food(2)
assert result is False
Loading

0 comments on commit f6e0c1e

Please sign in to comment.