Skip to content

fix(ci): install ti_mcu_nnc before tinyverse, bump to 2.1.2, add macOS step - #17

Merged
Adithya-Thonse merged 45 commits into
TexasInstruments:mainfrom
musicalplatypus:pr/fix-ci-tinyverse-install
Jul 28, 2026
Merged

fix(ci): install ti_mcu_nnc before tinyverse, bump to 2.1.2, add macOS step#17
Adithya-Thonse merged 45 commits into
TexasInstruments:mainfrom
musicalplatypus:pr/fix-ci-tinyverse-install

Conversation

@musicalplatypus

Copy link
Copy Markdown
Contributor

Summary

The Tier 1 Component Tests have been failing on all platforms with ModuleNotFoundError: No module named 'tinyml_tinyverse'. This is a CI configuration bug, not a code bug — tinyml_tinyverse never gets installed due to three compounding issues.

Root causes

1. Wrong install order
pip install -e tinyml-tinyverse runs before ti_mcu_nnc is installed. Since tinyml-tinyverse/pyproject.toml lists ti_mcu_nnc as a required dependency, pip tries to fetch it during the tinyverse install — and fails.

2. Version mismatch
The CI workflow installs ti_mcu_nnc 2.1.1, but tinyml-tinyverse/pyproject.toml requires 2.1.2. The URL for 2.1.1 cannot satisfy the declared dependency.

3. Silent failure (the sneaky one)
The Install dependencies step is a multi-command run: block without set -e. When pip install -e tinyml-tinyverse exits with code 1, bash continues. The final pip install pytest succeeds, so the step is marked green. tinyml_tinyverse is never installed, and the failure only surfaces at test collection time — two steps later.

4. macOS had no ti_mcu_nnc install step
Linux and Windows had explicit steps; macOS relied entirely on tinyverse's own dependency resolution, which was broken by the above.

Fix

  • Move all ti_mcu_nnc install steps before Install dependencies
  • Bump version 2.1.1 → 2.1.2 to match tinyml-tinyverse/pyproject.toml
  • Add a macOS ARM64 ti_mcu_nnc install step
  • Add set -e to Install dependencies so future failures surface immediately

Verification

Confirmed that checking out upstream/main and running the tests locally produces the identical ModuleNotFoundError — the failure predates all open PRs and is purely a CI configuration issue.

🤖 Generated with Claude Code

musicalplatypus and others added 30 commits February 22, 2026 13:18
updates from the TI main branch.  Most likely the 1.3.0 release
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… utils, symlink

1. Add missing TinyMLQuantizationVersion import in timeseries/runner.py and
   vision/runner.py — NameError on every training run that reaches packaging.
2. Fix argv splice when native_amp=True + quantization: strip boolean flags
   before fixed-offset slicing, re-append after, preventing malformed argv.
3. Fix len(split_factor) called on float in dataset_utils.py — use the
   accumulated split_factors list instead of the original parameter.
   Fixed in both create_inter_file_split and create_intra_file_split.
4. Add else branch in dataset_load() for unknown annotation_format —
   raises ValueError instead of UnboundLocalError.
5. Remove duplicate os.symlink() call in make_symlink() — was creating
   the link twice, second call always failed with FileExistsError.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… error handling

1. ConfigDict.__getstate__: add missing return — pickling now preserves state.
2. _parse_include_files: fix and→or — absolute/relative include paths now
   resolve correctly instead of always prepending base path.
3. TASK_CATEGORIES: use TASK_CATEGORY_TS_ANOMALYDETECTION instead of
   TASK_TYPE_GENERIC_TS_ANOMALYDETECTION — fixes wrong compilation
   parameters for anomaly detection tasks.
4. download_url: handle missing Content-Length header gracefully instead
   of crashing with TypeError on int(None).
5. download_files: track aggregate success across all URLs instead of
   reporting only the last download's status.
6. get_target_module: raise ValueError with available options instead of
   returning None — prevents confusing downstream AttributeError on
   NoneType. Applied to both timeseries and vision training modules.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1. Set args.quit_event before compile_scr.run() so compilation can
   actually be cancelled (was set after run() returned — too late).
2. Fix typo 'anomlay_list.txt' → 'anomaly_list.txt' in dataset handling.
3. Move cleanup_special_chars() file write outside the read block so the
   file isn't truncated while the read handle is still open — prevents
   data loss if the write fails mid-way.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…, dead code

