Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ Bingo is an open-source Python package developed by NASA for symbolic regression
- `sympy`: Symbolic mathematics
- `scikit-learn>=1.1`: Machine learning utilities
- `dill>=0.2.9`: Serialization
- `smcpy>=0.1.4`: Statistical methods
- `pybind11`: Python-C++ bindings

### Build System
Expand All @@ -34,7 +33,6 @@ bingo/
│ ├── evaluation/ # Fitness evaluation
│ ├── evolutionary_algorithms/ # EA implementations
│ ├── evolutionary_optimizers/ # Optimization strategies (Island, Archipelago)
│ ├── local_optimizers/ # Local optimization methods
│ ├── selection/ # Selection operators (Tournament, etc.)
│ ├── stats/ # Statistics and tracking
│ ├── expressions/ # Expression representations and variation
Expand Down Expand Up @@ -74,7 +72,7 @@ bingo/
- **Variation strategies**:
- `VarOr`: Mutation OR crossover OR replication
- `VarAnd`: Mutation AND crossover
- **Fitness evaluation**: Vector-based with aggregation metrics (MAE, RMSE, MSE, etc.)
- **Fitness evaluation**: Fitness functions return scalar lower-is-better values

### Code Organization Principles
- Each module should have a clear, single responsibility
Expand Down Expand Up @@ -216,8 +214,8 @@ pytest tests
6. Add usage examples if applicable

### Adding a New Fitness Function
1. Create class inheriting from `FitnessFunction` or `VectorBasedFunction`
2. Implement `evaluate_fitness_vector(self, individual)` method
1. Create a class inheriting from `FitnessFunction`
2. Implement `__call__(self, individual)` to return a scalar lower-is-better value
3. Add docstring explaining the metric
4. Add tests in `tests/unit/evaluation/`

