diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..7f68d65 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,95 @@ +# Changelog + +All notable changes to this project are documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [0.3.0] - 2026-05-11 + +### Deprecated +- `AdaptiveModeAdamW` and `AdaptiveModeSGD` are deprecated and will be removed + in 0.4.0. Use `AEES`, which wraps any `torch.optim.Optimizer`. Importing or + instantiating either class now emits a `DeprecationWarning`. + +## [0.2.2] - 2026-05-10 + +Patch release with packaging cleanup and security hardening. + +### Added +- `MANIFEST.in` to refine PyPI source distribution contents. +- Runnable examples included in the source distribution (wheel kept minimal). + +### Changed +- Clarified README documentation around examples and repository layout. +- Updated test configuration. +- Pinned third-party GitHub Actions to commit SHAs to reduce CI supply-chain risk. + +### Fixed +- Replaced an unsafe `assert` state check in `Scheduler.step_end` with an + explicit runtime check. + +## [0.2.1] - 2026-05-07 + +Metadata-only patch. + +### Added +- `Changelog` URL under `[project.urls]` so PyPI surfaces a link to the + changelog in the project sidebar. + +## [0.2.0] - 2026-05-07 + +Breaking-change release that removes the `trend_phase` context mode. + +### Removed +- `pulseopt.TREND_PHASE_CONTEXT_BUCKETS`. +- `context_mode="trend_phase"`. +- `total_training_steps` kwarg on `AEES` and `StructuredEpisodeManager` (was + only used to compute the `trend_phase` boundary). +- `StructuredSelection.context_bucket_id`, `.context_bucket_name`, + `.context_phase`, `.context_trend`. +- Episode log keys `context_bucket_ids`, `context_bucket_names`, + `context_phases`, `context_trends`. + +### Changed +- `StructuredSelection` now exposes a single `context_bucket: str | None` + field; episode logs expose a single `context_buckets` list (only present + when `context_mode != "none"`). + +### Migration +- If you used `context_mode="trend_phase"`, switch to `"trend"` (or `"none"`). +- If you read any of the removed `StructuredSelection` fields or log keys, + read `context_bucket` / `context_buckets` instead. + +## [0.1.6rc1] - 2026-05-06 + +### Added +- CI/CD workflows and ruff lint/format configuration. +- Release smoke test for the CI/CD pipeline. + +## [0.1.5] - 2026-05-06 + +### Removed +- Files irrelevant to the PyPI package that remained from thesis experiments. + +### Changed +- Default `reward_instability_lambda` set to `0.0` in `reward.py`. + +## [0.1.4] - 2026-05-05 + +### Fixed +- Changed repo-relative paths to absolute. + +### Changed +- Set reward instability constant to `0.0`. + +[Unreleased]: https://github.com/davidkfoss/pulseopt/compare/v0.3.0...HEAD +[0.3.0]: https://github.com/davidkfoss/pulseopt/compare/v0.2.2...v0.3.0 +[0.2.2]: https://github.com/davidkfoss/pulseopt/compare/v0.2.1...v0.2.2 +[0.2.1]: https://github.com/davidkfoss/pulseopt/compare/v0.2.0...v0.2.1 +[0.2.0]: https://github.com/davidkfoss/pulseopt/compare/v0.1.6rc1...v0.2.0 +[0.1.6rc1]: https://github.com/davidkfoss/pulseopt/compare/v0.1.5...v0.1.6rc1 +[0.1.5]: https://github.com/davidkfoss/pulseopt/compare/v0.1.4...v0.1.5 +[0.1.4]: https://github.com/davidkfoss/pulseopt/releases/tag/v0.1.4 diff --git a/pyproject.toml b/pyproject.toml index 8aadf80..b45be5a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -45,7 +45,7 @@ dev = [ Homepage = "https://github.com/davidkfoss/pulseopt" Repository = "https://github.com/davidkfoss/pulseopt" Issues = "https://github.com/davidkfoss/pulseopt/issues" -Changelog = "https://github.com/davidkfoss/pulseopt/releases" +Changelog = "https://github.com/davidkfoss/pulseopt/blob/main/CHANGELOG.md" [tool.setuptools] include-package-data = true diff --git a/src/pulseopt/__init__.py b/src/pulseopt/__init__.py index 4e5465f..080e720 100644 --- a/src/pulseopt/__init__.py +++ b/src/pulseopt/__init__.py @@ -1,6 +1,6 @@ """Adaptive Episodic Exploration Scheduling package.""" -__version__ = "0.2.2" +__version__ = "0.3.0" from pulseopt.controller import ( TREND_CONTEXT_BUCKETS, diff --git a/src/pulseopt/optimizer.py b/src/pulseopt/optimizer.py index 86d6f7f..1531ba8 100644 --- a/src/pulseopt/optimizer.py +++ b/src/pulseopt/optimizer.py @@ -1,7 +1,14 @@ -"""Scheduler-compatible optimizer wrappers with AEES mode scaling.""" +"""Scheduler-compatible optimizer wrappers with AEES mode scaling. + +.. deprecated:: 0.3.0 + ``AdaptiveModeAdamW`` and ``AdaptiveModeSGD`` are deprecated and will be + removed in 0.4.0. Use :class:`pulseopt.AEES`, which wraps any + ``torch.optim.Optimizer``. +""" from __future__ import annotations +import warnings from collections.abc import Iterable from copy import deepcopy from typing import Any @@ -11,9 +18,18 @@ from pulseopt.types import CandidateConfig +_DEPRECATION_MESSAGE = ( + "{cls} is deprecated and will be removed in pulseopt 0.4.0. " + "Use pulseopt.AEES, which wraps any torch.optim.Optimizer." +) + class AdaptiveModeAdamW(torch.optim.AdamW): - """AdamW optimizer that applies AEES multipliers on top of scheduled bases.""" + """AdamW optimizer that applies AEES multipliers on top of scheduled bases. + + .. deprecated:: 0.3.0 + Use :class:`pulseopt.AEES` instead. Will be removed in 0.4.0. + """ def __init__( self, @@ -26,6 +42,11 @@ def __init__( mode: CandidateConfig | None = None, noise_seed: int | None = None, ) -> None: + warnings.warn( + _DEPRECATION_MESSAGE.format(cls="AdaptiveModeAdamW"), + DeprecationWarning, + stacklevel=2, + ) if not torch.isfinite(torch.tensor(float(lr))): raise ValueError("lr must be finite.") if lr <= 0.0: @@ -269,7 +290,11 @@ def _restore_base_list( class AdaptiveModeSGD(torch.optim.SGD): - """SGD optimizer that applies AEES multipliers on top of scheduled bases.""" + """SGD optimizer that applies AEES multipliers on top of scheduled bases. + + .. deprecated:: 0.3.0 + Use :class:`pulseopt.AEES` instead. Will be removed in 0.4.0. + """ def __init__( self, @@ -282,6 +307,11 @@ def __init__( mode: CandidateConfig | None = None, noise_seed: int | None = None, ) -> None: + warnings.warn( + _DEPRECATION_MESSAGE.format(cls="AdaptiveModeSGD"), + DeprecationWarning, + stacklevel=2, + ) if not torch.isfinite(torch.tensor(float(lr))): raise ValueError("lr must be finite.") if lr <= 0.0: diff --git a/tests/test_optimizer_wrapper.py b/tests/test_optimizer_wrapper.py index 8261054..6fe7316 100644 --- a/tests/test_optimizer_wrapper.py +++ b/tests/test_optimizer_wrapper.py @@ -2,11 +2,14 @@ from __future__ import annotations +import pytest import torch from pulseopt.optimizer import AdaptiveModeAdamW from pulseopt.types import CandidateConfig +pytestmark = pytest.mark.filterwarnings("ignore:AdaptiveModeAdamW is deprecated:DeprecationWarning") + def build_model(weight: float = 1.0) -> torch.nn.Linear: """Create a tiny deterministic linear model."""