1. Persistent validation iterator: replace next(iter(valid_loader)) with
   a cycling iterator so architecture updates see different batches each step
2. Structural params: pass steps/multiplier/stem_multiplier from search to
   final model so the evaluated architecture matches what was searched
3. Best genotype tracking: select genotype with highest validation accuracy
   instead of always using the last epoch
4. Device abstraction: add get_device() (CUDA > MPS > CPU), replace all
   hardcoded .cuda() calls with .to(device) across search, model, architect
5. Differentiable resource penalty: replace inert scalar penalties with
   sum(softmax(alpha) * param_counts) normalized to [0,1], fully
   differentiable w.r.t. architecture parameters
6. Remove dead code: delete unused RNN genotypes, Genotype_RNN, save(),
   create_exp_dir(), and all RNN branches from architect

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The previous MPS optimization commit moved .item() from MetricLogger
to SmoothedValue but it still fired on every batch update, causing a
GPU command-buffer flush each time (no actual improvement).

Fix by storing detached tensors in the deque and accumulating total
on-device.  Property accessors (median, avg, global_avg, max, value)
call .item() lazily — only at print time (every print_freq batches).
Benchmarked at 7.8x faster for the metric-logging path on MPS.

Also reverts MPS memory reporting (torch.mps.current_allocated_memory)
which introduced a new GPU sync that never existed before the previous
commit.  CUDA memory reporting is unchanged.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…kers

persistent_workers=True keeps worker processes alive across epochs, but on
macOS (spawn start method) the resource_tracker detects unreleased semaphores
at exit. Add shutdown_data_loaders() to explicitly terminate workers after
training completes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…cripts

The previous fix (f3cc7c0) added shutdown_data_loaders() to train.py files
but missed all test_onnx scripts. These are the last step in the pipeline
and create DataLoaders with num_workers=8 without cleanup, causing
macOS resource_tracker to report leaked semaphores at process exit.

Add shutdown_data_loaders() calls to all 6 test_onnx files and harden
the function itself with try/except and gc.collect() to ensure worker
processes fully release their semaphores before exit.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Enhanced shutdown_data_loaders to explicitly break the iterator's
references to multiprocessing Queue/Lock/Event objects after calling
_shutdown_workers().  Without this, the POSIX named semaphores inside
those objects survive until Python's resource_tracker atexit handler,
which reports them as leaked.  By clearing the references eagerly,
CPython's refcount-based deallocation calls sem_unlink immediately.

Also added the missing shutdown_data_loaders call in
image_classification/train.py, which was the only train.py that
did not shut down its DataLoaders.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The previous approach (breaking Queue references for gc) didn't work
because on Python <=3.11, _multiprocessing.SemLock's C dealloc only
calls sem_close() — it never calls resource_tracker.unregister().
The resource_tracker therefore reports every semaphore as leaked
regardless of cleanup efforts.  (Fixed in Python 3.12+.)

New approach: after _shutdown_workers() joins all worker processes,
walk the iterator's Queue and Event objects to find their internal
SemLock names, then explicitly call resource_tracker.unregister()
for each one.  This removes them from the resource_tracker's
registry so its atexit handler has nothing to warn about.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…, eval efficiency

- Wire up dead --compile-model arg to torch.compile (aot_eager for MPS, inductor for CUDA)
- Add --native-amp flag for PyTorch native AMP (torch.amp.autocast) on CUDA and MPS
- Add persistent_workers=True to DataLoaders (avoids respawning on macOS spawn)
- Fix pin_memory logic to use torch.cuda.is_available() instead of broken gpu>0 check
- Use optimizer.zero_grad(set_to_none=True) across all training loops
- Fix O(n²) torch.cat accumulation in evaluate_classification with list-based collection
- Move per-batch f1/confusion_matrix to epoch-end in classification eval

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…er config pipeline

Wire torch.compile and native AMP opt-in flags from modelmaker params through
timeseries_base.py argv construction to tinyml-tinyverse training scripts.
Update ARCHITECTURE.md with Training Performance Optimizations section and
PORTING_ASSESSMENT.md with post-porting development history.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ve_paths()

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add TRAINING_BACKEND_TINYML_TINYVERSE, DATA_DIR_CLASSES, DATA_DIR_FILES,
DATA_DIR_IMAGES constants. Use existing TRAINING_DEVICE_CUDA in params.py
defaults instead of bare 'cuda' strings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…nyml-modelmaker

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…er, etc.)

