opal-optimizer 0.3.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- opal_optimizer-0.3.5/CHANGELOG.md +66 -0
- opal_optimizer-0.3.5/LICENSE +21 -0
- opal_optimizer-0.3.5/MANIFEST.in +7 -0
- opal_optimizer-0.3.5/PKG-INFO +232 -0
- opal_optimizer-0.3.5/README.md +207 -0
- opal_optimizer-0.3.5/docs/GUIDE.md +665 -0
- opal_optimizer-0.3.5/examples/README.md +63 -0
- opal_optimizer-0.3.5/examples/cifar10_resnet.py +455 -0
- opal_optimizer-0.3.5/examples/diffusion_mnist.py +217 -0
- opal_optimizer-0.3.5/examples/goldfish_swahili/README.md +81 -0
- opal_optimizer-0.3.5/examples/gpt_small.py +430 -0
- opal_optimizer-0.3.5/examples/mnist_mlp.py +154 -0
- opal_optimizer-0.3.5/examples/modded_nanogpt/README.md +89 -0
- opal_optimizer-0.3.5/examples/modded_nanogpt/opal.patch +76 -0
- opal_optimizer-0.3.5/examples/overfit/README.md +82 -0
- opal_optimizer-0.3.5/examples/overfit/records.py +175 -0
- opal_optimizer-0.3.5/examples/overfit/resnet.py +45 -0
- opal_optimizer-0.3.5/examples/overfit/splits.py +170 -0
- opal_optimizer-0.3.5/examples/overfit/train.py +303 -0
- opal_optimizer-0.3.5/examples/raev2_b/README.md +100 -0
- opal_optimizer-0.3.5/examples/sit_b2/README.md +88 -0
- opal_optimizer-0.3.5/examples/sit_b2/opal.patch +72 -0
- opal_optimizer-0.3.5/opal/__init__.py +7 -0
- opal_optimizer-0.3.5/opal/_version.py +3 -0
- opal_optimizer-0.3.5/opal/agent.py +394 -0
- opal_optimizer-0.3.5/opal/backends/__init__.py +31 -0
- opal_optimizer-0.3.5/opal/backends/_codex_client.py +450 -0
- opal_optimizer-0.3.5/opal/backends/_privacy.py +116 -0
- opal_optimizer-0.3.5/opal/backends/base.py +248 -0
- opal_optimizer-0.3.5/opal/backends/claude.py +665 -0
- opal_optimizer-0.3.5/opal/backends/codex.py +356 -0
- opal_optimizer-0.3.5/opal/backends/mailbox.py +1034 -0
- opal_optimizer-0.3.5/opal/backends/unavailable.py +161 -0
- opal_optimizer-0.3.5/opal/cli.py +396 -0
- opal_optimizer-0.3.5/opal/context.py +111 -0
- opal_optimizer-0.3.5/opal/defaults.json +11 -0
- opal_optimizer-0.3.5/opal/defaults.py +54 -0
- opal_optimizer-0.3.5/opal/estimate.py +120 -0
- opal_optimizer-0.3.5/opal/hf.py +109 -0
- opal_optimizer-0.3.5/opal/misc/__init__.py +2 -0
- opal_optimizer-0.3.5/opal/misc/aggressive.py +157 -0
- opal_optimizer-0.3.5/opal/misc/audit.py +84 -0
- opal_optimizer-0.3.5/opal/misc/boundary.py +226 -0
- opal_optimizer-0.3.5/opal/misc/checkpoint.py +399 -0
- opal_optimizer-0.3.5/opal/misc/ddp.py +147 -0
- opal_optimizer-0.3.5/opal/misc/integrity.py +263 -0
- opal_optimizer-0.3.5/opal/misc/nested.py +67 -0
- opal_optimizer-0.3.5/opal/misc/neutral_analysis.py +166 -0
- opal_optimizer-0.3.5/opal/misc/precision.py +210 -0
- opal_optimizer-0.3.5/opal/misc/recovery.py +231 -0
- opal_optimizer-0.3.5/opal/misc/schedulers.py +214 -0
- opal_optimizer-0.3.5/opal/misc/setup.py +253 -0
- opal_optimizer-0.3.5/opal/misc/skip.py +165 -0
- opal_optimizer-0.3.5/opal/misc/taskfacts.py +293 -0
- opal_optimizer-0.3.5/opal/misc/waiting.py +58 -0
- opal_optimizer-0.3.5/opal/programs.py +482 -0
- opal_optimizer-0.3.5/opal/prompts/example_updater.py +252 -0
- opal_optimizer-0.3.5/opal/prompts/neutral.md +206 -0
- opal_optimizer-0.3.5/opal/report.py +1001 -0
- opal_optimizer-0.3.5/opal/report_page.py +175 -0
- opal_optimizer-0.3.5/opal/report_privacy.py +159 -0
- opal_optimizer-0.3.5/opal/results.py +80 -0
- opal_optimizer-0.3.5/opal/schedule.py +134 -0
- opal_optimizer-0.3.5/opal/summaries.py +224 -0
- opal_optimizer-0.3.5/opal/wrapper.py +1674 -0
- opal_optimizer-0.3.5/opal_optimizer.egg-info/PKG-INFO +232 -0
- opal_optimizer-0.3.5/opal_optimizer.egg-info/SOURCES.txt +72 -0
- opal_optimizer-0.3.5/opal_optimizer.egg-info/dependency_links.txt +1 -0
- opal_optimizer-0.3.5/opal_optimizer.egg-info/entry_points.txt +2 -0
- opal_optimizer-0.3.5/opal_optimizer.egg-info/requires.txt +25 -0
- opal_optimizer-0.3.5/opal_optimizer.egg-info/top_level.txt +1 -0
- opal_optimizer-0.3.5/pyproject.toml +47 -0
- opal_optimizer-0.3.5/setup.cfg +4 -0
- opal_optimizer-0.3.5/skills/train-with-agent/SKILL.md +69 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.3.5 (2026-10-21)
|
|
4
|
+
|
|
5
|
+
- Reject uneven DDP inputs on every rank through the optimizer's Joinable interface instead of hanging (#2).
|
|
6
|
+
- Update installation, LightningDiT results and run records, remove the overfit result, and document mailbox leases, checkpoint state and the unfreezing workaround.
|
|
7
|
+
- Exclude local opal_runs artifacts from the wheel and source distribution.
|
|
8
|
+
|
|
9
|
+
- Normalize logged NumPy metrics and allowlist checkpoint observations for safe plain and Accelerate resumes (B1).
|
|
10
|
+
- Send SSH lease arguments and large answers over stdin; failed answer retries no longer block new requests (B2).
|
|
11
|
+
- Clear and release claims after SSH failures; let a restarted broker reclaim its own lease with generation fencing (B3).
|
|
12
|
+
- Keep the remote lease helper compatible with Python 3.6+ and check python3 availability in mailbox doctor (B4).
|
|
13
|
+
- Reclaim the same broker's lease immediately after restart instead of waiting for the stale timeout (F1).
|
|
14
|
+
- Log mailbox lease backoff once per request (F2).
|
|
15
|
+
- Keep aggressive LR interpolation on-device with tensor-list weights and exact zero/one endpoints (F3).
|
|
16
|
+
- Normalize NumPy updater state without calling rejected container hooks; record dropped fields in events and HTML, including quiet runs (F4).
|
|
17
|
+
|
|
18
|
+
- Check updater state with a closed allowlist without invoking custom serialization hooks.
|
|
19
|
+
- Redact form-encoded bearers, access tokens, triple-quoted, adjacent and bytes literals in diagnostics.
|
|
20
|
+
- Serialize mailbox lease takeovers and fence stale renewals, releases and responses by generation (#1).
|
|
21
|
+
- Save pending losses, analysis reports and authorized metrics so checkpoint resumes preserve observations (#3).
|
|
22
|
+
|
|
23
|
+
- Preserve Accelerate's public optimizer state, parameter groups and defaults through a checkpoint adapter.
|
|
24
|
+
- Resolve conjugate and negative parameter views before DDP byte fingerprints.
|
|
25
|
+
- Accept weights-only-safe updater values and preserve the starting updater exemption after analysis edits.
|
|
26
|
+
- Redact credentials inside nested escaped JSON, exception reprs and adjacent string fragments through the shared filter.
|
|
27
|
+
- Restore legacy Accelerate raw optimizer checkpoints with one warning about missing OPAL programs and progress.
|
|
28
|
+
- Preserve ordinary model names, identifiers, hashes, base64 code and credential-free URLs during redaction.
|
|
29
|
+
- Bound noncontiguous parameter fingerprint copies by slicing before flattening.
|
|
30
|
+
- Keep pending and unreported decision costs unknown in summaries and HTML until a numeric charge arrives.
|
|
31
|
+
|
|
32
|
+
- Redact API keys and bearer tokens from backend diagnostics, decision records, prompts, logs and HTML reports.
|
|
33
|
+
- Reject raw optimizer parameter-set changes after wrapping, before step or zero_grad (#5).
|
|
34
|
+
- Reject GradScaler steps on wrappers combining multiple optimizers.
|
|
35
|
+
- Include OPAL programs, versions and update counts in Accelerate save_state/load_state checkpoints.
|
|
36
|
+
- Share one version source across package metadata, runtime, Codex client and provenance.
|
|
37
|
+
- Update wrap docstrings for cost-budget stopping and the Codex backend.
|
|
38
|
+
- Advance truncated loss-window indices and record omitted observation counts.
|
|
39
|
+
- Settle cancelled decisions with a bounded wait and include late charges in reports.
|
|
40
|
+
- Discard pending wake reasons when no further agent call can use them.
|
|
41
|
+
- Reject unsupported or cyclic agent updater state before installation and checkpointing.
|
|
42
|
+
- Compare ordered parameter bytes with SHA-256 in DDP consistency checks.
|
|
43
|
+
|
|
44
|
+
- Resolve repository documentation from test files for non-editable installs.
|
|
45
|
+
- Set tested direct dependency lower bounds and require Python 3.10 or newer.
|
|
46
|
+
- Make the mailbox cancellation test use an instance-level wait.
|
|
47
|
+
- Publish complete mailbox claims atomically so concurrent brokers cannot run the same request twice.
|
|
48
|
+
- Use file rendezvous in the DDP crash-resume test to avoid ephemeral-port races.
|
|
49
|
+
- Include docs, skills, examples and the changelog in the source distribution.
|
|
50
|
+
- Add MNIST --max-cost-usd and --no-summarize flags.
|
|
51
|
+
- Run CPU CI across Linux, macOS and supported Python versions, including direct dependency minima.
|
|
52
|
+
|
|
53
|
+
## 0.3.4 (2026-10-21)
|
|
54
|
+
|
|
55
|
+
- Stop agent calls when the remaining cost budget is below the estimated call cost (#6).
|
|
56
|
+
- Apply the default waiting cap to the remaining schedule after early decisions, excluding agent waits from training time (#4).
|
|
57
|
+
|
|
58
|
+
## 0.3.3 (2026-10-21)
|
|
59
|
+
|
|
60
|
+
First public release.
|
|
61
|
+
|
|
62
|
+
- `opal.wrap` turns a PyTorch optimizer into a reactive optimizer: a coding agent (Claude Code by default) reads the
|
|
63
|
+
training run and rewrites the update rule and its own analysis while the model trains.
|
|
64
|
+
- Examples on CPU and one GPU, and the reproductions in the README (SiT-B/2, RAEv2-B, Goldfish, modded-nanogpt).
|
|
65
|
+
- `opal report`, `opal show`, `opal estimate`, `opal doctor`; the Hugging Face Trainer through `opal.hf.OpalTrainer`.
|
|
66
|
+
- An experimental Codex backend (`backend="codex"`).
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Xingjian Bai, Jia-Hao Liang, Jayden Teoh, Vincent Sitzmann
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: opal-optimizer
|
|
3
|
+
Version: 0.3.5
|
|
4
|
+
Summary: A coding agent inside your training run, rewriting the optimizer while it trains
|
|
5
|
+
Author: Xingjian Bai, Jia-Hao Liang, Jayden Teoh, Vincent Sitzmann
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Dist: torch>=2.5; python_version < "3.13"
|
|
11
|
+
Requires-Dist: torch>=2.6; python_version >= "3.13"
|
|
12
|
+
Requires-Dist: numpy>=1.26.4
|
|
13
|
+
Provides-Extra: examples
|
|
14
|
+
Requires-Dist: torchvision>=0.20; python_version < "3.13" and extra == "examples"
|
|
15
|
+
Requires-Dist: torchvision>=0.21; python_version >= "3.13" and extra == "examples"
|
|
16
|
+
Requires-Dist: numpy>=1.26.4; extra == "examples"
|
|
17
|
+
Requires-Dist: pyarrow>=15.0; extra == "examples"
|
|
18
|
+
Requires-Dist: pillow>=10.2; extra == "examples"
|
|
19
|
+
Provides-Extra: test
|
|
20
|
+
Requires-Dist: pytest>=8.2; extra == "test"
|
|
21
|
+
Provides-Extra: hf
|
|
22
|
+
Requires-Dist: transformers>=4.57.1; extra == "hf"
|
|
23
|
+
Requires-Dist: accelerate>=1.4.0; extra == "hf"
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
<h1 align="center">OPAL: a reactive optimizer</h1>
|
|
28
|
+
|
|
29
|
+
<p align="center"><b>A PyTorch optimizer wrapper that lets a coding agent change the optimizer while your model trains.</b></p>
|
|
30
|
+
|
|
31
|
+
<p align="center">
|
|
32
|
+
<a href="https://xingjianbai.com/reactive-optimizers/">Blog post</a> ·
|
|
33
|
+
<a href="docs/GUIDE.md">Guide</a> ·
|
|
34
|
+
<a href="#start-with-your-coding-agent">Start with your agent</a> ·
|
|
35
|
+
<a href="examples/README.md">Examples</a>
|
|
36
|
+
</p>
|
|
37
|
+
|
|
38
|
+
Most networks are trained with an optimizer that is fixed before training starts. OPAL (OPtimizer with an Agent in
|
|
39
|
+
the Loop) turns the optimizer into a program that an agent keeps rewriting during the run. You wrap your PyTorch
|
|
40
|
+
optimizer in one line. Every so often the agent (Claude Code) reads what the run reports and decides whether to
|
|
41
|
+
rewrite two small programs: the optimizer program, which turns gradients into updates at every step, and an analysis
|
|
42
|
+
program, which measures whatever the agent wants to look at.
|
|
43
|
+
|
|
44
|
+
The agent can change anything that can be written as optimizer code: the update rule of each parameter group, the
|
|
45
|
+
learning rates and their schedules, momentum and β₂, preconditioning, clipping, guards against loss spikes. It is
|
|
46
|
+
asked not to touch your model, data, loss, batch size, number of steps or evaluation. Its programs run inside your
|
|
47
|
+
training process, so this is a rule, not a sandbox (see [Safety](docs/GUIDE.md#safety)). Until the agent changes
|
|
48
|
+
something, the wrapped run is bit-for-bit the same as the plain one.
|
|
49
|
+
|
|
50
|
+
## Start with your coding agent
|
|
51
|
+
|
|
52
|
+
Clone this repository, open your own project in a coding agent (Claude Code, Codex, Cursor or similar) and paste this
|
|
53
|
+
prompt. The agent installs OPAL, wires it into your training script, asks you what it cannot know, and checks that
|
|
54
|
+
nothing changed before anything costs money.
|
|
55
|
+
|
|
56
|
+
```text
|
|
57
|
+
I want to train my model with OPAL, a reactive optimizer: a coding agent that rewrites the optimizer while the
|
|
58
|
+
model trains. OPAL is cloned at <PATH TO OPAL>. Please set it up for this project:
|
|
59
|
+
|
|
60
|
+
1. Read <PATH TO OPAL>/README.md and <PATH TO OPAL>/skills/train-with-agent/SKILL.md, and follow the skill. Do not
|
|
61
|
+
edit any file inside <PATH TO OPAL>; configure everything through opal.wrap arguments.
|
|
62
|
+
2. Install OPAL into the Python environment this project trains with (pip install -e <PATH TO OPAL>), and
|
|
63
|
+
run `opal doctor` (free, offline) to check that torch and Claude Code are ready on the machine that will run
|
|
64
|
+
training; run `opal doctor --live` (one tiny paid agent call) only once I approve an agent run. If Claude Code is
|
|
65
|
+
missing or not logged in there, tell me what to run; do not log in for me.
|
|
66
|
+
3. Find my training script and loop. Tell me what you found (optimizer, scheduler, number of steps, single GPU or
|
|
67
|
+
DDP, gradient clipping, mixed precision, checkpoints) and anything OPAL does not support.
|
|
68
|
+
4. Ask me for the goal sentence: what the run trains and what it is judged by at the end. Do not invent the metric.
|
|
69
|
+
5. Wrap the optimizer and show me the diff before running anything.
|
|
70
|
+
6. Smoke test: about 100 steps unwrapped and 100 steps wrapped with backend="keep" (no agent, no cost); the losses
|
|
71
|
+
must match step for step. Show me both. If I only asked for a dry run, stop here.
|
|
72
|
+
7. Before the real run, time an update in the smoke test and run `opal estimate --total-steps N --sec-per-step S`;
|
|
73
|
+
show me what it prints (how long training will wait for the agent, also as a multiple of the run's own time, and
|
|
74
|
+
the cost). Ask me for a cost cap (max_cost_usd) and how many times the agent should decide, and wait for my
|
|
75
|
+
go-ahead; then give me the exact command to launch it, and how to follow it (opal show, opal report).
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Start by hand
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
pip install opal-optimizer # or: git clone, then pip install -e ".[examples]"
|
|
82
|
+
opal doctor # torch and Claude Code ready?
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
The package is called `opal-optimizer` on PyPI, because `opal` there is an unrelated web framework. Both install as `import opal`, so do not install both.
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
import torch, opal
|
|
89
|
+
|
|
90
|
+
opt = torch.optim.AdamW(model.parameters(), lr=3e-4) # your optimizer, unchanged
|
|
91
|
+
sched = torch.optim.lr_scheduler.CosineAnnealingLR(opt, T_max=steps) # your scheduler, unchanged
|
|
92
|
+
opt = opal.wrap(opt, model, scheduler=sched, total_steps=steps,
|
|
93
|
+
goal="ResNet-18 on CIFAR-10; judged by test accuracy after the last update",
|
|
94
|
+
backend="keep") # a dry run: no agent, no cost. For the agent run, use max_cost_usd=20 instead.
|
|
95
|
+
|
|
96
|
+
for step in range(steps):
|
|
97
|
+
loss = criterion(model(x), y)
|
|
98
|
+
loss.backward()
|
|
99
|
+
opt.step(loss=loss) # pass the loss so the agent sees it
|
|
100
|
+
opt.zero_grad()
|
|
101
|
+
sched.step()
|
|
102
|
+
if step % 500 == 0:
|
|
103
|
+
opt.log(test_acc=evaluate(model)) # recorded for you, not shown to the agent
|
|
104
|
+
opt.close()
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
`goal` is one sentence: what you train and how it is judged. Then:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
python train.py # backend="keep": must match the plain run exactly
|
|
111
|
+
opal doctor --live # one tiny agent call to check the setup
|
|
112
|
+
opal estimate --total-steps 10000 --sec-per-step 0.05 # waiting time and cost, before you launch
|
|
113
|
+
python train.py # with max_cost_usd=20: the agent rewrites the optimizer
|
|
114
|
+
opal show opal_runs/<folder> # one line per decision
|
|
115
|
+
opal report opal_runs/<folder> # one HTML page: loss, learning rates, rationales, code diffs
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
The guide shows how to check the keep run against your plain run byte for byte, and how to use the Hugging Face
|
|
119
|
+
Trainer: [Try it without an agent](docs/GUIDE.md#try-it-without-an-agent).
|
|
120
|
+
|
|
121
|
+
## Prerequisites
|
|
122
|
+
|
|
123
|
+
- **A PyTorch training script** that uses `torch.optim` optimizers, on CPU, one GPU or several GPUs of one node (DDP).
|
|
124
|
+
Python 3.10 or newer, PyTorch 2.5 or newer (2.6 on Python 3.13).
|
|
125
|
+
- **For an agent run, Claude Code, logged in on the machine that runs rank 0.** Install
|
|
126
|
+
[Claude Code](https://docs.claude.com/en/docs/claude-code), run `claude` once and log in with a Claude subscription
|
|
127
|
+
or an API key. The agent's usage is billed to that account. `max_cost_usd` caps it; there is no cap by default.
|
|
128
|
+
The short summaries of code changes in the report are not counted (`summarize=False` turns them off). A dry run
|
|
129
|
+
with `backend="keep"` needs neither and makes no model calls.
|
|
130
|
+
- **No internet or no login on the GPU machine?** Run the agent on your laptop or a login node and connect it to the
|
|
131
|
+
training job through a shared folder (see [the mailbox broker](docs/GUIDE.md#gpu-machines-without-an-agent)).
|
|
132
|
+
|
|
133
|
+
## Results
|
|
134
|
+
|
|
135
|
+
Each number is one run, and each agent started from the task's own recipe:
|
|
136
|
+
|
|
137
|
+
| Example | Measure | Official recipe | Reactive optimizer |
|
|
138
|
+
|---|---|---|---|
|
|
139
|
+
| SiT-B/2, ImageNet 256, 400K updates | FID-50K ↓ | 35.7 (our evaluator) | 23.6 |
|
|
140
|
+
| RAEv2-B, ImageNet 256, 20 epochs | FID-50K ↓ | 3.39 | 3.10 (prompt asked for efficiency), 3.46 (neutral prompt); rerun on OPAL 0.3: 3.19 |
|
|
141
|
+
| Goldfish 125M GPT, Swahili | FLORES-200 bits ↓ | 79.37 | 77.60; rerun on OPAL 0.3: 77.25 |
|
|
142
|
+
| modded-nanogpt record #36 | validation loss ↓ | 3.2781 | 3.2749 |
|
|
143
|
+
| LightningDiT-B/1, ImageNet 256, 160 epochs | FID-50K ↓ (with guidance) | 3.22 | rerun on OPAL 0.3: 2.31 |
|
|
144
|
+
|
|
145
|
+
It does not always help: a run of modded-nanogpt on OPAL 0.2.0 ended at 3.2878, worse than record #36. Your
|
|
146
|
+
numbers may also differ from ours, because the agent can choose differently from one run to the next. The
|
|
147
|
+
[examples page](examples/README.md) has every number with its version, hardware, time and cost, the small CPU and
|
|
148
|
+
one-GPU scripts, and how to rerun each setting.
|
|
149
|
+
|
|
150
|
+
Our run records and final weights are on Hugging Face: [snow-leopard/opal-results](https://huggingface.co/datasets/snow-leopard/opal-results).
|
|
151
|
+
|
|
152
|
+
## How it works
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
training script ──grads──▶ updater.py ──▶ parameters (every step, every rank)
|
|
156
|
+
│ ▲
|
|
157
|
+
└──observations──▶ analysis.py ──reports──▶ agent (rank 0, every `every` steps / at decisions)
|
|
158
|
+
▲ │
|
|
159
|
+
└──── new programs ◀───┘ (at decision points)
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
- **Two programs.** `updater.py` starts as a call to your own optimizer and scheduler. `analysis.py` starts with a few
|
|
163
|
+
statistics of the loss and the gradients. The agent edits both in `opal_runs/<folder>/workspace/`.
|
|
164
|
+
- **One conversation for the whole run.** The agent remembers what it tried, what it predicted and what happened. Each
|
|
165
|
+
decision ends with a short rationale: what it saw, what it changed and what it expects.
|
|
166
|
+
- **Safe swaps.** A new optimizer program takes over at the next step, on every GPU at once, and runs on trial for 20
|
|
167
|
+
steps. If it raises an error, the run goes back to the previous program. A failure at its first step is undone
|
|
168
|
+
exactly when a copy of the parameters and optimizer state fits in `snapshot_limit_gb` (default 2); a later failure
|
|
169
|
+
is not undone, and the agent is told. Every version is kept in `programs/`.
|
|
170
|
+
- **Training waits for the agent by default.** With `blocking=False` training keeps going while the agent thinks, and
|
|
171
|
+
`"auto"` waits only when an edit would land too late. Both are faster, and noisier.
|
|
172
|
+
- **What the agent sees.** Your goal sentence, the names and shapes of the parameters, the training losses, its own
|
|
173
|
+
analysis reports, and read-only copies of your training script and model code (`share_code=False` keeps them out).
|
|
174
|
+
Values you log are only recorded, unless you pass `show_agent=True`. With `validation=N` it also sees the
|
|
175
|
+
development-set values you report with `opt.report_dev(...)`.
|
|
176
|
+
|
|
177
|
+
## Options
|
|
178
|
+
|
|
179
|
+
| Option | Default | Meaning |
|
|
180
|
+
|---|---|---|
|
|
181
|
+
| `decisions` | see right | How many times the agent is asked. Each time it may also keep everything as it is. If you do not set it, a run that waits for the agent asks at most as many times as keep the waiting under 4× the run's own training time (`opal estimate` shows the number), and at most 50. If you set it, that cap is off. A fifth of the decisions fall in the first 5% of the run; `schedule="uniform"` spaces them evenly. More in the [guide](docs/GUIDE.md#cost-and-limits). |
|
|
182
|
+
| `every` | 50 | Steps between checks: losses are read, a non-finite loss is caught, the analysis program runs. |
|
|
183
|
+
| `blocking` | `True` | `False` or `"auto"` to keep training while the agent thinks. |
|
|
184
|
+
| `max_cost_usd` | none | Stop asking the agent once it has spent this much. |
|
|
185
|
+
| `backend_options` | `{}` | `model=` (default `claude-opus-5-5`), `effort=` (default `high`) and `timeout=`. A failed agent turn is retried with growing pauses; an agent that is unavailable (login, usage limit, outage) is waited for, with no deadline unless you set `unavailable_timeout=`. Details in the [guide](docs/GUIDE.md#cost-and-limits). |
|
|
186
|
+
| `prompt` | `"neutral"` | The agent's instructions: the facts of the run and the protocol, with no recommended values. `"aggressive"` adds a review of every group's hyperparameters at each decision. You can also pass the path of your own template. |
|
|
187
|
+
| `validation` | none | `N`: your script evaluates a held-out development set about every N updates, at times it does not announce, and reports it with `opt.report_dev(loss=..., accuracy=...)`. The agent sees each result. See the [guide](docs/GUIDE.md#what-it-can-and-cannot-change). |
|
|
188
|
+
| `eval_integrity` | on with `validation` | Flags a run whose program moves the parameters somewhere else for an evaluation and back afterwards. The agent is not told. |
|
|
189
|
+
|
|
190
|
+
Gradient accumulation, GradScaler, checkpoints and resume, DDP, GPU machines without internet, privacy and cost are
|
|
191
|
+
covered in the [guide](docs/GUIDE.md).
|
|
192
|
+
|
|
193
|
+
### Experimental Codex backend
|
|
194
|
+
|
|
195
|
+
Claude is the default. To use Codex, pass `backend="codex", backend_options=dict(model="gpt-6.1-sol", effort="high")`
|
|
196
|
+
(`xhigh` works too), or run `opal broker <box> --backend codex --model gpt-6.1-sol --effort high`. Log in separately
|
|
197
|
+
with `CODEX_HOME=~/.codex-opal codex login --device-auth`, and do not copy or link the `auth.json` of your default
|
|
198
|
+
Codex home. `max_cost_usd` does not cap Codex; `backend_options=dict(max_tokens=N)` is a soft token cap that a turn
|
|
199
|
+
in progress can exceed. The run stops with an error if Codex compacts the conversation (around 450k tokens of context
|
|
200
|
+
with Codex CLI 0.160.1).
|
|
201
|
+
|
|
202
|
+
## Tests
|
|
203
|
+
|
|
204
|
+
The whole test suite runs on CPU with no agent and no cost: `pip install -e ".[examples,test,hf]"`, then `pytest -q`. It
|
|
205
|
+
takes a few minutes on a laptop. Three tests are skipped unless you set `OPAL_REAL_CLI=1`; they start the real Claude
|
|
206
|
+
Code CLI against a local mock.
|
|
207
|
+
|
|
208
|
+
## Status and limitations
|
|
209
|
+
|
|
210
|
+
v0.3.5, under active development. Tested on one GPU and on DDP within one node (NCCL, eager and `torch.compile`).
|
|
211
|
+
Multi-node, FSDP and DeepSpeed are not supported. Accelerate works if you wrap after `accelerator.prepare`, without
|
|
212
|
+
fp16 GradScaler or Accelerate's gradient accumulation; the HF Trainer works through `opal.hf.OpalTrainer`. Both are
|
|
213
|
+
tested on CPU. Lightning should work through `opt.observe(loss)` but we have not tried it. The agent can make a run
|
|
214
|
+
worse: it cannot restart or roll back training, it can only fix its programs at the next decision.
|
|
215
|
+
|
|
216
|
+
## Citation
|
|
217
|
+
|
|
218
|
+
```bibtex
|
|
219
|
+
@misc{bai2026reactive,
|
|
220
|
+
title = {Reactive Optimizers},
|
|
221
|
+
author = {Bai, Xingjian and Liang, Jia-Hao and Teoh, Jayden and Sitzmann, Vincent},
|
|
222
|
+
year = {2026},
|
|
223
|
+
month = {oct},
|
|
224
|
+
note = {Blog post}
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## License
|
|
229
|
+
|
|
230
|
+
MIT, see [LICENSE](LICENSE).
|
|
231
|
+
|
|
232
|
+
*The name:* OPAL stands for OPtimizer with an Agent in the Loop.
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
|
|
2
|
+
<h1 align="center">OPAL: a reactive optimizer</h1>
|
|
3
|
+
|
|
4
|
+
<p align="center"><b>A PyTorch optimizer wrapper that lets a coding agent change the optimizer while your model trains.</b></p>
|
|
5
|
+
|
|
6
|
+
<p align="center">
|
|
7
|
+
<a href="https://xingjianbai.com/reactive-optimizers/">Blog post</a> ·
|
|
8
|
+
<a href="docs/GUIDE.md">Guide</a> ·
|
|
9
|
+
<a href="#start-with-your-coding-agent">Start with your agent</a> ·
|
|
10
|
+
<a href="examples/README.md">Examples</a>
|
|
11
|
+
</p>
|
|
12
|
+
|
|
13
|
+
Most networks are trained with an optimizer that is fixed before training starts. OPAL (OPtimizer with an Agent in
|
|
14
|
+
the Loop) turns the optimizer into a program that an agent keeps rewriting during the run. You wrap your PyTorch
|
|
15
|
+
optimizer in one line. Every so often the agent (Claude Code) reads what the run reports and decides whether to
|
|
16
|
+
rewrite two small programs: the optimizer program, which turns gradients into updates at every step, and an analysis
|
|
17
|
+
program, which measures whatever the agent wants to look at.
|
|
18
|
+
|
|
19
|
+
The agent can change anything that can be written as optimizer code: the update rule of each parameter group, the
|
|
20
|
+
learning rates and their schedules, momentum and β₂, preconditioning, clipping, guards against loss spikes. It is
|
|
21
|
+
asked not to touch your model, data, loss, batch size, number of steps or evaluation. Its programs run inside your
|
|
22
|
+
training process, so this is a rule, not a sandbox (see [Safety](docs/GUIDE.md#safety)). Until the agent changes
|
|
23
|
+
something, the wrapped run is bit-for-bit the same as the plain one.
|
|
24
|
+
|
|
25
|
+
## Start with your coding agent
|
|
26
|
+
|
|
27
|
+
Clone this repository, open your own project in a coding agent (Claude Code, Codex, Cursor or similar) and paste this
|
|
28
|
+
prompt. The agent installs OPAL, wires it into your training script, asks you what it cannot know, and checks that
|
|
29
|
+
nothing changed before anything costs money.
|
|
30
|
+
|
|
31
|
+
```text
|
|
32
|
+
I want to train my model with OPAL, a reactive optimizer: a coding agent that rewrites the optimizer while the
|
|
33
|
+
model trains. OPAL is cloned at <PATH TO OPAL>. Please set it up for this project:
|
|
34
|
+
|
|
35
|
+
1. Read <PATH TO OPAL>/README.md and <PATH TO OPAL>/skills/train-with-agent/SKILL.md, and follow the skill. Do not
|
|
36
|
+
edit any file inside <PATH TO OPAL>; configure everything through opal.wrap arguments.
|
|
37
|
+
2. Install OPAL into the Python environment this project trains with (pip install -e <PATH TO OPAL>), and
|
|
38
|
+
run `opal doctor` (free, offline) to check that torch and Claude Code are ready on the machine that will run
|
|
39
|
+
training; run `opal doctor --live` (one tiny paid agent call) only once I approve an agent run. If Claude Code is
|
|
40
|
+
missing or not logged in there, tell me what to run; do not log in for me.
|
|
41
|
+
3. Find my training script and loop. Tell me what you found (optimizer, scheduler, number of steps, single GPU or
|
|
42
|
+
DDP, gradient clipping, mixed precision, checkpoints) and anything OPAL does not support.
|
|
43
|
+
4. Ask me for the goal sentence: what the run trains and what it is judged by at the end. Do not invent the metric.
|
|
44
|
+
5. Wrap the optimizer and show me the diff before running anything.
|
|
45
|
+
6. Smoke test: about 100 steps unwrapped and 100 steps wrapped with backend="keep" (no agent, no cost); the losses
|
|
46
|
+
must match step for step. Show me both. If I only asked for a dry run, stop here.
|
|
47
|
+
7. Before the real run, time an update in the smoke test and run `opal estimate --total-steps N --sec-per-step S`;
|
|
48
|
+
show me what it prints (how long training will wait for the agent, also as a multiple of the run's own time, and
|
|
49
|
+
the cost). Ask me for a cost cap (max_cost_usd) and how many times the agent should decide, and wait for my
|
|
50
|
+
go-ahead; then give me the exact command to launch it, and how to follow it (opal show, opal report).
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Start by hand
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
pip install opal-optimizer # or: git clone, then pip install -e ".[examples]"
|
|
57
|
+
opal doctor # torch and Claude Code ready?
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
The package is called `opal-optimizer` on PyPI, because `opal` there is an unrelated web framework. Both install as `import opal`, so do not install both.
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
import torch, opal
|
|
64
|
+
|
|
65
|
+
opt = torch.optim.AdamW(model.parameters(), lr=3e-4) # your optimizer, unchanged
|
|
66
|
+
sched = torch.optim.lr_scheduler.CosineAnnealingLR(opt, T_max=steps) # your scheduler, unchanged
|
|
67
|
+
opt = opal.wrap(opt, model, scheduler=sched, total_steps=steps,
|
|
68
|
+
goal="ResNet-18 on CIFAR-10; judged by test accuracy after the last update",
|
|
69
|
+
backend="keep") # a dry run: no agent, no cost. For the agent run, use max_cost_usd=20 instead.
|
|
70
|
+
|
|
71
|
+
for step in range(steps):
|
|
72
|
+
loss = criterion(model(x), y)
|
|
73
|
+
loss.backward()
|
|
74
|
+
opt.step(loss=loss) # pass the loss so the agent sees it
|
|
75
|
+
opt.zero_grad()
|
|
76
|
+
sched.step()
|
|
77
|
+
if step % 500 == 0:
|
|
78
|
+
opt.log(test_acc=evaluate(model)) # recorded for you, not shown to the agent
|
|
79
|
+
opt.close()
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
`goal` is one sentence: what you train and how it is judged. Then:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
python train.py # backend="keep": must match the plain run exactly
|
|
86
|
+
opal doctor --live # one tiny agent call to check the setup
|
|
87
|
+
opal estimate --total-steps 10000 --sec-per-step 0.05 # waiting time and cost, before you launch
|
|
88
|
+
python train.py # with max_cost_usd=20: the agent rewrites the optimizer
|
|
89
|
+
opal show opal_runs/<folder> # one line per decision
|
|
90
|
+
opal report opal_runs/<folder> # one HTML page: loss, learning rates, rationales, code diffs
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
The guide shows how to check the keep run against your plain run byte for byte, and how to use the Hugging Face
|
|
94
|
+
Trainer: [Try it without an agent](docs/GUIDE.md#try-it-without-an-agent).
|
|
95
|
+
|
|
96
|
+
## Prerequisites
|
|
97
|
+
|
|
98
|
+
- **A PyTorch training script** that uses `torch.optim` optimizers, on CPU, one GPU or several GPUs of one node (DDP).
|
|
99
|
+
Python 3.10 or newer, PyTorch 2.5 or newer (2.6 on Python 3.13).
|
|
100
|
+
- **For an agent run, Claude Code, logged in on the machine that runs rank 0.** Install
|
|
101
|
+
[Claude Code](https://docs.claude.com/en/docs/claude-code), run `claude` once and log in with a Claude subscription
|
|
102
|
+
or an API key. The agent's usage is billed to that account. `max_cost_usd` caps it; there is no cap by default.
|
|
103
|
+
The short summaries of code changes in the report are not counted (`summarize=False` turns them off). A dry run
|
|
104
|
+
with `backend="keep"` needs neither and makes no model calls.
|
|
105
|
+
- **No internet or no login on the GPU machine?** Run the agent on your laptop or a login node and connect it to the
|
|
106
|
+
training job through a shared folder (see [the mailbox broker](docs/GUIDE.md#gpu-machines-without-an-agent)).
|
|
107
|
+
|
|
108
|
+
## Results
|
|
109
|
+
|
|
110
|
+
Each number is one run, and each agent started from the task's own recipe:
|
|
111
|
+
|
|
112
|
+
| Example | Measure | Official recipe | Reactive optimizer |
|
|
113
|
+
|---|---|---|---|
|
|
114
|
+
| SiT-B/2, ImageNet 256, 400K updates | FID-50K ↓ | 35.7 (our evaluator) | 23.6 |
|
|
115
|
+
| RAEv2-B, ImageNet 256, 20 epochs | FID-50K ↓ | 3.39 | 3.10 (prompt asked for efficiency), 3.46 (neutral prompt); rerun on OPAL 0.3: 3.19 |
|
|
116
|
+
| Goldfish 125M GPT, Swahili | FLORES-200 bits ↓ | 79.37 | 77.60; rerun on OPAL 0.3: 77.25 |
|
|
117
|
+
| modded-nanogpt record #36 | validation loss ↓ | 3.2781 | 3.2749 |
|
|
118
|
+
| LightningDiT-B/1, ImageNet 256, 160 epochs | FID-50K ↓ (with guidance) | 3.22 | rerun on OPAL 0.3: 2.31 |
|
|
119
|
+
|
|
120
|
+
It does not always help: a run of modded-nanogpt on OPAL 0.2.0 ended at 3.2878, worse than record #36. Your
|
|
121
|
+
numbers may also differ from ours, because the agent can choose differently from one run to the next. The
|
|
122
|
+
[examples page](examples/README.md) has every number with its version, hardware, time and cost, the small CPU and
|
|
123
|
+
one-GPU scripts, and how to rerun each setting.
|
|
124
|
+
|
|
125
|
+
Our run records and final weights are on Hugging Face: [snow-leopard/opal-results](https://huggingface.co/datasets/snow-leopard/opal-results).
|
|
126
|
+
|
|
127
|
+
## How it works
|
|
128
|
+
|
|
129
|
+
```
|
|
130
|
+
training script ──grads──▶ updater.py ──▶ parameters (every step, every rank)
|
|
131
|
+
│ ▲
|
|
132
|
+
└──observations──▶ analysis.py ──reports──▶ agent (rank 0, every `every` steps / at decisions)
|
|
133
|
+
▲ │
|
|
134
|
+
└──── new programs ◀───┘ (at decision points)
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
- **Two programs.** `updater.py` starts as a call to your own optimizer and scheduler. `analysis.py` starts with a few
|
|
138
|
+
statistics of the loss and the gradients. The agent edits both in `opal_runs/<folder>/workspace/`.
|
|
139
|
+
- **One conversation for the whole run.** The agent remembers what it tried, what it predicted and what happened. Each
|
|
140
|
+
decision ends with a short rationale: what it saw, what it changed and what it expects.
|
|
141
|
+
- **Safe swaps.** A new optimizer program takes over at the next step, on every GPU at once, and runs on trial for 20
|
|
142
|
+
steps. If it raises an error, the run goes back to the previous program. A failure at its first step is undone
|
|
143
|
+
exactly when a copy of the parameters and optimizer state fits in `snapshot_limit_gb` (default 2); a later failure
|
|
144
|
+
is not undone, and the agent is told. Every version is kept in `programs/`.
|
|
145
|
+
- **Training waits for the agent by default.** With `blocking=False` training keeps going while the agent thinks, and
|
|
146
|
+
`"auto"` waits only when an edit would land too late. Both are faster, and noisier.
|
|
147
|
+
- **What the agent sees.** Your goal sentence, the names and shapes of the parameters, the training losses, its own
|
|
148
|
+
analysis reports, and read-only copies of your training script and model code (`share_code=False` keeps them out).
|
|
149
|
+
Values you log are only recorded, unless you pass `show_agent=True`. With `validation=N` it also sees the
|
|
150
|
+
development-set values you report with `opt.report_dev(...)`.
|
|
151
|
+
|
|
152
|
+
## Options
|
|
153
|
+
|
|
154
|
+
| Option | Default | Meaning |
|
|
155
|
+
|---|---|---|
|
|
156
|
+
| `decisions` | see right | How many times the agent is asked. Each time it may also keep everything as it is. If you do not set it, a run that waits for the agent asks at most as many times as keep the waiting under 4× the run's own training time (`opal estimate` shows the number), and at most 50. If you set it, that cap is off. A fifth of the decisions fall in the first 5% of the run; `schedule="uniform"` spaces them evenly. More in the [guide](docs/GUIDE.md#cost-and-limits). |
|
|
157
|
+
| `every` | 50 | Steps between checks: losses are read, a non-finite loss is caught, the analysis program runs. |
|
|
158
|
+
| `blocking` | `True` | `False` or `"auto"` to keep training while the agent thinks. |
|
|
159
|
+
| `max_cost_usd` | none | Stop asking the agent once it has spent this much. |
|
|
160
|
+
| `backend_options` | `{}` | `model=` (default `claude-opus-5-5`), `effort=` (default `high`) and `timeout=`. A failed agent turn is retried with growing pauses; an agent that is unavailable (login, usage limit, outage) is waited for, with no deadline unless you set `unavailable_timeout=`. Details in the [guide](docs/GUIDE.md#cost-and-limits). |
|
|
161
|
+
| `prompt` | `"neutral"` | The agent's instructions: the facts of the run and the protocol, with no recommended values. `"aggressive"` adds a review of every group's hyperparameters at each decision. You can also pass the path of your own template. |
|
|
162
|
+
| `validation` | none | `N`: your script evaluates a held-out development set about every N updates, at times it does not announce, and reports it with `opt.report_dev(loss=..., accuracy=...)`. The agent sees each result. See the [guide](docs/GUIDE.md#what-it-can-and-cannot-change). |
|
|
163
|
+
| `eval_integrity` | on with `validation` | Flags a run whose program moves the parameters somewhere else for an evaluation and back afterwards. The agent is not told. |
|
|
164
|
+
|
|
165
|
+
Gradient accumulation, GradScaler, checkpoints and resume, DDP, GPU machines without internet, privacy and cost are
|
|
166
|
+
covered in the [guide](docs/GUIDE.md).
|
|
167
|
+
|
|
168
|
+
### Experimental Codex backend
|
|
169
|
+
|
|
170
|
+
Claude is the default. To use Codex, pass `backend="codex", backend_options=dict(model="gpt-6.1-sol", effort="high")`
|
|
171
|
+
(`xhigh` works too), or run `opal broker <box> --backend codex --model gpt-6.1-sol --effort high`. Log in separately
|
|
172
|
+
with `CODEX_HOME=~/.codex-opal codex login --device-auth`, and do not copy or link the `auth.json` of your default
|
|
173
|
+
Codex home. `max_cost_usd` does not cap Codex; `backend_options=dict(max_tokens=N)` is a soft token cap that a turn
|
|
174
|
+
in progress can exceed. The run stops with an error if Codex compacts the conversation (around 450k tokens of context
|
|
175
|
+
with Codex CLI 0.160.1).
|
|
176
|
+
|
|
177
|
+
## Tests
|
|
178
|
+
|
|
179
|
+
The whole test suite runs on CPU with no agent and no cost: `pip install -e ".[examples,test,hf]"`, then `pytest -q`. It
|
|
180
|
+
takes a few minutes on a laptop. Three tests are skipped unless you set `OPAL_REAL_CLI=1`; they start the real Claude
|
|
181
|
+
Code CLI against a local mock.
|
|
182
|
+
|
|
183
|
+
## Status and limitations
|
|
184
|
+
|
|
185
|
+
v0.3.5, under active development. Tested on one GPU and on DDP within one node (NCCL, eager and `torch.compile`).
|
|
186
|
+
Multi-node, FSDP and DeepSpeed are not supported. Accelerate works if you wrap after `accelerator.prepare`, without
|
|
187
|
+
fp16 GradScaler or Accelerate's gradient accumulation; the HF Trainer works through `opal.hf.OpalTrainer`. Both are
|
|
188
|
+
tested on CPU. Lightning should work through `opt.observe(loss)` but we have not tried it. The agent can make a run
|
|
189
|
+
worse: it cannot restart or roll back training, it can only fix its programs at the next decision.
|
|
190
|
+
|
|
191
|
+
## Citation
|
|
192
|
+
|
|
193
|
+
```bibtex
|
|
194
|
+
@misc{bai2026reactive,
|
|
195
|
+
title = {Reactive Optimizers},
|
|
196
|
+
author = {Bai, Xingjian and Liang, Jia-Hao and Teoh, Jayden and Sitzmann, Vincent},
|
|
197
|
+
year = {2026},
|
|
198
|
+
month = {oct},
|
|
199
|
+
note = {Blog post}
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## License
|
|
204
|
+
|
|
205
|
+
MIT, see [LICENSE](LICENSE).
|
|
206
|
+
|
|
207
|
+
*The name:* OPAL stands for OPtimizer with an Agent in the Loop.
|