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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
Binary file removed .DS_Store
Binary file not shown.
69 changes: 69 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
name: CI

on:
push:
branches: [main]
pull_request:

permissions:
contents: read

jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.9", "3.11"]

steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
cache: pip
- name: Install ALDes and dependencies
run: |
python -m pip install --upgrade pip
python -m pip install -e ".[test]"
- name: Verify installed commands and dependencies
run: |
python -m pip check
aldes-train --help
aldes-paper-subset --help
- name: Lint
run: |
python -m ruff check .
python -m ruff format --check .
- name: Test
env:
ALDES_DEVICE: cpu
ALDES_EVAL_WORKERS: "2"
run: python -m pytest -q

package:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v6
with:
python-version: "3.11"
cache: pip
- run: python -m pip install --upgrade pip build twine
- run: python -m build
- run: python -m twine check dist/*
- run: python -m venv /tmp/aldes-wheel
- run: /tmp/aldes-wheel/bin/python -m pip install dist/*.whl
- name: Validate the installed wheel outside the repository
working-directory: /tmp
run: |
/tmp/aldes-wheel/bin/python -m pip check
/tmp/aldes-wheel/bin/aldes-train --help
/tmp/aldes-wheel/bin/aldes-paper-subset --help
/tmp/aldes-wheel/bin/python - <<'PY'
import importlib.metadata
from run_paper_subset import DEFAULT_REFERENCE_DIR, _reference_result

assert importlib.metadata.version("aldes") == "2.0.0"
assert DEFAULT_REFERENCE_DIR.is_dir()
assert _reference_result(DEFAULT_REFERENCE_DIR, 1).size == 30
PY
26 changes: 26 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: release assets

on:
release:
types: [published]

permissions:
contents: write

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
ref: ${{ github.event.release.tag_name }}
- uses: actions/setup-python@v6
with:
python-version: "3.11"
- run: python -m pip install --upgrade pip build twine
- run: python -m build
- run: python -m twine check dist/*
- name: Attach distributions to the GitHub release
env:
GH_TOKEN: ${{ github.token }}
run: gh release upload "${{ github.event.release.tag_name }}" dist/* --clobber
15 changes: 15 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
.DS_Store
__pycache__/
*.py[cod]
.pytest_cache/
.ruff_cache/
.venv/
build/
dist/
*.egg-info/
logs/
experiments/
ela/
*.pt
*.pth
*.ckpt
43 changes: 43 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Changelog

All notable changes to ALDes are documented here.

## 2.0.0 - 2026-07-20

### Added

- A pure-Python execution path backed by AutoOptLib 1.3.0, with no MATLAB or
MATLAB Engine runtime dependency.
- Automatic PyTorch accelerator selection for NVIDIA CUDA, AMD ROCm, Apple
MPS, and CPU, with environment-variable overrides.
- Deterministic CPU multiprocessing for generated-algorithm and objective
evaluation while neural-network work remains on the selected PyTorch device.
- Explicit independent single-problem and feature-conditioned continual-design
modes. Independent design is the default and does not extract problem
features; continual design uses landscape features and EWC.
- A constrained generator and matching executor grammar: fork can follow
choose, each branch contains one search operation except that crossover may
be followed by mutation, and branches merge into one shared update.
- Command-line entry points for training and the time-bounded paper subset,
portable packaged reference results, tests, and cross-version CI.

### Changed

- Aligned the PBO training protocol with the paper: 100 PPO epochs, 16 sampled
algorithms per epoch, five PPO updates, 5,000 training evaluations, and an
optional 30-run/50,000-evaluation final test.
- Replaced the historical Python-to-MATLAB bridge with the public Python
AutoOptLib ALDes backend and its 32-token vocabulary.
- Made one PBO problem and one seed the safe default command-line scope.
- Moved historical plotting results from the removed MATLAB tree into
`draw/datas/reference_results` without changing their contents.

### Removed

- Bundled MATLAB source, MATLAB-specific bridge scripts, stale bytecode,
obsolete TorchText data loaders, and generated experiment artifacts.

## 1.0.0

- Original research release combining Python model training with MATLAB
algorithm execution.
40 changes: 40 additions & 0 deletions CITATION.cff
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
cff-version: 1.2.0
message: "If you use ALDes, please cite the paper below."
title: "ALDes: Automated Metaheuristic Algorithm Design with Autoregressive Learning"
type: software
version: 2.0.0
date-released: 2026-07-20
repository-code: "https://github.com/auto4opt/ALDes"
license: Apache-2.0
authors:
- family-names: Zhao
given-names: Qi
- family-names: Liu
given-names: Tengfei
- family-names: Yan
given-names: Bai
- family-names: Duan
given-names: Qiqi
- family-names: Yang
given-names: Jian
- family-names: Shi
given-names: Yuhui
preferred-citation:
type: article
title: "Automated Metaheuristic Algorithm Design with Autoregressive Learning"
year: 2024
doi: "10.1109/TEVC.2024.3464677"
journal: "IEEE Transactions on Evolutionary Computation"
authors:
- family-names: Zhao
given-names: Qi
- family-names: Liu
given-names: Tengfei
- family-names: Yan
given-names: Bai
- family-names: Duan
given-names: Qiqi
- family-names: Yang
given-names: Jian
- family-names: Shi
given-names: Yuhui
88 changes: 34 additions & 54 deletions EWC.py
Original file line number Diff line number Diff line change
@@ -1,60 +1,40 @@
from copy import deepcopy
"""Diagonal-Fisher elastic weight consolidation for continual ALDes."""

from __future__ import annotations

import torch
from torch import nn
from torch.nn import functional as F
from torch.autograd import Variable
import torch.utils.data


def variable(t: torch.Tensor, use_cuda=True, **kwargs):
if torch.cuda.is_available() and use_cuda:
t = t.cuda()
return Variable(t, **kwargs)


class EWC(object):
class EWC:
def __init__(self, model: nn.Module):

self.model = model

self.params = {n: p for n, p in self.model.named_parameters() if p.requires_grad}
self._means = {}
self._precision_matrices = None #self._diag_fisher()

for n, p in deepcopy(self.params).items():
self._means[n] = variable(p.data)

def _diag_fisher(self):
precision_matrices = {}
for n, p in deepcopy(self.params).items():
p.data.zero_()
precision_matrices[n] = variable(p.data)

#self.model.eval()
for n, p in self.model.named_parameters():
if p.grad != None:
precision_matrices[n].data += p.grad.data ** 2
precision_matrices = {n: p for n, p in precision_matrices.items()}
return precision_matrices

def update_diag_fisher(self,model):
precision_matrices = {}
for n, p in deepcopy(self.params).items():
p.data.zero_()
precision_matrices[n] = variable(p.data)
for n, p in model.named_parameters():
if p.grad != None:
precision_matrices[n].data += p.grad.data ** 2
precision_matrices = {n: p for n, p in precision_matrices.items()}
if self._precision_matrices is None:
self._precision_matrices = precision_matrices
else:
for key in precision_matrices:
self._precision_matrices[key] +=precision_matrices[key]
def penalty(self, model: nn.Module):
loss = 0
for n, p in model.named_parameters():
_loss = self._precision_matrices[n] * (p - self._means[n]) ** 2
loss += _loss.sum()
return loss*100
self._means = {
name: parameter.detach().clone()
for name, parameter in model.named_parameters()
if parameter.requires_grad
}
self._precision_matrices = {
name: torch.zeros_like(parameter)
for name, parameter in model.named_parameters()
if parameter.requires_grad
}

def update_diag_fisher(self, model: nn.Module) -> None:
"""Accumulate squared policy gradients for one sampled batch."""

for name, parameter in model.named_parameters():
if name in self._precision_matrices and parameter.grad is not None:
self._precision_matrices[name] += parameter.grad.detach().square()

def penalty(self, model: nn.Module) -> torch.Tensor:
loss = torch.zeros((), device=next(model.parameters()).device)
for name, parameter in model.named_parameters():
if name in self._precision_matrices:
loss = (
loss
+ (
self._precision_matrices[name]
* (parameter - self._means[name]).square()
).sum()
)
return loss
Loading