Define typing.Protocol classes that formalize the implicit contracts already
followed by ModelRunner, ModelTraining, ModelCompilation, and DatasetHandling.
Uses structural subtyping so no existing classes need modification. Enables
static type checking and documents the interface contracts for future
implementations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ment analysis

Documents the full tinyml-tensorlab architecture including all four sub-repos,
pipeline flow, configuration system, model/quantization/NAS subsystems, a Mermaid
architecture diagram, and 12 design/implementation improvement recommendations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Restore matplotlib.use('Agg') in utils.py: removing it left
  classification/anomaly/regression scripts without a headless backend
  guard, causing potential display failures on CI/servers.

- Restore logger and MSE logging in train_one_epoch_anomalydetection
  and evaluate_anomalydetection: both lines were accidentally dropped
  during the AMP refactor, silencing per-epoch reconstruction error.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…scripts

Ensures data loaders are always shut down (releasing macOS POSIX semaphores)
even when an exception is raised or an early return path is taken mid-function.
Affects all 5 train.py scripts and all 6 test_onnx*.py scripts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- nas/train_cnn_search.py: capture genotype after training step so best_genotype
  matches the architecture that achieved best_valid_acc; initialize to -inf so
  zero-accuracy epochs are correctly recorded
- download_utils.py: guard content-length header against non-numeric values
- config_dict.py: use os.path.isabs() for cross-platform include-path detection
- nas/model.py: derive C_prev from cell.multiplier (actual genotype concat width)
  and validate it matches the network multiplier parameter
- common/utils/utils.py: convert to float before .mean() on stacked tensors
- nas/utils.py: validate gpu_index range before constructing cuda device

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The np.zeros((num_classes, num_classes)) at function entry was always
overwritten by get_confusion_matrix at epoch end; it was also a latent
TypeError when num_classes was None. Replaced with an explicit ValueError.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- dataset_utils.py: validate scalar split_factor in (0, 1) range; use
  split_factors list (not raw split_factor param) in post-normalization checks
  to avoid TypeError on float inputs
- misc_utils.py: guard input_data_path before os.path.basename (prevents
  TypeError when both dataset_name and input_data_path are unset); use
  absolutized projects_path as base for model_packaged_path; fix archive
  name for flat run_names (filter(None,...) removes empty leading segment
  so 'myrun' → myrun.zip instead of _myrun.zip)
- vision/runner.py: use v.get() for inference_time_us/sram/flash to avoid
  KeyError when a device entry is missing a metric

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sections 1 (No Test Suite) and 6 (No Abstract Base Classes or Protocols)
now accurately reflect what the PR stack addressed: the protocols module
in ai_modules/protocols.py and the test_protocols.py test suite.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Resolved conflicts by preserving the try/finally structure from
pr/macos-semaphore-fixes and integrating compile_model_if_enabled,
get_amp_context, and get_grad_scaler additions from the performance PR.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
t5fkg8d44d-beep and others added 14 commits July 19, 2026 14:40
Resolved __init__.py conflicts by keeping the ValueError-raising
get_target_module() from pr/bug-fixes rather than the return-None
pattern from pr/code-quality.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- NAS infer(): extend torch.no_grad() to cover forward pass + criterion
  (previously only covered device transfers; gradients computed needlessly)
- constants.py get_default_data_dir_for_task(): raise ValueError on unknown
  task_category instead of silently returning DATA_DIR_CLASSES
- utils.py evaluate_classification(): accumulate tensors on CPU (.cpu())
  to avoid OOM from full-dataset tensors staying on GPU between batches
- utils.py evaluate_classification(): use .squeeze() consistently for
  get_au_roc and get_confusion_matrix; remove now-redundant .cpu() calls
- image_classification/train.py: fix AUC ROC log to use best['auc']
  not best['f1'] (copy-paste bug)
- misc_utils.py: apply filter(None, ...) to compilation archive name paths
  (same fix as training paths — leading underscore for bare run names)
- ARCHITECTURE.md: fix CLI entry-point command and update summary table
  rows 1 and 6 to reflect addressed status

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…s refactor

