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
1 change: 1 addition & 0 deletions .python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.11
25 changes: 25 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Include repository examples in the source distribution
recursive-include examples *.py

# Exclude development and environment files
exclude uv.lock
exclude .python-version
exclude .gitignore
exclude .env
exclude .env.*
exclude .DS_Store

# Exclude local/build artifacts
prune .venv
prune dist
prune build
prune *.egg-info
prune .pytest_cache
prune .ruff_cache
prune .mypy_cache
prune .coverage
prune htmlcov

# Exclude repository/CI metadata from the source distribution
prune .git
prune .github
30 changes: 17 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,16 +61,16 @@ Axes with a single candidate are treated as fixed constants and get no controlle

## Common knobs

| Argument | Meaning |
| --------------------------- | --------------------------------------------------------------------------------------------------- |
| `lr_candidates` | Multipliers tried against the optimizer's base LR. |
| `noise_candidates` | Gradient-noise std values; `0.0` means no noise. |
| `episode_length` | Steps per episode; reward is computed at episode end. |
| `lr_scheduler` | Optional `torch.optim.lr_scheduler.*` instance; `step()` is called for you. |
| `structured_control_mode` | `"independent"` (default) or `"conditional"` (one noise controller per LR arm). |
| `context_mode` | `"none"` (default) or `"trend"`. |
| `reward_instability_lambda` | Weight on the variance penalty in the reward. |
| `seed` | Seeds controllers and gradient-noise generators. |
| Argument | Meaning |
| --------------------------- | ------------------------------------------------------------------------------- |
| `lr_candidates` | Multipliers tried against the optimizer's base LR. |
| `noise_candidates` | Gradient-noise std values; `0.0` means no noise. |
| `episode_length` | Steps per episode; reward is computed at episode end. |
| `lr_scheduler` | Optional `torch.optim.lr_scheduler.*` instance; `step()` is called for you. |
| `structured_control_mode` | `"independent"` (default) or `"conditional"` (one noise controller per LR arm). |
| `context_mode` | `"none"` (default) or `"trend"`. |
| `reward_instability_lambda` | Weight on the variance penalty in the reward. |
| `seed` | Seeds controllers and gradient-noise generators. |

`AEES.step_end(loss)` raises `ValueError` on a non-finite loss. If you train with mixed precision (`torch.cuda.amp` / `torch.amp`) and expect occasional NaN/Inf during loss-scaling backoff, guard the call yourself or skip the step.

Expand All @@ -82,14 +82,18 @@ Axes with a single candidate are treated as fixed constants and get no controlle

## Runnable examples

End-to-end demos that use only the public `pulseopt` API (`from pulseopt import AEES`) on real datasets. Each script is short, self-contained, and runs against a `pip install pulseopt`-only environment — no helpers from this repository are imported. Each writes a per-epoch text log to the path given by `--output`.
End-to-end demos that use only the public `pulseopt` API (`from pulseopt import AEES`) on real datasets. The examples are included in the source distribution published to PyPI and are also available in the GitHub repository. They are written to run against a normal `pip install pulseopt` environment — no internal helpers from this repository are imported.

Each script is short, self-contained, and writes a per-epoch text log to the path given by `--output`.

- [`examples/task_cifar100.py`](https://github.com/davidkfoss/pulseopt/blob/main/examples/task_cifar100.py) — ResNet-18 on CIFAR-100. Picks AdamW or SGD via `--optimizer`. Needs `torch`, `torchvision`.
- [`examples/task_sst2.py`](https://github.com/davidkfoss/pulseopt/blob/main/examples/task_sst2.py) — DistilBERT on GLUE SST-2. AdamW. Needs `torch`, `transformers`, `datasets`.
- [`examples/task_agnews.py`](https://github.com/davidkfoss/pulseopt/blob/main/examples/task_agnews.py) — DistilBERT on AG News. AdamW. Needs `torch`, `transformers`, `datasets`.

```bash
pip install pulseopt torch torchvision
git clone https://github.com/davidkfoss/pulseopt.git
cd pulseopt
pip install "pulseopt[examples]"
python examples/task_cifar100.py --epochs 10 --output cifar100.log
```

Expand All @@ -98,7 +102,7 @@ These are the recommended starting point if you want to see how AEES plugs into
## Repo layout

- [`src/pulseopt/`](https://github.com/davidkfoss/pulseopt/tree/main/src/pulseopt) — published library: controllers, episode manager, reward, optimizer wrappers, and the `AEES` high-level API.
- [`examples/`](https://github.com/davidkfoss/pulseopt/tree/main/examples) — short, self-contained PyPI-side demos using the public `AEES` API.
- [`examples/`](https://github.com/davidkfoss/pulseopt/tree/main/examples) — short, self-contained demos using only the public `AEES` API. Included in the PyPI source distribution, but not installed as part of the wheel.
- [`tests/`](https://github.com/davidkfoss/pulseopt/tree/main/tests) — regression and unit tests.

## Development
Expand Down
17 changes: 12 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,15 @@ classifiers = [
dependencies = ["torch>=2.0"]

[project.optional-dependencies]
examples = ["torchvision>=0.22", "transformers>=4.52", "datasets>=3.6"]
dev = ["pytest>=8,<9", "ruff>=0.6"]
examples = [
"torchvision>=0.22",
"transformers>=4.52",
"datasets>=3.6",
]
dev = [
"pytest>=9,<10",
"ruff>=0.6",
]

[project.urls]
Homepage = "https://github.com/davidkfoss/pulseopt"
Expand All @@ -41,7 +48,6 @@ Issues = "https://github.com/davidkfoss/pulseopt/issues"
Changelog = "https://github.com/davidkfoss/pulseopt/releases"

[tool.setuptools]
package-dir = { "" = "src" }
include-package-data = true

[tool.setuptools.dynamic]
Expand All @@ -53,9 +59,10 @@ where = ["src"]
[tool.setuptools.package-data]
pulseopt = ["py.typed"]

[tool.pytest.ini_options]
[tool.pytest]
minversion = "9.0"
testpaths = ["tests"]
pythonpath = ["."]
pythonpath = ["src"]

[tool.ruff]
line-length = 100
Expand Down
2 changes: 1 addition & 1 deletion src/pulseopt/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Adaptive Episodic Exploration Scheduling package."""

__version__ = "0.2.1"
__version__ = "0.2.2"

from pulseopt.controller import (
TREND_CONTEXT_BUCKETS,
Expand Down
Loading