Skip to content

Fix float32 precision leak in LTI stability guarantee and stale RoPE test contract - #86

Open
kelseyjmccorkle-stack wants to merge 7 commits into
kyegomez:mainfrom
kelseyjmccorkle-stack:feature/dj-style-engine
Open

Fix float32 precision leak in LTI stability guarantee and stale RoPE test contract#86
kelseyjmccorkle-stack wants to merge 7 commits into
kyegomez:mainfrom
kelseyjmccorkle-stack:feature/dj-style-engine

Conversation

@kelseyjmccorkle-stack

Copy link
Copy Markdown

Summary

  • LTIInjection.get_A() is documented/tested to guarantee ρ(A) < 1 by construction, but after a large gradient step the discretized exponent underflows and exp(-tiny) rounds to exactly 1.0 in float32, silently breaking the invariant that the whole stability section of the README is built around. Added an explicit clamp below 1.0 so the guarantee holds regardless of float precision.
  • 18cca89 correctly moved RoPE position-slicing responsibility from apply_rope up to OpenMythos.forward (fixing decode tokens stuck at position 0), but the direct-submodule unit tests (TestGQAttention, TestMLAttention, TestTransformerBlock, TestRecurrentBlock) were never updated to the new contract — they still passed the full unsliced freqs_cis buffer straight into GQAttention/MLAttention/etc., causing shape-mismatch failures whenever T < max_seq_len. Updated them to pre-slice to the input's sequence length (and cache offset, for the two KV-cache-accumulation tests).

Test plan

  • pytest tests/ — 118 passed, 1 skipped (was 14 failed / 104 passed before this change)
  • Verified LTIInjection.get_A() stays strictly below 1.0 after an aggressive SGD step (lr=1e3) that previously produced exactly 1.0

🤖 Generated with Claude Code

Home-Server and others added 7 commits August 1, 2026 10:23
Learn a DJ's mixing personality and apply it to any music library, with a
bridge to the OpenMythos RDT for the learned (deep-learning) path.

Modules:
- harmonic.py  Camelot-wheel key parsing + compatibility scoring
- profile.py   DJStyleProfile (tunable personality) + built-in archetypes
- analysis.py  Track + library analysis (optional librosa/mutagen; KS key est.)
- planner.py   MixPlanner -> MixPlan (harmonic + tempo + energy-curve ordering)
- render.py    render_mix -> beatmatched, crossfaded audio + cue sheet
- setlist.py   ingest setlists (incl. timestamped tracklists) + learn a profile
- enrich.py    pluggable BPM/key/energy lookup (CSV/MusicBrainz/GetSongBPM) + cache
- mixlang.py   serialize mixes to a token string for RDT training
- __main__.py  CLI: plan / analyze / render / enrich / learn

The parent open_mythos/__init__.py is made lazy (PEP 562) so the torch-free DJ
engine imports without pulling in the model stack. pyproject gains an [audio]
extra. 34 tests (28 torch/audio-free + 6 audio) cover all layers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NYYoch4CutCodkPevPXHLq
For DJs who don't publish tracklists, identify tracks from the mix audio.

- identify.py: pluggable AudioIdentifier (AudD API), scan_mix slices a mix into
  windows, recognizes each, and assemble_setlist dedups consecutive hits into
  an ordered Setlist with timestamp-derived durations -> flows into enrich/learn.
- CLI: `identify <audio> --provider audd:TOKEN --out set.json`.
- Network call and audio slicing are isolated so assembly is unit-tested offline;
  6 new identify tests + a scan_mix test over synthesized audio.

The module never downloads audio — the user supplies the file (legally).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NYYoch4CutCodkPevPXHLq
Getting an AudD API token is a signup hurdle; ShazamIdentifier needs no key.
It uses the unofficial `shazamio` client (Shazam's engine is robust to the
tempo/EQ shifts in DJ mixes). CLI: `identify <audio> --provider shazam`.

- ShazamIdentifier + parse_shazam_result; async recognize isolated in
  _recognize so parsing is unit-tested offline (no shazamio dep in tests).
- build_identifier accepts 'shazam'; pyproject gains a [shazam] extra.
- shazamio is unofficial (ToS gray area) and may break if Shazam changes
  their protocol — documented in the class.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NYYoch4CutCodkPevPXHLq
Add setlist_from_folder: treat an ordered folder of audio files (a split
mixtape, one file per track in play order) as a DJ's setlist. Filenames give
the clean "Artist - Title" (mixtape tags often store the DJ as artist), while
audio analysis fills bpm/key/energy per track.

Also fix energy on loud commercial masters: absolute RMS saturates at 1.0, so
_analyze_signal now also exposes raw loudness and normalize_energy() min-max
scales it across a set, yielding a meaningful relative energy curve.

`learn` accepts folders as inputs. Verified on a real 22-track R&B mixtape:
the learned profile correctly shows low harmonic strictness (0.19, song-cut
mixing) and wide BPM drift (13) characteristic of an open-format R&B DJ.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NYYoch4CutCodkPevPXHLq
Recognition (Shazam/AudD) yields only artist+title, leaving bpm/key/energy as
placeholders on a scanned mix. Add analyze_setlist_audio: slice the mix at each
track's stored offset and analyze it for bpm/key/energy, then normalize energy
across the set.

- assemble_setlist now records each track's mix offset in meta['offset'].
- Factor array-level analysis into analysis.analyze_samples (reused by whole-
  file analysis and per-segment mix analysis).

Verified on the 57-track reggae scan: profile went from placeholder defaults to
real values (bpm_range 99-137, drift 2 — tight riddim-locked tempo, a genuine
contrast to the hip-hop mixtapes' 6-13 drift).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NYYoch4CutCodkPevPXHLq
render_mix previously downmixed every source to mono before any DSP, so every
rendered file lost stereo width regardless of source quality.

Internal representation is now (channels, n) throughout: loading, resampling,
time-stretching, crossfading, and the lowpass-sweep transition all operate on
2D arrays. _to_stereo upmixes mono sources to dual-mono so every segment
entering the mix shares a channel count; output is interleaved stereo PCM16
(soundfile preferred, stdlib wave as fallback). Mono sources still work fine
end to end.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NYYoch4CutCodkPevPXHLq
…test contract

LTIInjection.get_A() is documented and tested to guarantee ρ(A) < 1 by
construction, but after a large gradient step the discretized exponent
underflows and exp(-tiny) rounds to exactly 1.0 in float32, silently
breaking the invariant. Clamp the result explicitly below 1.0.

Also update TestGQAttention/TestMLAttention/TestTransformerBlock/
TestRecurrentBlock to pre-slice freqs_cis to the input's sequence length
(and cache offset, for the KV-cache-accumulation tests) before calling
submodules directly. 18cca89 moved RoPE position slicing responsibility
to the caller (fixing decode tokens stuck at position 0), but these
direct-submodule tests were never updated to the new contract and were
passing the full unsliced buffer, causing shape mismatches in apply_rope.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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.

1 participant