0c1b23d moved path resolution out of ModelRunner and removed the
tinyml_torchmodelopt.quantization import along with it, but two uses remain in
run() at the packaging step. Any run that reaches model packaging raises
NameError: name 'TinyMLQuantizationVersion' is not defined, which makes
compilation fail on every platform.

Restore the import, matching how params.py, misc_utils.py and timeseries_base.py
already reference it.

Verified: compiling an exported ONNX for F28P55 now completes and produces
artifacts/mod.a, tvmgen_default.h and the deployment zip. All 20 Python files
changed on this branch import cleanly.
Companion to 3607b15. The resolve_paths refactor in 0c1b23d dropped this
import from both ai_modules/timeseries/runner.py and ai_modules/vision/runner.py;
only the timeseries one was restored. vision/runner.py still raises
NameError at lines 154 and 207 when a run reaches model packaging.

Found by running pyflakes across every file this branch touches — the earlier
import-only check could not catch it, since the name is referenced inside
run() and never evaluated at import time.
image_classification/test_onnx.py and timeseries_anomalydetection/test_onnx_cls.py
call .astype(np.float32) without importing numpy, so the
nn_for_feature_extraction path raises NameError. Pre-existing rather than
introduced here, but both files are already modified by this branch.

Found with pyflakes; the name is only referenced inside a conditional branch,
so it is invisible to an import-time check.
audio_classification/test_onnx.py calls .astype(np.float32) without importing
numpy, raising NameError on the nn_for_feature_extraction path.

The same fix exists on pr/mps-support, but that branch has never been merged
into main, so it never propagated here. The sibling fixes for
image_classification and timeseries_anomalydetection arrived via
pr/macos-semaphore-fixes.

Pre-existing upstream: TI's main carries the same bug. PR TexasInstruments#8 carries the fix
back to them.
Three bug fixes for MPS/Apple Silicon users, verified by 7/7 passing
TestEndToEnd on macOS ARM64:

- Isolate onnxsim.simplify() in a subprocess on darwin/arm64 so the
  SIGSEGV during Python interpreter shutdown cannot kill the main process
  before packaging and compilation run (quant_base.py)

- Set PYTORCH_ENABLE_MPS_FALLBACK=1 inside _get_device() when MPS is
  selected, so ops unsupported on MPS fall back to CPU transparently;
  previously this was only set in the e2e test environment
  (timeseries_base.py, audio_base.py, image_base.py)

- Fix MPS float64 crash in all reference test/train scripts: move dtype
  cast (.long()/.float()) before .to(device) so float64 tensors are
  converted on CPU before being moved to MPS, which rejects float64
  (8 test_onnx.py and train.py files across all task types)

Also remove spurious f-prefix from 63 f-strings with no placeholders
across modelmaker, tinyverse, modelzoo, and agent-skills.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ization

The auto-quantization calibration and eval loops moved float64 feature tensors
to the device before casting, so MPS raised "Cannot convert a MPS Tensor to
float64 dtype" and killed the run right after training finished — the model
trained and exported, then quantization aborted and nothing compiled.

main already carries the equivalent fixes for tinyverse utils.py and the
reference scripts; this was the one remaining site.
Backfills main with the two pieces that lived only on the MPS branch: the
nas_enabled model-description fallback (so a NAS id such as NAS_m is not
rejected as an unknown model) and tests/test_nas_support.py. Both now ship in
the dedicated NAS PR.
…S step

Three bugs caused tinyml_tinyverse to silently not be installed:

1. Wrong order: ti_mcu_nnc was installed after pip install -e tinyml-tinyverse,
   so pip tried to fetch ti_mcu_nnc 2.1.2 (from pyproject.toml) during the
   tinyverse install and failed.

2. Version mismatch: CI installed ti_mcu_nnc 2.1.1 but tinyml-tinyverse/
   pyproject.toml requires 2.1.2.

3. Silent failure: the Install dependencies step is a multi-command run block
   with no set -e. When pip install -e tinyml-tinyverse exits 1, bash continues
   and the step appears green because pip install pytest (the last command)
   succeeds. The module is never installed.

4. macOS had no ti_mcu_nnc install step at all.

Fix: move all ti_mcu_nnc installs before Install dependencies, bump to 2.1.2,
add a macOS ARM64 step, and add set -e so install failures are visible.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@Adithya-Thonse
Adithya-Thonse merged commit 331388a into TexasInstruments:main Jul 28, 2026
0 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants