Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
c94f6dc
fix(ci): trigger on tinyml-tinyverse changes too
t5fkg8d44d-beep Jul 29, 2026
6b8d3c5
fix: validate torch.compile with a warmup pass so failures actually f…
t5fkg8d44d-beep Jul 28, 2026
6f892fd
fix: restore training mode unconditionally after torch.compile warmup
t5fkg8d44d-beep Jul 28, 2026
3fd2bed
fix: pass input_shape to compile_model_if_enabled at all 4 call sites
t5fkg8d44d-beep Jul 28, 2026
57ff586
docs: clarify compile warmup only validates one graph variant, not th…
t5fkg8d44d-beep Jul 29, 2026
d0f420a
fix: unwrap torch.compile wrapper before ONNX/TorchScript export
t5fkg8d44d-beep Jul 29, 2026
4ff3e62
fix: recursively unwrap compiled submodules before export, not just t…
t5fkg8d44d-beep Jul 29, 2026
7f9c0f7
fix: skip torch.compile when quantization is enabled — FX tracing can…
t5fkg8d44d-beep Jul 29, 2026
106e2f9
test: strengthen skip-compile tests with torch.compile mock and INFO-…
t5fkg8d44d-beep Jul 29, 2026
e0d03c3
fix: strip torch.compile wrapper prefix from saved checkpoints
t5fkg8d44d-beep Jul 29, 2026
1ed7eff
fix: strip _orig_mod prefix from EMA checkpoint keys too, symmetric s…
t5fkg8d44d-beep Jul 29, 2026
2268e22
test: add asymmetric compiled-save/uncompiled-load EMA case — the rea…
t5fkg8d44d-beep Jul 29, 2026
aff21fd
fix(ci): run tinyml-tinyverse tests
t5fkg8d44d-beep Jul 29, 2026
0326b50
fix: strip _orig_mod prefix in load_weights() as defense-in-depth
t5fkg8d44d-beep Jul 29, 2026
898bc4a
fix: resume_from_checkpoint unsafe deserialization + old-checkpoint c…
t5fkg8d44d-beep Jul 29, 2026
0179c2d
fix(tests): use real argparse.Namespace for checkpoint round-trip tests
t5fkg8d44d-beep Jul 29, 2026
a2c05eb
feat: add apply_hardware_defaults — auto-enable compile/AMP on CUDA
t5fkg8d44d-beep Jul 28, 2026
edbabba
feat: wire apply_hardware_defaults into timeseries init_params
t5fkg8d44d-beep Jul 28, 2026
85873b3
fix(tests): rewrite native_amp override test to use production call p…
t5fkg8d44d-beep Jul 28, 2026
e27dd78
fix: init_params crashes on non-dict first argument
t5fkg8d44d-beep Jul 29, 2026
9c2f625
fix: train.py post-loop bugs in timeseries classification and forecas…
t5fkg8d44d-beep Aug 4, 2026
b7814cf
Merge remote-tracking branch 'upstream/main' into pr/hardware-defaults
t5fkg8d44d-beep Aug 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions tinyml-modelmaker/tests/test_hardware_defaults.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
from unittest.mock import patch
import pytest


def _make_params():
"""Build a minimal ConfigDict with the two training flags at their defaults."""
from tinyml_modelmaker.utils.config_dict import ConfigDict
return ConfigDict(dict(training=dict(compile_model=0, native_amp=False)))


def test_auto_enables_both_on_cuda_with_no_explicit_keys():
from tinyml_modelmaker.utils.hardware_defaults import apply_hardware_defaults
params = _make_params()
with patch('torch.cuda.is_available', return_value=True):
apply_hardware_defaults(params, set())
assert params.training.compile_model == 1
assert params.training.native_amp is True


def test_respects_explicit_native_amp_false():
from tinyml_modelmaker.utils.hardware_defaults import apply_hardware_defaults
params = _make_params()
with patch('torch.cuda.is_available', return_value=True):
apply_hardware_defaults(params, {'native_amp'})
assert params.training.compile_model == 1 # still auto-enabled
assert params.training.native_amp is False # explicit choice respected


def test_respects_explicit_compile_model_zero():
from tinyml_modelmaker.utils.hardware_defaults import apply_hardware_defaults
params = _make_params()
with patch('torch.cuda.is_available', return_value=True):
apply_hardware_defaults(params, {'compile_model'})
assert params.training.compile_model == 0 # explicit choice respected
assert params.training.native_amp is True # still auto-enabled


def test_no_change_without_cuda():
from tinyml_modelmaker.utils.hardware_defaults import apply_hardware_defaults
params = _make_params()
with patch('torch.cuda.is_available', return_value=False):
apply_hardware_defaults(params, set())
assert params.training.compile_model == 0
assert params.training.native_amp is False


