constraintml 0.1.0__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.
- constraintml-0.1.0/LICENSE +21 -0
- constraintml-0.1.0/MANIFEST.in +2 -0
- constraintml-0.1.0/PKG-INFO +261 -0
- constraintml-0.1.0/README.md +228 -0
- constraintml-0.1.0/pyproject.toml +58 -0
- constraintml-0.1.0/setup.cfg +4 -0
- constraintml-0.1.0/src/constraintml/__init__.py +5 -0
- constraintml-0.1.0/src/constraintml/cloud/__init__.py +29 -0
- constraintml-0.1.0/src/constraintml/constraints/__init__.py +3 -0
- constraintml-0.1.0/src/constraintml/constraints/parsing.py +98 -0
- constraintml-0.1.0/src/constraintml/constraints/spec.py +81 -0
- constraintml-0.1.0/src/constraintml/constraints/units.py +54 -0
- constraintml-0.1.0/src/constraintml/optimization/__init__.py +17 -0
- constraintml-0.1.0/src/constraintml/optimization/backends/__init__.py +4 -0
- constraintml-0.1.0/src/constraintml/optimization/backends/base.py +29 -0
- constraintml-0.1.0/src/constraintml/optimization/backends/pytorch_backend.py +116 -0
- constraintml-0.1.0/src/constraintml/optimization/carbon.py +20 -0
- constraintml-0.1.0/src/constraintml/optimization/engine.py +76 -0
- constraintml-0.1.0/src/constraintml/optimization/planner.py +151 -0
- constraintml-0.1.0/src/constraintml/optimization/state.py +49 -0
- constraintml-0.1.0/src/constraintml/optimization/strategies.py +34 -0
- constraintml-0.1.0/src/constraintml/optimization/telemetry/__init__.py +12 -0
- constraintml-0.1.0/src/constraintml/optimization/telemetry/base.py +41 -0
- constraintml-0.1.0/src/constraintml/optimization/telemetry/factory.py +34 -0
- constraintml-0.1.0/src/constraintml/optimization/telemetry/nvml.py +51 -0
- constraintml-0.1.0/src/constraintml/optimization/telemetry/simulated.py +38 -0
- constraintml-0.1.0/src/constraintml/py.typed +0 -0
- constraintml-0.1.0/src/constraintml/trainers/__init__.py +5 -0
- constraintml-0.1.0/src/constraintml/trainers/base.py +211 -0
- constraintml-0.1.0/src/constraintml/trainers/green_trainer.py +37 -0
- constraintml-0.1.0/src/constraintml/trainers/smart_trainer.py +41 -0
- constraintml-0.1.0/src/constraintml.egg-info/PKG-INFO +261 -0
- constraintml-0.1.0/src/constraintml.egg-info/SOURCES.txt +43 -0
- constraintml-0.1.0/src/constraintml.egg-info/dependency_links.txt +1 -0
- constraintml-0.1.0/src/constraintml.egg-info/requires.txt +10 -0
- constraintml-0.1.0/src/constraintml.egg-info/top_level.txt +1 -0
- constraintml-0.1.0/tests/test_carbon_accounting.py +19 -0
- constraintml-0.1.0/tests/test_carbon_savings_demo.py +71 -0
- constraintml-0.1.0/tests/test_constraint_parsing.py +109 -0
- constraintml-0.1.0/tests/test_engine.py +126 -0
- constraintml-0.1.0/tests/test_planner.py +110 -0
- constraintml-0.1.0/tests/test_resource_budget_portability_demo.py +61 -0
- constraintml-0.1.0/tests/test_telemetry_nvml.py +84 -0
- constraintml-0.1.0/tests/test_telemetry_simulated.py +36 -0
- constraintml-0.1.0/tests/test_trainers_integration.py +98 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 sofajailgreeting
|
|
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,261 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: constraintml
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A constraint-aware layer that wraps existing ML training frameworks to reduce energy/carbon costs and support budgetary compliance.
|
|
5
|
+
Author-email: sofajailgreeting <ercoleluke@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/sofajailgreeting/ConstraintML
|
|
8
|
+
Project-URL: Repository, https://github.com/sofajailgreeting/ConstraintML
|
|
9
|
+
Project-URL: Issues, https://github.com/sofajailgreeting/ConstraintML/issues
|
|
10
|
+
Keywords: pytorch,machine-learning,sustainability,carbon,energy,green-ai,mlops
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Requires-Python: >=3.9
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Requires-Dist: torch>=2.0
|
|
26
|
+
Provides-Extra: nvidia
|
|
27
|
+
Requires-Dist: pynvml>=11.5.0; extra == "nvidia"
|
|
28
|
+
Provides-Extra: viz
|
|
29
|
+
Requires-Dist: matplotlib>=3.7; extra == "viz"
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: pytest>=7; extra == "dev"
|
|
32
|
+
Dynamic: license-file
|
|
33
|
+
|
|
34
|
+
# ConstraintML
|
|
35
|
+
|
|
36
|
+
A constraint-aware layer that wraps PyTorch training so you specify **budgets** — energy, carbon, cost, a deadline, an acceptable accuracy loss — instead of manually tuning precision, batch size, gradient accumulation, checkpointing, and early stopping by hand. A deterministic, rule-based runtime observes training as it runs and adjusts execution strategy to satisfy those budgets while preserving model quality. ConstraintML looks to create a shift from today's configuration-driven ML tooling toward constraint-driven ML infrastructure, enabling organizations to easily configure production flows that fit into various budgetary and compliance requirements.
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
trainer = GreenTrainer(
|
|
40
|
+
model=model,
|
|
41
|
+
optimizer=optimizer,
|
|
42
|
+
energy_budget=100, # kWh
|
|
43
|
+
carbon_budget=25, # kg CO2e
|
|
44
|
+
deadline="8h",
|
|
45
|
+
)
|
|
46
|
+
trainer.train()
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Status
|
|
50
|
+
|
|
51
|
+
v1 is implemented and working, scoped to **PyTorch only**. `ConstraintTrainer`, `GreenTrainer`, and `SmartTrainer` (`src/constraintml/`) wrap a real PyTorch training loop, backed by:
|
|
52
|
+
|
|
53
|
+
- a deterministic, rule-based `ConstraintPlanner` — explicitly a heuristic, not a learned/ML optimizer, despite the project's tooling category
|
|
54
|
+
- a `RuntimeOptimizationEngine` that samples telemetry every step and applies planner decisions to the backend
|
|
55
|
+
- hardware telemetry that uses real NVIDIA NVML power readings when available (optional `pynvml` dependency) and falls back to a simulated constant-wattage collector otherwise, so the library imports and trains on CPU-only machines with no GPU at all
|
|
56
|
+
|
|
57
|
+
62 tests pass (1 additional test is skipped unless it's run on a real NVIDIA GPU with NVML available) covering constraint parsing, the planner's decision ladder, telemetry, carbon accounting, and end-to-end trainer integration. See `examples/` below for runnable demonstrations with real output.
|
|
58
|
+
|
|
59
|
+
## Installation
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pip install -e ".[dev]" # editable install + pytest
|
|
63
|
+
pip install -e ".[dev,nvidia]" # + real NVML GPU telemetry via pynvml
|
|
64
|
+
pip install -e ".[dev,viz]" # + matplotlib, for the example charts below
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Once published:
|
|
68
|
+
```bash
|
|
69
|
+
pip install git+https://github.com/sofajailgreeting/ConstraintML
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Requires Python 3.9+ and PyTorch 2.0+ (`torch>=2.0` is the only hard runtime dependency).
|
|
73
|
+
|
|
74
|
+
## Running
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
pytest # full test suite, no GPU required
|
|
78
|
+
pytest tests/test_planner.py::test_ladder_progression_through_all_levers # a single test
|
|
79
|
+
|
|
80
|
+
python examples/green_trainer_quickstart.py # end-to-end sanity check, CPU-only
|
|
81
|
+
python examples/carbon_savings_demo.py # baseline vs. deadline-constrained run
|
|
82
|
+
python examples/resource_budget_portability_demo.py # same deadline across two model sizes
|
|
83
|
+
python examples/execution_strategy_ladder_demo.py # forces the full lever ladder end-to-end
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Usage
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
trainer = GreenTrainer(
|
|
90
|
+
model=model,
|
|
91
|
+
optimizer=optimizer,
|
|
92
|
+
energy_budget=100, # kWh
|
|
93
|
+
carbon_budget=25, # kg CO2e
|
|
94
|
+
deadline="8h",
|
|
95
|
+
)
|
|
96
|
+
trainer.train()
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
trainer = SmartTrainer(
|
|
101
|
+
optimize_for="carbon",
|
|
102
|
+
carbon_budget="8 kgCO2e",
|
|
103
|
+
)
|
|
104
|
+
# model/optimizer/train_loader can be attached later via trainer.attach(...) --
|
|
105
|
+
# useful when not available at construction time.
|
|
106
|
+
trainer.train()
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
trainer = ConstraintTrainer(
|
|
111
|
+
model=model,
|
|
112
|
+
constraints={
|
|
113
|
+
"energy": "<50kWh",
|
|
114
|
+
"carbon": "<10kgCO2e",
|
|
115
|
+
"deadline": "8h",
|
|
116
|
+
"max_accuracy_loss": "0.5%",
|
|
117
|
+
},
|
|
118
|
+
)
|
|
119
|
+
trainer.train()
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
`GreenTrainer` fixes `optimize_for="accuracy"` and hides that parameter. `SmartTrainer` exposes `optimize_for` as its primary knob (used as a planner tie-breaker among competing budgets, not a different algorithm). `ConstraintTrainer` is the shared base both build on.
|
|
123
|
+
|
|
124
|
+
## v1 Architecture
|
|
125
|
+
|
|
126
|
+
Layering, thinnest/user-facing at the top:
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
Application
|
|
130
|
+
│
|
|
131
|
+
trainers/ <- ConstraintTrainer, GreenTrainer, SmartTrainer (thin, user-facing)
|
|
132
|
+
│
|
|
133
|
+
optimization/ <- planner, engine, telemetry, carbon accounting, backends
|
|
134
|
+
│
|
|
135
|
+
PyTorch
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
- **`constraints/`** — parses user-facing budget strings (`"8h"`, `"<50kWh"`, `"25 kg CO2e"`, `"0.5%"`) into a typed, frozen `ConstraintSpec`. Shared by `trainers/` and `optimization/planner.py`.
|
|
139
|
+
- **`trainers/`** — `ConstraintTrainer` (`base.py`) merges a `constraints={...}` dict with named kwargs into one `ConstraintSpec` (named kwargs win), supports deferred `model`/`optimizer` binding via `.attach()`, and owns the training loop in `.train()`, wiring telemetry + planner + backend together via `RuntimeOptimizationEngine` and returning a `TrainingReport`.
|
|
140
|
+
- **`optimization/planner.py`** — `ConstraintPlanner`: every `evaluate_every_n_steps`, linearly projects cumulative energy/carbon/cost to end-of-run, compares against budgets, and — if the worst metric is trending over the 85% warning threshold — applies the next untried lever in a fixed ladder:
|
|
141
|
+
|
|
142
|
+
**reduce precision (fp32→fp16) → enable activation checkpointing → grow batch size (capped at 4× initial) → increase gradient accumulation → early stop.**
|
|
143
|
+
|
|
144
|
+
An accuracy-loss guard disables further precision reduction once validation accuracy has regressed too far; hysteresis requires two consecutive over-warning evaluations before early-stopping (bypassed immediately if a budget is already exceeded). The ladder is monotonic — v1 never steps back down once a lever is used.
|
|
145
|
+
- **`optimization/engine.py`** — `RuntimeOptimizationEngine`: per-step `begin_step()`/`end_step()` sample telemetry, integrate energy (trapezoidal power integration), update carbon/cost via `CarbonModel`, and apply any resulting `ExecutionStrategy` to the backend on planner-evaluation steps.
|
|
146
|
+
- **`optimization/telemetry/`** — `TelemetryCollector` interface. `NVMLTelemetryCollector` lazily imports `pynvml` for real GPU power/utilization/temperature. `SimulatedTelemetryCollector` (constant assumed wattage) is the guaranteed CPU-safe fallback. `factory.get_telemetry_collector()` auto-selects between them by probing `pynvml.nvmlInit()`.
|
|
147
|
+
- **`optimization/backends/pytorch_backend.py`** — `PyTorchBackend`, the only real `ExecutionBackend`: real `torch.autocast` precision switches (dtype chosen per device — `bfloat16` on CPU, `float16` on CUDA), batch-size growth by rebuilding the `DataLoader` (map-style datasets only), gradient-accumulation micro-stepping, and `checkpoint_sequential`-based activation checkpointing (`nn.Sequential` models only — logs a warning and no-ops for arbitrary models it can't safely wrap).
|
|
148
|
+
- **`optimization/carbon.py`** — `CarbonModel`: a static `energy_kwh * intensity_kg_per_kwh` conversion (default 0.4 kg CO2e/kWh, a rough global-average grid estimate), overridable per-trainer.
|
|
149
|
+
- **`cloud/`** — a docstring-only `CloudAdapter` Protocol stub. No network/auth code; not used (yet) for v1 — exists purely so `CarbonModel.source` and future regional cost/carbon lookups have a defined shape to grow into.
|
|
150
|
+
|
|
151
|
+
## Examples & Results
|
|
152
|
+
|
|
153
|
+
Four runnable scripts in `examples/`, in increasing order of what they demonstrate. The numbers below are one real run on a CPU-only dev machine (`SimulatedTelemetryCollector`, fixed random seeds) — exact energy/time figures will vary by hardware and machine load, but the qualitative story (relative proportions, which lever fires, stop reasons) is what to look at, and each script prints/plots its own numbers when you run it.
|
|
154
|
+
|
|
155
|
+
### 1. `green_trainer_quickstart.py` — minimal sanity check
|
|
156
|
+
Trains a tiny model on synthetic data, CPU-only, no GPU or extras required.
|
|
157
|
+
```
|
|
158
|
+
Steps completed: 65
|
|
159
|
+
Epochs completed: 5
|
|
160
|
+
Energy used: 0.000002 kWh
|
|
161
|
+
Carbon emitted: 0.000001 kg CO2e
|
|
162
|
+
Final strategy: ExecutionStrategy(precision='fp32', batch_size=16, grad_accumulation_steps=1, activation_checkpointing=False)
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### 2. `carbon_savings_demo.py` — deadline-triggered early stop, measured end-to-end
|
|
166
|
+
Baseline (no budget) vs. `GreenTrainer` with a `deadline` set to 40% of the baseline's wall time, on a task designed to plateau then mildly overfit. `energy_budget`/`carbon_budget` are also passed (with generous headroom) so they never bind — `deadline` (a live `elapsed_seconds / deadline_seconds` ratio) is the only thing that actually triggers the stop here. Energy/carbon budgets are projected by extrapolating the current per-step rate to the end of the run instead, so a tight one would fire almost immediately rather than partway through; see the script's docstring for the full explanation.
|
|
167
|
+
```
|
|
168
|
+
Baseline Constrained
|
|
169
|
+
Epochs completed 25 14
|
|
170
|
+
Energy (kWh) 0.000073 0.000045
|
|
171
|
+
Carbon (kg CO2e) 0.000029 0.000018
|
|
172
|
+
Final val accuracy* -0.0878 -0.0859
|
|
173
|
+
|
|
174
|
+
Energy saved: 38.3%
|
|
175
|
+
Carbon saved: 38.3% (mirrors energy 1:1 -- CarbonModel is a static multiplier, not an independent measurement)
|
|
176
|
+
Accuracy delta: +0.0019 (constrained is actually *better* here -- stopping near the plateau avoided the overfitting tail)
|
|
177
|
+
```
|
|
178
|
+
*accuracy is `-avg validation loss`, not a task-specific accuracy metric.
|
|
179
|
+
|
|
180
|
+
**What this does and doesn't show:** the energy/carbon savings above are a real, measured side effect of stopping sooner on the clock — not evidence that ConstraintML detected an accuracy plateau or weighed carbon against accuracy to pick this stopping point. The planner has no such lookahead; `deadline` is a plain wall-clock ratio (see `_fractions` in `optimization/planner.py`). The accuracy-neutral (here, slightly better) outcome is a property of this dataset, which is deliberately constructed to plateau then mildly overfit, and of `deadline` having been calibrated after the fact from the baseline's own wall time — a hand-picked epoch count inspecting the same curve would land similarly, for this one model on this one machine. What the example *does* demonstrate honestly: the full telemetry → energy integration → carbon conversion → report pipeline runs end-to-end on real, non-mocked numbers, and a `deadline` constraint gives you a resource-anchored stand-in for a hardcoded epoch count that costs nothing here. Whether that portability actually holds *across* model size and hardware — the harder and more useful claim — is what Example 3 tests.
|
|
181
|
+
|
|
182
|
+
### 3. `resource_budget_portability_demo.py` — the actual differentiator
|
|
183
|
+
The same hardcoded epoch count (30) vs. the same `deadline`, applied first to a small model, then to a 24x-wider model, on identical data:
|
|
184
|
+
```
|
|
185
|
+
elapsed x budget epochs
|
|
186
|
+
Small model, naive fixed-epoch 1.328s 1.00x 30
|
|
187
|
+
Small model, ConstraintML deadline 1.288s 0.97x 30
|
|
188
|
+
Large model, naive fixed-epoch 5.707s 4.30x 30
|
|
189
|
+
Large model, ConstraintML deadline 1.722s 1.30x 10
|
|
190
|
+
```
|
|
191
|
+
Reusing the same epoch count on the larger model blows the same real time budget by 4.3x, because an epoch count carries no information about actual compute cost. The same `deadline` constraint instead self-adjusts to 10 epochs and lands at 1.3x, automatically, with no re-tuning. **This is the real differentiator of ConstraintML, especially in v1** — not smarter plateau detection, but a resource constraint that stays meaningful across model size and hardware.
|
|
192
|
+
|
|
193
|
+
### 4. `execution_strategy_ladder_demo.py` — the full lever ladder
|
|
194
|
+
A deliberately tiny `energy_budget` (2% of one measured unconstrained epoch) forces `ConstraintPlanner` through every lever, against a real `PyTorchBackend`:
|
|
195
|
+
```
|
|
196
|
+
step 5: REDUCE_PRECISION -> precision=fp16
|
|
197
|
+
step 10: ENABLE_CHECKPOINTING -> activation_checkpointing=True
|
|
198
|
+
step 15-30: INCREASE_BATCH_SIZE -> batch_size 15 -> 22 -> 33 -> 40 (capped at 4x initial)
|
|
199
|
+
step 35-65: INCREASE_GRAD_ACCUM -> grad_accumulation_steps 2 -> 8 (capped)
|
|
200
|
+
Stopped early: True, after 70 steps spanning 6 real epochs
|
|
201
|
+
Stop reason: "energy projected at 126008% of budget; all optimization levers exhausted."
|
|
202
|
+
```
|
|
203
|
+
This does **not** claim these levers make training faster on CPU — see the script's docstring and the CPU autocast note below. It demonstrates that the ladder itself executes correctly end-to-end against real training code (real autocast, real `checkpoint_sequential`, a real `DataLoader` rebuild), not just in a unit test against a mocked planner state.
|
|
204
|
+
|
|
205
|
+
## Current Scope: What This Means for Production Teams
|
|
206
|
+
|
|
207
|
+
v1 targets a narrower use case than the long-term vision below. Concretely:
|
|
208
|
+
|
|
209
|
+
**Good fit today:**
|
|
210
|
+
- Single-process PyTorch training jobs (one GPU, or CPU-only) where you want automatic, soft guardrails against blowing a time, energy, carbon, or cost budget.
|
|
211
|
+
- Teams who currently hardcode an epoch count "that felt about right" and want that number to keep working when the model or hardware changes, without re-tuning — this is exactly what `resource_budget_portability_demo.py` demonstrates.
|
|
212
|
+
- Environments with no GPU at all: the library never requires a GPU or `pynvml`; `SimulatedTelemetryCollector` is a real, tested fallback, not a stub.
|
|
213
|
+
|
|
214
|
+
**Not yet a fit:**
|
|
215
|
+
- **Multi-GPU or distributed training** — v1 has no distributed strategy switching; the planner and backend assume a single device.
|
|
216
|
+
- **Regulatory- or audit-grade carbon accounting** — `CarbonModel` is a static `energy_kwh * 0.4` global-average multiplier, not a regional or real-time grid intensity figure. Treat carbon numbers as directional, not compliance-ready, until a `cloud/` adapter supplies real intensity data.
|
|
217
|
+
- **Cost-sensitive cloud scheduling** — there's no cloud pricing/spot integration; `cost_budget` exists in the API but has no cloud cost signal behind it today.
|
|
218
|
+
- **Guaranteed speedups from precision reduction** — the `REDUCE_PRECISION` lever's real-world payoff is hardware-dependent. Measured on this project's dev CPU, `torch.autocast(dtype=torch.float16)` was ~44x *slower* than fp32 (CPUs lack native fp16 compute kernels and fall back to emulation); v1 uses `bfloat16` on CPU instead to avoid that catastrophic case, but even `bfloat16` wasn't a clear win on hardware without low-precision acceleration. As such, expect real wins mainly on CUDA GPUs with tensor cores.
|
|
219
|
+
- **Precision "recovery"** — once the planner reduces precision or grows batch size, v1 never steps back down, even if usage later drops back under budget. This is a deliberate, monotonic simplification (to avoid oscillation), not an oversight.
|
|
220
|
+
|
|
221
|
+
## What's Out of Scope for v1 (documented extension points)
|
|
222
|
+
|
|
223
|
+
These are architectural extension points the codebase is designed to grow into — **not committed or scheduled features**:
|
|
224
|
+
|
|
225
|
+
- JAX / TensorFlow execution backends (v1 is PyTorch-only)
|
|
226
|
+
- Real cloud adapters (AWS / Azure / GCP) for regional carbon intensity, spot pricing, and instance recommendations — `cloud/` currently ships only a docstring-only `CloudAdapter` Protocol stub
|
|
227
|
+
- FP8 / INT8 execution (named in the `ExecutionStrategy` precision enum for API completeness, never selected by v1's planner or backend)
|
|
228
|
+
- AMD ROCm SMI / Intel RAPL telemetry (NVIDIA NVML and the CPU-fallback collector only today)
|
|
229
|
+
- Multi-GPU / distributed strategy switching
|
|
230
|
+
- Pause/resume or checkpoint migration across hardware
|
|
231
|
+
- Reporting/visualization dashboards beyond the `[viz]` matplotlib charts in `examples/`
|
|
232
|
+
- Precision "recovery" (stepping back up once back under budget)
|
|
233
|
+
|
|
234
|
+
If your use case depends on any of these, treat it as a gap to plan around today rather than an assumed near-term feature — check current project status directly before committing to a timeline around any of them.
|
|
235
|
+
|
|
236
|
+
## Long-Term Vision
|
|
237
|
+
|
|
238
|
+
Beyond v1's PyTorch-only, single-device scope, the project's direction is to treat energy, carbon, execution time, and cost as first-class optimization objectives, on equal footing with accuracy, across any ML framework and any hardware:
|
|
239
|
+
|
|
240
|
+
- **Local-first, cloud-independent**: run unmodified on a laptop, an on-prem GPU server, a university HPC cluster, a Kubernetes cluster, or any cloud provider, gathering telemetry directly from hardware (NVML, ROCm SMI, RAPL) rather than depending on cloud-provider APIs.
|
|
241
|
+
- **Genuine multi-objective planning**: evolve `ConstraintPlanner` from today's fixed rule-based ladder toward optimizing across competing goals simultaneously (accuracy vs. energy vs. carbon vs. deadline vs. cost), and toward hardware-specific execution kernel selection and dynamic parallelism control.
|
|
242
|
+
- **Optional cloud adapters**: `cloud/`'s `CloudAdapter` Protocol is meant to eventually supply regional carbon intensity, electricity pricing, spot pricing, and checkpoint-migration signals — extending the planner's visibility without replacing its core logic or requiring cloud auth for the base library to function.
|
|
243
|
+
|
|
244
|
+
This is a direction the architecture is designed to grow into, not a promise of any specific feature landing in any specific release - although most of this should be expected sooner rather than later.
|
|
245
|
+
|
|
246
|
+
## Publishing (for maintainers)
|
|
247
|
+
|
|
248
|
+
Packaging is `pyproject.toml`-only (PEP 517/518 via `setuptools.build_meta`). To build and upload a release:
|
|
249
|
+
|
|
250
|
+
```bash
|
|
251
|
+
python -m pip install --upgrade build twine
|
|
252
|
+
python -m build # produces dist/*.whl and dist/*.tar.gz
|
|
253
|
+
python -m twine check dist/* # validates metadata/README rendering before upload
|
|
254
|
+
python -m twine upload dist/* # prompts for PyPI credentials (or use a PyPI API token)
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
Bump `version` in `pyproject.toml` (and `__version__` in `src/constraintml/__init__.py`, which is not currently read from the package metadata) before each release. `MANIFEST.in` ensures `LICENSE` and `README.md` are included in the sdist.
|
|
258
|
+
|
|
259
|
+
## License
|
|
260
|
+
|
|
261
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
# ConstraintML
|
|
2
|
+
|
|
3
|
+
A constraint-aware layer that wraps PyTorch training so you specify **budgets** — energy, carbon, cost, a deadline, an acceptable accuracy loss — instead of manually tuning precision, batch size, gradient accumulation, checkpointing, and early stopping by hand. A deterministic, rule-based runtime observes training as it runs and adjusts execution strategy to satisfy those budgets while preserving model quality. ConstraintML looks to create a shift from today's configuration-driven ML tooling toward constraint-driven ML infrastructure, enabling organizations to easily configure production flows that fit into various budgetary and compliance requirements.
|
|
4
|
+
|
|
5
|
+
```python
|
|
6
|
+
trainer = GreenTrainer(
|
|
7
|
+
model=model,
|
|
8
|
+
optimizer=optimizer,
|
|
9
|
+
energy_budget=100, # kWh
|
|
10
|
+
carbon_budget=25, # kg CO2e
|
|
11
|
+
deadline="8h",
|
|
12
|
+
)
|
|
13
|
+
trainer.train()
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Status
|
|
17
|
+
|
|
18
|
+
v1 is implemented and working, scoped to **PyTorch only**. `ConstraintTrainer`, `GreenTrainer`, and `SmartTrainer` (`src/constraintml/`) wrap a real PyTorch training loop, backed by:
|
|
19
|
+
|
|
20
|
+
- a deterministic, rule-based `ConstraintPlanner` — explicitly a heuristic, not a learned/ML optimizer, despite the project's tooling category
|
|
21
|
+
- a `RuntimeOptimizationEngine` that samples telemetry every step and applies planner decisions to the backend
|
|
22
|
+
- hardware telemetry that uses real NVIDIA NVML power readings when available (optional `pynvml` dependency) and falls back to a simulated constant-wattage collector otherwise, so the library imports and trains on CPU-only machines with no GPU at all
|
|
23
|
+
|
|
24
|
+
62 tests pass (1 additional test is skipped unless it's run on a real NVIDIA GPU with NVML available) covering constraint parsing, the planner's decision ladder, telemetry, carbon accounting, and end-to-end trainer integration. See `examples/` below for runnable demonstrations with real output.
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install -e ".[dev]" # editable install + pytest
|
|
30
|
+
pip install -e ".[dev,nvidia]" # + real NVML GPU telemetry via pynvml
|
|
31
|
+
pip install -e ".[dev,viz]" # + matplotlib, for the example charts below
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Once published:
|
|
35
|
+
```bash
|
|
36
|
+
pip install git+https://github.com/sofajailgreeting/ConstraintML
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Requires Python 3.9+ and PyTorch 2.0+ (`torch>=2.0` is the only hard runtime dependency).
|
|
40
|
+
|
|
41
|
+
## Running
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pytest # full test suite, no GPU required
|
|
45
|
+
pytest tests/test_planner.py::test_ladder_progression_through_all_levers # a single test
|
|
46
|
+
|
|
47
|
+
python examples/green_trainer_quickstart.py # end-to-end sanity check, CPU-only
|
|
48
|
+
python examples/carbon_savings_demo.py # baseline vs. deadline-constrained run
|
|
49
|
+
python examples/resource_budget_portability_demo.py # same deadline across two model sizes
|
|
50
|
+
python examples/execution_strategy_ladder_demo.py # forces the full lever ladder end-to-end
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Usage
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
trainer = GreenTrainer(
|
|
57
|
+
model=model,
|
|
58
|
+
optimizer=optimizer,
|
|
59
|
+
energy_budget=100, # kWh
|
|
60
|
+
carbon_budget=25, # kg CO2e
|
|
61
|
+
deadline="8h",
|
|
62
|
+
)
|
|
63
|
+
trainer.train()
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
trainer = SmartTrainer(
|
|
68
|
+
optimize_for="carbon",
|
|
69
|
+
carbon_budget="8 kgCO2e",
|
|
70
|
+
)
|
|
71
|
+
# model/optimizer/train_loader can be attached later via trainer.attach(...) --
|
|
72
|
+
# useful when not available at construction time.
|
|
73
|
+
trainer.train()
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
trainer = ConstraintTrainer(
|
|
78
|
+
model=model,
|
|
79
|
+
constraints={
|
|
80
|
+
"energy": "<50kWh",
|
|
81
|
+
"carbon": "<10kgCO2e",
|
|
82
|
+
"deadline": "8h",
|
|
83
|
+
"max_accuracy_loss": "0.5%",
|
|
84
|
+
},
|
|
85
|
+
)
|
|
86
|
+
trainer.train()
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
`GreenTrainer` fixes `optimize_for="accuracy"` and hides that parameter. `SmartTrainer` exposes `optimize_for` as its primary knob (used as a planner tie-breaker among competing budgets, not a different algorithm). `ConstraintTrainer` is the shared base both build on.
|
|
90
|
+
|
|
91
|
+
## v1 Architecture
|
|
92
|
+
|
|
93
|
+
Layering, thinnest/user-facing at the top:
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
Application
|
|
97
|
+
│
|
|
98
|
+
trainers/ <- ConstraintTrainer, GreenTrainer, SmartTrainer (thin, user-facing)
|
|
99
|
+
│
|
|
100
|
+
optimization/ <- planner, engine, telemetry, carbon accounting, backends
|
|
101
|
+
│
|
|
102
|
+
PyTorch
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
- **`constraints/`** — parses user-facing budget strings (`"8h"`, `"<50kWh"`, `"25 kg CO2e"`, `"0.5%"`) into a typed, frozen `ConstraintSpec`. Shared by `trainers/` and `optimization/planner.py`.
|
|
106
|
+
- **`trainers/`** — `ConstraintTrainer` (`base.py`) merges a `constraints={...}` dict with named kwargs into one `ConstraintSpec` (named kwargs win), supports deferred `model`/`optimizer` binding via `.attach()`, and owns the training loop in `.train()`, wiring telemetry + planner + backend together via `RuntimeOptimizationEngine` and returning a `TrainingReport`.
|
|
107
|
+
- **`optimization/planner.py`** — `ConstraintPlanner`: every `evaluate_every_n_steps`, linearly projects cumulative energy/carbon/cost to end-of-run, compares against budgets, and — if the worst metric is trending over the 85% warning threshold — applies the next untried lever in a fixed ladder:
|
|
108
|
+
|
|
109
|
+
**reduce precision (fp32→fp16) → enable activation checkpointing → grow batch size (capped at 4× initial) → increase gradient accumulation → early stop.**
|
|
110
|
+
|
|
111
|
+
An accuracy-loss guard disables further precision reduction once validation accuracy has regressed too far; hysteresis requires two consecutive over-warning evaluations before early-stopping (bypassed immediately if a budget is already exceeded). The ladder is monotonic — v1 never steps back down once a lever is used.
|
|
112
|
+
- **`optimization/engine.py`** — `RuntimeOptimizationEngine`: per-step `begin_step()`/`end_step()` sample telemetry, integrate energy (trapezoidal power integration), update carbon/cost via `CarbonModel`, and apply any resulting `ExecutionStrategy` to the backend on planner-evaluation steps.
|
|
113
|
+
- **`optimization/telemetry/`** — `TelemetryCollector` interface. `NVMLTelemetryCollector` lazily imports `pynvml` for real GPU power/utilization/temperature. `SimulatedTelemetryCollector` (constant assumed wattage) is the guaranteed CPU-safe fallback. `factory.get_telemetry_collector()` auto-selects between them by probing `pynvml.nvmlInit()`.
|
|
114
|
+
- **`optimization/backends/pytorch_backend.py`** — `PyTorchBackend`, the only real `ExecutionBackend`: real `torch.autocast` precision switches (dtype chosen per device — `bfloat16` on CPU, `float16` on CUDA), batch-size growth by rebuilding the `DataLoader` (map-style datasets only), gradient-accumulation micro-stepping, and `checkpoint_sequential`-based activation checkpointing (`nn.Sequential` models only — logs a warning and no-ops for arbitrary models it can't safely wrap).
|
|
115
|
+
- **`optimization/carbon.py`** — `CarbonModel`: a static `energy_kwh * intensity_kg_per_kwh` conversion (default 0.4 kg CO2e/kWh, a rough global-average grid estimate), overridable per-trainer.
|
|
116
|
+
- **`cloud/`** — a docstring-only `CloudAdapter` Protocol stub. No network/auth code; not used (yet) for v1 — exists purely so `CarbonModel.source` and future regional cost/carbon lookups have a defined shape to grow into.
|
|
117
|
+
|
|
118
|
+
## Examples & Results
|
|
119
|
+
|
|
120
|
+
Four runnable scripts in `examples/`, in increasing order of what they demonstrate. The numbers below are one real run on a CPU-only dev machine (`SimulatedTelemetryCollector`, fixed random seeds) — exact energy/time figures will vary by hardware and machine load, but the qualitative story (relative proportions, which lever fires, stop reasons) is what to look at, and each script prints/plots its own numbers when you run it.
|
|
121
|
+
|
|
122
|
+
### 1. `green_trainer_quickstart.py` — minimal sanity check
|
|
123
|
+
Trains a tiny model on synthetic data, CPU-only, no GPU or extras required.
|
|
124
|
+
```
|
|
125
|
+
Steps completed: 65
|
|
126
|
+
Epochs completed: 5
|
|
127
|
+
Energy used: 0.000002 kWh
|
|
128
|
+
Carbon emitted: 0.000001 kg CO2e
|
|
129
|
+
Final strategy: ExecutionStrategy(precision='fp32', batch_size=16, grad_accumulation_steps=1, activation_checkpointing=False)
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### 2. `carbon_savings_demo.py` — deadline-triggered early stop, measured end-to-end
|
|
133
|
+
Baseline (no budget) vs. `GreenTrainer` with a `deadline` set to 40% of the baseline's wall time, on a task designed to plateau then mildly overfit. `energy_budget`/`carbon_budget` are also passed (with generous headroom) so they never bind — `deadline` (a live `elapsed_seconds / deadline_seconds` ratio) is the only thing that actually triggers the stop here. Energy/carbon budgets are projected by extrapolating the current per-step rate to the end of the run instead, so a tight one would fire almost immediately rather than partway through; see the script's docstring for the full explanation.
|
|
134
|
+
```
|
|
135
|
+
Baseline Constrained
|
|
136
|
+
Epochs completed 25 14
|
|
137
|
+
Energy (kWh) 0.000073 0.000045
|
|
138
|
+
Carbon (kg CO2e) 0.000029 0.000018
|
|
139
|
+
Final val accuracy* -0.0878 -0.0859
|
|
140
|
+
|
|
141
|
+
Energy saved: 38.3%
|
|
142
|
+
Carbon saved: 38.3% (mirrors energy 1:1 -- CarbonModel is a static multiplier, not an independent measurement)
|
|
143
|
+
Accuracy delta: +0.0019 (constrained is actually *better* here -- stopping near the plateau avoided the overfitting tail)
|
|
144
|
+
```
|
|
145
|
+
*accuracy is `-avg validation loss`, not a task-specific accuracy metric.
|
|
146
|
+
|
|
147
|
+
**What this does and doesn't show:** the energy/carbon savings above are a real, measured side effect of stopping sooner on the clock — not evidence that ConstraintML detected an accuracy plateau or weighed carbon against accuracy to pick this stopping point. The planner has no such lookahead; `deadline` is a plain wall-clock ratio (see `_fractions` in `optimization/planner.py`). The accuracy-neutral (here, slightly better) outcome is a property of this dataset, which is deliberately constructed to plateau then mildly overfit, and of `deadline` having been calibrated after the fact from the baseline's own wall time — a hand-picked epoch count inspecting the same curve would land similarly, for this one model on this one machine. What the example *does* demonstrate honestly: the full telemetry → energy integration → carbon conversion → report pipeline runs end-to-end on real, non-mocked numbers, and a `deadline` constraint gives you a resource-anchored stand-in for a hardcoded epoch count that costs nothing here. Whether that portability actually holds *across* model size and hardware — the harder and more useful claim — is what Example 3 tests.
|
|
148
|
+
|
|
149
|
+
### 3. `resource_budget_portability_demo.py` — the actual differentiator
|
|
150
|
+
The same hardcoded epoch count (30) vs. the same `deadline`, applied first to a small model, then to a 24x-wider model, on identical data:
|
|
151
|
+
```
|
|
152
|
+
elapsed x budget epochs
|
|
153
|
+
Small model, naive fixed-epoch 1.328s 1.00x 30
|
|
154
|
+
Small model, ConstraintML deadline 1.288s 0.97x 30
|
|
155
|
+
Large model, naive fixed-epoch 5.707s 4.30x 30
|
|
156
|
+
Large model, ConstraintML deadline 1.722s 1.30x 10
|
|
157
|
+
```
|
|
158
|
+
Reusing the same epoch count on the larger model blows the same real time budget by 4.3x, because an epoch count carries no information about actual compute cost. The same `deadline` constraint instead self-adjusts to 10 epochs and lands at 1.3x, automatically, with no re-tuning. **This is the real differentiator of ConstraintML, especially in v1** — not smarter plateau detection, but a resource constraint that stays meaningful across model size and hardware.
|
|
159
|
+
|
|
160
|
+
### 4. `execution_strategy_ladder_demo.py` — the full lever ladder
|
|
161
|
+
A deliberately tiny `energy_budget` (2% of one measured unconstrained epoch) forces `ConstraintPlanner` through every lever, against a real `PyTorchBackend`:
|
|
162
|
+
```
|
|
163
|
+
step 5: REDUCE_PRECISION -> precision=fp16
|
|
164
|
+
step 10: ENABLE_CHECKPOINTING -> activation_checkpointing=True
|
|
165
|
+
step 15-30: INCREASE_BATCH_SIZE -> batch_size 15 -> 22 -> 33 -> 40 (capped at 4x initial)
|
|
166
|
+
step 35-65: INCREASE_GRAD_ACCUM -> grad_accumulation_steps 2 -> 8 (capped)
|
|
167
|
+
Stopped early: True, after 70 steps spanning 6 real epochs
|
|
168
|
+
Stop reason: "energy projected at 126008% of budget; all optimization levers exhausted."
|
|
169
|
+
```
|
|
170
|
+
This does **not** claim these levers make training faster on CPU — see the script's docstring and the CPU autocast note below. It demonstrates that the ladder itself executes correctly end-to-end against real training code (real autocast, real `checkpoint_sequential`, a real `DataLoader` rebuild), not just in a unit test against a mocked planner state.
|
|
171
|
+
|
|
172
|
+
## Current Scope: What This Means for Production Teams
|
|
173
|
+
|
|
174
|
+
v1 targets a narrower use case than the long-term vision below. Concretely:
|
|
175
|
+
|
|
176
|
+
**Good fit today:**
|
|
177
|
+
- Single-process PyTorch training jobs (one GPU, or CPU-only) where you want automatic, soft guardrails against blowing a time, energy, carbon, or cost budget.
|
|
178
|
+
- Teams who currently hardcode an epoch count "that felt about right" and want that number to keep working when the model or hardware changes, without re-tuning — this is exactly what `resource_budget_portability_demo.py` demonstrates.
|
|
179
|
+
- Environments with no GPU at all: the library never requires a GPU or `pynvml`; `SimulatedTelemetryCollector` is a real, tested fallback, not a stub.
|
|
180
|
+
|
|
181
|
+
**Not yet a fit:**
|
|
182
|
+
- **Multi-GPU or distributed training** — v1 has no distributed strategy switching; the planner and backend assume a single device.
|
|
183
|
+
- **Regulatory- or audit-grade carbon accounting** — `CarbonModel` is a static `energy_kwh * 0.4` global-average multiplier, not a regional or real-time grid intensity figure. Treat carbon numbers as directional, not compliance-ready, until a `cloud/` adapter supplies real intensity data.
|
|
184
|
+
- **Cost-sensitive cloud scheduling** — there's no cloud pricing/spot integration; `cost_budget` exists in the API but has no cloud cost signal behind it today.
|
|
185
|
+
- **Guaranteed speedups from precision reduction** — the `REDUCE_PRECISION` lever's real-world payoff is hardware-dependent. Measured on this project's dev CPU, `torch.autocast(dtype=torch.float16)` was ~44x *slower* than fp32 (CPUs lack native fp16 compute kernels and fall back to emulation); v1 uses `bfloat16` on CPU instead to avoid that catastrophic case, but even `bfloat16` wasn't a clear win on hardware without low-precision acceleration. As such, expect real wins mainly on CUDA GPUs with tensor cores.
|
|
186
|
+
- **Precision "recovery"** — once the planner reduces precision or grows batch size, v1 never steps back down, even if usage later drops back under budget. This is a deliberate, monotonic simplification (to avoid oscillation), not an oversight.
|
|
187
|
+
|
|
188
|
+
## What's Out of Scope for v1 (documented extension points)
|
|
189
|
+
|
|
190
|
+
These are architectural extension points the codebase is designed to grow into — **not committed or scheduled features**:
|
|
191
|
+
|
|
192
|
+
- JAX / TensorFlow execution backends (v1 is PyTorch-only)
|
|
193
|
+
- Real cloud adapters (AWS / Azure / GCP) for regional carbon intensity, spot pricing, and instance recommendations — `cloud/` currently ships only a docstring-only `CloudAdapter` Protocol stub
|
|
194
|
+
- FP8 / INT8 execution (named in the `ExecutionStrategy` precision enum for API completeness, never selected by v1's planner or backend)
|
|
195
|
+
- AMD ROCm SMI / Intel RAPL telemetry (NVIDIA NVML and the CPU-fallback collector only today)
|
|
196
|
+
- Multi-GPU / distributed strategy switching
|
|
197
|
+
- Pause/resume or checkpoint migration across hardware
|
|
198
|
+
- Reporting/visualization dashboards beyond the `[viz]` matplotlib charts in `examples/`
|
|
199
|
+
- Precision "recovery" (stepping back up once back under budget)
|
|
200
|
+
|
|
201
|
+
If your use case depends on any of these, treat it as a gap to plan around today rather than an assumed near-term feature — check current project status directly before committing to a timeline around any of them.
|
|
202
|
+
|
|
203
|
+
## Long-Term Vision
|
|
204
|
+
|
|
205
|
+
Beyond v1's PyTorch-only, single-device scope, the project's direction is to treat energy, carbon, execution time, and cost as first-class optimization objectives, on equal footing with accuracy, across any ML framework and any hardware:
|
|
206
|
+
|
|
207
|
+
- **Local-first, cloud-independent**: run unmodified on a laptop, an on-prem GPU server, a university HPC cluster, a Kubernetes cluster, or any cloud provider, gathering telemetry directly from hardware (NVML, ROCm SMI, RAPL) rather than depending on cloud-provider APIs.
|
|
208
|
+
- **Genuine multi-objective planning**: evolve `ConstraintPlanner` from today's fixed rule-based ladder toward optimizing across competing goals simultaneously (accuracy vs. energy vs. carbon vs. deadline vs. cost), and toward hardware-specific execution kernel selection and dynamic parallelism control.
|
|
209
|
+
- **Optional cloud adapters**: `cloud/`'s `CloudAdapter` Protocol is meant to eventually supply regional carbon intensity, electricity pricing, spot pricing, and checkpoint-migration signals — extending the planner's visibility without replacing its core logic or requiring cloud auth for the base library to function.
|
|
210
|
+
|
|
211
|
+
This is a direction the architecture is designed to grow into, not a promise of any specific feature landing in any specific release - although most of this should be expected sooner rather than later.
|
|
212
|
+
|
|
213
|
+
## Publishing (for maintainers)
|
|
214
|
+
|
|
215
|
+
Packaging is `pyproject.toml`-only (PEP 517/518 via `setuptools.build_meta`). To build and upload a release:
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
python -m pip install --upgrade build twine
|
|
219
|
+
python -m build # produces dist/*.whl and dist/*.tar.gz
|
|
220
|
+
python -m twine check dist/* # validates metadata/README rendering before upload
|
|
221
|
+
python -m twine upload dist/* # prompts for PyPI credentials (or use a PyPI API token)
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Bump `version` in `pyproject.toml` (and `__version__` in `src/constraintml/__init__.py`, which is not currently read from the package metadata) before each release. `MANIFEST.in` ensures `LICENSE` and `README.md` are included in the sdist.
|
|
225
|
+
|
|
226
|
+
## License
|
|
227
|
+
|
|
228
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "constraintml"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "A constraint-aware layer that wraps existing ML training frameworks to reduce energy/carbon costs and support budgetary compliance."
|
|
9
|
+
readme = { file = "README.md", content-type = "text/markdown" }
|
|
10
|
+
license = "MIT"
|
|
11
|
+
requires-python = ">=3.9"
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "sofajailgreeting", email = "ercoleluke@gmail.com" },
|
|
14
|
+
]
|
|
15
|
+
keywords = [
|
|
16
|
+
"pytorch",
|
|
17
|
+
"machine-learning",
|
|
18
|
+
"sustainability",
|
|
19
|
+
"carbon",
|
|
20
|
+
"energy",
|
|
21
|
+
"green-ai",
|
|
22
|
+
"mlops",
|
|
23
|
+
]
|
|
24
|
+
classifiers = [
|
|
25
|
+
"Development Status :: 4 - Beta",
|
|
26
|
+
"Intended Audience :: Developers",
|
|
27
|
+
"Intended Audience :: Science/Research",
|
|
28
|
+
"Operating System :: OS Independent",
|
|
29
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
30
|
+
"Programming Language :: Python :: 3.9",
|
|
31
|
+
"Programming Language :: Python :: 3.10",
|
|
32
|
+
"Programming Language :: Python :: 3.11",
|
|
33
|
+
"Programming Language :: Python :: 3.12",
|
|
34
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
35
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
36
|
+
]
|
|
37
|
+
dependencies = [
|
|
38
|
+
"torch>=2.0",
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
[project.urls]
|
|
42
|
+
Homepage = "https://github.com/sofajailgreeting/ConstraintML"
|
|
43
|
+
Repository = "https://github.com/sofajailgreeting/ConstraintML"
|
|
44
|
+
Issues = "https://github.com/sofajailgreeting/ConstraintML/issues"
|
|
45
|
+
|
|
46
|
+
[project.optional-dependencies]
|
|
47
|
+
nvidia = ["pynvml>=11.5.0"]
|
|
48
|
+
viz = ["matplotlib>=3.7"]
|
|
49
|
+
dev = ["pytest>=7"]
|
|
50
|
+
|
|
51
|
+
[tool.setuptools.packages.find]
|
|
52
|
+
where = ["src"]
|
|
53
|
+
|
|
54
|
+
[tool.setuptools.package-data]
|
|
55
|
+
constraintml = ["py.typed"]
|
|
56
|
+
|
|
57
|
+
[tool.pytest.ini_options]
|
|
58
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Extension point for optional, provider-specific cloud adapters (AWS/Azure/GCP).
|
|
2
|
+
|
|
3
|
+
For v2+ - not implemented in v1 - ConstraintML is local-first and must work fully without
|
|
4
|
+
any cloud adapter. This module only documents the shape a future adapter would take
|
|
5
|
+
so that `CarbonModel.source` and cost accounting have a defined place to plug into.
|
|
6
|
+
No network or authentication code lives here.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from typing import Protocol, runtime_checkable
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@runtime_checkable
|
|
15
|
+
class CloudAdapter(Protocol):
|
|
16
|
+
"""Shape a future cloud adapter would implement. Not used by v1 -- the
|
|
17
|
+
RuntimeOptimizationEngine never depends on this Protocol being satisfied.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def get_carbon_intensity(self, region: str) -> float:
|
|
21
|
+
"""Return regional grid carbon intensity in kg CO2e/kWh."""
|
|
22
|
+
...
|
|
23
|
+
|
|
24
|
+
def get_energy_price(self, region: str) -> float:
|
|
25
|
+
"""Return regional electricity price in USD/kWh."""
|
|
26
|
+
...
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
__all__ = ["CloudAdapter"]
|