Expand Down
26 changes: 24 additions & 2 deletions .github/workflows/pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,26 @@ jobs:
- name: Clean-install and import source distribution
run: python -m pip install --upgrade pip && python -m pip install dist/*.tar.gz && python -c "import bingo"

smoke-test-evidence-extra:
name: Smoke test Evidence extra
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v4
with:
name: distributions
path: dist
- uses: actions/setup-python@v5
with:
python-version: "3.13"
- name: Clean-install and import Evidence extra
shell: bash
run: |
python -m pip install --upgrade pip
wheel=$(printf '%s\n' dist/*cp313-*manylinux*.whl)
python -m pip install --only-binary=:all: "${wheel}[evidence]"
python -c "import bingo.symbolic_regression; import smcpy"

installed-tests:
name: Installed artifact tests (Python ${{ matrix.python-version }})
needs: build
Expand All @@ -114,16 +134,18 @@ jobs:
include:
- python-version: "3.13"
wheel-tag: cp313
dependencies: ".[MPI,ONNX,TESTS,evidence]"
- python-version: "3.14"
wheel-tag: cp314
dependencies: ".[MPI,ONNX,TESTS,evidence]"
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: ./.github/actions/setup-bingo
with:
python-version: ${{ matrix.python-version }}
dependencies: ".[MPI,ONNX,TESTS]"
dependencies: ${{ matrix.dependencies }}
- uses: actions/download-artifact@v4
with:
name: distributions
Expand All @@ -138,7 +160,7 @@ jobs:

release-gate:
name: Verify release tag and artifact version
needs: [metadata, smoke-test-wheel, smoke-test-sdist, installed-tests]
needs: [metadata, smoke-test-wheel, smoke-test-sdist, smoke-test-evidence-extra, installed-tests]
if: startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
outputs:
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Bingo is an open source package for performing symbolic regression, though it
can be used as a general purpose evolutionary optimization package.

## Key Features
* Integrated local optimization strategies
* Expression-specific symbolic-regression fitting
* Parallel island evolution strategy implemented with mpi4py
* Coevolution of fitness predictors

Expand Down
38 changes: 0 additions & 38 deletions bingo/chromosomes/chromosome.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,41 +103,3 @@ def distance(self, other):
Distance from self to other
"""
raise NotImplementedError

def needs_local_optimization(self):
"""Does the `Chromosome` need local optimization

Returns
-------
bool
Whether `Chromosome` needs optimization
"""
raise NotImplementedError(
"This Chromosome cannot be used in local "
"optimization until its local optimization "
"interface has been implemented"
)

def get_number_local_optimization_params(self):
"""Get number of parameters in local optimization

Returns
-------
int
Number of parameters to be optimized
"""
return 0

def set_local_optimization_params(self, params):
"""Set local optimization parameters

Parameters
----------
params : list-like of numeric
Values to set the parameters to
"""
raise NotImplementedError(
"This Chromosome cannot be used in local "
"optimization until its local optimization "
"interface has been implemented"
)
82 changes: 2 additions & 80 deletions bingo/chromosomes/multiple_floats.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,60 +14,7 @@ class MultipleFloatChromosome(MultipleValueChromosome):
----------
values : list of floats
The genetic information stored in an individual chromosome.
needs_opt_list : list of ints
(optional) The indices of the `individual_list` in a
`chromosomes` object that are subject local optimization.
This list may be empty
"""
def __init__(self, values, needs_opt_list=None):
super().__init__(values)
if needs_opt_list is None:
needs_opt_list = []
self._needs_opt_list = needs_opt_list

def needs_local_optimization(self):
"""Does the individual need local optimization

Returns
-------
bool
Individual needs optimization
"""
if not self._needs_opt_list:
return False
return True

def get_number_local_optimization_params(self):
"""Get number of parameters in local optimization

Returns
-------
int
number of parameters to be optimized
"""
return len(self._needs_opt_list)

def set_local_optimization_params(self, params):
"""Set local optimization parameters

Parameters
----------
params : list-like of numeric
Values to set the parameters
"""
for param, index in zip(params, self._needs_opt_list):
self.values[index] = param

def get_local_optimization_params(self):
"""Gets local optimization_params

Returns
-------
list
"""
return [self.values[i] for i in self._needs_opt_list]


class MultipleFloatChromosomeGenerator(MultipleValueChromosomeGenerator):
"""Generation of a population of Multi-Value chromosomes

Expand All @@ -77,18 +24,7 @@ class MultipleFloatChromosomeGenerator(MultipleValueChromosomeGenerator):
A function that returns a randomly generated float value.
values_per_chromosome : int
The number of values that each chromosome will hold
needs_opt_list : list of ints
The indices of the `individual_list` in a `chromosomes` object
that are subject local optimization. This list may be empty
"""
def __init__(self, random_value_function, values_per_chromosome,
needs_opt_list=None):
super().__init__(random_value_function, values_per_chromosome)
if needs_opt_list is None:
needs_opt_list = []
self._check_opt_list_contains_feasible_values(needs_opt_list)
self._needs_opt_list = self._remove_duplicates(needs_opt_list)

def __call__(self):
"""Generation of a population of size `population_size`
of Multi-Value chromosomes with lists that contain
Expand All @@ -99,21 +35,7 @@ def __call__(self):
list of chromosomes :
The chromosomes which their values are generated by
`random_value_function` with the optimization list
`needs_opt_list`.
generated by `random_value_function`.
"""
random_list = self._generate_list(self._values_per_chromosome)
return MultipleFloatChromosome(random_list, self._needs_opt_list)

def _check_opt_list_contains_feasible_values(self, list_of_indices):
if not all(isinstance(x, int) for x in list_of_indices):
raise ValueError("The list of optimization indices must be \
unsigned integers.")
if list_of_indices and (min(list_of_indices) < 0 or
max(list_of_indices) >= self._values_per_chromosome):
raise ValueError("The list of optimization indices must be within \
the length of the list of values.")

@staticmethod
def _remove_duplicates(list_of_ints):
set_of_ints = set(list_of_ints)
return sorted(list(set_of_ints))
return MultipleFloatChromosome(random_list)
10 changes: 0 additions & 10 deletions bingo/chromosomes/multiple_values.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,16 +55,6 @@ def distance(self, other):
dist = sum(v1 != v2 for v1, v2 in zip(self.values, other.values))
return dist

def get_number_local_optimization_params(self):
raise NotImplementedError

def needs_local_optimization(self):
raise NotImplementedError

def set_local_optimization_params(self, params):
raise NotImplementedError


class MultipleValueChromosomeGenerator(Generator):
"""Generation of a population of Multi-Value chromosomes

Expand Down
Loading
Loading