def test_safe_when_params_lacks_flags():
"""hasattr guards: calling on a params dict without compile_model/native_amp must not raise."""
from tinyml_modelmaker.utils.config_dict import ConfigDict
from tinyml_modelmaker.utils.hardware_defaults import apply_hardware_defaults
params = ConfigDict(dict(training=dict(batch_size=32)))
with patch('torch.cuda.is_available', return_value=True):
apply_hardware_defaults(params, set()) # must not raise
assert params.training.batch_size == 32
69 changes: 69 additions & 0 deletions tinyml-modelmaker/tests/test_hardware_defaults_integration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
from unittest.mock import patch
import pytest


def test_init_params_auto_enables_on_cuda():
"""init_params with no training overrides auto-enables both flags on CUDA."""
from tinyml_modelmaker.ai_modules.timeseries.params import init_params
user_config = dict(common=dict(task_category='timeseries_classification'))
with patch('torch.cuda.is_available', return_value=True):
params = init_params(user_config)
assert params.training.compile_model == 1
assert params.training.native_amp is True


def test_init_params_respects_native_amp_false_override():
"""Explicit native_amp: false in user config is not overridden.

Mirrors the real production call pattern in run_tinyml_modelmaker.py:
ModelRunner.init_params() is called with zero arguments (so hardware
defaults auto-enable via apply_hardware_defaults with an empty
explicitly_set), and the user's config is merged in afterward via
ConfigDict.update() -- a deep merge -- not passed into the ConfigDict
constructor. (The constructor's *args merge path has a separate,
pre-existing shallow-merge bug that this test must avoid triggering.)
"""
from tinyml_modelmaker.ai_modules.timeseries.params import init_params
user_config = dict(
common=dict(task_category='timeseries_classification'),
training=dict(native_amp=False),
)
with patch('torch.cuda.is_available', return_value=True):
params = init_params()
params.update(user_config)
assert params.training.native_amp is False
assert params.training.compile_model == 1 # still auto-enabled


def test_init_params_no_change_without_cuda():
"""Without CUDA, both flags stay at defaults."""
from tinyml_modelmaker.ai_modules.timeseries.params import init_params
user_config = dict(common=dict(task_category='timeseries_classification'))
with patch('torch.cuda.is_available', return_value=False):
params = init_params(user_config)
assert params.training.compile_model == 0
assert params.training.native_amp is False


def test_init_params_does_not_crash_on_non_dict_first_arg():
"""ConfigDict documents a YAML path string (or None) as valid first
positional input, not just a dict. init_params's own explicitly-set-key
detection must not assume args[0] is dict-like and crash instead."""
from tinyml_modelmaker.ai_modules.timeseries.params import init_params
with patch('torch.cuda.is_available', return_value=True):
# A bogus path is fine here -- ConfigDict's *args merge loop only
# merges dict/ConfigDict values and silently ignores strings, so
# this never actually attempts to read the file. The point of this
# test is solely that passing a string doesn't crash init_params's
# own explicitly-set-key detection before ConfigDict is even built.
params = init_params('/nonexistent/path/to/config.yaml')
assert params.training.compile_model == 1
assert params.training.native_amp is True


def test_init_params_does_not_crash_on_none_first_arg():
from tinyml_modelmaker.ai_modules.timeseries.params import init_params
with patch('torch.cuda.is_available', return_value=True):
params = init_params(None)
assert params.training.compile_model == 1
assert params.training.native_amp is True
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@

from ... import utils
from . import constants
from ...utils.hardware_defaults import apply_hardware_defaults


def init_params(*args, **kwargs):
Expand Down Expand Up @@ -222,5 +223,11 @@ def init_params(*args, **kwargs):
),
)

# args[0] is usually a user config dict, but ConfigDict itself also
# accepts a YAML path string or None as a first positional argument --
# only inspect it as a mapping when it actually is one.
user_training_keys = set(args[0].get('training', {}).keys()) \
if args and isinstance(args[0], dict) else set()
params = utils.ConfigDict(default_params, *args, **kwargs)
apply_hardware_defaults(params, user_training_keys)
return params
19 changes: 19 additions & 0 deletions tinyml-modelmaker/tinyml_modelmaker/utils/hardware_defaults.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import torch


def apply_hardware_defaults(params, explicitly_set: set) -> None:
"""Auto-enable compile_model and native_amp when CUDA is available.

Skips fields present in explicitly_set — those are deliberate user
choices from the YAML config and must not be overridden.
hasattr guards keep this safe for params that don't carry these fields
yet (vision, audio — Phase 2).
"""
if not torch.cuda.is_available():
return
if 'compile_model' not in explicitly_set and hasattr(params.training, 'compile_model'):
if getattr(params.training, 'compile_model', 0) == 0:
params.training.compile_model = 1
if 'native_amp' not in explicitly_set and hasattr(params.training, 'native_amp'):
if not getattr(params.training, 'native_amp', False):
params.training.native_amp = True
Loading
Loading