cloverxion commited on
Commit
96145a9
·
verified ·
1 Parent(s): c75436e

sync(v1.6.0 / v1.0.9): synchronize full luminamoon monorepo, LSO-Eval v1.1.1 results, and LuminaV v1.6.0 bundle

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .env.example +7 -7
  2. .github/dependabot.yml +37 -37
  3. .github/workflows/ci.yml +308 -308
  4. .github/workflows/docs.yml +83 -83
  5. .github/workflows/hf_sync.yml +49 -49
  6. .github/workflows/security.yml +91 -91
  7. .gitignore +54 -54
  8. CHANGELOG.md +430 -430
  9. Cargo.lock +1218 -1218
  10. Cargo.toml +39 -39
  11. LICENSE +13 -13
  12. README.md +334 -334
  13. SECURITY.md +50 -50
  14. benchmarks/bench_data_processing.py +411 -411
  15. benchmarks/bench_luminav_vision_modes.py +398 -398
  16. changelogs/LUMINAV_ALGORITHM_CHANGELOG.md +430 -430
  17. changelogs/README.md +25 -25
  18. changelogs/SOFTWARE_CHANGELOG.md +261 -261
  19. config.json +122 -122
  20. data/Cargo.toml +24 -24
  21. data/README.md +206 -206
  22. data/docs/README.md +62 -62
  23. data/docs/library_audit/01_rust_toolchain_simd_and_mmap.md +56 -56
  24. data/docs/library_audit/02_python_pyo3_abi3t_and_maturin.md +53 -53
  25. data/docs/library_audit/03_parquet_arrow_and_http206_streaming.md +48 -48
  26. data/docs/library_audit/04_xxh3_dedup_crc32_and_hf_hub_security.md +49 -49
  27. data/docs/library_audit/05_universal_formats_compression_and_diagnostics.md +87 -87
  28. data/docs/library_audit/README.md +30 -30
  29. data/src/constants.rs +21 -21
  30. data/src/coord.rs +39 -39
  31. data/src/dedup.rs +112 -112
  32. data/src/error.rs +166 -166
  33. data/src/lib.rs +15 -15
  34. data/src/metadata.rs +264 -264
  35. data/src/minhash.rs +186 -186
  36. data/src/postprocess.rs +135 -135
  37. data/src/py_bridge.rs +839 -839
  38. data/src/quality.rs +169 -169
  39. data/src/serialize.rs +268 -268
  40. data/src/simd_cpu.rs +313 -313
  41. data/src/string_tensor.rs +269 -269
  42. data/src/tests.rs +384 -384
  43. docs/README.md +101 -101
  44. docs/dataloader/README.md +69 -69
  45. docs/dataloader/coord_and_metadata/README.md +70 -70
  46. docs/dataloader/dedup/README.md +66 -66
  47. docs/dataloader/multimodal_and_hf_hub/README.md +38 -38
  48. docs/dataloader/preram/README.md +37 -37
  49. docs/dataloader/python_ffi_and_runtime/README.md +29 -29
  50. docs/dataloader/simd_and_postprocess/README.md +62 -62
.env.example CHANGED
@@ -1,7 +1,7 @@
1
- # GitHub API Personal Access Tokens
2
- GITHUB_TOKEN=
3
- GITHUB_TOKEN_2=
4
-
5
- # Distribution & Registry Deployment Tokens
6
- HF_TOKEN=
7
- PYPI_TOKEN=
 
1
+ # GitHub API Personal Access Tokens
2
+ GITHUB_TOKEN=
3
+ GITHUB_TOKEN_2=
4
+
5
+ # Distribution & Registry Deployment Tokens
6
+ HF_TOKEN=
7
+ PYPI_TOKEN=
.github/dependabot.yml CHANGED
@@ -1,37 +1,37 @@
1
- version: 2
2
- updates:
3
- - package-ecosystem: "cargo"
4
- directory: "/"
5
- target-branch: "development"
6
- schedule:
7
- interval: "weekly"
8
- day: "monday"
9
- open-pull-requests-limit: 0
10
- labels:
11
- - "dependencies"
12
- - "rust"
13
- - "security"
14
-
15
- - package-ecosystem: "pip"
16
- directory: "/"
17
- target-branch: "development"
18
- schedule:
19
- interval: "weekly"
20
- day: "monday"
21
- open-pull-requests-limit: 0
22
- labels:
23
- - "dependencies"
24
- - "python"
25
- - "security"
26
-
27
- - package-ecosystem: "github-actions"
28
- directory: "/"
29
- target-branch: "development"
30
- schedule:
31
- interval: "weekly"
32
- day: "monday"
33
- open-pull-requests-limit: 0
34
- labels:
35
- - "dependencies"
36
- - "github-actions"
37
- - "security"
 
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "cargo"
4
+ directory: "/"
5
+ target-branch: "development"
6
+ schedule:
7
+ interval: "weekly"
8
+ day: "monday"
9
+ open-pull-requests-limit: 0
10
+ labels:
11
+ - "dependencies"
12
+ - "rust"
13
+ - "security"
14
+
15
+ - package-ecosystem: "pip"
16
+ directory: "/"
17
+ target-branch: "development"
18
+ schedule:
19
+ interval: "weekly"
20
+ day: "monday"
21
+ open-pull-requests-limit: 0
22
+ labels:
23
+ - "dependencies"
24
+ - "python"
25
+ - "security"
26
+
27
+ - package-ecosystem: "github-actions"
28
+ directory: "/"
29
+ target-branch: "development"
30
+ schedule:
31
+ interval: "weekly"
32
+ day: "monday"
33
+ open-pull-requests-limit: 0
34
+ labels:
35
+ - "dependencies"
36
+ - "github-actions"
37
+ - "security"
.github/workflows/ci.yml CHANGED
@@ -1,309 +1,309 @@
1
- name: CI/CD Pipeline
2
-
3
- on:
4
- push:
5
- branches: [ "main", "master" ]
6
- tags: [ "v*" ]
7
- pull_request:
8
- branches: [ "main", "master", "development" ]
9
- release:
10
- types: [published]
11
- workflow_dispatch:
12
-
13
-
14
- concurrency:
15
- group: ${{ github.workflow }}-${{ github.ref }}
16
- cancel-in-progress: true
17
-
18
- permissions:
19
- contents: read
20
-
21
- jobs:
22
- rust-quality:
23
- name: Rust Quality & Parity (${{ matrix.os }})
24
- if: github.event_name != 'release'
25
- runs-on: ${{ matrix.os }}
26
- strategy:
27
- fail-fast: false
28
- matrix:
29
- os: ${{ (startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch' || github.ref == 'refs/heads/main' || github.base_ref == 'main') && fromJSON('["ubuntu-latest", "windows-latest", "macos-latest"]') || fromJSON('["ubuntu-latest"]') }}
30
- steps:
31
- - name: Checkout repository
32
- uses: actions/checkout@v7
33
-
34
- - name: Set up Python
35
- uses: actions/setup-python@v7
36
- with:
37
- python-version: "3.12"
38
-
39
- - name: Set up Rust toolchain
40
- uses: dtolnay/rust-toolchain@stable
41
- with:
42
- components: clippy, rustfmt
43
-
44
- - name: Rust Cache
45
- uses: Swatinem/rust-cache@v2
46
-
47
- - name: Verify Formatting (cargo fmt)
48
- if: matrix.os == 'ubuntu-latest'
49
- run: cargo fmt --check
50
-
51
- - name: Cargo Clippy Lints
52
- run: cargo clippy --workspace --all-targets -- -D warnings
53
-
54
- - name: Cargo Unit & Parity Tests
55
- run: cargo test --workspace --verbose
56
-
57
- type-check:
58
- name: Python Static Typing (Pyright)
59
- if: github.event_name != 'release'
60
- runs-on: ubuntu-latest
61
- steps:
62
- - name: Checkout repository
63
- uses: actions/checkout@v7
64
-
65
- - name: Set up Python 3.14
66
- uses: actions/setup-python@v7
67
- with:
68
- python-version: "3.14"
69
-
70
- - name: Set up Node.js
71
- uses: actions/setup-node@v7
72
- with:
73
- node-version: "24"
74
-
75
- - name: Install Pyright Dependencies
76
- run: |
77
- python -m pip install --upgrade pip
78
- pip install "numpy>=2.0.0"
79
-
80
- - name: Run Pyright
81
- run: npx pyright python/luminamoon
82
-
83
- - name: Verify Universal Fallback Wheel Builder
84
- run: |
85
- python tools/build_universal_wheel.py --out dist
86
- python -c "import zipfile, sys, tempfile; td = tempfile.TemporaryDirectory(); import glob; whl = glob.glob('dist/luminamoon-*-py3-none-any.whl')[0]; z = zipfile.ZipFile(whl); z.extractall(td.name); sys.path.insert(0, td.name); import luminamoon.optimizer.luminav as lum; import numpy as np; p = np.zeros(5, dtype=np.float32); opt = lum.LuminaV([p], lr=1e-3); opt.step(grads=[np.ones(5, dtype=np.float32)]); assert opt.state[p]['step'] == 1; print('Universal fallback verified successfully')"
87
-
88
- python-matrix:
89
- name: Python Tests (${{ matrix.os }}, Py${{ matrix.python-version }})
90
- if: github.event_name != 'release'
91
- needs: [rust-quality]
92
- runs-on: ${{ matrix.os }}
93
- strategy:
94
- fail-fast: false
95
- matrix:
96
- os: ${{ (startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch') && fromJSON('["ubuntu-latest", "windows-latest", "macos-latest"]') || ((github.ref == 'refs/heads/main' || github.base_ref == 'main') && fromJSON('["ubuntu-latest", "windows-latest"]') || fromJSON('["ubuntu-latest"]')) }}
97
- python-version: ${{ (startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch') && fromJSON('["3.10", "3.11", "3.12", "3.13", "3.14"]') || fromJSON('["3.14"]') }}
98
- steps:
99
- - name: Checkout repository
100
- uses: actions/checkout@v7
101
-
102
- - name: Set up Rust toolchain
103
- uses: dtolnay/rust-toolchain@stable
104
-
105
- - name: Rust Cache
106
- uses: Swatinem/rust-cache@v2
107
-
108
- - name: Set up Python
109
- uses: actions/setup-python@v7
110
- with:
111
- python-version: ${{ matrix.python-version }}
112
- allow-prereleases: true
113
- cache: "pip"
114
-
115
- - name: Upgrade Pip & Install Build Tools
116
- run: |
117
- python -m pip install --upgrade pip
118
- pip install maturin "numpy>=2.0.0" pytest
119
-
120
- - name: Build & Install LuminaMoon (Native CPython Extension)
121
- run: python tools/build_wheel.py --install
122
-
123
- - name: Run Standalone NumPy Verification
124
- run: python optimizer/tests/test_standalone_numpy.py
125
-
126
- - name: Run Data Modular Verification
127
- run: python -B tests/test_data_modular.py
128
-
129
- - name: Run Adversarial String Verification
130
- run: python -B tests/test_gpu_vs_cpu_strings.py
131
-
132
- - name: Install Optional PyTorch Dependency
133
- id: install-torch
134
- run: python -c "import os, subprocess, sys; r = subprocess.run([sys.executable, '-m', 'pip', 'install', 'torch>=2.0.0']); open(os.environ['GITHUB_OUTPUT'], 'a', encoding='utf-8').write(f'installed={str(r.returncode == 0).lower()}\n'); print('PyTorch installed:', r.returncode == 0)"
135
-
136
-
137
- - name: Run PyTorch End-to-End Training Tests
138
- if: steps.install-torch.outputs.installed == 'true'
139
- run: python optimizer/tests/test_end_to_end_training.py
140
-
141
- - name: Run LSO-Eval Vision (V-Eval) Protocol Verification
142
- if: steps.install-torch.outputs.installed == 'true'
143
- run: python eval/v_eval_suite.py --verify-architecture
144
-
145
- - name: Verify Universal Dynamic Fallback
146
- run: |
147
- python -c "import luminamoon.optimizer.luminav as lum; lum._luminamoon_rs = None; import numpy as np; p = np.zeros(10, dtype=np.float32); opt = lum.LuminaV([p], lr=1e-3); opt.step(grads=[np.ones(10, dtype=np.float32)]); assert opt.state[p]['step'] == 1; print('Dynamic fallback verified successfully')"
148
-
149
- free-threaded:
150
- name: Free-Threaded No-GIL (${{ matrix.python-version }})
151
- if: github.event_name != 'release'
152
- needs: [rust-quality]
153
- runs-on: ubuntu-latest
154
- continue-on-error: true
155
- strategy:
156
- fail-fast: false
157
- matrix:
158
- python-version: ["3.14t", "3.15t"]
159
- steps:
160
- - name: Checkout repository
161
- uses: actions/checkout@v7
162
-
163
- - name: Set up Rust toolchain
164
- uses: dtolnay/rust-toolchain@stable
165
-
166
- - name: Rust Cache
167
- uses: Swatinem/rust-cache@v2
168
-
169
- - name: Set up Python (Free-Threaded)
170
- uses: actions/setup-python@v7
171
- with:
172
- python-version: ${{ matrix.python-version }}
173
- allow-prereleases: true
174
-
175
- - name: Install Maturin
176
- run: |
177
- python -m pip install --upgrade pip
178
- pip install maturin
179
-
180
- - name: Verify abi3t Wheel Compilation & Import
181
- run: |
182
- maturin build --release -i python --out dist
183
- pip install dist/*.whl
184
- python -c "import luminamoon.optimizer._luminamoon_optimizer_rs, sys; assert not sys._is_gil_enabled(), 'GIL must remain disabled'; print('Free-threaded abi3t extension verified successfully')"
185
-
186
- release-build:
187
- name: Distribution Wheel Build (${{ matrix.os }})
188
- needs: [python-matrix, type-check]
189
- if: (startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch') && github.event_name != 'release'
190
- runs-on: ${{ matrix.os }}
191
- strategy:
192
- matrix:
193
- os: [ubuntu-latest, windows-latest, macos-latest]
194
- steps:
195
- - name: Checkout repository
196
- uses: actions/checkout@v7
197
-
198
- - name: Set up Python for Build Scripts
199
- if: matrix.os == 'ubuntu-latest' || startsWith(github.ref, 'refs/tags/v')
200
- uses: actions/setup-python@v7
201
- with:
202
- python-version: "3.14"
203
-
204
- - name: Sync version from Git Tag
205
- if: startsWith(github.ref, 'refs/tags/v')
206
- run: python tools/sync_version.py "${{ github.ref_name }}"
207
-
208
- - name: Build Universal Pure-Python Fallback Wheel (py3-none-any)
209
- if: matrix.os == 'ubuntu-latest'
210
- run: python tools/build_universal_wheel.py --out dist
211
-
212
- - name: Set up Rust toolchain
213
- uses: dtolnay/rust-toolchain@stable
214
-
215
- - name: Build sdist (Source Distribution)
216
- if: matrix.os == 'ubuntu-latest'
217
- uses: PyO3/maturin-action@v1
218
- with:
219
- command: sdist
220
- args: --out dist
221
-
222
- - name: Build Wheel via Maturin
223
- uses: PyO3/maturin-action@v1
224
- with:
225
- command: build
226
- args: --release --out dist
227
- manylinux: auto
228
-
229
- - name: Upload Artifacts
230
- uses: actions/upload-artifact@v7
231
- with:
232
- name: wheels-${{ matrix.os }}
233
- path: dist/*
234
-
235
- draft-release:
236
- name: Create Draft GitHub Release
237
- needs: [release-build]
238
- if: startsWith(github.ref, 'refs/tags/v') && github.event_name != 'release'
239
- runs-on: ubuntu-latest
240
- permissions:
241
- contents: write
242
- steps:
243
- - name: Checkout repository
244
- uses: actions/checkout@v7
245
-
246
- - name: Download all built wheels
247
- uses: actions/download-artifact@v8
248
- with:
249
- pattern: wheels-*
250
- merge-multiple: true
251
- path: dist
252
-
253
- - name: Extract Structured Release Notes from Software Changelog
254
- env:
255
- RELEASE_TAG: ${{ github.ref_name }}
256
- run: |
257
- python3 -c '
258
- import os, pathlib, re
259
- tag = os.environ["RELEASE_TAG"].lstrip("v")
260
- text = pathlib.Path("changelogs/SOFTWARE_CHANGELOG.md").read_text(encoding="utf-8")
261
- pattern = rf"^## \[{re.escape(tag)}\].*?(?=^## \[|\Z)"
262
- match = re.search(pattern, text, flags=re.MULTILINE | re.DOTALL)
263
- body = re.sub(r"\n+---\s*$", "", match.group(0).strip()) if match else f"## [{tag}]\n\nRelease of Loomy (`luminamoon`) v{tag}."
264
- header = (
265
- f"{body}\n\n---\n\n"
266
- "## Installation\n\n"
267
- f"```bash\npip install --upgrade luminamoon=={tag}\n```\n"
268
- )
269
- pathlib.Path("RELEASE_NOTES.md").write_text(header, encoding="utf-8")
270
- '
271
-
272
- - name: Create Draft GitHub Release
273
- uses: softprops/action-gh-release@v2
274
- with:
275
- name: "${{ github.ref_name }}"
276
- body_path: RELEASE_NOTES.md
277
- files: dist/*
278
- draft: true
279
- prerelease: ${{ contains(github.ref_name, 'a') || contains(github.ref_name, 'b') || contains(github.ref_name, 'rc') }}
280
- make_latest: ${{ !contains(github.ref_name, 'a') && !contains(github.ref_name, 'b') && !contains(github.ref_name, 'rc') }}
281
- generate_release_notes: true
282
-
283
- publish-pypi:
284
- name: Publish Wheels to PyPI
285
- needs: [draft-release]
286
- if: always() && (github.event_name == 'release' && github.event.action == 'published')
287
- runs-on: ubuntu-latest
288
- permissions:
289
- contents: read
290
- id-token: write
291
- steps:
292
- - name: Checkout repository
293
- uses: actions/checkout@v7
294
-
295
- - name: Download release assets via GitHub CLI
296
- env:
297
- GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
298
- RELEASE_TAG: ${{ github.event.release.tag_name || github.ref_name }}
299
- run: |
300
- mkdir -p dist
301
- gh release download "$RELEASE_TAG" --dir dist --pattern "*.whl" --pattern "*.tar.gz"
302
- ls -la dist
303
-
304
- - name: Publish Distribution to PyPI
305
- uses: pypa/gh-action-pypi-publish@release/v1
306
- with:
307
- password: ${{ secrets.PYPI_API_TOKEN }}
308
- packages-dir: dist/
309
  skip-existing: true
 
1
+ name: CI/CD Pipeline
2
+
3
+ on:
4
+ push:
5
+ branches: [ "main", "master" ]
6
+ tags: [ "v*" ]
7
+ pull_request:
8
+ branches: [ "main", "master", "development" ]
9
+ release:
10
+ types: [published]
11
+ workflow_dispatch:
12
+
13
+
14
+ concurrency:
15
+ group: ${{ github.workflow }}-${{ github.ref }}
16
+ cancel-in-progress: true
17
+
18
+ permissions:
19
+ contents: read
20
+
21
+ jobs:
22
+ rust-quality:
23
+ name: Rust Quality & Parity (${{ matrix.os }})
24
+ if: github.event_name != 'release'
25
+ runs-on: ${{ matrix.os }}
26
+ strategy:
27
+ fail-fast: false
28
+ matrix:
29
+ os: ${{ (startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch' || github.ref == 'refs/heads/main' || github.base_ref == 'main') && fromJSON('["ubuntu-latest", "windows-latest", "macos-latest"]') || fromJSON('["ubuntu-latest"]') }}
30
+ steps:
31
+ - name: Checkout repository
32
+ uses: actions/checkout@v7
33
+
34
+ - name: Set up Python
35
+ uses: actions/setup-python@v7
36
+ with:
37
+ python-version: "3.12"
38
+
39
+ - name: Set up Rust toolchain
40
+ uses: dtolnay/rust-toolchain@stable
41
+ with:
42
+ components: clippy, rustfmt
43
+
44
+ - name: Rust Cache
45
+ uses: Swatinem/rust-cache@v2
46
+
47
+ - name: Verify Formatting (cargo fmt)
48
+ if: matrix.os == 'ubuntu-latest'
49
+ run: cargo fmt --check
50
+
51
+ - name: Cargo Clippy Lints
52
+ run: cargo clippy --workspace --all-targets -- -D warnings
53
+
54
+ - name: Cargo Unit & Parity Tests
55
+ run: cargo test --workspace --verbose
56
+
57
+ type-check:
58
+ name: Python Static Typing (Pyright)
59
+ if: github.event_name != 'release'
60
+ runs-on: ubuntu-latest
61
+ steps:
62
+ - name: Checkout repository
63
+ uses: actions/checkout@v7
64
+
65
+ - name: Set up Python 3.14
66
+ uses: actions/setup-python@v7
67
+ with:
68
+ python-version: "3.14"
69
+
70
+ - name: Set up Node.js
71
+ uses: actions/setup-node@v7
72
+ with:
73
+ node-version: "24"
74
+
75
+ - name: Install Pyright Dependencies
76
+ run: |
77
+ python -m pip install --upgrade pip
78
+ pip install "numpy>=2.0.0"
79
+
80
+ - name: Run Pyright
81
+ run: npx pyright python/luminamoon
82
+
83
+ - name: Verify Universal Fallback Wheel Builder
84
+ run: |
85
+ python tools/build_universal_wheel.py --out dist
86
+ python -c "import zipfile, sys, tempfile; td = tempfile.TemporaryDirectory(); import glob; whl = glob.glob('dist/luminamoon-*-py3-none-any.whl')[0]; z = zipfile.ZipFile(whl); z.extractall(td.name); sys.path.insert(0, td.name); import luminamoon.optimizer.luminav as lum; import numpy as np; p = np.zeros(5, dtype=np.float32); opt = lum.LuminaV([p], lr=1e-3); opt.step(grads=[np.ones(5, dtype=np.float32)]); assert opt.state[p]['step'] == 1; print('Universal fallback verified successfully')"
87
+
88
+ python-matrix:
89
+ name: Python Tests (${{ matrix.os }}, Py${{ matrix.python-version }})
90
+ if: github.event_name != 'release'
91
+ needs: [rust-quality]
92
+ runs-on: ${{ matrix.os }}
93
+ strategy:
94
+ fail-fast: false
95
+ matrix:
96
+ os: ${{ (startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch') && fromJSON('["ubuntu-latest", "windows-latest", "macos-latest"]') || ((github.ref == 'refs/heads/main' || github.base_ref == 'main') && fromJSON('["ubuntu-latest", "windows-latest"]') || fromJSON('["ubuntu-latest"]')) }}
97
+ python-version: ${{ (startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch') && fromJSON('["3.10", "3.11", "3.12", "3.13", "3.14"]') || fromJSON('["3.14"]') }}
98
+ steps:
99
+ - name: Checkout repository
100
+ uses: actions/checkout@v7
101
+
102
+ - name: Set up Rust toolchain
103
+ uses: dtolnay/rust-toolchain@stable
104
+
105
+ - name: Rust Cache
106
+ uses: Swatinem/rust-cache@v2
107
+
108
+ - name: Set up Python
109
+ uses: actions/setup-python@v7
110
+ with:
111
+ python-version: ${{ matrix.python-version }}
112
+ allow-prereleases: true
113
+ cache: "pip"
114
+
115
+ - name: Upgrade Pip & Install Build Tools
116
+ run: |
117
+ python -m pip install --upgrade pip
118
+ pip install maturin "numpy>=2.0.0" pytest
119
+
120
+ - name: Build & Install LuminaMoon (Native CPython Extension)
121
+ run: python tools/build_wheel.py --install
122
+
123
+ - name: Run Standalone NumPy Verification
124
+ run: python optimizer/tests/test_standalone_numpy.py
125
+
126
+ - name: Run Data Modular Verification
127
+ run: python -B tests/test_data_modular.py
128
+
129
+ - name: Run Adversarial String Verification
130
+ run: python -B tests/test_gpu_vs_cpu_strings.py
131
+
132
+ - name: Install Optional PyTorch Dependency
133
+ id: install-torch
134
+ run: python -c "import os, subprocess, sys; r = subprocess.run([sys.executable, '-m', 'pip', 'install', 'torch>=2.0.0']); open(os.environ['GITHUB_OUTPUT'], 'a', encoding='utf-8').write(f'installed={str(r.returncode == 0).lower()}\n'); print('PyTorch installed:', r.returncode == 0)"
135
+
136
+
137
+ - name: Run PyTorch End-to-End Training Tests
138
+ if: steps.install-torch.outputs.installed == 'true'
139
+ run: python optimizer/tests/test_end_to_end_training.py
140
+
141
+ - name: Run LSO-Eval Vision (V-Eval) Protocol Verification
142
+ if: steps.install-torch.outputs.installed == 'true'
143
+ run: python eval/v_eval_suite.py --verify-architecture
144
+
145
+ - name: Verify Universal Dynamic Fallback
146
+ run: |
147
+ python -c "import luminamoon.optimizer.luminav as lum; lum._luminamoon_rs = None; import numpy as np; p = np.zeros(10, dtype=np.float32); opt = lum.LuminaV([p], lr=1e-3); opt.step(grads=[np.ones(10, dtype=np.float32)]); assert opt.state[p]['step'] == 1; print('Dynamic fallback verified successfully')"
148
+
149
+ free-threaded:
150
+ name: Free-Threaded No-GIL (${{ matrix.python-version }})
151
+ if: github.event_name != 'release'
152
+ needs: [rust-quality]
153
+ runs-on: ubuntu-latest
154
+ continue-on-error: true
155
+ strategy:
156
+ fail-fast: false
157
+ matrix:
158
+ python-version: ["3.14t", "3.15t"]
159
+ steps:
160
+ - name: Checkout repository
161
+ uses: actions/checkout@v7
162
+
163
+ - name: Set up Rust toolchain
164
+ uses: dtolnay/rust-toolchain@stable
165
+
166
+ - name: Rust Cache
167
+ uses: Swatinem/rust-cache@v2
168
+
169
+ - name: Set up Python (Free-Threaded)
170
+ uses: actions/setup-python@v7
171
+ with:
172
+ python-version: ${{ matrix.python-version }}
173
+ allow-prereleases: true
174
+
175
+ - name: Install Maturin
176
+ run: |
177
+ python -m pip install --upgrade pip
178
+ pip install maturin
179
+
180
+ - name: Verify abi3t Wheel Compilation & Import
181
+ run: |
182
+ maturin build --release -i python --out dist
183
+ pip install dist/*.whl
184
+ python -c "import luminamoon.optimizer._luminamoon_optimizer_rs, sys; assert not sys._is_gil_enabled(), 'GIL must remain disabled'; print('Free-threaded abi3t extension verified successfully')"
185
+
186
+ release-build:
187
+ name: Distribution Wheel Build (${{ matrix.os }})
188
+ needs: [python-matrix, type-check]
189
+ if: (startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch') && github.event_name != 'release'
190
+ runs-on: ${{ matrix.os }}
191
+ strategy:
192
+ matrix:
193
+ os: [ubuntu-latest, windows-latest, macos-latest]
194
+ steps:
195
+ - name: Checkout repository
196
+ uses: actions/checkout@v7
197
+
198
+ - name: Set up Python for Build Scripts
199
+ if: matrix.os == 'ubuntu-latest' || startsWith(github.ref, 'refs/tags/v')
200
+ uses: actions/setup-python@v7
201
+ with:
202
+ python-version: "3.14"
203
+
204
+ - name: Sync version from Git Tag
205
+ if: startsWith(github.ref, 'refs/tags/v')
206
+ run: python tools/sync_version.py "${{ github.ref_name }}"
207
+
208
+ - name: Build Universal Pure-Python Fallback Wheel (py3-none-any)
209
+ if: matrix.os == 'ubuntu-latest'
210
+ run: python tools/build_universal_wheel.py --out dist
211
+
212
+ - name: Set up Rust toolchain
213
+ uses: dtolnay/rust-toolchain@stable
214
+
215
+ - name: Build sdist (Source Distribution)
216
+ if: matrix.os == 'ubuntu-latest'
217
+ uses: PyO3/maturin-action@v1
218
+ with:
219
+ command: sdist
220
+ args: --out dist
221
+
222
+ - name: Build Wheel via Maturin
223
+ uses: PyO3/maturin-action@v1
224
+ with:
225
+ command: build
226
+ args: --release --out dist
227
+ manylinux: auto
228
+
229
+ - name: Upload Artifacts
230
+ uses: actions/upload-artifact@v7
231
+ with:
232
+ name: wheels-${{ matrix.os }}
233
+ path: dist/*
234
+
235
+ draft-release:
236
+ name: Create Draft GitHub Release
237
+ needs: [release-build]
238
+ if: startsWith(github.ref, 'refs/tags/v') && github.event_name != 'release'
239
+ runs-on: ubuntu-latest
240
+ permissions:
241
+ contents: write
242
+ steps:
243
+ - name: Checkout repository
244
+ uses: actions/checkout@v7
245
+
246
+ - name: Download all built wheels
247
+ uses: actions/download-artifact@v8
248
+ with:
249
+ pattern: wheels-*
250
+ merge-multiple: true
251
+ path: dist
252
+
253
+ - name: Extract Structured Release Notes from Software Changelog
254
+ env:
255
+ RELEASE_TAG: ${{ github.ref_name }}
256
+ run: |
257
+ python3 -c '
258
+ import os, pathlib, re
259
+ tag = os.environ["RELEASE_TAG"].lstrip("v")
260
+ text = pathlib.Path("changelogs/SOFTWARE_CHANGELOG.md").read_text(encoding="utf-8")
261
+ pattern = rf"^## \[{re.escape(tag)}\].*?(?=^## \[|\Z)"
262
+ match = re.search(pattern, text, flags=re.MULTILINE | re.DOTALL)
263
+ body = re.sub(r"\n+---\s*$", "", match.group(0).strip()) if match else f"## [{tag}]\n\nRelease of Loomy (`luminamoon`) v{tag}."
264
+ header = (
265
+ f"{body}\n\n---\n\n"
266
+ "## Installation\n\n"
267
+ f"```bash\npip install --upgrade luminamoon=={tag}\n```\n"
268
+ )
269
+ pathlib.Path("RELEASE_NOTES.md").write_text(header, encoding="utf-8")
270
+ '
271
+
272
+ - name: Create Draft GitHub Release
273
+ uses: softprops/action-gh-release@v2
274
+ with:
275
+ name: "${{ github.ref_name }}"
276
+ body_path: RELEASE_NOTES.md
277
+ files: dist/*
278
+ draft: true
279
+ prerelease: ${{ contains(github.ref_name, 'a') || contains(github.ref_name, 'b') || contains(github.ref_name, 'rc') }}
280
+ make_latest: ${{ !contains(github.ref_name, 'a') && !contains(github.ref_name, 'b') && !contains(github.ref_name, 'rc') }}
281
+ generate_release_notes: true
282
+
283
+ publish-pypi:
284
+ name: Publish Wheels to PyPI
285
+ needs: [draft-release]
286
+ if: always() && (github.event_name == 'release' && github.event.action == 'published')
287
+ runs-on: ubuntu-latest
288
+ permissions:
289
+ contents: read
290
+ id-token: write
291
+ steps:
292
+ - name: Checkout repository
293
+ uses: actions/checkout@v7
294
+
295
+ - name: Download release assets via GitHub CLI
296
+ env:
297
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
298
+ RELEASE_TAG: ${{ github.event.release.tag_name || github.ref_name }}
299
+ run: |
300
+ mkdir -p dist
301
+ gh release download "$RELEASE_TAG" --dir dist --pattern "*.whl" --pattern "*.tar.gz"
302
+ ls -la dist
303
+
304
+ - name: Publish Distribution to PyPI
305
+ uses: pypa/gh-action-pypi-publish@release/v1
306
+ with:
307
+ password: ${{ secrets.PYPI_API_TOKEN }}
308
+ packages-dir: dist/
309
  skip-existing: true
.github/workflows/docs.yml CHANGED
@@ -1,83 +1,83 @@
1
- name: Documentation Portal (GitHub Pages)
2
-
3
- on:
4
- push:
5
- branches: [ "main" ]
6
- pull_request:
7
- branches: [ "main", "development" ]
8
- paths:
9
- - "docs/**"
10
- - "mkdocs.yml"
11
- - "README.md"
12
- - "**/README.md"
13
- - "changelogs/**"
14
- - "SECURITY.md"
15
- - "tools/prepare_mkdocs.py"
16
- - "tools/prune_deployments.py"
17
- - ".github/workflows/docs.yml"
18
- workflow_dispatch:
19
-
20
- permissions:
21
- contents: read
22
- pages: write
23
- id-token: write
24
- deployments: write
25
-
26
- concurrency:
27
- group: "pages-${{ github.ref }}"
28
- cancel-in-progress: true
29
-
30
- jobs:
31
- build:
32
- name: Build MkDocs Material Site
33
- runs-on: ubuntu-latest
34
- steps:
35
- - name: Checkout repository
36
- uses: actions/checkout@v7
37
-
38
- - name: Set up Python
39
- uses: actions/setup-python@v7
40
- with:
41
- python-version: "3.14"
42
- cache: "pip"
43
-
44
- - name: Install MkDocs Material
45
- run: |
46
- python -m pip install --upgrade pip
47
- pip install "mkdocs-material>=9.5.0"
48
-
49
- - name: Prepare Documentation Stage & Normalize Math/Links
50
- run: python tools/prepare_mkdocs.py
51
-
52
- - name: Build Static Documentation Site
53
- run: mkdocs build
54
-
55
- - name: Upload GitHub Pages Artifact
56
- if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main'
57
- uses: actions/upload-pages-artifact@v5
58
- with:
59
- path: site
60
-
61
- deploy:
62
- name: Deploy to GitHub Pages
63
- if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main'
64
- needs: build
65
- runs-on: ubuntu-latest
66
- environment:
67
- name: github-pages
68
- url: ${{ steps.deployment.outputs.page_url }}
69
- steps:
70
- - name: Deploy to GitHub Pages
71
- id: deployment
72
- uses: actions/deploy-pages@v5
73
-
74
- - name: Checkout repository for tools
75
- if: success()
76
- uses: actions/checkout@v7
77
-
78
- - name: Prune Older Deployments (Retention Max 3)
79
- if: success()
80
- env:
81
- GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
82
- GH_REPO: ${{ github.repository }}
83
- run: python tools/prune_deployments.py --retention 3
 
1
+ name: Documentation Portal (GitHub Pages)
2
+
3
+ on:
4
+ push:
5
+ branches: [ "main" ]
6
+ pull_request:
7
+ branches: [ "main", "development" ]
8
+ paths:
9
+ - "docs/**"
10
+ - "mkdocs.yml"
11
+ - "README.md"
12
+ - "**/README.md"
13
+ - "changelogs/**"
14
+ - "SECURITY.md"
15
+ - "tools/prepare_mkdocs.py"
16
+ - "tools/prune_deployments.py"
17
+ - ".github/workflows/docs.yml"
18
+ workflow_dispatch:
19
+
20
+ permissions:
21
+ contents: read
22
+ pages: write
23
+ id-token: write
24
+ deployments: write
25
+
26
+ concurrency:
27
+ group: "pages-${{ github.ref }}"
28
+ cancel-in-progress: true
29
+
30
+ jobs:
31
+ build:
32
+ name: Build MkDocs Material Site
33
+ runs-on: ubuntu-latest
34
+ steps:
35
+ - name: Checkout repository
36
+ uses: actions/checkout@v7
37
+
38
+ - name: Set up Python
39
+ uses: actions/setup-python@v7
40
+ with:
41
+ python-version: "3.14"
42
+ cache: "pip"
43
+
44
+ - name: Install MkDocs Material
45
+ run: |
46
+ python -m pip install --upgrade pip
47
+ pip install "mkdocs-material>=9.5.0"
48
+
49
+ - name: Prepare Documentation Stage & Normalize Math/Links
50
+ run: python tools/prepare_mkdocs.py
51
+
52
+ - name: Build Static Documentation Site
53
+ run: mkdocs build
54
+
55
+ - name: Upload GitHub Pages Artifact
56
+ if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main'
57
+ uses: actions/upload-pages-artifact@v5
58
+ with:
59
+ path: site
60
+
61
+ deploy:
62
+ name: Deploy to GitHub Pages
63
+ if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main'
64
+ needs: build
65
+ runs-on: ubuntu-latest
66
+ environment:
67
+ name: github-pages
68
+ url: ${{ steps.deployment.outputs.page_url }}
69
+ steps:
70
+ - name: Deploy to GitHub Pages
71
+ id: deployment
72
+ uses: actions/deploy-pages@v5
73
+
74
+ - name: Checkout repository for tools
75
+ if: success()
76
+ uses: actions/checkout@v7
77
+
78
+ - name: Prune Older Deployments (Retention Max 3)
79
+ if: success()
80
+ env:
81
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
82
+ GH_REPO: ${{ github.repository }}
83
+ run: python tools/prune_deployments.py --retention 3
.github/workflows/hf_sync.yml CHANGED
@@ -1,49 +1,49 @@
1
- name: Hugging Face Hub Auto-Sync
2
-
3
- on:
4
- push:
5
- branches: [ "main" ]
6
- tags: [ "v*" ]
7
- pull_request:
8
- branches: [ "main" ]
9
- release:
10
- types: [published]
11
- workflow_dispatch:
12
-
13
- concurrency:
14
- group: "hf-sync-${{ github.ref }}"
15
- cancel-in-progress: true
16
-
17
- permissions:
18
- contents: read
19
-
20
- jobs:
21
- sync-huggingface:
22
- name: Stage & Sync Hugging Face Repositories
23
- runs-on: ubuntu-latest
24
- steps:
25
- - name: Checkout repository
26
- uses: actions/checkout@v7
27
-
28
- - name: Set up Python
29
- uses: actions/setup-python@v7
30
- with:
31
- python-version: "3.14"
32
- cache: "pip"
33
-
34
- - name: Install Dependencies
35
- run: |
36
- python -m pip install --upgrade pip
37
- pip install "huggingface_hub>=0.25.0" --extra-index-url https://download.pytorch.org/whl/cpu torch
38
-
39
- - name: Stage & Verify Hugging Face Bundles (PR Check)
40
- if: github.event_name == 'pull_request'
41
- run: python tools/sync_huggingface.py
42
-
43
- - name: Stage, Verify & Push to Hugging Face Hub (main / release)
44
- if: github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') || github.event_name == 'release' || github.event_name == 'workflow_dispatch')
45
- env:
46
- HF_TOKEN: ${{ secrets.HF_TOKEN }}
47
- HF_ORG: ${{ secrets.HF_ORG }}
48
- HF_USERNAME: ${{ secrets.HF_USERNAME }}
49
- run: python tools/sync_huggingface.py --push
 
1
+ name: Hugging Face Hub Auto-Sync
2
+
3
+ on:
4
+ push:
5
+ branches: [ "main" ]
6
+ tags: [ "v*" ]
7
+ pull_request:
8
+ branches: [ "main" ]
9
+ release:
10
+ types: [published]
11
+ workflow_dispatch:
12
+
13
+ concurrency:
14
+ group: "hf-sync-${{ github.ref }}"
15
+ cancel-in-progress: true
16
+
17
+ permissions:
18
+ contents: read
19
+
20
+ jobs:
21
+ sync-huggingface:
22
+ name: Stage & Sync Hugging Face Repositories
23
+ runs-on: ubuntu-latest
24
+ steps:
25
+ - name: Checkout repository
26
+ uses: actions/checkout@v7
27
+
28
+ - name: Set up Python
29
+ uses: actions/setup-python@v7
30
+ with:
31
+ python-version: "3.14"
32
+ cache: "pip"
33
+
34
+ - name: Install Dependencies
35
+ run: |
36
+ python -m pip install --upgrade pip
37
+ pip install "huggingface_hub>=0.25.0" --extra-index-url https://download.pytorch.org/whl/cpu torch
38
+
39
+ - name: Stage & Verify Hugging Face Bundles (PR Check)
40
+ if: github.event_name == 'pull_request'
41
+ run: python tools/sync_huggingface.py
42
+
43
+ - name: Stage, Verify & Push to Hugging Face Hub (main / release)
44
+ if: github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') || github.event_name == 'release' || github.event_name == 'workflow_dispatch')
45
+ env:
46
+ HF_TOKEN: ${{ secrets.HF_TOKEN }}
47
+ HF_ORG: ${{ secrets.HF_ORG }}
48
+ HF_USERNAME: ${{ secrets.HF_USERNAME }}
49
+ run: python tools/sync_huggingface.py --push
.github/workflows/security.yml CHANGED
@@ -1,91 +1,91 @@
1
- name: Security & Vulnerability Audit (CVE / CWE / SAST)
2
-
3
- on:
4
- push:
5
- branches: [ "main" ]
6
- pull_request:
7
- branches: [ "main", "development" ]
8
- schedule:
9
- - cron: "0 3 * * 1"
10
- workflow_dispatch:
11
-
12
- permissions:
13
- contents: read
14
-
15
- concurrency:
16
- group: "security-${{ github.ref }}"
17
- cancel-in-progress: true
18
-
19
- jobs:
20
- codeql-cwe-sast:
21
- name: CodeQL SAST & CWE Analysis (${{ matrix.language }})
22
- runs-on: ubuntu-latest
23
- permissions:
24
- actions: read
25
- contents: read
26
- security-events: write
27
- strategy:
28
- fail-fast: false
29
- matrix:
30
- include:
31
- - language: python
32
- build-mode: none
33
- - language: actions
34
- build-mode: none
35
- steps:
36
- - name: Checkout repository
37
- uses: actions/checkout@v7
38
-
39
- - name: Initialize CodeQL (CWE Static Analyzer)
40
- uses: github/codeql-action/init@v4
41
- with:
42
- languages: ${{ matrix.language }}
43
- build-mode: ${{ matrix.build-mode }}
44
-
45
- - name: Perform CodeQL CWE Analysis & Upload SARIF
46
- uses: github/codeql-action/analyze@v4
47
- with:
48
- category: "/language:${{ matrix.language }}"
49
-
50
- supply-chain-cve-audit:
51
- name: RustSec, OSV & PyPA CVE Supply-Chain Audit
52
- runs-on: ubuntu-latest
53
- steps:
54
- - name: Checkout repository
55
- uses: actions/checkout@v7
56
-
57
- - name: Set up Python
58
- uses: actions/setup-python@v7
59
- with:
60
- python-version: "3.14"
61
- cache: "pip"
62
-
63
- - name: Install PyPA pip-audit & Build Tools
64
- run: |
65
- python -m pip install --upgrade pip
66
- pip install "pip-audit>=2.7.0"
67
-
68
- - name: Audit Cargo.lock (RustSec/OSV/CVE) & Repository Secret Hygiene
69
- run: python tools/security_audit.py
70
-
71
- - name: Audit Python Environment via PyPA pip-audit (OSV / PYSEC / CVE)
72
- run: |
73
- python tools/build_universal_wheel.py --out dist
74
- pip install dist/luminamoon-*-py3-none-any.whl
75
- pip-audit --desc on
76
-
77
- dependency-review:
78
- name: PR Dependency & License Vulnerability Gate
79
- if: github.event_name == 'pull_request'
80
- runs-on: ubuntu-latest
81
- permissions:
82
- contents: read
83
- pull-requests: read
84
- steps:
85
- - name: Checkout repository
86
- uses: actions/checkout@v7
87
-
88
- - name: Dependency Review (CVE / GHSA Severity Gate)
89
- uses: actions/dependency-review-action@v5
90
- with:
91
- fail-on-severity: moderate
 
1
+ name: Security & Vulnerability Audit (CVE / CWE / SAST)
2
+
3
+ on:
4
+ push:
5
+ branches: [ "main" ]
6
+ pull_request:
7
+ branches: [ "main", "development" ]
8
+ schedule:
9
+ - cron: "0 3 * * 1"
10
+ workflow_dispatch:
11
+
12
+ permissions:
13
+ contents: read
14
+
15
+ concurrency:
16
+ group: "security-${{ github.ref }}"
17
+ cancel-in-progress: true
18
+
19
+ jobs:
20
+ codeql-cwe-sast:
21
+ name: CodeQL SAST & CWE Analysis (${{ matrix.language }})
22
+ runs-on: ubuntu-latest
23
+ permissions:
24
+ actions: read
25
+ contents: read
26
+ security-events: write
27
+ strategy:
28
+ fail-fast: false
29
+ matrix:
30
+ include:
31
+ - language: python
32
+ build-mode: none
33
+ - language: actions
34
+ build-mode: none
35
+ steps:
36
+ - name: Checkout repository
37
+ uses: actions/checkout@v7
38
+
39
+ - name: Initialize CodeQL (CWE Static Analyzer)
40
+ uses: github/codeql-action/init@v4
41
+ with:
42
+ languages: ${{ matrix.language }}
43
+ build-mode: ${{ matrix.build-mode }}
44
+
45
+ - name: Perform CodeQL CWE Analysis & Upload SARIF
46
+ uses: github/codeql-action/analyze@v4
47
+ with:
48
+ category: "/language:${{ matrix.language }}"
49
+
50
+ supply-chain-cve-audit:
51
+ name: RustSec, OSV & PyPA CVE Supply-Chain Audit
52
+ runs-on: ubuntu-latest
53
+ steps:
54
+ - name: Checkout repository
55
+ uses: actions/checkout@v7
56
+
57
+ - name: Set up Python
58
+ uses: actions/setup-python@v7
59
+ with:
60
+ python-version: "3.14"
61
+ cache: "pip"
62
+
63
+ - name: Install PyPA pip-audit & Build Tools
64
+ run: |
65
+ python -m pip install --upgrade pip
66
+ pip install "pip-audit>=2.7.0"
67
+
68
+ - name: Audit Cargo.lock (RustSec/OSV/CVE) & Repository Secret Hygiene
69
+ run: python tools/security_audit.py
70
+
71
+ - name: Audit Python Environment via PyPA pip-audit (OSV / PYSEC / CVE)
72
+ run: |
73
+ python tools/build_universal_wheel.py --out dist
74
+ pip install dist/luminamoon-*-py3-none-any.whl
75
+ pip-audit --desc on
76
+
77
+ dependency-review:
78
+ name: PR Dependency & License Vulnerability Gate
79
+ if: github.event_name == 'pull_request'
80
+ runs-on: ubuntu-latest
81
+ permissions:
82
+ contents: read
83
+ pull-requests: read
84
+ steps:
85
+ - name: Checkout repository
86
+ uses: actions/checkout@v7
87
+
88
+ - name: Dependency Review (CVE / GHSA Severity Gate)
89
+ uses: actions/dependency-review-action@v5
90
+ with:
91
+ fail-on-severity: moderate
.gitignore CHANGED
@@ -1,54 +1,54 @@
1
- /target/
2
- /optimizer/target/
3
- **/target/
4
- __pycache__/
5
- *.py[cod]
6
- *$py.class
7
- *.so
8
- *.pyd
9
- *.dll
10
- *.dylib
11
- *.exe
12
- *.lib
13
- *.a
14
- *.o
15
- *.obj
16
- *.pdb
17
- *.exp
18
- .pytest_cache/
19
- .ruff_cache/
20
- .mypy_cache/
21
- build/
22
- dist/
23
- wheels/
24
- *.whl
25
- *.egg-info/
26
- .venv/
27
- venv/
28
- .env
29
- .env.*
30
- !.env.example
31
- .DS_Store
32
- Thumbs.db
33
- *.log
34
- .coverage
35
- htmlcov/
36
- .benchmarks/
37
- .vscode/
38
- .idea/
39
- optimizer/tools/aot_compiler/glslang/
40
- .luminamoon_cache/
41
- *.meta
42
- *.meta.dedup
43
- !eval/artifacts/**/*.meta
44
- !eval/artifacts/**/*.meta.dedup
45
- x.py
46
- logs/
47
- site/
48
- docs/overview.md
49
- docs/security.md
50
- docs/lso_eval.md
51
- docs/optimize/standalone_guide.md
52
- docs/dataloader/standalone_guide.md
53
- docs/changelogs/
54
- scratch/
 
1
+ /target/
2
+ /optimizer/target/
3
+ **/target/
4
+ __pycache__/
5
+ *.py[cod]
6
+ *$py.class
7
+ *.so
8
+ *.pyd
9
+ *.dll
10
+ *.dylib
11
+ *.exe
12
+ *.lib
13
+ *.a
14
+ *.o
15
+ *.obj
16
+ *.pdb
17
+ *.exp
18
+ .pytest_cache/
19
+ .ruff_cache/
20
+ .mypy_cache/
21
+ build/
22
+ dist/
23
+ wheels/
24
+ *.whl
25
+ *.egg-info/
26
+ .venv/
27
+ venv/
28
+ .env
29
+ .env.*
30
+ !.env.example
31
+ .DS_Store
32
+ Thumbs.db
33
+ *.log
34
+ .coverage
35
+ htmlcov/
36
+ .benchmarks/
37
+ .vscode/
38
+ .idea/
39
+ optimizer/tools/aot_compiler/glslang/
40
+ .luminamoon_cache/
41
+ *.meta
42
+ *.meta.dedup
43
+ !eval/artifacts/**/*.meta
44
+ !eval/artifacts/**/*.meta.dedup
45
+ x.py
46
+ logs/
47
+ site/
48
+ docs/overview.md
49
+ docs/security.md
50
+ docs/lso_eval.md
51
+ docs/optimize/standalone_guide.md
52
+ docs/dataloader/standalone_guide.md
53
+ docs/changelogs/
54
+ scratch/
CHANGELOG.md CHANGED
@@ -1,430 +1,430 @@
1
- # LuminaV Optimizer Algorithm Changelog
2
-
3
- All notable changes to the LuminaV Optimizer algorithm are documented in this file.
4
-
5
- The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
-
7
- * [Software Package Changelog (`luminamoon`)](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/changelogs/SOFTWARE_CHANGELOG.md)
8
-
9
- ---
10
-
11
- ## [1.6.0] - 2026-11-03
12
-
13
- ### Added
14
- - **T-Family (Triangle Family) tanh-free Modes (`buffer="T1"` & `buffer="T2"`)**:
15
- * **`buffer="T2"` (2-Buffer Triangle Family Mode)**: Keeps the 2-buffer state (`exp_avg` and `exp_avg_sq`) without using $\tanh$, computing the gradient-momentum residual $d_t = g_t - m _ {t-1}$ and bounding the lookahead update $\tilde{m}_t$ via the **Triangle Inequality Envelope** $\mathcal{E}_t^{(\text{T2})}$:
16
-
17
- $\displaystyle d_t = g_t - m _ {t-1}, \qquad v_t = \beta_2 v _ {t-1} + (1 - \beta_2) d_t^2$
18
-
19
- $\displaystyle \tilde{m} _ t = w_1 m _ {t-1} + w_2 g_t, \qquad w_1 = \beta_1^2, \qquad w_2 = 1 - \beta_1^2$
20
-
21
- $\displaystyle \mathcal{E} _ t^{(\text{T2})} = w_1 |m _ {t-1}| + w_2 |g_t| + 0.5\lambda |d_t| + 0.5\lambda \sigma_t + \epsilon _ {\text{eff}}, \qquad u_t^{(\text{T2})} = \frac{\tilde{m} _ t}{\mathcal{E} _ t^{(\text{T2})}} \in (-1, 1)$
22
-
23
- By the Triangle Inequality ($|w_1 m _ {t-1} + w_2 g_t| \le w_1 |m _ {t-1}| + w_2 |g_t| \le \mathcal{E} _ t^{(\text{T2})}$), $|u_t^{(\text{T2})}| < 1$ is guaranteed without evaluating $\tanh$. The term $0.5\lambda |d_t|$ acts as a **triangle-defect penalty**, and $0.5\lambda \sigma_t$ provides belief-variance damping.
24
- * **`buffer="T1"` (1-Buffer Triangle Family Mode)**: Combines the single-buffer **Triangle-Defect** denominator $\mathcal{E} _ t^{(\text{T1})} = w_1 |m _ {t-1}| + w_2 |g_t| + \lambda |d_t| + \epsilon _ {\text{eff}}$ (using residual $d_t = g_t - m _ {t-1}$) with the rational update bounds below, reducing optimizer state memory by 50% via the Triangle Inequality.
25
- * **Rational Update Bounds (for `T1` & `T2`)**: Replaces $\tanh$ and $\sqrt{\cdot}$ in the coordinate, radial, and FP16 update bounds with simple rational formulas:
26
-
27
- $\displaystyle b_i = \mathrm{bound} _ {\text{ratio}} \cdot (|w_i| + 10^{-2}), \qquad \Delta w_i = \frac{b_i (b_i + |s_i|) s_i}{b_i (b_i + |s_i|) + s_i^2}, \qquad \mathrm{scale} _ {\text{radial}} = \frac{1 + r}{1 + r + r^2}$
28
- * Keeps 100% backward compatibility and identical outputs for existing modes (`buffer=1`, `buffer=2`, and `version="1.5.5"`).
29
-
30
- ---
31
-
32
- ## [1.5.5] - 2026-10-29
33
-
34
- ### Added
35
- - **Contiguous Bucket Fusion (`fuse_tensors: bool = False`)**:
36
- * Added opt-in contiguous parameter and state coalescing (`_build_contiguous_buckets`) that groups parameters within each `(param_group, device, dtype)` domain $\mathcal{B}_{g,d,\tau}$ into contiguous 1D storage (`flat_p`, `flat_g`, `flat_m`, `flat_v`, `flat_mp`) and rebinds `p.data`, `p.grad`, and `self.state[p]` entries as zero-copy views (`flat_*[offset : offset + n].view(p.shape)`).
37
- * Eliminates per-tensor CPU-to-GPU kernel launch latency on fragmented vision and edge architectures by executing an entire bucket in a single fused kernel dispatch while computing `cautious_scale` and `radial` trust bounds across the bucket domain $\mathcal{B}_{g,d,\tau}$.
38
- * Added in-place `zero_grad()` view preservation (`flat_g.zero_()`) and full `state_dict()` / `load_state_dict()` checkpoint compatibility without leaking temporary buffer keys (LSO-Eval Stage II compliant).
39
- - **Triton JIT Pre-Warmup (`prewarm_jit: bool = True`)**:
40
- * Added automatic initialization-time Triton JIT compilation (`_prewarm_all_configs`) using 64-element dummy buffers (`128–256 bytes` VRAM) with `lr=0.0` and `weight_decay=0.0` across both Step 1 (`pass1` + `pass2`) and Step 2+ (`fused_single_pass` + `post_step_ema`) paths with target `BLOCK_SIZE` specialization (`1024` or `2048`), eliminating first-step compilation stalls.
41
- - **Bounded Bucket Chunking & Sequential In-Place Migration (`bucket_cap_mb: float = 500.0`)**:
42
- * Bound contiguous bucket coalescing by an upper memory capacity ceiling $C_{\text{cap}} = 500\text{ MB}$, partitioning model parameters into ordered contiguous chunks $\mathcal{B}^{(1)}, \mathcal{B}^{(2)}, \dots, \mathcal{B}^{(K)}$ such that $\sum_{p \in \mathcal{B}^{(k)}} \text{bytes}(p) \le C_{\text{cap}}$ (with giant single layers $> C_{\text{cap}}$ isolated into individual buckets).
43
- * Enforced sequential in-place migration with immediate parameter storage recycling (`del old_data`, `del old_grad`) and garbage collection, constraining peak transient memory allocation spikes during optimizer initialization to at most $\min(\text{model bytes}, C _ {\text{cap}})$.
44
-
45
- ---
46
-
47
- ## [1.5.4] - 2026-10-29
48
-
49
- ### Added
50
- - **In-Container Native Bitwise-BF16 State Promotion (`dtype_mode == 2`, `master_weights="none"`)**:
51
- * Promoted 16-bit optimizer state buffers ($m_t$ via stochastic rounding `f32_to_bf16_bits_sr` and $v_t$ via round-to-nearest-even `f32_to_bf16_bits_rne`) to native Bitwise-BF16 stored inside the existing 16-bit container during pure `FP16` execution, expanding state exponent dynamic range to 8 bits ($1.18 \times 10^{-38}$) with zero additional VRAM overhead.
52
-
53
- ### Changed
54
- - **Unified Denominator Stability Constant ($\epsilon_{\text{eff}} = \epsilon = 10^{-8}$)**:
55
- * Unified the effective denominator constant $\epsilon_{\text{eff}} = \epsilon = 10^{-8}$ across all precision modes and hardware backends (replacing the legacy `FP16` floor clamp $\max(\epsilon, 10^{-4})$ introduced in `1.1.5`), preventing update velocity attenuation and late-stage convergence loss resets when $\sqrt{v_t} \ll 10^{-4}$.
56
-
57
- ### Removed
58
- - **Centered Defect Compensation (`centered_defect`, $\mu_d$)**:
59
- * Removed the `centered_defect` hyperparameter and the global defect mean reduction $\mu_d = \frac{1}{N}\sum_{i=1}^N (g_{t,i} - m_{t-1,i})$ across both Dual-Buffer Convex Lookahead Momentum (`buffer=2` / CLM) and Single-Buffer Triangle-Defect Convex Lookahead Momentum (`buffer=1` / TD-CLM).
60
- * Simplified the instantaneous gradient-momentum residual to purely local coordinate-wise differences:
61
-
62
- $\displaystyle d_t^{(\text{B1})} = g_t - m_{t-1}, \qquad d_t^{(\text{B2})} = g_t - m_t$
63
-
64
- * Eliminated the pre-step global reduction pass (`_lumina_diff_sum_kernel` in Triton JIT/AOT, `diff_sum.comp` in Vulkan SPIR-V, `reduce_diff_sum_simd` in x86\_64 AVX-512/AVX2, and slot 5 of the 6-element `ScratchBuffer`), removing cross-lane/cross-rank reduction latency while retaining the 6-element physical `ScratchBuffer` layout (`[mask_sum, u_sq_sum, p_sq_sum, cautious_scale, bound_scale, _reserved]`, 5 active slots + 1 reserved slot for ABI alignment).
65
-
66
- ---
67
-
68
- ## [1.5.3] - 2026-10-20
69
-
70
- No versions were published between 1.3.3 and 1.5.3. This release notes all changes made since version 1.3.3.
71
-
72
- ### Added
73
- - **Single-Buffer Triangle-Defect Convex Lookahead Momentum (TD-CLM, `buffer=1`)**:
74
- * Replaced the legacy 3-pass RMS single-buffer update with a single-pass formulation:
75
-
76
- $\displaystyle \tilde{m} _ t = w_1 m _ {t-1} + w_2 g_t, \quad w_1 = \beta_1^2, \quad w_2 = 1 - \beta_1^2$
77
-
78
- $\displaystyle \mathcal{E} _ t = w_1 |m _ {t-1}| + w_2 |g_t| + \lambda |d_t^{(\text{B1})}| + \epsilon _ {\text{eff}}$
79
-
80
- $\displaystyle u_t^{(\text{B1})} = \frac{\tilde{m} _ t}{\mathcal{E} _ t} \in (-1, 1)$
81
-
82
- * In 1.3.3, single-buffer mode required three sequential GPU kernel launches (`_lumina_v1_pass1_kernel`, `_lumina_v1_pass2_kernel`, `_lumina_v1_pass3_update_kernel`) and global atomic reductions across thread blocks to compute tensor-wide root-mean-square ($\text{rms} = \sqrt{\frac{1}{N}\sum \tilde{m} _ i^2 + \epsilon}$).
83
- * TD-CLM replaces the tensor-wide reduction with coordinate-wise envelope bounding via the Triangle Inequality Envelope $\mathcal{E} _ t$. This reduces single-buffer execution to a single GPU pass and eliminates all $\tanh$ evaluations in the momentum path.
84
- * Calibrated the defect penalty weight $\lambda$ to the symmetric spike-reversal equilibrium:
85
-
86
- $\displaystyle \lambda^* = \frac{2(1 - \beta_1^2)^2}{4\beta_1^2 - 3}$
87
-
88
- For the default $\beta_1 = 0.9$, $\lambda^* \approx 0.3008$, setting the default parameter to `lam=0.3`.
89
-
90
- - **Padé [13/6] Rational Approximation (`tanh_pade_f32`)**:
91
- * Implemented a rational Padé [13/6] polynomial for hyperbolic tangent calculations:
92
-
93
- $\displaystyle \tanh _ {\text{Pade}}(x) = \frac{x(1 + c_1 x^2 + c_2 x^4 + c_3 x^6 + c_4 x^8 + c_5 x^{10} + c_6 x^{12})}{1 + d_1 x^2 + d_2 x^4 + d_3 x^6}$
94
-
95
- * Evaluated over the clamped domain $x \in [-7.9988117, 7.9988117]$ with asymptotic saturation to $\pm 1.0$.
96
- * In 1.3.3, `_triton_tanh_fast` used hardware-dependent SFU instructions (`tl.math.tanh`) or analytical sigmoid fallbacks (`2 / (1 + exp(-2x)) - 1`). Different GPU architectures (e.g. Turing, Ampere, Hopper) evaluate SFU transcendental functions with slightly different mantissa rounding, leading to bitwise divergence during distributed training.
97
- * Padé [13/6] provides uniform precision with $\lVert \tanh _ {\text{Pade}}(x) - \tanh(x) \rVert _ \infty < 1.5 \times 10^{-6}$ across CPU Scalar, AVX2, AVX-512, Vulkan SPIR-V, and Triton GPU kernels.
98
-
99
- - **Centered Innovation Defect (`centered_defect`)**:
100
- * Added batch-mean centering for gradient innovation tracking (`centered_defect=False` by default):
101
-
102
- $\displaystyle \mu_d = \frac{1}{N}\sum _ {i=1}^N (g _ {t,i} - m _ {t-1,i})$
103
-
104
- $\displaystyle d_t^{(\text{B1})} = (g_t - m _ {t-1}) - \mu_d$
105
-
106
- $\displaystyle d_t^{(\text{B2})} = (g_t - m_t) - \beta_1 \mu_d$
107
-
108
- * In 1.3.3, uncentered innovation $g_t - m_t$ was used directly. On layers where gradients have a non-zero mean (such as normalization scales, biases, and token embeddings), the raw difference caused systematic drift in the second-moment accumulator. Centering eliminates this mean shift.
109
-
110
- - **Subnormal-Safe IEEE-754 FP16 Stochastic Rounding**:
111
- * Added `f32_to_f16_bits_rne` to handle IEEE-754 subnormal numbers ($|x| < 2^{-14}$) during FP16 stochastic rounding.
112
- * In 1.3.3, stochastic rounding added 13-bit random noise and masked with `& -8192`. When a floating-point value entered the subnormal range, bitwise addition directly corrupted the exponent field (bit 10), causing values to flip between normal and subnormal representations or trigger hardware flush-to-zero traps.
113
- * The updated routine checks for zero exponent, shifts the significand into the subnormal format, and applies round-to-nearest-even tie-breaking before bit truncation.
114
-
115
- - **Multi-Backend Execution Engine**:
116
- * Replaced the Python-only runtime with a multi-backend execution layer:
117
- * **Rust SIMD (`luminav-simd`)**: AVX-512 (512-bit registers), AVX2 with FMA, portable scalar fallback, and Rayon parallel chunk scheduling (`ELEM_CHUNK_SIZE = 2,097,152`).
118
- * **Vulkan UMA (`luminav-vulkan`)**: SPIR-V compute pipeline for cross-vendor GPUs (AMD, Intel, Apple Silicon via MoltenVK, and NVIDIA) with zero-copy host-visible memory buffers.
119
- * **Triton AOT (`luminav-triton-aot`)**: C-ABI wrapper dispatching ahead-of-time precompiled kernels for 84 GPU architectures (Blackwell SM100/SM120, Hopper SM90, Ada SM89, Ampere SM80/SM86, Turing SM75, and AMD ROCm gfx90a/gfx942/gfx1100).
120
- * **Standalone Runtime (`luminamoon.optimizer.standalone`)**: Pure NumPy and C-ABI execution that runs without requiring PyTorch.
121
-
122
- ### Changed
123
- - **Parameter `alpha_ss`**: Removed. In 1.3.3, `alpha_ss=0.5` was used for soft-sign scaling $z / (1 + \alpha _ {\text{ss}} |z|)$ in the legacy single-buffer path. In 1.5.3, TD-CLM envelope normalization bounds updates directly to $(-1, 1)$, making soft-sign obsolete.
124
- - **Parameter `lam`**: Added with default `0.3`. Governs the defect penalty weight in TD-CLM.
125
- - **Parameter `centered_defect`**: Added with default `False`. Enables batch-mean centering of the innovation defect.
126
- - **Parameter `bound_type`**: Default changed from `"radial"` (1.3.3) to `"coordinate"` (1.5.3). Coordinate-wise hyperbolic bounding $(B_i \tanh(\Delta \theta_i / B_i))$ evaluates locally on each parameter without computing a tensor-wide norm reduction.
127
- - **Parameter `bound_ratio`**: Default changed from `0.03` to `0.05` to provide better update headroom on large model architectures.
128
- - **Parameter `ema_decay`**: Default changed from static `0.8` to `"auto"`. Resolves to `0.80` for Dual-Buffer mode (`buffer=2`) and `0.95` for Single-Buffer mode (`buffer=1`).
129
- - **Scratch Buffer**: Expanded from 5 floats `[mask_sum, u_sq_sum, p_sq_sum, cautious_scale, bound_scale]` to 6 floats, adding `diff_sum` at index 5 to accumulate batch-mean defect statistics.
130
- - **Cautious Scaling Floor**: Enforced `cautious_clamp_min=0.5` across all backends to cap maximum cautious energy scaling at $2.0\times$ ($2^1$).
131
-
132
- ### Fixed
133
- - **Hardware Mantissa Divergence**: Fixed bitwise output differences across GPU families by replacing hardware SFU `tl.math.tanh` with Padé [13/6] rational approximation.
134
- - **FP16 Subnormal Bit Corruption**: Fixed exponent corruption in subnormal FP16 stochastic rounding by separating exponent extraction from significand shift logic.
135
- - **Single-Buffer Kernel Launch Latency**: Reduced single-buffer kernel dispatches from 3 passes to 1 pass, eliminating host-side synchronization bubbles and intermediate L2 atomic reduction overhead.
136
- - **Non-Zero Mean Gradient Drift**: Resolved directional accumulator drift on bias and normalization layers via `centered_defect`.
137
-
138
- ### Parameter Comparison (1.3.3 vs 1.5.3)
139
-
140
- | Parameter | Version 1.3.3 | Version 1.5.3 | Technical Notes |
141
- | :--- | :--- | :--- | :--- |
142
- | `params` | `params` | `params` | Iterable of parameters or parameter groups. |
143
- | `lr` | `8e-4` | `8e-4` | Base learning rate. |
144
- | `betas` | `(0.9, 0.999)` | `(0.9, 0.999)` | First and second moment exponential decay rates. |
145
- | `eps` | `1e-8` | `1e-8` | Numerical stability floor. |
146
- | `weight_decay` | `8e-2` | `8e-2` | Decoupled weight decay rate. |
147
- | `tau` | `0.8` | `0.8` | Noise dispersion scale factor. |
148
- | `alpha_ss` | `0.5` | *(removed)* | Removed; superseded by TD-CLM envelope normalization. |
149
- | `lam` | *(none)* | `0.3` | Defect penalty weight in TD-CLM (calibrated to $\lambda^* \approx 0.3008$). |
150
- | `cautious` | `True` | `True` | Directional verification gate ($u_i g_i > 0$). |
151
- | `cautious_clamp_min` | `0.5` | `0.5` | Maximum cautious scale cap ($2.0\times$). |
152
- | `buffer` | `2` | `2` | `1` = Single-Buffer (TD-CLM), `2` = Dual-Buffer (CLM). |
153
- | `centered_defect` | *(none)* | `False` | Enables subtraction of batch-mean defect $\mu_d$. |
154
- | `stochastic_rounding` | `True` | `True` | Bitwise stochastic rounding on FP16/BF16 weights. |
155
- | `bound` | `True` | `True` | Enables update trust-region bounding. |
156
- | `bound_type` | `"radial"` | `"coordinate"` | Default changed to coordinate-wise bounding (zero reduction overhead). |
157
- | `bound_ratio` | `0.03` | `0.05` | Maximum step magnitude relative to parameter magnitude. |
158
- | `master_weights` | `False` | `"none"` | `"none"`, `"semi"`, or `"full"`. |
159
- | `execution` | `"auto"` | `"auto"` | Dynamic routing: SIMD, Vulkan, Triton, or Foreach. |
160
- | `fused_single_pass` | `False` | `True` | Enables single-pass streaming with EMA scaling. |
161
- | `ema_decay` | `0.8` | `"auto"` | Automatically sets `0.80` for dual-buffer, `0.95` for single-buffer. |
162
- | `fsdp_sync` | `False` | `False` | Cross-rank collective all-reduce synchronization. |
163
- | `seed` | `1337` | `1337` | PRNG seed for deterministic stochastic rounding. |
164
-
165
- ---
166
-
167
- ## [1.3.3] - 2026-10-06
168
-
169
- ### Fixed
170
- * **Triton AST Compiler Early-Return Inlining Error:**
171
- * Resolved fatal MLIR/CFG code generation failure (`at 35:4: _store_state_buffer(...) ^`) triggered by mid-branch `return` statements in `@triton.jit` helper functions (`_store_param` and `_store_state_buffer`).
172
- * Refactored helper control flow into structured, single-exit `if ... elif ... else` branches, ensuring compliant JIT function inlining across all kernel entry points.
173
- * **Bitcast Width Mismatch (Int64 Promotion in Stochastic Rounding):**
174
- * Fixed implicit 64-bit integer promotion caused by Python integer literals (`0x9E3779B9`, `~0x1FFF`, `~0xFFFF`) inside Triton bitwise operations.
175
- * Enforced strict 32-bit signed arithmetic via two's complement constants (`-1640531527`, `-2048173461`, `-1028477387`) with explicit `.to(tl.int32)` coercion, and standardized bitwise masks (`-65536` for BF16, `-8192` for FP16), ensuring type-safe 32-bit bitcasting (`tl.int32` to `tl.float32`).
176
- * **JIT Kernel Call Keyword Arguments:**
177
- * Replaced internal keyword argument invocations (`use_sr=False`) with standard positional boolean parameters inside Triton JIT kernels to eliminate compiler argument binding warnings across Triton runtime versions.
178
-
179
- ### Improved
180
- * **Throttled Fallback Telemetry & Console De-Noising:**
181
- * Added `self._warned_triton_failure` state tracking to prevent multi-line compiler AST dumps from flooding the console across every parameter tensor in a parameter group.
182
- * Formatted fallback notifications into a single concise one-line `WARNING` log on initial encounter, routing full tracebacks to `DEBUG` level.
183
-
184
- ### Verified
185
- * **End-to-End JIT Execution on NVIDIA Architectures (Tesla T4):**
186
- * Validated JIT execution across both Dual-Pass (`fused_single_pass=False`) and Fused Single-Pass (`fused_single_pass=True`) modes in native FP16.
187
- * Confirmed non-zero weight update dynamics ($|\Delta W| > 0$) without gradient underflow or weight freezing in pure master-free training for Version 1.3.3.
188
-
189
- ---
190
-
191
- ## [1.3.2] - 2026-10-06
192
-
193
- ### Added
194
- - **Distributed FSDP Collective Synchronization (`fsdp_sync`)**:
195
- - Introduced the `fsdp_sync: bool = False` configuration flag on optimizer initialization, backed by automated runtime guarding via `torch.distributed.is_initialized()`.
196
- - Integrated cross-GPU collective communications (`torch.distributed.all_reduce(..., op=torch.distributed.ReduceOp.SUM)`) for global reduction metrics (`mask_sum`, `u_sq_sum`, and `p_sq_sum`) across Triton kernels, single-step execution, and vectorized foreach loops.
197
- - Maintains bit-exact scalar consistency for `m_bar`, `cautious_scale`, and `bound_scale` across distributed parameter shards during large-scale pretraining under PyTorch FSDP (Fully Sharded Data Parallel) and DeepSpeed ZeRO-3.
198
- - **Dedicated Low-Precision Stochastic Quantization Routine (`_sr_quantize`)**:
199
- - Factored out bitwise stochastic rounding into a dedicated, modular helper function: `_sr_quantize(target, val_f32, use_sr, generator)`.
200
- - Enabled native stochastic rounding on half-precision momentum states (`exp_avg` in BF16/FP16) across all PyTorch CPU/CUDA fallback paths (`_single_step_v2`, `_foreach_step_v2`, `_single_step_v1`, and `_foreach_step_v1`), systematically eliminating gradient stagnation induced by fractional bit truncation (LSB underflow).
201
- - **Persistent Contiguous Buffer Caching**:
202
- - Introduced persistent state cache tensors within `self.state[p]` (`p_contig`, `exp_avg_contig`, `exp_avg_sq_contig`, and `mp_contig`) to stage strided or sliced parameters before kernel launch.
203
- - Eliminates dynamic memory allocations (`.contiguous()`) during iterative training loops, preserving PyTorch VRAM caching allocator stability and preventing CUDA Graph capture invalidation.
204
- - **Zero-Copy Direct Tensor Aliasing for FP32 Master Weights**:
205
- - When parameter tensors are natively `torch.float32`, `state["master_param"]` now directly aliases the parameter tensor pointer (`state["master_param"] = p`), avoiding additional master weight VRAM overhead for FP32 layers (0 bytes auxiliary memory footprint).
206
- - **Checkpoint Sanitizer via State Dict Filter (`state_dict`)**:
207
- - Overrode `state_dict()` to automatically purge temporary contiguous cache buffers (`p_contig`, `exp_avg_contig`, `exp_avg_sq_contig`, `mp_contig`), ensuring on-disk checkpoint payloads remain clean, compact, and fully backward-compatible.
208
- - **Defensive Device Pipeline Flush on Kernel Fault (`torch.cuda.synchronize`)**:
209
- - Embedded explicit hardware device synchronization (`torch.cuda.synchronize(p.device)`) within Triton exception recovery handlers prior to PyTorch fallback execution, preventing asynchronous CUDA error cascades into subsequent tensor operations.
210
-
211
- ### Changed
212
- - **High-Entropy Bit-Avalanche PRNG Hash Function (`_prng`)**:
213
- - Replaced the standard Linear Congruential Generator (LCG) with a MurmurHash3 / SplitMix32 integer bit-avalanche hashing routine:
214
- ```python
215
- z = idx + seed + 0x9E3779B9
216
- z = (z ^ (z >> 16)) * 0x85EBCA6B
217
- z = (z ^ (z >> 13)) * 0xC2B2AE35
218
- z = z ^ (z >> 16)
219
- ```
220
- - Delivers uniform pseudo-random noise distributions with zero low-order bit periodicity, significantly refining stochastic rounding fidelity on BF16/FP16 parameters.
221
- - **Memory-Contiguous Scratch Buffer Realignment for v1 Triton Kernels**:
222
- - Re-indexed reduction buffer offsets in Triton v1 kernels:
223
- - Pass 1 metrics (`[rms_sum, p_sq_sum]`) mapped to contiguous indices `[0, 1]`.
224
- - Pass 2 metrics (`[mask_sum, u_sq_sum]`) mapped to contiguous indices `[2, 3]`.
225
- - Enables vectorized contiguous slice all-reduces (`scratch_param[:2]` and `scratch_param[2:4]`) under FSDP without requiring non-contiguous memory gather/scatter overhead.
226
- - **Multi-GPU Device Index Resolution in RNG Dispatcher (`_get_generator`)**:
227
- - Fixed device index resolution when `device` is passed as a generic `torch.device("cuda")` without an explicit index by defaulting to `torch.cuda.current_device()`, avoiding cross-GPU generator key collisions during multi-GPU distributed runs.
228
- - **Conditional Atomic Reduction Pruning in v2 Pass 1 Kernel**:
229
- - Guarded `mask_sum` atomic accumulation in `_lumina_v2_pass1_kernel` with `if cautious:`, bypassing CTA reductions and L2 atomic additions when `cautious=False`.
230
- - **Expanded Master Weight Precision Configuration Aliases**:
231
- - Added the `"half"` alias for `"semi"` mode alongside `"fp32"` and `"masterweight"` aliases for `"full"` mode.
232
- - **Metadata & Documentation Header Update**:
233
- - Bumped specification version to `1.3.2`.
234
-
235
- ### Fixed
236
- - **Singular Zero-Division & Step-Zero Bias Correction (`safe_base_step`)**:
237
- - Introduced `safe_base_step = max(base_step, 1)` and `bc2_base = max(1.0 - (beta2 ** safe_base_step), 1e-15)` across Triton, single-step, and foreach loops.
238
- - Fully resolves potential `ZeroDivisionError` and NaN blowups in `c1` / `c2` computations when steps evaluate at `step == 0`.
239
- - **Numerical Overflow Clamp in Sigmoid-Based Tanh Fallback**:
240
- - Applied register clamp bounds `x_clamped = tl.maximum(tl.minimum(x, 20.0), -20.0)` in analytical fallback `_triton_tanh_fast`, preventing `tl.exp(-2.0 * x)` from overflowing to `inf` or `nan` on devices lacking hardware SFU tanh instructions.
241
- - **Missing Argument Forwarding in PyTorch Single-Step v1**:
242
- - Corrected `_single_step_v1` method signature and call-sites to accept and propagate `scratches`, eliminating runtime `TypeError` during fallback invocation.
243
- - **Multi-Rank Synchronization Skew in Sharded Parameter Bounding**:
244
- - Aligned tensor norms and radial bounding factors across sharded ranks via synchronized global scalar reductions, preventing parameter update drift across distributed ranks.
245
-
246
- ### Performance & Engine Refactoring
247
- - **Zero Dynamic VRAM Allocation on Strided / Sliced Tensors**:
248
- - Persistent pre-allocated contiguous staging buffers avoid PyTorch caching allocator churn and eliminate CUDA Graph memory address invalidations.
249
- - Preserves deterministic memory footprints across long pre-training runs.
250
- - **Optimized Distributed Collective Bandwidth**:
251
- - Grouping scalar reductions into contiguous vector slices compresses distributed synchronization overhead into a single collective kernel launch per pass.
252
-
253
- ---
254
-
255
- ## [1.3.0] - 2026-09-23
256
-
257
- ### Added
258
- - **Fused Single-Pass Kernel with Post-Step EMA (`fused_single_pass`, `ema_decay`)**:
259
- - Introduced `_lumina_v2_fused_single_pass_kernel` coupled with an asynchronous CTA micro-kernel `_lumina_v2_post_step_ema_kernel[(1,)]`.
260
- - When `fused_single_pass=True`, avoids two-pass global reductions by leveraging running Exponential Moving Average (EMA) estimates of `cautious_scale` ($1 / \bar{M}$) and `bound_scale` ($\tanh(r) / r$).
261
- - Unifies gradient filtering, momentum accumulation, variance dispersion, cautious masking, radial bounding, decoupled weight decay, stochastic rounding, and block partial reductions into a **single unified VRAM-resident pass**, reducing global GPU memory bandwidth traffic per optimizer step.
262
- - **Hybrid Master Weight Precision Architecture (`master_weights`)**:
263
- - Added multi-tier master weight flexibility via `master_weights: Union[bool, str] = False`:
264
- - `"none"` / `False` *(Default)*: Pure master-free low-precision training leveraging on-chip bitwise stochastic rounding directly on native half-precision weights.
265
- - `"semi"` / `"hybrid"` / `"half"`: 32-bit FP32 master parameters (`state["master_param"]`) coupled with 16-bit low-memory optimizer states (`exp_avg`, `exp_avg_sq` in BF16/FP16), saving ~50% optimizer state VRAM compared to traditional FP32 setups while maintaining full FP32 weight accumulation accuracy.
266
- - `"full"` / `"fp32"` / `"masterweight"`: Traditional full FP32 master parameters with full FP32 optimizer states for standard baseline comparisons.
267
- - **In-Kernel In-Flight NaN / Inf Gradient Sanitization**:
268
- - Embedded register-level defensive sanitization across all Triton GPU kernels: `is_finite_g = (g == g) & (g < 1e38) & (g > -1e38); g = tl.where(is_finite_g, g, 0.0)`.
269
- - Replaces non-finite gradients and subnormal corruptions with zero in registers before they can contaminate momentum (`exp_avg`) or variance (`exp_avg_sq`) state buffers.
270
- - **Low-Precision State Buffer Serialization (`_store_state_buffer`)**:
271
- - Added a dedicated Triton state serialization routine supporting FP32, BF16, and FP16 state representations with optional bitwise stochastic rounding applied directly to momentum buffers.
272
- - **Multi-Device & Cross-Accelerator Hardware Context Isolation (`_get_device_context`)**:
273
- - Unified device stream scoping supporting NVIDIA CUDA (`torch.cuda`), Intel XPU (`torch.xpu`), and modern PyTorch 2.x unified accelerator interfaces (`torch.accelerator.device`), eliminating multi-GPU stream hijacking and cross-device race conditions.
274
- - **Strict Determinism Auto-Routing**:
275
- - Automatically queries `torch.are_deterministic_algorithms_enabled()`. When enabled under `execution="auto"`, dynamically routes execution to C++ vectorized `foreach` to preserve bitwise cross-run reproducibility, circumventing non-deterministic floating-point hardware atomic reordering.
276
- - **Granular Fault Recovery with State Guarding (`completed_indices`)**:
277
- - Implemented per-parameter completion tracking in `_dispatch_triton`. If a kernel encounters an unexpected runtime fault, completed parameters are preserved while uncompleted ones fall back cleanly to `foreach`.
278
- - Added `_single_step_v2_apply_param` and `_single_step_v1_apply_param` to safely finalize parameter updates if Pass 1 had already committed state transitions, strictly preventing double-update state corruption.
279
-
280
- ### Changed
281
- - **Per-Parameter Persistent Scratch Tensors (Elimination of Global Slicing)**:
282
- - Replaced the global dynamic slice allocator (`self._scratch_tensors[dev][:count]`) with isolated per-parameter persistent state allocations (`state["scratch"] = torch.zeros((5,), device=p.device, dtype=torch.float32)`).
283
- - Memory pointers for `[mask_sum, u_sq_sum, p_sq_sum, cautious_scale, bound_scale]` remain completely invariant throughout the entire training lifecycle, preserving pointer stability for **CUDA Graph Capture** (`torch.accelerator.Graph`) and TorchInductor AOT compilation without memory aliasing.
284
- - **Analytically Exact Nesterov Bias Correction ($\beta_1^{\text{step} + 1}$)**:
285
- - Corrected first-moment bias correction from the classical Adam formulation ($1.0 - \beta_1^{\text{step}}$) to the exact analytical expectation of the Nesterov blended momentum: `bc1_nes = 1.0 - beta1^(step + 1)`. Removes residual momentum bias during early warmup iterations.
286
- - **Adaptive CTA Tile Sizing and Dynamic Thread Warp Scaling**:
287
- - Introduced hardware-aware CTA tile scaling based on parameter volume: parameters with $\ge 2,097,152$ elements (2M+) dynamically configure `BLOCK_SIZE = 2048` and `num_warps = 8` (versus `BLOCK_SIZE = 1024`, `num_warps = 4` for smaller tensors).
288
- - Cuts total thread-block grid counts and L2 cache atomic addition pressure by 50% on large MLP, Attention Projection, and Vocabulary layers.
289
- - **Hardware-Native Tanh Instruction Dispatch (`tl.math.tanh` / `tl.tanh`)**:
290
- - Upgraded `_triton_tanh_fast` to dynamically inspect compiler capabilities, binding directly to native PTX Special Function Unit (SFU) instructions (`tanh.approx.f32`) on modern Triton compilers while retaining analytical sigmoid fallbacks for legacy environments.
291
- - **PRNG Decorrelation between Momentum and Parameter Updates**:
292
- - Separated RNG seed streams using bitwise alternating mask shifts: `seed_p = (seed + 0x55555555) & 0x7FFFFFFF`, mathematically eliminating cross-coupling noise correlation between state buffer rounding and parameter update rounding.
293
- - **Bumped specification version metadata to 1.3.0** across `luminav.py`.
294
-
295
- ### Fixed
296
- - **Floating-Point Cancellation in Radial Bounding ($r \to 0$)**:
297
- - Fixed precision jitter and roundoff degradation near zero by adding an asymptotic Taylor-series guard across Triton and PyTorch bounding engines: `torch.where(r < 1e-4, 1.0, tanh(r) / r)`.
298
- - **Artificial Denominator Dilation in Dispersion Scale ($\sigma$)**:
299
- - Replaced additive epsilon inflation (`sigma + 1e-6`) with a non-distorting floor clamp (`tl.maximum(sigma, 1e-12)`), preventing artificial dampening of subtle gradient signals.
300
- - **Singular Zero-Division in Parameter Norm Clamping**:
301
- - Replaced `norm_p + 1e-6` with `torch.clamp_min(norm_p, 1.0)` / `tl.maximum(norm_p, 1.0)`, maintaining numerical stability even when parameters are initialized near zero.
302
- - **Transient Memory Pool Spikes during PyTorch Fallback Stochastic Rounding**:
303
- - Refactored `_sr_update` to stream stochastic noise generation in bounded chunks of 1,048,576 elements (~4 MB chunk size), eliminating multi-hundred megabyte temporary int32 tensor spikes during fallback execution on large parameter matrices.
304
- - **Non-Contiguous Buffer Safety & Strided Memory Copy**:
305
- - Added strict contiguous layout verification for parameters, states, and master weights with in-place synchronization (`p.copy_(p_contig)`), guarded by a one-time developer warning.
306
-
307
- ### Performance & Engine Refactoring
308
- - **~50% Global Memory Bandwidth Reduction via Fused Single-Pass Mode**:
309
- - By unifying parameter loading, gradient evaluation, state transformation, and bounding into one GPU pass under `fused_single_pass=True`, VRAM read/write cycles are halved for cautious and radially bounded steps.
310
- - **Sub-Microsecond L2 Hardware Atomic Reduction Architecture**:
311
- - Formally documented the hybrid Intra-Block Tree Reduction + Inter-Block Hardware Atomic pattern. With `BLOCK_SIZE=2048`, a 10M-parameter tensor issues only 4,883 atomic additions directly into GPU L2 cache crossbars, executing in < 0.5 µs and strictly outperforming multi-tier cascaded reduction micro-kernels by eliminating 3–5 µs driver dispatch bubbles.
312
- - **Static Buffer Address Invariance for CUDA Graphs & TorchInductor AOT**:
313
- - Persistent 5-slot state scratch buffers eliminate all dynamic slicing and memory reallocations during optimization loops, preserving pointer address invariance for CUDA Graph replay.
314
-
315
- ---
316
-
317
- ## [1.2.1] - 2026-09-17
318
-
319
- ### Added
320
- - **In-Kernel GPU Parameter Norm Reduction (`p_sq_sum_ptr`)**: Fused parameter Euclidean norm squared $\sum(p_i^2)$ calculation directly into Pass 1 Triton reduction kernels (`_lumina_v2_pass1_kernel` and `_lumina_v1_pass1_kernel`). Pass 2 and Pass 3 now load and resolve `norm_p` (`tl.sqrt(tl.maximum(p_sq_sum, 0.0))`) entirely inside GPU SRAM/registers without host intervention.
321
- - **Stateless CPU Integer Multiplicative PRNG Hashing**: Replaced dynamic GPU RNG tensor allocation with a pure CPU integer hash using Knuth's 32-bit golden ratio constant: `(step * 0x9E3779B9 + i * 10007) & 0x7FFFFFFF`. Generates uncorrelated 31-bit integer seeds per layer with zero GPU kernel launch overhead and zero PCIe host-device latency.
322
- - **Isolated Generator Cache for Fallback Engine (`self._generators`)**: Added an internal per-device `torch.Generator` cache, ensuring stochastic rounding in `_sr_update` operates in complete isolation from PyTorch's default CUDA generator state.
323
-
324
- ### Changed
325
- - **Consolidated Scratch Buffer Zeroing**: Removed redundant per-parameter slice zeroing inside parameter loops. Buffer zeroing is now batched into a single consolidated `scratch.zero_()` call per step inside `_get_scratch_buffer`.
326
- - **Scratch Buffer Stride Expansion**:
327
- - `buffer=2`: Expanded stride from 2×N to 3×N elements per parameter to host `[mask_sum, u_sq_sum, p_sq_sum]`.
328
- - `buffer=1`: Expanded stride from 3×N to 4×N elements per parameter to host `[rms_sum, mask_sum, u_sq_sum, p_sq_sum]`.
329
- - **Bumped specification version metadata to 1.2.1**.
330
-
331
- ### Fixed
332
- - **Host-Device Synchronization & Pipeline Stalls (`.item()` Bottleneck)**: Removed `norm_p = float(p_contig.float().norm().item())` and `seed = torch.randint(...).item()` from Triton step pipelines. Eliminated 200–400 synchronous PCIe roundtrips and GPU pipeline stalls per step on deep architectures (e.g., 28-layer Transformers), restoring full asynchronous GPU compute utilization.
333
- - **CUDA Graphs & `torch.compile` Graph Breaks**: Fixed fatal graph capture crashes and TorchDynamo graph breaks caused by blocking host-device synchronization during radial bounding and seed generation. The optimizer step is now compatible with CUDA Graphs (`torch.accelerator.Graph`) and TorchInductor compilation.
334
- - **Transient VRAM Allocator Thrashing**: Eliminated repeated temporary FP32 parameter cloning (`p_contig.float()`) during norm evaluation, preventing memory pool fragmentation in tight VRAM regimes.
335
- - **Global RNG Pollution & Determinism Compliance**: Fixed global RNG state mutation in fallback stochastic rounding. Solved reproducibility drift in upstream Dropout, DataLoader shuffling, and RLHF reward modeling, ensuring strict adherence to PyTorch 2.10+ `torch.use_deterministic_algorithms(True)`.
336
-
337
- ### Performance & Engine Refactoring
338
- - **Batch Kernel Launch Overhead Elimination**: Removed up to 400–800 individual `cudaMemsetAsync` micro-calls per step previously triggered by per-slice `.zero_()` invocations on 1-element scratch tensors.
339
- - **Single-Pass Bypass for Coordinate Bounding**: Streamlined `_triton_step_v2` to immediately bypass two-pass reductions and invoke `_lumina_v2_single_pass_kernel` when `cautious=False` and coordinate bounding (`bound_mode == 2`) is selected.
340
-
341
- ---
342
-
343
- ## [1.2.0] - 2026-09-17
344
-
345
- ### Added
346
- - **Automatic Dynamic FP16 Cliff Governor:** Built-in asymptotic boundary governor (`_triton_tanh_fast` in GPU register and PyTorch fallback) that automatically activates when `p.dtype == torch.float16`. Prevents unclipped steps from pushing parameters, activations, and residual streams into the IEEE-754 FP16 overflow cliff (> 65,504). Remains completely dormant (zero overhead) on `bfloat16` (TPU / Ampere+) and `float32`.
347
- - **Smooth Asymptotic Parameter Bounding (`bound`, `bound_type`, `bound_ratio`):** Added non-intrusive step bounding with two mathematically continuous formulations:
348
- - `"radial"`: Direction-Preserving Radial Squashing (`update * tanh(r) / r`) preserving gradient direction (Cosine Similarity = 1.000).
349
- - `"coordinate"`: Dual-Hyperbolic Coordinate Squashing for fine-grained per-element damping.
350
- - Configurable via constructor: `bound: bool = True`, `bound_type: str = "radial"`, `bound_ratio: float = 0.03`.
351
-
352
- ### Changed
353
- - **Increased Default `cautious_clamp_min` from `0.2` to `0.5`:** Enforces an exact power-of-two maximum energy scaling factor (2.00x, $2^1$). Eliminates floating-point mantissa roundoff distortion in IEEE-754 hardware.
354
- - Bumped specification version metadata to `1.2.0`.
355
-
356
- ### Fixed
357
- * **FP16 Step Divergence & NaN Overflow at High Learning Rates:** Solved step divergence and subsequent NaN crashes when training deep 28-layer Transformer architectures (e.g., Qwen3-0.6B) in pure FP16 at nominal learning rates (`lr = 8e-4`).
358
- * **Sparse Embedding & LM-Head Amplification:** Mitigated 5x over-amplification on sparse vocabulary gradients where active token counts are much smaller than total tensor parameters ($N \gg N_{\text{active}}$).
359
- * **Verified stable 1,000-step pretraining from scratch on Qwen3-0.6B** (seq length 512, pure FP16, `lr = 8e-4`) on Nvidia Tesla T4 with zero gradient clipping and zero LR scheduler (loss dropped from 12.09 down to ~5.7 with zero NaNs).
360
- * **Weight Decay Numerical Parity on Fallback Engine:** Resolved a silent precision-stagnation issue in `_single_step` and `_foreach_step`. Previously, decoupled weight decay (`p.mul_(1.0 - lr * weight_decay)`) was executed directly on native FP16/BF16 tensors prior to `_sr_update`. Due to IEEE-754 Round-to-Nearest (RTN) limits, `(1 - lr * weight_decay) ≈ 0.999936` rounded back to `1.0`, causing weight decay to freeze on half-precision parameters. Weight decay is now unified into FP32 registers inside `_sr_update` alongside stochastic rounding, matching the numerical behavior with the Triton kernel.
361
- * **Fallback Foreach Indentation & Scope Alignment:** Corrected tensor iteration scope and indentation for `torch._foreach_add_` in non-SR fallback branches.
362
- * **In-Place GPU Clamping in `_apply_bound`:** Replaced dynamic GPU scalar allocations (`torch.tensor(1.5e-4, device=p.device)`) with native `torch.clamp_min`, eliminating transient CUDA malloc spikes per update step.
363
-
364
- ### Performance & Engine Refactoring
365
- * **Unified Bound Execution Pipeline:** Added an immediate short-circuit guard (`if lr == 0.0: return update`) and streamlined the execution flow: intermediate updates are scaled in FP32 (`step_val = lr * u_float`), passed through radial/coordinate bounding, chained cleanly into the FP16 Cliff Governor, and returned in a single normalized step.
366
-
367
- ---
368
-
369
- ## [1.1.5] - 2026-09-15
370
-
371
- ### Added
372
- - **Native Hardware Capability Detection (`_supports_native_bf16`):** Added automatic device capability detection via `torch.cuda.is_bf16_supported()` to identify Ampere SM80+ architectures and streamline native execution paths.
373
- - **Hugging Face Repository Reference:** Added official Hugging Face repository link in module docstrings.
374
-
375
- ### Changed
376
- - **Dynamic Epsilon Clamping for FP16:** Dynamically floors effective epsilon to `max(eps, 1e-4)` when parameters or states are in `torch.float16`. Prevents bias-corrected scale factor `c2` from underflowing into IEEE-754 subnormal/zero limits on step 1.
377
- - Bumped specification version to `1.1.5`.
378
-
379
- ### Fixed
380
- - **FP16 Step-1 `NaN` from Epsilon Underflow:** Fixed `0.0 / 0.0 = NaN` division occurring on initial training steps when parameters receive zero gradients (e.g., unselected vocabulary embeddings, padding tokens, or dropout paths).
381
- - **Second-Moment Variance Saturation (`+inf`):** Added safe upper-bound register clamping (`tl.minimum(v_new, 65000.0)` in Triton kernels and `.clamp_(0.0, 65000.0)` in PyTorch C++ foreach/single loops). Prevents unclipped initial pretraining gradient surges ($|g| > 256$) from overflowing the FP16 maximum dynamic range (65,504) into irreversible `+inf`.
382
- - **Zero-Sigma Division Guard:** Injected explicit `sigma + 1e-6` denominator guards across all Triton kernel passes and PyTorch update steps to ensure numerical safety during cold-start iterations.
383
- - Verified stable 100-step pretraining from scratch on `SmolLM-135M` using Wikimedia Wikipedia in pure FP16 on Nvidia Tesla T4 GPU with zero gradient clipping.
384
-
385
- ---
386
-
387
- ## [1.1.0] - 2026-09-11
388
-
389
- ### Added
390
- - **Bitwise Stochastic Rounding (SR):** Integrated on-chip PRNG integer bit-manipulation hash in Triton kernels and vectorized PyTorch fallback for native FP16/BF16 training without master weights.
391
- - Unified Triton store routine introducing compile-time branching (`dtype_mode` / `use_sr: tl.constexpr`) with explicit safe casting for FP16 and BF16 pointers.
392
- - Vectorized fallback improvements utilizing C++ multi-tensor operations when stochastic rounding is disabled or tensors are FP32.
393
- - State buffer contiguity validation (`exp_avg_contig`, `exp_avg_sq_contig`) ensuring safe linear memory addressing before kernel dispatch.
394
- - Declarative configuration file (`config.json`) defining hyperparameters and optimizer presets.
395
-
396
- ### Changed
397
- - Refactored PRNG integer arithmetic to strictly stay within signed 32-bit integer limits ($< 2^{31} - 1$), eliminating JIT compilation failures on newer Triton releases.
398
- - State buffers (`exp_avg`, `exp_avg_sq`) now initialize with strict contiguous memory format.
399
-
400
- ### Fixed
401
- - Fixed `ValueError: Scalar out of range for type int32` and Triton LLVM compile errors during unsigned 32-bit bitmask operations.
402
- - Resolved kernel fallback hangs and compilation overhead on CUDA devices.
403
-
404
- ---
405
-
406
- ## [1.0.2] - 2026-09-08
407
-
408
- ### Added
409
- - Official Apache-2.0 license notice and copyright headers.
410
- - Standardized package exports (`__all__`, `__version__`, `__author__`, `__license__`).
411
-
412
- ---
413
-
414
- ## [1.0.1] - 2026-08-31
415
-
416
- ### Fixed
417
- - **Memory Synchronization Fix (`423d171`):** Fixed a critical bug where non-contiguous parameter tensors (e.g., transposed linear layers or sliced weights) were updated on temporary contiguous copies without reflecting changes back to model weights. Added `p.copy_(p_contig)` after Triton execution.
418
-
419
- ---
420
-
421
- ## [1.0.0] - 2026-08-30
422
-
423
- ### Added
424
- - Initial release of the LuminaV optimizer.
425
- - **Zero Master-Weight Copies:** Direct parameter updates in native half-precision without allocating 4-byte FP32 master weights.
426
- - **Hyperbolic Tangent (`tanh`) Bounding Envelope:** Projects normalized momentum ratio into `(-1.0, 1.0)` to eliminate exploding gradient shocks.
427
- - **Directional Alignment Gate:** Cautious update masking that zeroes out momentum steps opposing mini-batch gradient directions.
428
- - **Central Innovation Variance:** Noise dispersion tracking $(g_t - m_t)^2$ decoupling mini-batch turbulence from directional descent.
429
- - Dual-buffer mode (`buffer=2`, LuminaV-2B) and single-buffer mode (`buffer=1`, LuminaV-1).
430
- - Custom fused OpenAI Triton kernels with automated fallback execution.
 
1
+ # LuminaV Optimizer Algorithm Changelog
2
+
3
+ All notable changes to the LuminaV Optimizer algorithm are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ * [Software Package Changelog (`luminamoon`)](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/changelogs/SOFTWARE_CHANGELOG.md)
8
+
9
+ ---
10
+
11
+ ## [1.6.0] - 2026-11-03
12
+
13
+ ### Added
14
+ - **T-Family (Triangle Family) tanh-free Modes (`buffer="T1"` & `buffer="T2"`)**:
15
+ * **`buffer="T2"` (2-Buffer Triangle Family Mode)**: Keeps the 2-buffer state (`exp_avg` and `exp_avg_sq`) without using $\tanh$, computing the gradient-momentum residual $d_t = g_t - m _ {t-1}$ and bounding the lookahead update $\tilde{m}_t$ via the **Triangle Inequality Envelope** $\mathcal{E}_t^{(\text{T2})}$:
16
+
17
+ $\displaystyle d_t = g_t - m _ {t-1}, \qquad v_t = \beta_2 v _ {t-1} + (1 - \beta_2) d_t^2$
18
+
19
+ $\displaystyle \tilde{m} _ t = w_1 m _ {t-1} + w_2 g_t, \qquad w_1 = \beta_1^2, \qquad w_2 = 1 - \beta_1^2$
20
+
21
+ $\displaystyle \mathcal{E} _ t^{(\text{T2})} = w_1 |m _ {t-1}| + w_2 |g_t| + 0.5\lambda |d_t| + 0.5\lambda \sigma_t + \epsilon _ {\text{eff}}, \qquad u_t^{(\text{T2})} = \frac{\tilde{m} _ t}{\mathcal{E} _ t^{(\text{T2})}} \in (-1, 1)$
22
+
23
+ By the Triangle Inequality ($|w_1 m _ {t-1} + w_2 g_t| \le w_1 |m _ {t-1}| + w_2 |g_t| \le \mathcal{E} _ t^{(\text{T2})}$), $|u_t^{(\text{T2})}| < 1$ is guaranteed without evaluating $\tanh$. The term $0.5\lambda |d_t|$ acts as a **triangle-defect penalty**, and $0.5\lambda \sigma_t$ provides belief-variance damping.
24
+ * **`buffer="T1"` (1-Buffer Triangle Family Mode)**: Combines the single-buffer **Triangle-Defect** denominator $\mathcal{E} _ t^{(\text{T1})} = w_1 |m _ {t-1}| + w_2 |g_t| + \lambda |d_t| + \epsilon _ {\text{eff}}$ (using residual $d_t = g_t - m _ {t-1}$) with the rational update bounds below, reducing optimizer state memory by 50% via the Triangle Inequality.
25
+ * **Rational Update Bounds (for `T1` & `T2`)**: Replaces $\tanh$ and $\sqrt{\cdot}$ in the coordinate, radial, and FP16 update bounds with simple rational formulas:
26
+
27
+ $\displaystyle b_i = \mathrm{bound} _ {\text{ratio}} \cdot (|w_i| + 10^{-2}), \qquad \Delta w_i = \frac{b_i (b_i + |s_i|) s_i}{b_i (b_i + |s_i|) + s_i^2}, \qquad \mathrm{scale} _ {\text{radial}} = \frac{1 + r}{1 + r + r^2}$
28
+ * Keeps 100% backward compatibility and identical outputs for existing modes (`buffer=1`, `buffer=2`, and `version="1.5.5"`).
29
+
30
+ ---
31
+
32
+ ## [1.5.5] - 2026-10-29
33
+
34
+ ### Added
35
+ - **Contiguous Bucket Fusion (`fuse_tensors: bool = False`)**:
36
+ * Added opt-in contiguous parameter and state coalescing (`_build_contiguous_buckets`) that groups parameters within each `(param_group, device, dtype)` domain $\mathcal{B}_{g,d,\tau}$ into contiguous 1D storage (`flat_p`, `flat_g`, `flat_m`, `flat_v`, `flat_mp`) and rebinds `p.data`, `p.grad`, and `self.state[p]` entries as zero-copy views (`flat_*[offset : offset + n].view(p.shape)`).
37
+ * Eliminates per-tensor CPU-to-GPU kernel launch latency on fragmented vision and edge architectures by executing an entire bucket in a single fused kernel dispatch while computing `cautious_scale` and `radial` trust bounds across the bucket domain $\mathcal{B}_{g,d,\tau}$.
38
+ * Added in-place `zero_grad()` view preservation (`flat_g.zero_()`) and full `state_dict()` / `load_state_dict()` checkpoint compatibility without leaking temporary buffer keys (LSO-Eval Stage II compliant).
39
+ - **Triton JIT Pre-Warmup (`prewarm_jit: bool = True`)**:
40
+ * Added automatic initialization-time Triton JIT compilation (`_prewarm_all_configs`) using 64-element dummy buffers (`128–256 bytes` VRAM) with `lr=0.0` and `weight_decay=0.0` across both Step 1 (`pass1` + `pass2`) and Step 2+ (`fused_single_pass` + `post_step_ema`) paths with target `BLOCK_SIZE` specialization (`1024` or `2048`), eliminating first-step compilation stalls.
41
+ - **Bounded Bucket Chunking & Sequential In-Place Migration (`bucket_cap_mb: float = 500.0`)**:
42
+ * Bound contiguous bucket coalescing by an upper memory capacity ceiling $C_{\text{cap}} = 500\text{ MB}$, partitioning model parameters into ordered contiguous chunks $\mathcal{B}^{(1)}, \mathcal{B}^{(2)}, \dots, \mathcal{B}^{(K)}$ such that $\sum_{p \in \mathcal{B}^{(k)}} \text{bytes}(p) \le C_{\text{cap}}$ (with giant single layers $> C_{\text{cap}}$ isolated into individual buckets).
43
+ * Enforced sequential in-place migration with immediate parameter storage recycling (`del old_data`, `del old_grad`) and garbage collection, constraining peak transient memory allocation spikes during optimizer initialization to at most $\min(\text{model bytes}, C _ {\text{cap}})$.
44
+
45
+ ---
46
+
47
+ ## [1.5.4] - 2026-10-29
48
+
49
+ ### Added
50
+ - **In-Container Native Bitwise-BF16 State Promotion (`dtype_mode == 2`, `master_weights="none"`)**:
51
+ * Promoted 16-bit optimizer state buffers ($m_t$ via stochastic rounding `f32_to_bf16_bits_sr` and $v_t$ via round-to-nearest-even `f32_to_bf16_bits_rne`) to native Bitwise-BF16 stored inside the existing 16-bit container during pure `FP16` execution, expanding state exponent dynamic range to 8 bits ($1.18 \times 10^{-38}$) with zero additional VRAM overhead.
52
+
53
+ ### Changed
54
+ - **Unified Denominator Stability Constant ($\epsilon_{\text{eff}} = \epsilon = 10^{-8}$)**:
55
+ * Unified the effective denominator constant $\epsilon_{\text{eff}} = \epsilon = 10^{-8}$ across all precision modes and hardware backends (replacing the legacy `FP16` floor clamp $\max(\epsilon, 10^{-4})$ introduced in `1.1.5`), preventing update velocity attenuation and late-stage convergence loss resets when $\sqrt{v_t} \ll 10^{-4}$.
56
+
57
+ ### Removed
58
+ - **Centered Defect Compensation (`centered_defect`, $\mu_d$)**:
59
+ * Removed the `centered_defect` hyperparameter and the global defect mean reduction $\mu_d = \frac{1}{N}\sum_{i=1}^N (g_{t,i} - m_{t-1,i})$ across both Dual-Buffer Convex Lookahead Momentum (`buffer=2` / CLM) and Single-Buffer Triangle-Defect Convex Lookahead Momentum (`buffer=1` / TD-CLM).
60
+ * Simplified the instantaneous gradient-momentum residual to purely local coordinate-wise differences:
61
+
62
+ $\displaystyle d_t^{(\text{B1})} = g_t - m_{t-1}, \qquad d_t^{(\text{B2})} = g_t - m_t$
63
+
64
+ * Eliminated the pre-step global reduction pass (`_lumina_diff_sum_kernel` in Triton JIT/AOT, `diff_sum.comp` in Vulkan SPIR-V, `reduce_diff_sum_simd` in x86\_64 AVX-512/AVX2, and slot 5 of the 6-element `ScratchBuffer`), removing cross-lane/cross-rank reduction latency while retaining the 6-element physical `ScratchBuffer` layout (`[mask_sum, u_sq_sum, p_sq_sum, cautious_scale, bound_scale, _reserved]`, 5 active slots + 1 reserved slot for ABI alignment).
65
+
66
+ ---
67
+
68
+ ## [1.5.3] - 2026-10-20
69
+
70
+ No versions were published between 1.3.3 and 1.5.3. This release notes all changes made since version 1.3.3.
71
+
72
+ ### Added
73
+ - **Single-Buffer Triangle-Defect Convex Lookahead Momentum (TD-CLM, `buffer=1`)**:
74
+ * Replaced the legacy 3-pass RMS single-buffer update with a single-pass formulation:
75
+
76
+ $\displaystyle \tilde{m} _ t = w_1 m _ {t-1} + w_2 g_t, \quad w_1 = \beta_1^2, \quad w_2 = 1 - \beta_1^2$
77
+
78
+ $\displaystyle \mathcal{E} _ t = w_1 |m _ {t-1}| + w_2 |g_t| + \lambda |d_t^{(\text{B1})}| + \epsilon _ {\text{eff}}$
79
+
80
+ $\displaystyle u_t^{(\text{B1})} = \frac{\tilde{m} _ t}{\mathcal{E} _ t} \in (-1, 1)$
81
+
82
+ * In 1.3.3, single-buffer mode required three sequential GPU kernel launches (`_lumina_v1_pass1_kernel`, `_lumina_v1_pass2_kernel`, `_lumina_v1_pass3_update_kernel`) and global atomic reductions across thread blocks to compute tensor-wide root-mean-square ($\text{rms} = \sqrt{\frac{1}{N}\sum \tilde{m} _ i^2 + \epsilon}$).
83
+ * TD-CLM replaces the tensor-wide reduction with coordinate-wise envelope bounding via the Triangle Inequality Envelope $\mathcal{E} _ t$. This reduces single-buffer execution to a single GPU pass and eliminates all $\tanh$ evaluations in the momentum path.
84
+ * Calibrated the defect penalty weight $\lambda$ to the symmetric spike-reversal equilibrium:
85
+
86
+ $\displaystyle \lambda^* = \frac{2(1 - \beta_1^2)^2}{4\beta_1^2 - 3}$
87
+
88
+ For the default $\beta_1 = 0.9$, $\lambda^* \approx 0.3008$, setting the default parameter to `lam=0.3`.
89
+
90
+ - **Padé [13/6] Rational Approximation (`tanh_pade_f32`)**:
91
+ * Implemented a rational Padé [13/6] polynomial for hyperbolic tangent calculations:
92
+
93
+ $\displaystyle \tanh _ {\text{Pade}}(x) = \frac{x(1 + c_1 x^2 + c_2 x^4 + c_3 x^6 + c_4 x^8 + c_5 x^{10} + c_6 x^{12})}{1 + d_1 x^2 + d_2 x^4 + d_3 x^6}$
94
+
95
+ * Evaluated over the clamped domain $x \in [-7.9988117, 7.9988117]$ with asymptotic saturation to $\pm 1.0$.
96
+ * In 1.3.3, `_triton_tanh_fast` used hardware-dependent SFU instructions (`tl.math.tanh`) or analytical sigmoid fallbacks (`2 / (1 + exp(-2x)) - 1`). Different GPU architectures (e.g. Turing, Ampere, Hopper) evaluate SFU transcendental functions with slightly different mantissa rounding, leading to bitwise divergence during distributed training.
97
+ * Padé [13/6] provides uniform precision with $\lVert \tanh _ {\text{Pade}}(x) - \tanh(x) \rVert _ \infty < 1.5 \times 10^{-6}$ across CPU Scalar, AVX2, AVX-512, Vulkan SPIR-V, and Triton GPU kernels.
98
+
99
+ - **Centered Innovation Defect (`centered_defect`)**:
100
+ * Added batch-mean centering for gradient innovation tracking (`centered_defect=False` by default):
101
+
102
+ $\displaystyle \mu_d = \frac{1}{N}\sum _ {i=1}^N (g _ {t,i} - m _ {t-1,i})$
103
+
104
+ $\displaystyle d_t^{(\text{B1})} = (g_t - m _ {t-1}) - \mu_d$
105
+
106
+ $\displaystyle d_t^{(\text{B2})} = (g_t - m_t) - \beta_1 \mu_d$
107
+
108
+ * In 1.3.3, uncentered innovation $g_t - m_t$ was used directly. On layers where gradients have a non-zero mean (such as normalization scales, biases, and token embeddings), the raw difference caused systematic drift in the second-moment accumulator. Centering eliminates this mean shift.
109
+
110
+ - **Subnormal-Safe IEEE-754 FP16 Stochastic Rounding**:
111
+ * Added `f32_to_f16_bits_rne` to handle IEEE-754 subnormal numbers ($|x| < 2^{-14}$) during FP16 stochastic rounding.
112
+ * In 1.3.3, stochastic rounding added 13-bit random noise and masked with `& -8192`. When a floating-point value entered the subnormal range, bitwise addition directly corrupted the exponent field (bit 10), causing values to flip between normal and subnormal representations or trigger hardware flush-to-zero traps.
113
+ * The updated routine checks for zero exponent, shifts the significand into the subnormal format, and applies round-to-nearest-even tie-breaking before bit truncation.
114
+
115
+ - **Multi-Backend Execution Engine**:
116
+ * Replaced the Python-only runtime with a multi-backend execution layer:
117
+ * **Rust SIMD (`luminav-simd`)**: AVX-512 (512-bit registers), AVX2 with FMA, portable scalar fallback, and Rayon parallel chunk scheduling (`ELEM_CHUNK_SIZE = 2,097,152`).
118
+ * **Vulkan UMA (`luminav-vulkan`)**: SPIR-V compute pipeline for cross-vendor GPUs (AMD, Intel, Apple Silicon via MoltenVK, and NVIDIA) with zero-copy host-visible memory buffers.
119
+ * **Triton AOT (`luminav-triton-aot`)**: C-ABI wrapper dispatching ahead-of-time precompiled kernels for 84 GPU architectures (Blackwell SM100/SM120, Hopper SM90, Ada SM89, Ampere SM80/SM86, Turing SM75, and AMD ROCm gfx90a/gfx942/gfx1100).
120
+ * **Standalone Runtime (`luminamoon.optimizer.standalone`)**: Pure NumPy and C-ABI execution that runs without requiring PyTorch.
121
+
122
+ ### Changed
123
+ - **Parameter `alpha_ss`**: Removed. In 1.3.3, `alpha_ss=0.5` was used for soft-sign scaling $z / (1 + \alpha _ {\text{ss}} |z|)$ in the legacy single-buffer path. In 1.5.3, TD-CLM envelope normalization bounds updates directly to $(-1, 1)$, making soft-sign obsolete.
124
+ - **Parameter `lam`**: Added with default `0.3`. Governs the defect penalty weight in TD-CLM.
125
+ - **Parameter `centered_defect`**: Added with default `False`. Enables batch-mean centering of the innovation defect.
126
+ - **Parameter `bound_type`**: Default changed from `"radial"` (1.3.3) to `"coordinate"` (1.5.3). Coordinate-wise hyperbolic bounding $(B_i \tanh(\Delta \theta_i / B_i))$ evaluates locally on each parameter without computing a tensor-wide norm reduction.
127
+ - **Parameter `bound_ratio`**: Default changed from `0.03` to `0.05` to provide better update headroom on large model architectures.
128
+ - **Parameter `ema_decay`**: Default changed from static `0.8` to `"auto"`. Resolves to `0.80` for Dual-Buffer mode (`buffer=2`) and `0.95` for Single-Buffer mode (`buffer=1`).
129
+ - **Scratch Buffer**: Expanded from 5 floats `[mask_sum, u_sq_sum, p_sq_sum, cautious_scale, bound_scale]` to 6 floats, adding `diff_sum` at index 5 to accumulate batch-mean defect statistics.
130
+ - **Cautious Scaling Floor**: Enforced `cautious_clamp_min=0.5` across all backends to cap maximum cautious energy scaling at $2.0\times$ ($2^1$).
131
+
132
+ ### Fixed
133
+ - **Hardware Mantissa Divergence**: Fixed bitwise output differences across GPU families by replacing hardware SFU `tl.math.tanh` with Padé [13/6] rational approximation.
134
+ - **FP16 Subnormal Bit Corruption**: Fixed exponent corruption in subnormal FP16 stochastic rounding by separating exponent extraction from significand shift logic.
135
+ - **Single-Buffer Kernel Launch Latency**: Reduced single-buffer kernel dispatches from 3 passes to 1 pass, eliminating host-side synchronization bubbles and intermediate L2 atomic reduction overhead.
136
+ - **Non-Zero Mean Gradient Drift**: Resolved directional accumulator drift on bias and normalization layers via `centered_defect`.
137
+
138
+ ### Parameter Comparison (1.3.3 vs 1.5.3)
139
+
140
+ | Parameter | Version 1.3.3 | Version 1.5.3 | Technical Notes |
141
+ | :--- | :--- | :--- | :--- |
142
+ | `params` | `params` | `params` | Iterable of parameters or parameter groups. |
143
+ | `lr` | `8e-4` | `8e-4` | Base learning rate. |
144
+ | `betas` | `(0.9, 0.999)` | `(0.9, 0.999)` | First and second moment exponential decay rates. |
145
+ | `eps` | `1e-8` | `1e-8` | Numerical stability floor. |
146
+ | `weight_decay` | `8e-2` | `8e-2` | Decoupled weight decay rate. |
147
+ | `tau` | `0.8` | `0.8` | Noise dispersion scale factor. |
148
+ | `alpha_ss` | `0.5` | *(removed)* | Removed; superseded by TD-CLM envelope normalization. |
149
+ | `lam` | *(none)* | `0.3` | Defect penalty weight in TD-CLM (calibrated to $\lambda^* \approx 0.3008$). |
150
+ | `cautious` | `True` | `True` | Directional verification gate ($u_i g_i > 0$). |
151
+ | `cautious_clamp_min` | `0.5` | `0.5` | Maximum cautious scale cap ($2.0\times$). |
152
+ | `buffer` | `2` | `2` | `1` = Single-Buffer (TD-CLM), `2` = Dual-Buffer (CLM). |
153
+ | `centered_defect` | *(none)* | `False` | Enables subtraction of batch-mean defect $\mu_d$. |
154
+ | `stochastic_rounding` | `True` | `True` | Bitwise stochastic rounding on FP16/BF16 weights. |
155
+ | `bound` | `True` | `True` | Enables update trust-region bounding. |
156
+ | `bound_type` | `"radial"` | `"coordinate"` | Default changed to coordinate-wise bounding (zero reduction overhead). |
157
+ | `bound_ratio` | `0.03` | `0.05` | Maximum step magnitude relative to parameter magnitude. |
158
+ | `master_weights` | `False` | `"none"` | `"none"`, `"semi"`, or `"full"`. |
159
+ | `execution` | `"auto"` | `"auto"` | Dynamic routing: SIMD, Vulkan, Triton, or Foreach. |
160
+ | `fused_single_pass` | `False` | `True` | Enables single-pass streaming with EMA scaling. |
161
+ | `ema_decay` | `0.8` | `"auto"` | Automatically sets `0.80` for dual-buffer, `0.95` for single-buffer. |
162
+ | `fsdp_sync` | `False` | `False` | Cross-rank collective all-reduce synchronization. |
163
+ | `seed` | `1337` | `1337` | PRNG seed for deterministic stochastic rounding. |
164
+
165
+ ---
166
+
167
+ ## [1.3.3] - 2026-10-06
168
+
169
+ ### Fixed
170
+ * **Triton AST Compiler Early-Return Inlining Error:**
171
+ * Resolved fatal MLIR/CFG code generation failure (`at 35:4: _store_state_buffer(...) ^`) triggered by mid-branch `return` statements in `@triton.jit` helper functions (`_store_param` and `_store_state_buffer`).
172
+ * Refactored helper control flow into structured, single-exit `if ... elif ... else` branches, ensuring compliant JIT function inlining across all kernel entry points.
173
+ * **Bitcast Width Mismatch (Int64 Promotion in Stochastic Rounding):**
174
+ * Fixed implicit 64-bit integer promotion caused by Python integer literals (`0x9E3779B9`, `~0x1FFF`, `~0xFFFF`) inside Triton bitwise operations.
175
+ * Enforced strict 32-bit signed arithmetic via two's complement constants (`-1640531527`, `-2048173461`, `-1028477387`) with explicit `.to(tl.int32)` coercion, and standardized bitwise masks (`-65536` for BF16, `-8192` for FP16), ensuring type-safe 32-bit bitcasting (`tl.int32` to `tl.float32`).
176
+ * **JIT Kernel Call Keyword Arguments:**
177
+ * Replaced internal keyword argument invocations (`use_sr=False`) with standard positional boolean parameters inside Triton JIT kernels to eliminate compiler argument binding warnings across Triton runtime versions.
178
+
179
+ ### Improved
180
+ * **Throttled Fallback Telemetry & Console De-Noising:**
181
+ * Added `self._warned_triton_failure` state tracking to prevent multi-line compiler AST dumps from flooding the console across every parameter tensor in a parameter group.
182
+ * Formatted fallback notifications into a single concise one-line `WARNING` log on initial encounter, routing full tracebacks to `DEBUG` level.
183
+
184
+ ### Verified
185
+ * **End-to-End JIT Execution on NVIDIA Architectures (Tesla T4):**
186
+ * Validated JIT execution across both Dual-Pass (`fused_single_pass=False`) and Fused Single-Pass (`fused_single_pass=True`) modes in native FP16.
187
+ * Confirmed non-zero weight update dynamics ($|\Delta W| > 0$) without gradient underflow or weight freezing in pure master-free training for Version 1.3.3.
188
+
189
+ ---
190
+
191
+ ## [1.3.2] - 2026-10-06
192
+
193
+ ### Added
194
+ - **Distributed FSDP Collective Synchronization (`fsdp_sync`)**:
195
+ - Introduced the `fsdp_sync: bool = False` configuration flag on optimizer initialization, backed by automated runtime guarding via `torch.distributed.is_initialized()`.
196
+ - Integrated cross-GPU collective communications (`torch.distributed.all_reduce(..., op=torch.distributed.ReduceOp.SUM)`) for global reduction metrics (`mask_sum`, `u_sq_sum`, and `p_sq_sum`) across Triton kernels, single-step execution, and vectorized foreach loops.
197
+ - Maintains bit-exact scalar consistency for `m_bar`, `cautious_scale`, and `bound_scale` across distributed parameter shards during large-scale pretraining under PyTorch FSDP (Fully Sharded Data Parallel) and DeepSpeed ZeRO-3.
198
+ - **Dedicated Low-Precision Stochastic Quantization Routine (`_sr_quantize`)**:
199
+ - Factored out bitwise stochastic rounding into a dedicated, modular helper function: `_sr_quantize(target, val_f32, use_sr, generator)`.
200
+ - Enabled native stochastic rounding on half-precision momentum states (`exp_avg` in BF16/FP16) across all PyTorch CPU/CUDA fallback paths (`_single_step_v2`, `_foreach_step_v2`, `_single_step_v1`, and `_foreach_step_v1`), systematically eliminating gradient stagnation induced by fractional bit truncation (LSB underflow).
201
+ - **Persistent Contiguous Buffer Caching**:
202
+ - Introduced persistent state cache tensors within `self.state[p]` (`p_contig`, `exp_avg_contig`, `exp_avg_sq_contig`, and `mp_contig`) to stage strided or sliced parameters before kernel launch.
203
+ - Eliminates dynamic memory allocations (`.contiguous()`) during iterative training loops, preserving PyTorch VRAM caching allocator stability and preventing CUDA Graph capture invalidation.
204
+ - **Zero-Copy Direct Tensor Aliasing for FP32 Master Weights**:
205
+ - When parameter tensors are natively `torch.float32`, `state["master_param"]` now directly aliases the parameter tensor pointer (`state["master_param"] = p`), avoiding additional master weight VRAM overhead for FP32 layers (0 bytes auxiliary memory footprint).
206
+ - **Checkpoint Sanitizer via State Dict Filter (`state_dict`)**:
207
+ - Overrode `state_dict()` to automatically purge temporary contiguous cache buffers (`p_contig`, `exp_avg_contig`, `exp_avg_sq_contig`, `mp_contig`), ensuring on-disk checkpoint payloads remain clean, compact, and fully backward-compatible.
208
+ - **Defensive Device Pipeline Flush on Kernel Fault (`torch.cuda.synchronize`)**:
209
+ - Embedded explicit hardware device synchronization (`torch.cuda.synchronize(p.device)`) within Triton exception recovery handlers prior to PyTorch fallback execution, preventing asynchronous CUDA error cascades into subsequent tensor operations.
210
+
211
+ ### Changed
212
+ - **High-Entropy Bit-Avalanche PRNG Hash Function (`_prng`)**:
213
+ - Replaced the standard Linear Congruential Generator (LCG) with a MurmurHash3 / SplitMix32 integer bit-avalanche hashing routine:
214
+ ```python
215
+ z = idx + seed + 0x9E3779B9
216
+ z = (z ^ (z >> 16)) * 0x85EBCA6B
217
+ z = (z ^ (z >> 13)) * 0xC2B2AE35
218
+ z = z ^ (z >> 16)
219
+ ```
220
+ - Delivers uniform pseudo-random noise distributions with zero low-order bit periodicity, significantly refining stochastic rounding fidelity on BF16/FP16 parameters.
221
+ - **Memory-Contiguous Scratch Buffer Realignment for v1 Triton Kernels**:
222
+ - Re-indexed reduction buffer offsets in Triton v1 kernels:
223
+ - Pass 1 metrics (`[rms_sum, p_sq_sum]`) mapped to contiguous indices `[0, 1]`.
224
+ - Pass 2 metrics (`[mask_sum, u_sq_sum]`) mapped to contiguous indices `[2, 3]`.
225
+ - Enables vectorized contiguous slice all-reduces (`scratch_param[:2]` and `scratch_param[2:4]`) under FSDP without requiring non-contiguous memory gather/scatter overhead.
226
+ - **Multi-GPU Device Index Resolution in RNG Dispatcher (`_get_generator`)**:
227
+ - Fixed device index resolution when `device` is passed as a generic `torch.device("cuda")` without an explicit index by defaulting to `torch.cuda.current_device()`, avoiding cross-GPU generator key collisions during multi-GPU distributed runs.
228
+ - **Conditional Atomic Reduction Pruning in v2 Pass 1 Kernel**:
229
+ - Guarded `mask_sum` atomic accumulation in `_lumina_v2_pass1_kernel` with `if cautious:`, bypassing CTA reductions and L2 atomic additions when `cautious=False`.
230
+ - **Expanded Master Weight Precision Configuration Aliases**:
231
+ - Added the `"half"` alias for `"semi"` mode alongside `"fp32"` and `"masterweight"` aliases for `"full"` mode.
232
+ - **Metadata & Documentation Header Update**:
233
+ - Bumped specification version to `1.3.2`.
234
+
235
+ ### Fixed
236
+ - **Singular Zero-Division & Step-Zero Bias Correction (`safe_base_step`)**:
237
+ - Introduced `safe_base_step = max(base_step, 1)` and `bc2_base = max(1.0 - (beta2 ** safe_base_step), 1e-15)` across Triton, single-step, and foreach loops.
238
+ - Fully resolves potential `ZeroDivisionError` and NaN blowups in `c1` / `c2` computations when steps evaluate at `step == 0`.
239
+ - **Numerical Overflow Clamp in Sigmoid-Based Tanh Fallback**:
240
+ - Applied register clamp bounds `x_clamped = tl.maximum(tl.minimum(x, 20.0), -20.0)` in analytical fallback `_triton_tanh_fast`, preventing `tl.exp(-2.0 * x)` from overflowing to `inf` or `nan` on devices lacking hardware SFU tanh instructions.
241
+ - **Missing Argument Forwarding in PyTorch Single-Step v1**:
242
+ - Corrected `_single_step_v1` method signature and call-sites to accept and propagate `scratches`, eliminating runtime `TypeError` during fallback invocation.
243
+ - **Multi-Rank Synchronization Skew in Sharded Parameter Bounding**:
244
+ - Aligned tensor norms and radial bounding factors across sharded ranks via synchronized global scalar reductions, preventing parameter update drift across distributed ranks.
245
+
246
+ ### Performance & Engine Refactoring
247
+ - **Zero Dynamic VRAM Allocation on Strided / Sliced Tensors**:
248
+ - Persistent pre-allocated contiguous staging buffers avoid PyTorch caching allocator churn and eliminate CUDA Graph memory address invalidations.
249
+ - Preserves deterministic memory footprints across long pre-training runs.
250
+ - **Optimized Distributed Collective Bandwidth**:
251
+ - Grouping scalar reductions into contiguous vector slices compresses distributed synchronization overhead into a single collective kernel launch per pass.
252
+
253
+ ---
254
+
255
+ ## [1.3.0] - 2026-09-23
256
+
257
+ ### Added
258
+ - **Fused Single-Pass Kernel with Post-Step EMA (`fused_single_pass`, `ema_decay`)**:
259
+ - Introduced `_lumina_v2_fused_single_pass_kernel` coupled with an asynchronous CTA micro-kernel `_lumina_v2_post_step_ema_kernel[(1,)]`.
260
+ - When `fused_single_pass=True`, avoids two-pass global reductions by leveraging running Exponential Moving Average (EMA) estimates of `cautious_scale` ($1 / \bar{M}$) and `bound_scale` ($\tanh(r) / r$).
261
+ - Unifies gradient filtering, momentum accumulation, variance dispersion, cautious masking, radial bounding, decoupled weight decay, stochastic rounding, and block partial reductions into a **single unified VRAM-resident pass**, reducing global GPU memory bandwidth traffic per optimizer step.
262
+ - **Hybrid Master Weight Precision Architecture (`master_weights`)**:
263
+ - Added multi-tier master weight flexibility via `master_weights: Union[bool, str] = False`:
264
+ - `"none"` / `False` *(Default)*: Pure master-free low-precision training leveraging on-chip bitwise stochastic rounding directly on native half-precision weights.
265
+ - `"semi"` / `"hybrid"` / `"half"`: 32-bit FP32 master parameters (`state["master_param"]`) coupled with 16-bit low-memory optimizer states (`exp_avg`, `exp_avg_sq` in BF16/FP16), saving ~50% optimizer state VRAM compared to traditional FP32 setups while maintaining full FP32 weight accumulation accuracy.
266
+ - `"full"` / `"fp32"` / `"masterweight"`: Traditional full FP32 master parameters with full FP32 optimizer states for standard baseline comparisons.
267
+ - **In-Kernel In-Flight NaN / Inf Gradient Sanitization**:
268
+ - Embedded register-level defensive sanitization across all Triton GPU kernels: `is_finite_g = (g == g) & (g < 1e38) & (g > -1e38); g = tl.where(is_finite_g, g, 0.0)`.
269
+ - Replaces non-finite gradients and subnormal corruptions with zero in registers before they can contaminate momentum (`exp_avg`) or variance (`exp_avg_sq`) state buffers.
270
+ - **Low-Precision State Buffer Serialization (`_store_state_buffer`)**:
271
+ - Added a dedicated Triton state serialization routine supporting FP32, BF16, and FP16 state representations with optional bitwise stochastic rounding applied directly to momentum buffers.
272
+ - **Multi-Device & Cross-Accelerator Hardware Context Isolation (`_get_device_context`)**:
273
+ - Unified device stream scoping supporting NVIDIA CUDA (`torch.cuda`), Intel XPU (`torch.xpu`), and modern PyTorch 2.x unified accelerator interfaces (`torch.accelerator.device`), eliminating multi-GPU stream hijacking and cross-device race conditions.
274
+ - **Strict Determinism Auto-Routing**:
275
+ - Automatically queries `torch.are_deterministic_algorithms_enabled()`. When enabled under `execution="auto"`, dynamically routes execution to C++ vectorized `foreach` to preserve bitwise cross-run reproducibility, circumventing non-deterministic floating-point hardware atomic reordering.
276
+ - **Granular Fault Recovery with State Guarding (`completed_indices`)**:
277
+ - Implemented per-parameter completion tracking in `_dispatch_triton`. If a kernel encounters an unexpected runtime fault, completed parameters are preserved while uncompleted ones fall back cleanly to `foreach`.
278
+ - Added `_single_step_v2_apply_param` and `_single_step_v1_apply_param` to safely finalize parameter updates if Pass 1 had already committed state transitions, strictly preventing double-update state corruption.
279
+
280
+ ### Changed
281
+ - **Per-Parameter Persistent Scratch Tensors (Elimination of Global Slicing)**:
282
+ - Replaced the global dynamic slice allocator (`self._scratch_tensors[dev][:count]`) with isolated per-parameter persistent state allocations (`state["scratch"] = torch.zeros((5,), device=p.device, dtype=torch.float32)`).
283
+ - Memory pointers for `[mask_sum, u_sq_sum, p_sq_sum, cautious_scale, bound_scale]` remain completely invariant throughout the entire training lifecycle, preserving pointer stability for **CUDA Graph Capture** (`torch.accelerator.Graph`) and TorchInductor AOT compilation without memory aliasing.
284
+ - **Analytically Exact Nesterov Bias Correction ($\beta_1^{\text{step} + 1}$)**:
285
+ - Corrected first-moment bias correction from the classical Adam formulation ($1.0 - \beta_1^{\text{step}}$) to the exact analytical expectation of the Nesterov blended momentum: `bc1_nes = 1.0 - beta1^(step + 1)`. Removes residual momentum bias during early warmup iterations.
286
+ - **Adaptive CTA Tile Sizing and Dynamic Thread Warp Scaling**:
287
+ - Introduced hardware-aware CTA tile scaling based on parameter volume: parameters with $\ge 2,097,152$ elements (2M+) dynamically configure `BLOCK_SIZE = 2048` and `num_warps = 8` (versus `BLOCK_SIZE = 1024`, `num_warps = 4` for smaller tensors).
288
+ - Cuts total thread-block grid counts and L2 cache atomic addition pressure by 50% on large MLP, Attention Projection, and Vocabulary layers.
289
+ - **Hardware-Native Tanh Instruction Dispatch (`tl.math.tanh` / `tl.tanh`)**:
290
+ - Upgraded `_triton_tanh_fast` to dynamically inspect compiler capabilities, binding directly to native PTX Special Function Unit (SFU) instructions (`tanh.approx.f32`) on modern Triton compilers while retaining analytical sigmoid fallbacks for legacy environments.
291
+ - **PRNG Decorrelation between Momentum and Parameter Updates**:
292
+ - Separated RNG seed streams using bitwise alternating mask shifts: `seed_p = (seed + 0x55555555) & 0x7FFFFFFF`, mathematically eliminating cross-coupling noise correlation between state buffer rounding and parameter update rounding.
293
+ - **Bumped specification version metadata to 1.3.0** across `luminav.py`.
294
+
295
+ ### Fixed
296
+ - **Floating-Point Cancellation in Radial Bounding ($r \to 0$)**:
297
+ - Fixed precision jitter and roundoff degradation near zero by adding an asymptotic Taylor-series guard across Triton and PyTorch bounding engines: `torch.where(r < 1e-4, 1.0, tanh(r) / r)`.
298
+ - **Artificial Denominator Dilation in Dispersion Scale ($\sigma$)**:
299
+ - Replaced additive epsilon inflation (`sigma + 1e-6`) with a non-distorting floor clamp (`tl.maximum(sigma, 1e-12)`), preventing artificial dampening of subtle gradient signals.
300
+ - **Singular Zero-Division in Parameter Norm Clamping**:
301
+ - Replaced `norm_p + 1e-6` with `torch.clamp_min(norm_p, 1.0)` / `tl.maximum(norm_p, 1.0)`, maintaining numerical stability even when parameters are initialized near zero.
302
+ - **Transient Memory Pool Spikes during PyTorch Fallback Stochastic Rounding**:
303
+ - Refactored `_sr_update` to stream stochastic noise generation in bounded chunks of 1,048,576 elements (~4 MB chunk size), eliminating multi-hundred megabyte temporary int32 tensor spikes during fallback execution on large parameter matrices.
304
+ - **Non-Contiguous Buffer Safety & Strided Memory Copy**:
305
+ - Added strict contiguous layout verification for parameters, states, and master weights with in-place synchronization (`p.copy_(p_contig)`), guarded by a one-time developer warning.
306
+
307
+ ### Performance & Engine Refactoring
308
+ - **~50% Global Memory Bandwidth Reduction via Fused Single-Pass Mode**:
309
+ - By unifying parameter loading, gradient evaluation, state transformation, and bounding into one GPU pass under `fused_single_pass=True`, VRAM read/write cycles are halved for cautious and radially bounded steps.
310
+ - **Sub-Microsecond L2 Hardware Atomic Reduction Architecture**:
311
+ - Formally documented the hybrid Intra-Block Tree Reduction + Inter-Block Hardware Atomic pattern. With `BLOCK_SIZE=2048`, a 10M-parameter tensor issues only 4,883 atomic additions directly into GPU L2 cache crossbars, executing in < 0.5 µs and strictly outperforming multi-tier cascaded reduction micro-kernels by eliminating 3–5 µs driver dispatch bubbles.
312
+ - **Static Buffer Address Invariance for CUDA Graphs & TorchInductor AOT**:
313
+ - Persistent 5-slot state scratch buffers eliminate all dynamic slicing and memory reallocations during optimization loops, preserving pointer address invariance for CUDA Graph replay.
314
+
315
+ ---
316
+
317
+ ## [1.2.1] - 2026-09-17
318
+
319
+ ### Added
320
+ - **In-Kernel GPU Parameter Norm Reduction (`p_sq_sum_ptr`)**: Fused parameter Euclidean norm squared $\sum(p_i^2)$ calculation directly into Pass 1 Triton reduction kernels (`_lumina_v2_pass1_kernel` and `_lumina_v1_pass1_kernel`). Pass 2 and Pass 3 now load and resolve `norm_p` (`tl.sqrt(tl.maximum(p_sq_sum, 0.0))`) entirely inside GPU SRAM/registers without host intervention.
321
+ - **Stateless CPU Integer Multiplicative PRNG Hashing**: Replaced dynamic GPU RNG tensor allocation with a pure CPU integer hash using Knuth's 32-bit golden ratio constant: `(step * 0x9E3779B9 + i * 10007) & 0x7FFFFFFF`. Generates uncorrelated 31-bit integer seeds per layer with zero GPU kernel launch overhead and zero PCIe host-device latency.
322
+ - **Isolated Generator Cache for Fallback Engine (`self._generators`)**: Added an internal per-device `torch.Generator` cache, ensuring stochastic rounding in `_sr_update` operates in complete isolation from PyTorch's default CUDA generator state.
323
+
324
+ ### Changed
325
+ - **Consolidated Scratch Buffer Zeroing**: Removed redundant per-parameter slice zeroing inside parameter loops. Buffer zeroing is now batched into a single consolidated `scratch.zero_()` call per step inside `_get_scratch_buffer`.
326
+ - **Scratch Buffer Stride Expansion**:
327
+ - `buffer=2`: Expanded stride from 2×N to 3×N elements per parameter to host `[mask_sum, u_sq_sum, p_sq_sum]`.
328
+ - `buffer=1`: Expanded stride from 3×N to 4×N elements per parameter to host `[rms_sum, mask_sum, u_sq_sum, p_sq_sum]`.
329
+ - **Bumped specification version metadata to 1.2.1**.
330
+
331
+ ### Fixed
332
+ - **Host-Device Synchronization & Pipeline Stalls (`.item()` Bottleneck)**: Removed `norm_p = float(p_contig.float().norm().item())` and `seed = torch.randint(...).item()` from Triton step pipelines. Eliminated 200–400 synchronous PCIe roundtrips and GPU pipeline stalls per step on deep architectures (e.g., 28-layer Transformers), restoring full asynchronous GPU compute utilization.
333
+ - **CUDA Graphs & `torch.compile` Graph Breaks**: Fixed fatal graph capture crashes and TorchDynamo graph breaks caused by blocking host-device synchronization during radial bounding and seed generation. The optimizer step is now compatible with CUDA Graphs (`torch.accelerator.Graph`) and TorchInductor compilation.
334
+ - **Transient VRAM Allocator Thrashing**: Eliminated repeated temporary FP32 parameter cloning (`p_contig.float()`) during norm evaluation, preventing memory pool fragmentation in tight VRAM regimes.
335
+ - **Global RNG Pollution & Determinism Compliance**: Fixed global RNG state mutation in fallback stochastic rounding. Solved reproducibility drift in upstream Dropout, DataLoader shuffling, and RLHF reward modeling, ensuring strict adherence to PyTorch 2.10+ `torch.use_deterministic_algorithms(True)`.
336
+
337
+ ### Performance & Engine Refactoring
338
+ - **Batch Kernel Launch Overhead Elimination**: Removed up to 400–800 individual `cudaMemsetAsync` micro-calls per step previously triggered by per-slice `.zero_()` invocations on 1-element scratch tensors.
339
+ - **Single-Pass Bypass for Coordinate Bounding**: Streamlined `_triton_step_v2` to immediately bypass two-pass reductions and invoke `_lumina_v2_single_pass_kernel` when `cautious=False` and coordinate bounding (`bound_mode == 2`) is selected.
340
+
341
+ ---
342
+
343
+ ## [1.2.0] - 2026-09-17
344
+
345
+ ### Added
346
+ - **Automatic Dynamic FP16 Cliff Governor:** Built-in asymptotic boundary governor (`_triton_tanh_fast` in GPU register and PyTorch fallback) that automatically activates when `p.dtype == torch.float16`. Prevents unclipped steps from pushing parameters, activations, and residual streams into the IEEE-754 FP16 overflow cliff (> 65,504). Remains completely dormant (zero overhead) on `bfloat16` (TPU / Ampere+) and `float32`.
347
+ - **Smooth Asymptotic Parameter Bounding (`bound`, `bound_type`, `bound_ratio`):** Added non-intrusive step bounding with two mathematically continuous formulations:
348
+ - `"radial"`: Direction-Preserving Radial Squashing (`update * tanh(r) / r`) preserving gradient direction (Cosine Similarity = 1.000).
349
+ - `"coordinate"`: Dual-Hyperbolic Coordinate Squashing for fine-grained per-element damping.
350
+ - Configurable via constructor: `bound: bool = True`, `bound_type: str = "radial"`, `bound_ratio: float = 0.03`.
351
+
352
+ ### Changed
353
+ - **Increased Default `cautious_clamp_min` from `0.2` to `0.5`:** Enforces an exact power-of-two maximum energy scaling factor (2.00x, $2^1$). Eliminates floating-point mantissa roundoff distortion in IEEE-754 hardware.
354
+ - Bumped specification version metadata to `1.2.0`.
355
+
356
+ ### Fixed
357
+ * **FP16 Step Divergence & NaN Overflow at High Learning Rates:** Solved step divergence and subsequent NaN crashes when training deep 28-layer Transformer architectures (e.g., Qwen3-0.6B) in pure FP16 at nominal learning rates (`lr = 8e-4`).
358
+ * **Sparse Embedding & LM-Head Amplification:** Mitigated 5x over-amplification on sparse vocabulary gradients where active token counts are much smaller than total tensor parameters ($N \gg N_{\text{active}}$).
359
+ * **Verified stable 1,000-step pretraining from scratch on Qwen3-0.6B** (seq length 512, pure FP16, `lr = 8e-4`) on Nvidia Tesla T4 with zero gradient clipping and zero LR scheduler (loss dropped from 12.09 down to ~5.7 with zero NaNs).
360
+ * **Weight Decay Numerical Parity on Fallback Engine:** Resolved a silent precision-stagnation issue in `_single_step` and `_foreach_step`. Previously, decoupled weight decay (`p.mul_(1.0 - lr * weight_decay)`) was executed directly on native FP16/BF16 tensors prior to `_sr_update`. Due to IEEE-754 Round-to-Nearest (RTN) limits, `(1 - lr * weight_decay) ≈ 0.999936` rounded back to `1.0`, causing weight decay to freeze on half-precision parameters. Weight decay is now unified into FP32 registers inside `_sr_update` alongside stochastic rounding, matching the numerical behavior with the Triton kernel.
361
+ * **Fallback Foreach Indentation & Scope Alignment:** Corrected tensor iteration scope and indentation for `torch._foreach_add_` in non-SR fallback branches.
362
+ * **In-Place GPU Clamping in `_apply_bound`:** Replaced dynamic GPU scalar allocations (`torch.tensor(1.5e-4, device=p.device)`) with native `torch.clamp_min`, eliminating transient CUDA malloc spikes per update step.
363
+
364
+ ### Performance & Engine Refactoring
365
+ * **Unified Bound Execution Pipeline:** Added an immediate short-circuit guard (`if lr == 0.0: return update`) and streamlined the execution flow: intermediate updates are scaled in FP32 (`step_val = lr * u_float`), passed through radial/coordinate bounding, chained cleanly into the FP16 Cliff Governor, and returned in a single normalized step.
366
+
367
+ ---
368
+
369
+ ## [1.1.5] - 2026-09-15
370
+
371
+ ### Added
372
+ - **Native Hardware Capability Detection (`_supports_native_bf16`):** Added automatic device capability detection via `torch.cuda.is_bf16_supported()` to identify Ampere SM80+ architectures and streamline native execution paths.
373
+ - **Hugging Face Repository Reference:** Added official Hugging Face repository link in module docstrings.
374
+
375
+ ### Changed
376
+ - **Dynamic Epsilon Clamping for FP16:** Dynamically floors effective epsilon to `max(eps, 1e-4)` when parameters or states are in `torch.float16`. Prevents bias-corrected scale factor `c2` from underflowing into IEEE-754 subnormal/zero limits on step 1.
377
+ - Bumped specification version to `1.1.5`.
378
+
379
+ ### Fixed
380
+ - **FP16 Step-1 `NaN` from Epsilon Underflow:** Fixed `0.0 / 0.0 = NaN` division occurring on initial training steps when parameters receive zero gradients (e.g., unselected vocabulary embeddings, padding tokens, or dropout paths).
381
+ - **Second-Moment Variance Saturation (`+inf`):** Added safe upper-bound register clamping (`tl.minimum(v_new, 65000.0)` in Triton kernels and `.clamp_(0.0, 65000.0)` in PyTorch C++ foreach/single loops). Prevents unclipped initial pretraining gradient surges ($|g| > 256$) from overflowing the FP16 maximum dynamic range (65,504) into irreversible `+inf`.
382
+ - **Zero-Sigma Division Guard:** Injected explicit `sigma + 1e-6` denominator guards across all Triton kernel passes and PyTorch update steps to ensure numerical safety during cold-start iterations.
383
+ - Verified stable 100-step pretraining from scratch on `SmolLM-135M` using Wikimedia Wikipedia in pure FP16 on Nvidia Tesla T4 GPU with zero gradient clipping.
384
+
385
+ ---
386
+
387
+ ## [1.1.0] - 2026-09-11
388
+
389
+ ### Added
390
+ - **Bitwise Stochastic Rounding (SR):** Integrated on-chip PRNG integer bit-manipulation hash in Triton kernels and vectorized PyTorch fallback for native FP16/BF16 training without master weights.
391
+ - Unified Triton store routine introducing compile-time branching (`dtype_mode` / `use_sr: tl.constexpr`) with explicit safe casting for FP16 and BF16 pointers.
392
+ - Vectorized fallback improvements utilizing C++ multi-tensor operations when stochastic rounding is disabled or tensors are FP32.
393
+ - State buffer contiguity validation (`exp_avg_contig`, `exp_avg_sq_contig`) ensuring safe linear memory addressing before kernel dispatch.
394
+ - Declarative configuration file (`config.json`) defining hyperparameters and optimizer presets.
395
+
396
+ ### Changed
397
+ - Refactored PRNG integer arithmetic to strictly stay within signed 32-bit integer limits ($< 2^{31} - 1$), eliminating JIT compilation failures on newer Triton releases.
398
+ - State buffers (`exp_avg`, `exp_avg_sq`) now initialize with strict contiguous memory format.
399
+
400
+ ### Fixed
401
+ - Fixed `ValueError: Scalar out of range for type int32` and Triton LLVM compile errors during unsigned 32-bit bitmask operations.
402
+ - Resolved kernel fallback hangs and compilation overhead on CUDA devices.
403
+
404
+ ---
405
+
406
+ ## [1.0.2] - 2026-09-08
407
+
408
+ ### Added
409
+ - Official Apache-2.0 license notice and copyright headers.
410
+ - Standardized package exports (`__all__`, `__version__`, `__author__`, `__license__`).
411
+
412
+ ---
413
+
414
+ ## [1.0.1] - 2026-08-31
415
+
416
+ ### Fixed
417
+ - **Memory Synchronization Fix (`423d171`):** Fixed a critical bug where non-contiguous parameter tensors (e.g., transposed linear layers or sliced weights) were updated on temporary contiguous copies without reflecting changes back to model weights. Added `p.copy_(p_contig)` after Triton execution.
418
+
419
+ ---
420
+
421
+ ## [1.0.0] - 2026-08-30
422
+
423
+ ### Added
424
+ - Initial release of the LuminaV optimizer.
425
+ - **Zero Master-Weight Copies:** Direct parameter updates in native half-precision without allocating 4-byte FP32 master weights.
426
+ - **Hyperbolic Tangent (`tanh`) Bounding Envelope:** Projects normalized momentum ratio into `(-1.0, 1.0)` to eliminate exploding gradient shocks.
427
+ - **Directional Alignment Gate:** Cautious update masking that zeroes out momentum steps opposing mini-batch gradient directions.
428
+ - **Central Innovation Variance:** Noise dispersion tracking $(g_t - m_t)^2$ decoupling mini-batch turbulence from directional descent.
429
+ - Dual-buffer mode (`buffer=2`, LuminaV-2B) and single-buffer mode (`buffer=1`, LuminaV-1).
430
+ - Custom fused OpenAI Triton kernels with automated fallback execution.
Cargo.lock CHANGED
@@ -1,1218 +1,1218 @@
1
- # This file is automatically @generated by Cargo.
2
- # It is not intended for manual editing.
3
- version = 4
4
-
5
- [[package]]
6
- name = "ahash"
7
- version = "0.8.12"
8
- source = "registry+https://github.com/rust-lang/crates.io-index"
9
- checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
10
- dependencies = [
11
- "cfg-if",
12
- "const-random",
13
- "getrandom 0.3.4",
14
- "once_cell",
15
- "version_check",
16
- "zerocopy",
17
- ]
18
-
19
- [[package]]
20
- name = "alloc-no-stdlib"
21
- version = "3.0.0"
22
- source = "registry+https://github.com/rust-lang/crates.io-index"
23
- checksum = "c2fb6cfd47bf496ff64095c20eaba0c201404ee38714d4142fcfa1dc334fcc7a"
24
-
25
- [[package]]
26
- name = "alloc-stdlib"
27
- version = "0.3.0"
28
- source = "registry+https://github.com/rust-lang/crates.io-index"
29
- checksum = "0b5c1865780388bfa186411ab5f247819487fc4864c6e9c3106611fa347586e1"
30
- dependencies = [
31
- "alloc-no-stdlib",
32
- ]
33
-
34
- [[package]]
35
- name = "allocator-api2"
36
- version = "0.2.21"
37
- source = "registry+https://github.com/rust-lang/crates.io-index"
38
- checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
39
-
40
- [[package]]
41
- name = "android_system_properties"
42
- version = "0.1.6"
43
- source = "registry+https://github.com/rust-lang/crates.io-index"
44
- checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc"
45
- dependencies = [
46
- "libc",
47
- ]
48
-
49
- [[package]]
50
- name = "arrow-array"
51
- version = "60.1.0"
52
- source = "registry+https://github.com/rust-lang/crates.io-index"
53
- checksum = "0f3d22a41d3df78b71df0b685af219ae3a94188ab98d6fb29c958eb55c3247f4"
54
- dependencies = [
55
- "ahash",
56
- "arrow-buffer",
57
- "arrow-data",
58
- "arrow-schema",
59
- "chrono",
60
- "half",
61
- "hashbrown",
62
- "num-complex",
63
- "num-integer",
64
- "num-traits",
65
- ]
66
-
67
- [[package]]
68
- name = "arrow-buffer"
69
- version = "60.1.0"
70
- source = "registry+https://github.com/rust-lang/crates.io-index"
71
- checksum = "7bd46a530bc1a8e763102fc36c1dc0f51f3ffa150c78e7f5658ccb54b1458ade"
72
- dependencies = [
73
- "bytes",
74
- "half",
75
- "num-bigint",
76
- "num-traits",
77
- ]
78
-
79
- [[package]]
80
- name = "arrow-cmp"
81
- version = "60.1.0"
82
- source = "registry+https://github.com/rust-lang/crates.io-index"
83
- checksum = "1913e57a64fbf2c4502fa6d54b547d6a1ae59e3146cbc15321d47468c937abd1"
84
- dependencies = [
85
- "arrow-array",
86
- "arrow-buffer",
87
- "arrow-schema",
88
- ]
89
-
90
- [[package]]
91
- name = "arrow-data"
92
- version = "60.1.0"
93
- source = "registry+https://github.com/rust-lang/crates.io-index"
94
- checksum = "415b7fbc4cf56b080bb5677a4ae74211b5c79e571ed37a1d547633ec07f3b43b"
95
- dependencies = [
96
- "arrow-buffer",
97
- "arrow-schema",
98
- "half",
99
- "num-integer",
100
- "num-traits",
101
- ]
102
-
103
- [[package]]
104
- name = "arrow-ipc"
105
- version = "60.1.0"
106
- source = "registry+https://github.com/rust-lang/crates.io-index"
107
- checksum = "45f3e766bccf5a0cb57a5de52a4cba450dea5171547c2cb5939b486d08887cba"
108
- dependencies = [
109
- "arrow-array",
110
- "arrow-buffer",
111
- "arrow-data",
112
- "arrow-schema",
113
- "arrow-select",
114
- "flatbuffers",
115
- ]
116
-
117
- [[package]]
118
- name = "arrow-schema"
119
- version = "60.1.0"
120
- source = "registry+https://github.com/rust-lang/crates.io-index"
121
- checksum = "de2c8e714b466404cdc1189e89f892426efea614d938e16c15518c340cd43ba3"
122
-
123
- [[package]]
124
- name = "arrow-select"
125
- version = "60.1.0"
126
- source = "registry+https://github.com/rust-lang/crates.io-index"
127
- checksum = "a5af3f6656c5f2c6944f93d9a357a7f204c93d5f349385a44cf46c4a26fbcf30"
128
- dependencies = [
129
- "ahash",
130
- "arrow-array",
131
- "arrow-buffer",
132
- "arrow-cmp",
133
- "arrow-data",
134
- "arrow-schema",
135
- "num-traits",
136
- ]
137
-
138
- [[package]]
139
- name = "ash"
140
- version = "0.38.0+1.3.281"
141
- source = "registry+https://github.com/rust-lang/crates.io-index"
142
- checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f"
143
- dependencies = [
144
- "libloading 0.8.9",
145
- ]
146
-
147
- [[package]]
148
- name = "autocfg"
149
- version = "1.5.1"
150
- source = "registry+https://github.com/rust-lang/crates.io-index"
151
- checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
152
-
153
- [[package]]
154
- name = "base64"
155
- version = "0.23.1"
156
- source = "registry+https://github.com/rust-lang/crates.io-index"
157
- checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5"
158
-
159
- [[package]]
160
- name = "bitflags"
161
- version = "2.13.2"
162
- source = "registry+https://github.com/rust-lang/crates.io-index"
163
- checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06"
164
-
165
- [[package]]
166
- name = "brotli"
167
- version = "9.0.0"
168
- source = "registry+https://github.com/rust-lang/crates.io-index"
169
- checksum = "f8b851b75c23ca7873623d612fe49bd1989aeb03d08fb9432187eb253d3d4c6b"
170
- dependencies = [
171
- "alloc-no-stdlib",
172
- "alloc-stdlib",
173
- "brotli-decompressor",
174
- ]
175
-
176
- [[package]]
177
- name = "brotli-decompressor"
178
- version = "6.0.1"
179
- source = "registry+https://github.com/rust-lang/crates.io-index"
180
- checksum = "941cd9bd4ddab83cb46fa5a2d428f1c857b24ac78cb876cf7beb710840934bd7"
181
- dependencies = [
182
- "alloc-no-stdlib",
183
- "alloc-stdlib",
184
- ]
185
-
186
- [[package]]
187
- name = "bumpalo"
188
- version = "3.20.3"
189
- source = "registry+https://github.com/rust-lang/crates.io-index"
190
- checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
191
-
192
- [[package]]
193
- name = "bytes"
194
- version = "1.12.1"
195
- source = "registry+https://github.com/rust-lang/crates.io-index"
196
- checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
197
-
198
- [[package]]
199
- name = "cc"
200
- version = "1.8.0"
201
- source = "registry+https://github.com/rust-lang/crates.io-index"
202
- checksum = "6651c9ed80effdc7db0ff72512157f901af5e3549e341e24b1dd4887d836d838"
203
- dependencies = [
204
- "find-msvc-tools",
205
- "jobserver",
206
- "libc",
207
- "shlex",
208
- ]
209
-
210
- [[package]]
211
- name = "cfg-if"
212
- version = "1.0.5"
213
- source = "registry+https://github.com/rust-lang/crates.io-index"
214
- checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600"
215
-
216
- [[package]]
217
- name = "chrono"
218
- version = "0.4.45"
219
- source = "registry+https://github.com/rust-lang/crates.io-index"
220
- checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327"
221
- dependencies = [
222
- "iana-time-zone",
223
- "num-traits",
224
- "windows-link",
225
- ]
226
-
227
- [[package]]
228
- name = "const-random"
229
- version = "0.1.18"
230
- source = "registry+https://github.com/rust-lang/crates.io-index"
231
- checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359"
232
- dependencies = [
233
- "const-random-macro",
234
- ]
235
-
236
- [[package]]
237
- name = "const-random-macro"
238
- version = "0.1.16"
239
- source = "registry+https://github.com/rust-lang/crates.io-index"
240
- checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e"
241
- dependencies = [
242
- "getrandom 0.2.17",
243
- "once_cell",
244
- "tiny-keccak",
245
- ]
246
-
247
- [[package]]
248
- name = "core-foundation-sys"
249
- version = "0.8.7"
250
- source = "registry+https://github.com/rust-lang/crates.io-index"
251
- checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
252
-
253
- [[package]]
254
- name = "crc32fast"
255
- version = "1.5.2"
256
- source = "registry+https://github.com/rust-lang/crates.io-index"
257
- checksum = "01a7799fd6b852db0e61728dde9a204c423b44d689dbd432522543614b490e78"
258
- dependencies = [
259
- "cfg-if",
260
- ]
261
-
262
- [[package]]
263
- name = "crossbeam-deque"
264
- version = "0.8.8"
265
- source = "registry+https://github.com/rust-lang/crates.io-index"
266
- checksum = "622f3fc73690be383c7214310406f28a90e6edeadc3cea882f9d71e495b9711a"
267
- dependencies = [
268
- "crossbeam-epoch",
269
- "crossbeam-utils",
270
- ]
271
-
272
- [[package]]
273
- name = "crossbeam-epoch"
274
- version = "0.9.21"
275
- source = "registry+https://github.com/rust-lang/crates.io-index"
276
- checksum = "dc74980687109a3b14c72fd458107bf0baa1da1a1a805e178d15501ba9b86d9d"
277
- dependencies = [
278
- "crossbeam-utils",
279
- ]
280
-
281
- [[package]]
282
- name = "crossbeam-utils"
283
- version = "0.8.23"
284
- source = "registry+https://github.com/rust-lang/crates.io-index"
285
- checksum = "a31eee39dddec8330830986fcd7625edb5a24ec90ea038215273bbc3adb08ac6"
286
-
287
- [[package]]
288
- name = "crunchy"
289
- version = "0.2.4"
290
- source = "registry+https://github.com/rust-lang/crates.io-index"
291
- checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
292
-
293
- [[package]]
294
- name = "either"
295
- version = "1.19.0"
296
- source = "registry+https://github.com/rust-lang/crates.io-index"
297
- checksum = "0e9c71c2167ca323c882b99918929403426e2373ea17242ff5653e0d5e1058be"
298
-
299
- [[package]]
300
- name = "equivalent"
301
- version = "1.0.3"
302
- source = "registry+https://github.com/rust-lang/crates.io-index"
303
- checksum = "00d174d5400e5e8fd687ad1049e2f578285fa914201b1af7e8b112a4546bd826"
304
-
305
- [[package]]
306
- name = "find-msvc-tools"
307
- version = "0.1.14"
308
- source = "registry+https://github.com/rust-lang/crates.io-index"
309
- checksum = "aedcfb3409746eddb02b9e19ebda1c3394f759a152e48ee875a0844d1b955484"
310
-
311
- [[package]]
312
- name = "flatbuffers"
313
- version = "25.12.19"
314
- source = "registry+https://github.com/rust-lang/crates.io-index"
315
- checksum = "35f6839d7b3b98adde531effaf34f0c2badc6f4735d26fe74709d8e513a96ef3"
316
- dependencies = [
317
- "bitflags",
318
- "rustc_version",
319
- ]
320
-
321
- [[package]]
322
- name = "flate2"
323
- version = "1.1.10"
324
- source = "registry+https://github.com/rust-lang/crates.io-index"
325
- checksum = "6e634e2e0ebac1ee034020da1ca582e17ffe4e0f5e985823721e168928136dcb"
326
- dependencies = [
327
- "zlib-rs",
328
- ]
329
-
330
- [[package]]
331
- name = "foldhash"
332
- version = "0.2.0"
333
- source = "registry+https://github.com/rust-lang/crates.io-index"
334
- checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
335
-
336
- [[package]]
337
- name = "futures-core"
338
- version = "0.3.34"
339
- source = "registry+https://github.com/rust-lang/crates.io-index"
340
- checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e"
341
-
342
- [[package]]
343
- name = "futures-task"
344
- version = "0.3.34"
345
- source = "registry+https://github.com/rust-lang/crates.io-index"
346
- checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd"
347
-
348
- [[package]]
349
- name = "futures-util"
350
- version = "0.3.34"
351
- source = "registry+https://github.com/rust-lang/crates.io-index"
352
- checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc"
353
- dependencies = [
354
- "futures-core",
355
- "futures-task",
356
- "pin-project-lite",
357
- "slab",
358
- ]
359
-
360
- [[package]]
361
- name = "getrandom"
362
- version = "0.2.17"
363
- source = "registry+https://github.com/rust-lang/crates.io-index"
364
- checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
365
- dependencies = [
366
- "cfg-if",
367
- "libc",
368
- "wasi",
369
- ]
370
-
371
- [[package]]
372
- name = "getrandom"
373
- version = "0.3.4"
374
- source = "registry+https://github.com/rust-lang/crates.io-index"
375
- checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
376
- dependencies = [
377
- "cfg-if",
378
- "libc",
379
- "r-efi 5.3.0",
380
- "wasip2",
381
- ]
382
-
383
- [[package]]
384
- name = "getrandom"
385
- version = "0.4.3"
386
- source = "registry+https://github.com/rust-lang/crates.io-index"
387
- checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
388
- dependencies = [
389
- "cfg-if",
390
- "libc",
391
- "r-efi 6.0.0",
392
- ]
393
-
394
- [[package]]
395
- name = "half"
396
- version = "2.7.1"
397
- source = "registry+https://github.com/rust-lang/crates.io-index"
398
- checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b"
399
- dependencies = [
400
- "cfg-if",
401
- "crunchy",
402
- "num-traits",
403
- "zerocopy",
404
- ]
405
-
406
- [[package]]
407
- name = "hashbrown"
408
- version = "0.17.1"
409
- source = "registry+https://github.com/rust-lang/crates.io-index"
410
- checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
411
- dependencies = [
412
- "allocator-api2",
413
- "equivalent",
414
- "foldhash",
415
- ]
416
-
417
- [[package]]
418
- name = "heck"
419
- version = "0.5.0"
420
- source = "registry+https://github.com/rust-lang/crates.io-index"
421
- checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
422
-
423
- [[package]]
424
- name = "iana-time-zone"
425
- version = "0.1.65"
426
- source = "registry+https://github.com/rust-lang/crates.io-index"
427
- checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470"
428
- dependencies = [
429
- "android_system_properties",
430
- "core-foundation-sys",
431
- "iana-time-zone-haiku",
432
- "js-sys",
433
- "log",
434
- "wasm-bindgen",
435
- "windows-core",
436
- ]
437
-
438
- [[package]]
439
- name = "iana-time-zone-haiku"
440
- version = "0.1.2"
441
- source = "registry+https://github.com/rust-lang/crates.io-index"
442
- checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
443
- dependencies = [
444
- "cc",
445
- ]
446
-
447
- [[package]]
448
- name = "itoa"
449
- version = "1.0.18"
450
- source = "registry+https://github.com/rust-lang/crates.io-index"
451
- checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
452
-
453
- [[package]]
454
- name = "jobserver"
455
- version = "0.1.35"
456
- source = "registry+https://github.com/rust-lang/crates.io-index"
457
- checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3"
458
- dependencies = [
459
- "getrandom 0.4.3",
460
- "libc",
461
- ]
462
-
463
- [[package]]
464
- name = "js-sys"
465
- version = "0.3.106"
466
- source = "registry+https://github.com/rust-lang/crates.io-index"
467
- checksum = "7883d941dae510fb2d978fc3fe018c71c9e2892fd38854de3e8b92c2e5ad9cc5"
468
- dependencies = [
469
- "cfg-if",
470
- "futures-util",
471
- "wasm-bindgen",
472
- ]
473
-
474
- [[package]]
475
- name = "libc"
476
- version = "0.2.191"
477
- source = "registry+https://github.com/rust-lang/crates.io-index"
478
- checksum = "65cd87c9a6d60536580b316ad985726104730a5151c99845f2490428ef91240a"
479
-
480
- [[package]]
481
- name = "libloading"
482
- version = "0.8.9"
483
- source = "registry+https://github.com/rust-lang/crates.io-index"
484
- checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55"
485
- dependencies = [
486
- "cfg-if",
487
- "windows-link",
488
- ]
489
-
490
- [[package]]
491
- name = "libloading"
492
- version = "0.9.0"
493
- source = "registry+https://github.com/rust-lang/crates.io-index"
494
- checksum = "754ca22de805bb5744484a5b151a9e1a8e837d5dc232c2d7d8c2e3492edc8b60"
495
- dependencies = [
496
- "cfg-if",
497
- "windows-link",
498
- ]
499
-
500
- [[package]]
501
- name = "libm"
502
- version = "0.2.16"
503
- source = "registry+https://github.com/rust-lang/crates.io-index"
504
- checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981"
505
-
506
- [[package]]
507
- name = "lock_api"
508
- version = "0.4.14"
509
- source = "registry+https://github.com/rust-lang/crates.io-index"
510
- checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
511
- dependencies = [
512
- "scopeguard",
513
- ]
514
-
515
- [[package]]
516
- name = "log"
517
- version = "0.4.34"
518
- source = "registry+https://github.com/rust-lang/crates.io-index"
519
- checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6"
520
-
521
- [[package]]
522
- name = "luminamoon-dataloader"
523
- version = "1.1.0-post.3"
524
- dependencies = [
525
- "arrow-array",
526
- "arrow-ipc",
527
- "arrow-schema",
528
- "crc32fast",
529
- "hashbrown",
530
- "parking_lot",
531
- "parquet",
532
- "pyo3",
533
- "rayon",
534
- "serde",
535
- "serde_json",
536
- "thiserror",
537
- "xxhash-rust",
538
- ]
539
-
540
- [[package]]
541
- name = "luminamoon-optimizer"
542
- version = "1.1.0-post.3"
543
- dependencies = [
544
- "luminamoon-dataloader",
545
- "luminav-core",
546
- "luminav-dispatch",
547
- "luminav-simd",
548
- "luminav-triton-aot",
549
- "luminav-vulkan",
550
- "pyo3",
551
- "rayon",
552
- "thiserror",
553
- ]
554
-
555
- [[package]]
556
- name = "luminav-core"
557
- version = "1.1.0-post.3"
558
- dependencies = [
559
- "thiserror",
560
- ]
561
-
562
- [[package]]
563
- name = "luminav-dispatch"
564
- version = "1.1.0-post.3"
565
- dependencies = [
566
- "luminav-core",
567
- "luminav-simd",
568
- "luminav-triton-aot",
569
- "luminav-vulkan",
570
- "thiserror",
571
- ]
572
-
573
- [[package]]
574
- name = "luminav-simd"
575
- version = "1.1.0-post.3"
576
- dependencies = [
577
- "luminav-core",
578
- "rayon",
579
- ]
580
-
581
- [[package]]
582
- name = "luminav-triton-aot"
583
- version = "1.1.0-post.3"
584
- dependencies = [
585
- "libloading 0.9.0",
586
- "luminav-core",
587
- "serde",
588
- "serde_json",
589
- "thiserror",
590
- ]
591
-
592
- [[package]]
593
- name = "luminav-vulkan"
594
- version = "1.1.0-post.3"
595
- dependencies = [
596
- "ash",
597
- "luminav-core",
598
- "thiserror",
599
- ]
600
-
601
- [[package]]
602
- name = "lz4_flex"
603
- version = "0.14.0"
604
- source = "registry+https://github.com/rust-lang/crates.io-index"
605
- checksum = "ecbdfe44b1bd960b68170b417450a628c43f7cf56bb3c5317e61cb230ee7f226"
606
- dependencies = [
607
- "twox-hash",
608
- ]
609
-
610
- [[package]]
611
- name = "memchr"
612
- version = "2.8.3"
613
- source = "registry+https://github.com/rust-lang/crates.io-index"
614
- checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
615
-
616
- [[package]]
617
- name = "num-bigint"
618
- version = "0.5.1"
619
- source = "registry+https://github.com/rust-lang/crates.io-index"
620
- checksum = "93e7820bc0a80a0238e650327316f929ba18d5be054b647490a3a6a339f3e7c0"
621
- dependencies = [
622
- "num-integer",
623
- "num-traits",
624
- ]
625
-
626
- [[package]]
627
- name = "num-complex"
628
- version = "0.4.6"
629
- source = "registry+https://github.com/rust-lang/crates.io-index"
630
- checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495"
631
- dependencies = [
632
- "num-traits",
633
- ]
634
-
635
- [[package]]
636
- name = "num-integer"
637
- version = "0.1.47"
638
- source = "registry+https://github.com/rust-lang/crates.io-index"
639
- checksum = "7ce2d95d4b3734dc35aa2f45e1aa22cd416814592a4f9d9205e11affd5b8e10b"
640
- dependencies = [
641
- "num-traits",
642
- ]
643
-
644
- [[package]]
645
- name = "num-traits"
646
- version = "0.2.19"
647
- source = "registry+https://github.com/rust-lang/crates.io-index"
648
- checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
649
- dependencies = [
650
- "autocfg",
651
- "libm",
652
- ]
653
-
654
- [[package]]
655
- name = "once_cell"
656
- version = "1.21.4"
657
- source = "registry+https://github.com/rust-lang/crates.io-index"
658
- checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
659
-
660
- [[package]]
661
- name = "parking_lot"
662
- version = "0.12.5"
663
- source = "registry+https://github.com/rust-lang/crates.io-index"
664
- checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
665
- dependencies = [
666
- "lock_api",
667
- "parking_lot_core",
668
- ]
669
-
670
- [[package]]
671
- name = "parking_lot_core"
672
- version = "0.9.12"
673
- source = "registry+https://github.com/rust-lang/crates.io-index"
674
- checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
675
- dependencies = [
676
- "cfg-if",
677
- "libc",
678
- "redox_syscall",
679
- "smallvec",
680
- "windows-link",
681
- ]
682
-
683
- [[package]]
684
- name = "parquet"
685
- version = "60.1.0"
686
- source = "registry+https://github.com/rust-lang/crates.io-index"
687
- checksum = "806eaa0f1c0436dcb43bee8e6a82bf2efff90521a60172f8281cb9ef577b43a4"
688
- dependencies = [
689
- "ahash",
690
- "arrow-array",
691
- "arrow-buffer",
692
- "arrow-data",
693
- "arrow-ipc",
694
- "arrow-schema",
695
- "arrow-select",
696
- "base64",
697
- "brotli",
698
- "bytes",
699
- "chrono",
700
- "flate2",
701
- "half",
702
- "hashbrown",
703
- "lz4_flex",
704
- "num-bigint",
705
- "num-integer",
706
- "num-traits",
707
- "seq-macro",
708
- "simdutf8",
709
- "snap",
710
- "twox-hash",
711
- "zstd",
712
- ]
713
-
714
- [[package]]
715
- name = "pin-project-lite"
716
- version = "0.2.17"
717
- source = "registry+https://github.com/rust-lang/crates.io-index"
718
- checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
719
-
720
- [[package]]
721
- name = "pkg-config"
722
- version = "0.3.34"
723
- source = "registry+https://github.com/rust-lang/crates.io-index"
724
- checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548"
725
-
726
- [[package]]
727
- name = "portable-atomic"
728
- version = "1.15.0"
729
- source = "registry+https://github.com/rust-lang/crates.io-index"
730
- checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85"
731
-
732
- [[package]]
733
- name = "proc-macro2"
734
- version = "1.0.107"
735
- source = "registry+https://github.com/rust-lang/crates.io-index"
736
- checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
737
- dependencies = [
738
- "unicode-ident",
739
- ]
740
-
741
- [[package]]
742
- name = "pyo3"
743
- version = "0.30.1"
744
- source = "registry+https://github.com/rust-lang/crates.io-index"
745
- checksum = "229ca13b8a15bb0d1487b81ab975bfcd97513416499a8312011b6036e42e0de4"
746
- dependencies = [
747
- "libc",
748
- "once_cell",
749
- "portable-atomic",
750
- "pyo3-build-config",
751
- "pyo3-ffi",
752
- "pyo3-macros",
753
- ]
754
-
755
- [[package]]
756
- name = "pyo3-build-config"
757
- version = "0.30.1"
758
- source = "registry+https://github.com/rust-lang/crates.io-index"
759
- checksum = "70130e2b6dd1ee589a8788026a429561ca5c40fa7a2fa7914c2282eddae24700"
760
- dependencies = [
761
- "target-lexicon",
762
- ]
763
-
764
- [[package]]
765
- name = "pyo3-ffi"
766
- version = "0.30.1"
767
- source = "registry+https://github.com/rust-lang/crates.io-index"
768
- checksum = "5e7ff73c555c286d212d4ce7e5fbda845a973c4f61bd5f160f5f2c3c2a04d1c4"
769
- dependencies = [
770
- "libc",
771
- "pyo3-build-config",
772
- ]
773
-
774
- [[package]]
775
- name = "pyo3-macros"
776
- version = "0.30.1"
777
- source = "registry+https://github.com/rust-lang/crates.io-index"
778
- checksum = "c46d2e5862c7bb8207e3b639f713dda3c499933444784154c11d013c5a691b60"
779
- dependencies = [
780
- "proc-macro2",
781
- "pyo3-macros-backend",
782
- "quote",
783
- "syn 3.0.9",
784
- ]
785
-
786
- [[package]]
787
- name = "pyo3-macros-backend"
788
- version = "0.30.1"
789
- source = "registry+https://github.com/rust-lang/crates.io-index"
790
- checksum = "38b89f066f2ae72a2aef52d1537ca2fcd104440ebfadee06e97aa90738a25b9b"
791
- dependencies = [
792
- "heck",
793
- "proc-macro2",
794
- "quote",
795
- "syn 3.0.9",
796
- ]
797
-
798
- [[package]]
799
- name = "quote"
800
- version = "1.0.47"
801
- source = "registry+https://github.com/rust-lang/crates.io-index"
802
- checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
803
- dependencies = [
804
- "proc-macro2",
805
- ]
806
-
807
- [[package]]
808
- name = "r-efi"
809
- version = "5.3.0"
810
- source = "registry+https://github.com/rust-lang/crates.io-index"
811
- checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
812
-
813
- [[package]]
814
- name = "r-efi"
815
- version = "6.0.0"
816
- source = "registry+https://github.com/rust-lang/crates.io-index"
817
- checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
818
-
819
- [[package]]
820
- name = "rayon"
821
- version = "1.12.0"
822
- source = "registry+https://github.com/rust-lang/crates.io-index"
823
- checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d"
824
- dependencies = [
825
- "either",
826
- "rayon-core",
827
- ]
828
-
829
- [[package]]
830
- name = "rayon-core"
831
- version = "1.13.0"
832
- source = "registry+https://github.com/rust-lang/crates.io-index"
833
- checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
834
- dependencies = [
835
- "crossbeam-deque",
836
- "crossbeam-utils",
837
- ]
838
-
839
- [[package]]
840
- name = "redox_syscall"
841
- version = "0.5.18"
842
- source = "registry+https://github.com/rust-lang/crates.io-index"
843
- checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
844
- dependencies = [
845
- "bitflags",
846
- ]
847
-
848
- [[package]]
849
- name = "rustc_version"
850
- version = "0.4.1"
851
- source = "registry+https://github.com/rust-lang/crates.io-index"
852
- checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
853
- dependencies = [
854
- "semver",
855
- ]
856
-
857
- [[package]]
858
- name = "rustversion"
859
- version = "1.0.23"
860
- source = "registry+https://github.com/rust-lang/crates.io-index"
861
- checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
862
-
863
- [[package]]
864
- name = "scopeguard"
865
- version = "1.2.0"
866
- source = "registry+https://github.com/rust-lang/crates.io-index"
867
- checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
868
-
869
- [[package]]
870
- name = "semver"
871
- version = "1.0.28"
872
- source = "registry+https://github.com/rust-lang/crates.io-index"
873
- checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
874
-
875
- [[package]]
876
- name = "seq-macro"
877
- version = "0.3.6"
878
- source = "registry+https://github.com/rust-lang/crates.io-index"
879
- checksum = "1bc711410fbe7399f390ca1c3b60ad0f53f80e95c5eb935e52268a0e2cd49acc"
880
-
881
- [[package]]
882
- name = "serde"
883
- version = "1.0.229"
884
- source = "registry+https://github.com/rust-lang/crates.io-index"
885
- checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
886
- dependencies = [
887
- "serde_core",
888
- "serde_derive",
889
- ]
890
-
891
- [[package]]
892
- name = "serde_core"
893
- version = "1.0.229"
894
- source = "registry+https://github.com/rust-lang/crates.io-index"
895
- checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
896
- dependencies = [
897
- "serde_derive",
898
- ]
899
-
900
- [[package]]
901
- name = "serde_derive"
902
- version = "1.0.229"
903
- source = "registry+https://github.com/rust-lang/crates.io-index"
904
- checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
905
- dependencies = [
906
- "proc-macro2",
907
- "quote",
908
- "syn 3.0.9",
909
- ]
910
-
911
- [[package]]
912
- name = "serde_json"
913
- version = "1.0.154"
914
- source = "registry+https://github.com/rust-lang/crates.io-index"
915
- checksum = "e7e9cc8b1b85264074fbcc02a88680c4096b1e47df8f739dceb03bf482f04bd6"
916
- dependencies = [
917
- "itoa",
918
- "memchr",
919
- "serde",
920
- "serde_core",
921
- "zmij",
922
- ]
923
-
924
- [[package]]
925
- name = "shlex"
926
- version = "2.0.1"
927
- source = "registry+https://github.com/rust-lang/crates.io-index"
928
- checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
929
-
930
- [[package]]
931
- name = "simdutf8"
932
- version = "0.1.5"
933
- source = "registry+https://github.com/rust-lang/crates.io-index"
934
- checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
935
-
936
- [[package]]
937
- name = "slab"
938
- version = "0.4.12"
939
- source = "registry+https://github.com/rust-lang/crates.io-index"
940
- checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
941
-
942
- [[package]]
943
- name = "smallvec"
944
- version = "1.16.3"
945
- source = "registry+https://github.com/rust-lang/crates.io-index"
946
- checksum = "5b3dc8af474f516a851ff4bd12db780f948b9250ad37211e4eec0bccea54e01b"
947
-
948
- [[package]]
949
- name = "snap"
950
- version = "1.1.2"
951
- source = "registry+https://github.com/rust-lang/crates.io-index"
952
- checksum = "199905e6153d6405f9728fe44daace35f8f837bbf830bb6e85fbd5828709a886"
953
-
954
- [[package]]
955
- name = "syn"
956
- version = "2.0.119"
957
- source = "registry+https://github.com/rust-lang/crates.io-index"
958
- checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
959
- dependencies = [
960
- "proc-macro2",
961
- "quote",
962
- "unicode-ident",
963
- ]
964
-
965
- [[package]]
966
- name = "syn"
967
- version = "3.0.9"
968
- source = "registry+https://github.com/rust-lang/crates.io-index"
969
- checksum = "d78c8dee4c7bf0e14673097256fed6142ce9d3b85a408189d07482442145823b"
970
- dependencies = [
971
- "proc-macro2",
972
- "quote",
973
- "unicode-ident",
974
- ]
975
-
976
- [[package]]
977
- name = "target-lexicon"
978
- version = "0.13.5"
979
- source = "registry+https://github.com/rust-lang/crates.io-index"
980
- checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
981
-
982
- [[package]]
983
- name = "thiserror"
984
- version = "2.0.21"
985
- source = "registry+https://github.com/rust-lang/crates.io-index"
986
- checksum = "09e52cb86a36cede5cb101bf8908837b3e4c6e5e59fe7fd85c23fb56200d189e"
987
- dependencies = [
988
- "thiserror-impl",
989
- ]
990
-
991
- [[package]]
992
- name = "thiserror-impl"
993
- version = "2.0.21"
994
- source = "registry+https://github.com/rust-lang/crates.io-index"
995
- checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524"
996
- dependencies = [
997
- "proc-macro2",
998
- "quote",
999
- "syn 3.0.9",
1000
- ]
1001
-
1002
- [[package]]
1003
- name = "tiny-keccak"
1004
- version = "2.0.2"
1005
- source = "registry+https://github.com/rust-lang/crates.io-index"
1006
- checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
1007
- dependencies = [
1008
- "crunchy",
1009
- ]
1010
-
1011
- [[package]]
1012
- name = "twox-hash"
1013
- version = "2.1.5"
1014
- source = "registry+https://github.com/rust-lang/crates.io-index"
1015
- checksum = "86a801b3cea342a06d468c8710662aa29e5e05e4f5c0d62f00bbb7f2ad7941c2"
1016
-
1017
- [[package]]
1018
- name = "unicode-ident"
1019
- version = "1.0.27"
1020
- source = "registry+https://github.com/rust-lang/crates.io-index"
1021
- checksum = "a2c754d6c33795a1c324727428e5a7dedb5b06195f9890bdbcba760d3e246563"
1022
-
1023
- [[package]]
1024
- name = "version_check"
1025
- version = "0.9.5"
1026
- source = "registry+https://github.com/rust-lang/crates.io-index"
1027
- checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
1028
-
1029
- [[package]]
1030
- name = "wasi"
1031
- version = "0.11.1+wasi-snapshot-preview1"
1032
- source = "registry+https://github.com/rust-lang/crates.io-index"
1033
- checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
1034
-
1035
- [[package]]
1036
- name = "wasip2"
1037
- version = "1.0.4+wasi-0.2.12"
1038
- source = "registry+https://github.com/rust-lang/crates.io-index"
1039
- checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487"
1040
- dependencies = [
1041
- "wit-bindgen",
1042
- ]
1043
-
1044
- [[package]]
1045
- name = "wasm-bindgen"
1046
- version = "0.2.129"
1047
- source = "registry+https://github.com/rust-lang/crates.io-index"
1048
- checksum = "9bb54f33acc68fd454578d9820b0bde1a1a3d17aa17bb7b6595806d02886d409"
1049
- dependencies = [
1050
- "cfg-if",
1051
- "once_cell",
1052
- "rustversion",
1053
- "wasm-bindgen-macro",
1054
- "wasm-bindgen-shared",
1055
- ]
1056
-
1057
- [[package]]
1058
- name = "wasm-bindgen-macro"
1059
- version = "0.2.129"
1060
- source = "registry+https://github.com/rust-lang/crates.io-index"
1061
- checksum = "2e29d0c35b16e224a7eeb5cd2d25e3e1968fbd65604117b44d3b789d00ee8535"
1062
- dependencies = [
1063
- "quote",
1064
- "wasm-bindgen-macro-support",
1065
- ]
1066
-
1067
- [[package]]
1068
- name = "wasm-bindgen-macro-support"
1069
- version = "0.2.129"
1070
- source = "registry+https://github.com/rust-lang/crates.io-index"
1071
- checksum = "6f501a8bc3719dba86ef8ae4728879c08001bea749eb1333ac5b91e040e2a6b7"
1072
- dependencies = [
1073
- "bumpalo",
1074
- "proc-macro2",
1075
- "quote",
1076
- "syn 3.0.9",
1077
- "wasm-bindgen-shared",
1078
- ]
1079
-
1080
- [[package]]
1081
- name = "wasm-bindgen-shared"
1082
- version = "0.2.129"
1083
- source = "registry+https://github.com/rust-lang/crates.io-index"
1084
- checksum = "23f0c9c52aa7cd7d77769a4cfe2a9adb1b331f489a41d912ce14513d5ab995c6"
1085
- dependencies = [
1086
- "unicode-ident",
1087
- ]
1088
-
1089
- [[package]]
1090
- name = "windows-core"
1091
- version = "0.62.2"
1092
- source = "registry+https://github.com/rust-lang/crates.io-index"
1093
- checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
1094
- dependencies = [
1095
- "windows-implement",
1096
- "windows-interface",
1097
- "windows-link",
1098
- "windows-result",
1099
- "windows-strings",
1100
- ]
1101
-
1102
- [[package]]
1103
- name = "windows-implement"
1104
- version = "0.60.2"
1105
- source = "registry+https://github.com/rust-lang/crates.io-index"
1106
- checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
1107
- dependencies = [
1108
- "proc-macro2",
1109
- "quote",
1110
- "syn 2.0.119",
1111
- ]
1112
-
1113
- [[package]]
1114
- name = "windows-interface"
1115
- version = "0.59.3"
1116
- source = "registry+https://github.com/rust-lang/crates.io-index"
1117
- checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
1118
- dependencies = [
1119
- "proc-macro2",
1120
- "quote",
1121
- "syn 2.0.119",
1122
- ]
1123
-
1124
- [[package]]
1125
- name = "windows-link"
1126
- version = "0.2.1"
1127
- source = "registry+https://github.com/rust-lang/crates.io-index"
1128
- checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
1129
-
1130
- [[package]]
1131
- name = "windows-result"
1132
- version = "0.4.1"
1133
- source = "registry+https://github.com/rust-lang/crates.io-index"
1134
- checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
1135
- dependencies = [
1136
- "windows-link",
1137
- ]
1138
-
1139
- [[package]]
1140
- name = "windows-strings"
1141
- version = "0.5.1"
1142
- source = "registry+https://github.com/rust-lang/crates.io-index"
1143
- checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
1144
- dependencies = [
1145
- "windows-link",
1146
- ]
1147
-
1148
- [[package]]
1149
- name = "wit-bindgen"
1150
- version = "0.57.1"
1151
- source = "registry+https://github.com/rust-lang/crates.io-index"
1152
- checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
1153
-
1154
- [[package]]
1155
- name = "xxhash-rust"
1156
- version = "0.8.19"
1157
- source = "registry+https://github.com/rust-lang/crates.io-index"
1158
- checksum = "550a2b930b62486a393c52d5c3b84bff264b28aa437ed64694d31e93b1757af7"
1159
-
1160
- [[package]]
1161
- name = "zerocopy"
1162
- version = "0.8.63"
1163
- source = "registry+https://github.com/rust-lang/crates.io-index"
1164
- checksum = "e5fe1f8f1b06191a00962174c61aa5005e0bb391a6d80d07e24d115c01a92ed8"
1165
- dependencies = [
1166
- "zerocopy-derive",
1167
- ]
1168
-
1169
- [[package]]
1170
- name = "zerocopy-derive"
1171
- version = "0.8.63"
1172
- source = "registry+https://github.com/rust-lang/crates.io-index"
1173
- checksum = "863ad3ac83293fb4d740aedbfdc9240dd8d1a50c1099acd76ce80ce7c7230c7f"
1174
- dependencies = [
1175
- "proc-macro2",
1176
- "quote",
1177
- "syn 2.0.119",
1178
- ]
1179
-
1180
- [[package]]
1181
- name = "zlib-rs"
1182
- version = "0.6.8"
1183
- source = "registry+https://github.com/rust-lang/crates.io-index"
1184
- checksum = "b268e58e7c693d7c271f93ffc4ba3b380412554231c85bf61ca7af91042a4112"
1185
-
1186
- [[package]]
1187
- name = "zmij"
1188
- version = "1.0.23"
1189
- source = "registry+https://github.com/rust-lang/crates.io-index"
1190
- checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
1191
-
1192
- [[package]]
1193
- name = "zstd"
1194
- version = "0.14.3"
1195
- source = "registry+https://github.com/rust-lang/crates.io-index"
1196
- checksum = "db3ae9bf90af6e40197bef767eeefc46ad6397420fe873830bd85e5fbbfb3c70"
1197
- dependencies = [
1198
- "zstd-safe",
1199
- ]
1200
-
1201
- [[package]]
1202
- name = "zstd-safe"
1203
- version = "8.2.0"
1204
- source = "registry+https://github.com/rust-lang/crates.io-index"
1205
- checksum = "ded520f16b29e1a9b771c722221b2b0a0398c2316d9bb3c42b458a5414d7434b"
1206
- dependencies = [
1207
- "zstd-sys",
1208
- ]
1209
-
1210
- [[package]]
1211
- name = "zstd-sys"
1212
- version = "2.1.2+zstd.1.5.7"
1213
- source = "registry+https://github.com/rust-lang/crates.io-index"
1214
- checksum = "481ada9c543b2323cbb7bbdbb5249177fbf0188ed959ed2c15e4b22421c84ed3"
1215
- dependencies = [
1216
- "cc",
1217
- "pkg-config",
1218
- ]
 
1
+ # This file is automatically @generated by Cargo.
2
+ # It is not intended for manual editing.
3
+ version = 4
4
+
5
+ [[package]]
6
+ name = "ahash"
7
+ version = "0.8.12"
8
+ source = "registry+https://github.com/rust-lang/crates.io-index"
9
+ checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
10
+ dependencies = [
11
+ "cfg-if",
12
+ "const-random",
13
+ "getrandom 0.3.4",
14
+ "once_cell",
15
+ "version_check",
16
+ "zerocopy",
17
+ ]
18
+
19
+ [[package]]
20
+ name = "alloc-no-stdlib"
21
+ version = "3.0.0"
22
+ source = "registry+https://github.com/rust-lang/crates.io-index"
23
+ checksum = "c2fb6cfd47bf496ff64095c20eaba0c201404ee38714d4142fcfa1dc334fcc7a"
24
+
25
+ [[package]]
26
+ name = "alloc-stdlib"
27
+ version = "0.3.0"
28
+ source = "registry+https://github.com/rust-lang/crates.io-index"
29
+ checksum = "0b5c1865780388bfa186411ab5f247819487fc4864c6e9c3106611fa347586e1"
30
+ dependencies = [
31
+ "alloc-no-stdlib",
32
+ ]
33
+
34
+ [[package]]
35
+ name = "allocator-api2"
36
+ version = "0.2.21"
37
+ source = "registry+https://github.com/rust-lang/crates.io-index"
38
+ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
39
+
40
+ [[package]]
41
+ name = "android_system_properties"
42
+ version = "0.1.6"
43
+ source = "registry+https://github.com/rust-lang/crates.io-index"
44
+ checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc"
45
+ dependencies = [
46
+ "libc",
47
+ ]
48
+
49
+ [[package]]
50
+ name = "arrow-array"
51
+ version = "60.1.0"
52
+ source = "registry+https://github.com/rust-lang/crates.io-index"
53
+ checksum = "0f3d22a41d3df78b71df0b685af219ae3a94188ab98d6fb29c958eb55c3247f4"
54
+ dependencies = [
55
+ "ahash",
56
+ "arrow-buffer",
57
+ "arrow-data",
58
+ "arrow-schema",
59
+ "chrono",
60
+ "half",
61
+ "hashbrown",
62
+ "num-complex",
63
+ "num-integer",
64
+ "num-traits",
65
+ ]
66
+
67
+ [[package]]
68
+ name = "arrow-buffer"
69
+ version = "60.1.0"
70
+ source = "registry+https://github.com/rust-lang/crates.io-index"
71
+ checksum = "7bd46a530bc1a8e763102fc36c1dc0f51f3ffa150c78e7f5658ccb54b1458ade"
72
+ dependencies = [
73
+ "bytes",
74
+ "half",
75
+ "num-bigint",
76
+ "num-traits",
77
+ ]
78
+
79
+ [[package]]
80
+ name = "arrow-cmp"
81
+ version = "60.1.0"
82
+ source = "registry+https://github.com/rust-lang/crates.io-index"
83
+ checksum = "1913e57a64fbf2c4502fa6d54b547d6a1ae59e3146cbc15321d47468c937abd1"
84
+ dependencies = [
85
+ "arrow-array",
86
+ "arrow-buffer",
87
+ "arrow-schema",
88
+ ]
89
+
90
+ [[package]]
91
+ name = "arrow-data"
92
+ version = "60.1.0"
93
+ source = "registry+https://github.com/rust-lang/crates.io-index"
94
+ checksum = "415b7fbc4cf56b080bb5677a4ae74211b5c79e571ed37a1d547633ec07f3b43b"
95
+ dependencies = [
96
+ "arrow-buffer",
97
+ "arrow-schema",
98
+ "half",
99
+ "num-integer",
100
+ "num-traits",
101
+ ]
102
+
103
+ [[package]]
104
+ name = "arrow-ipc"
105
+ version = "60.1.0"
106
+ source = "registry+https://github.com/rust-lang/crates.io-index"
107
+ checksum = "45f3e766bccf5a0cb57a5de52a4cba450dea5171547c2cb5939b486d08887cba"
108
+ dependencies = [
109
+ "arrow-array",
110
+ "arrow-buffer",
111
+ "arrow-data",
112
+ "arrow-schema",
113
+ "arrow-select",
114
+ "flatbuffers",
115
+ ]
116
+
117
+ [[package]]
118
+ name = "arrow-schema"
119
+ version = "60.1.0"
120
+ source = "registry+https://github.com/rust-lang/crates.io-index"
121
+ checksum = "de2c8e714b466404cdc1189e89f892426efea614d938e16c15518c340cd43ba3"
122
+
123
+ [[package]]
124
+ name = "arrow-select"
125
+ version = "60.1.0"
126
+ source = "registry+https://github.com/rust-lang/crates.io-index"
127
+ checksum = "a5af3f6656c5f2c6944f93d9a357a7f204c93d5f349385a44cf46c4a26fbcf30"
128
+ dependencies = [
129
+ "ahash",
130
+ "arrow-array",
131
+ "arrow-buffer",
132
+ "arrow-cmp",
133
+ "arrow-data",
134
+ "arrow-schema",
135
+ "num-traits",
136
+ ]
137
+
138
+ [[package]]
139
+ name = "ash"
140
+ version = "0.38.0+1.3.281"
141
+ source = "registry+https://github.com/rust-lang/crates.io-index"
142
+ checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f"
143
+ dependencies = [
144
+ "libloading 0.8.9",
145
+ ]
146
+
147
+ [[package]]
148
+ name = "autocfg"
149
+ version = "1.5.1"
150
+ source = "registry+https://github.com/rust-lang/crates.io-index"
151
+ checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
152
+
153
+ [[package]]
154
+ name = "base64"
155
+ version = "0.23.1"
156
+ source = "registry+https://github.com/rust-lang/crates.io-index"
157
+ checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5"
158
+
159
+ [[package]]
160
+ name = "bitflags"
161
+ version = "2.13.2"
162
+ source = "registry+https://github.com/rust-lang/crates.io-index"
163
+ checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06"
164
+
165
+ [[package]]
166
+ name = "brotli"
167
+ version = "9.0.0"
168
+ source = "registry+https://github.com/rust-lang/crates.io-index"
169
+ checksum = "f8b851b75c23ca7873623d612fe49bd1989aeb03d08fb9432187eb253d3d4c6b"
170
+ dependencies = [
171
+ "alloc-no-stdlib",
172
+ "alloc-stdlib",
173
+ "brotli-decompressor",
174
+ ]
175
+
176
+ [[package]]
177
+ name = "brotli-decompressor"
178
+ version = "6.0.1"
179
+ source = "registry+https://github.com/rust-lang/crates.io-index"
180
+ checksum = "941cd9bd4ddab83cb46fa5a2d428f1c857b24ac78cb876cf7beb710840934bd7"
181
+ dependencies = [
182
+ "alloc-no-stdlib",
183
+ "alloc-stdlib",
184
+ ]
185
+
186
+ [[package]]
187
+ name = "bumpalo"
188
+ version = "3.20.3"
189
+ source = "registry+https://github.com/rust-lang/crates.io-index"
190
+ checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
191
+
192
+ [[package]]
193
+ name = "bytes"
194
+ version = "1.12.1"
195
+ source = "registry+https://github.com/rust-lang/crates.io-index"
196
+ checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
197
+
198
+ [[package]]
199
+ name = "cc"
200
+ version = "1.8.0"
201
+ source = "registry+https://github.com/rust-lang/crates.io-index"
202
+ checksum = "6651c9ed80effdc7db0ff72512157f901af5e3549e341e24b1dd4887d836d838"
203
+ dependencies = [
204
+ "find-msvc-tools",
205
+ "jobserver",
206
+ "libc",
207
+ "shlex",
208
+ ]
209
+
210
+ [[package]]
211
+ name = "cfg-if"
212
+ version = "1.0.5"
213
+ source = "registry+https://github.com/rust-lang/crates.io-index"
214
+ checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600"
215
+
216
+ [[package]]
217
+ name = "chrono"
218
+ version = "0.4.45"
219
+ source = "registry+https://github.com/rust-lang/crates.io-index"
220
+ checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327"
221
+ dependencies = [
222
+ "iana-time-zone",
223
+ "num-traits",
224
+ "windows-link",
225
+ ]
226
+
227
+ [[package]]
228
+ name = "const-random"
229
+ version = "0.1.18"
230
+ source = "registry+https://github.com/rust-lang/crates.io-index"
231
+ checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359"
232
+ dependencies = [
233
+ "const-random-macro",
234
+ ]
235
+
236
+ [[package]]
237
+ name = "const-random-macro"
238
+ version = "0.1.16"
239
+ source = "registry+https://github.com/rust-lang/crates.io-index"
240
+ checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e"
241
+ dependencies = [
242
+ "getrandom 0.2.17",
243
+ "once_cell",
244
+ "tiny-keccak",
245
+ ]
246
+
247
+ [[package]]
248
+ name = "core-foundation-sys"
249
+ version = "0.8.7"
250
+ source = "registry+https://github.com/rust-lang/crates.io-index"
251
+ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
252
+
253
+ [[package]]
254
+ name = "crc32fast"
255
+ version = "1.5.2"
256
+ source = "registry+https://github.com/rust-lang/crates.io-index"
257
+ checksum = "01a7799fd6b852db0e61728dde9a204c423b44d689dbd432522543614b490e78"
258
+ dependencies = [
259
+ "cfg-if",
260
+ ]
261
+
262
+ [[package]]
263
+ name = "crossbeam-deque"
264
+ version = "0.8.8"
265
+ source = "registry+https://github.com/rust-lang/crates.io-index"
266
+ checksum = "622f3fc73690be383c7214310406f28a90e6edeadc3cea882f9d71e495b9711a"
267
+ dependencies = [
268
+ "crossbeam-epoch",
269
+ "crossbeam-utils",
270
+ ]
271
+
272
+ [[package]]
273
+ name = "crossbeam-epoch"
274
+ version = "0.9.21"
275
+ source = "registry+https://github.com/rust-lang/crates.io-index"
276
+ checksum = "dc74980687109a3b14c72fd458107bf0baa1da1a1a805e178d15501ba9b86d9d"
277
+ dependencies = [
278
+ "crossbeam-utils",
279
+ ]
280
+
281
+ [[package]]
282
+ name = "crossbeam-utils"
283
+ version = "0.8.23"
284
+ source = "registry+https://github.com/rust-lang/crates.io-index"
285
+ checksum = "a31eee39dddec8330830986fcd7625edb5a24ec90ea038215273bbc3adb08ac6"
286
+
287
+ [[package]]
288
+ name = "crunchy"
289
+ version = "0.2.4"
290
+ source = "registry+https://github.com/rust-lang/crates.io-index"
291
+ checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
292
+
293
+ [[package]]
294
+ name = "either"
295
+ version = "1.19.0"
296
+ source = "registry+https://github.com/rust-lang/crates.io-index"
297
+ checksum = "0e9c71c2167ca323c882b99918929403426e2373ea17242ff5653e0d5e1058be"
298
+
299
+ [[package]]
300
+ name = "equivalent"
301
+ version = "1.0.3"
302
+ source = "registry+https://github.com/rust-lang/crates.io-index"
303
+ checksum = "00d174d5400e5e8fd687ad1049e2f578285fa914201b1af7e8b112a4546bd826"
304
+
305
+ [[package]]
306
+ name = "find-msvc-tools"
307
+ version = "0.1.14"
308
+ source = "registry+https://github.com/rust-lang/crates.io-index"
309
+ checksum = "aedcfb3409746eddb02b9e19ebda1c3394f759a152e48ee875a0844d1b955484"
310
+
311
+ [[package]]
312
+ name = "flatbuffers"
313
+ version = "25.12.19"
314
+ source = "registry+https://github.com/rust-lang/crates.io-index"
315
+ checksum = "35f6839d7b3b98adde531effaf34f0c2badc6f4735d26fe74709d8e513a96ef3"
316
+ dependencies = [
317
+ "bitflags",
318
+ "rustc_version",
319
+ ]
320
+
321
+ [[package]]
322
+ name = "flate2"
323
+ version = "1.1.10"
324
+ source = "registry+https://github.com/rust-lang/crates.io-index"
325
+ checksum = "6e634e2e0ebac1ee034020da1ca582e17ffe4e0f5e985823721e168928136dcb"
326
+ dependencies = [
327
+ "zlib-rs",
328
+ ]
329
+
330
+ [[package]]
331
+ name = "foldhash"
332
+ version = "0.2.0"
333
+ source = "registry+https://github.com/rust-lang/crates.io-index"
334
+ checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
335
+
336
+ [[package]]
337
+ name = "futures-core"
338
+ version = "0.3.34"
339
+ source = "registry+https://github.com/rust-lang/crates.io-index"
340
+ checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e"
341
+
342
+ [[package]]
343
+ name = "futures-task"
344
+ version = "0.3.34"
345
+ source = "registry+https://github.com/rust-lang/crates.io-index"
346
+ checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd"
347
+
348
+ [[package]]
349
+ name = "futures-util"
350
+ version = "0.3.34"
351
+ source = "registry+https://github.com/rust-lang/crates.io-index"
352
+ checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc"
353
+ dependencies = [
354
+ "futures-core",
355
+ "futures-task",
356
+ "pin-project-lite",
357
+ "slab",
358
+ ]
359
+
360
+ [[package]]
361
+ name = "getrandom"
362
+ version = "0.2.17"
363
+ source = "registry+https://github.com/rust-lang/crates.io-index"
364
+ checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
365
+ dependencies = [
366
+ "cfg-if",
367
+ "libc",
368
+ "wasi",
369
+ ]
370
+
371
+ [[package]]
372
+ name = "getrandom"
373
+ version = "0.3.4"
374
+ source = "registry+https://github.com/rust-lang/crates.io-index"
375
+ checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
376
+ dependencies = [
377
+ "cfg-if",
378
+ "libc",
379
+ "r-efi 5.3.0",
380
+ "wasip2",
381
+ ]
382
+
383
+ [[package]]
384
+ name = "getrandom"
385
+ version = "0.4.3"
386
+ source = "registry+https://github.com/rust-lang/crates.io-index"
387
+ checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
388
+ dependencies = [
389
+ "cfg-if",
390
+ "libc",
391
+ "r-efi 6.0.0",
392
+ ]
393
+
394
+ [[package]]
395
+ name = "half"
396
+ version = "2.7.1"
397
+ source = "registry+https://github.com/rust-lang/crates.io-index"
398
+ checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b"
399
+ dependencies = [
400
+ "cfg-if",
401
+ "crunchy",
402
+ "num-traits",
403
+ "zerocopy",
404
+ ]
405
+
406
+ [[package]]
407
+ name = "hashbrown"
408
+ version = "0.17.1"
409
+ source = "registry+https://github.com/rust-lang/crates.io-index"
410
+ checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
411
+ dependencies = [
412
+ "allocator-api2",
413
+ "equivalent",
414
+ "foldhash",
415
+ ]
416
+
417
+ [[package]]
418
+ name = "heck"
419
+ version = "0.5.0"
420
+ source = "registry+https://github.com/rust-lang/crates.io-index"
421
+ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
422
+
423
+ [[package]]
424
+ name = "iana-time-zone"
425
+ version = "0.1.65"
426
+ source = "registry+https://github.com/rust-lang/crates.io-index"
427
+ checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470"
428
+ dependencies = [
429
+ "android_system_properties",
430
+ "core-foundation-sys",
431
+ "iana-time-zone-haiku",
432
+ "js-sys",
433
+ "log",
434
+ "wasm-bindgen",
435
+ "windows-core",
436
+ ]
437
+
438
+ [[package]]
439
+ name = "iana-time-zone-haiku"
440
+ version = "0.1.2"
441
+ source = "registry+https://github.com/rust-lang/crates.io-index"
442
+ checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
443
+ dependencies = [
444
+ "cc",
445
+ ]
446
+
447
+ [[package]]
448
+ name = "itoa"
449
+ version = "1.0.18"
450
+ source = "registry+https://github.com/rust-lang/crates.io-index"
451
+ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
452
+
453
+ [[package]]
454
+ name = "jobserver"
455
+ version = "0.1.35"
456
+ source = "registry+https://github.com/rust-lang/crates.io-index"
457
+ checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3"
458
+ dependencies = [
459
+ "getrandom 0.4.3",
460
+ "libc",
461
+ ]
462
+
463
+ [[package]]
464
+ name = "js-sys"
465
+ version = "0.3.106"
466
+ source = "registry+https://github.com/rust-lang/crates.io-index"
467
+ checksum = "7883d941dae510fb2d978fc3fe018c71c9e2892fd38854de3e8b92c2e5ad9cc5"
468
+ dependencies = [
469
+ "cfg-if",
470
+ "futures-util",
471
+ "wasm-bindgen",
472
+ ]
473
+
474
+ [[package]]
475
+ name = "libc"
476
+ version = "0.2.191"
477
+ source = "registry+https://github.com/rust-lang/crates.io-index"
478
+ checksum = "65cd87c9a6d60536580b316ad985726104730a5151c99845f2490428ef91240a"
479
+
480
+ [[package]]
481
+ name = "libloading"
482
+ version = "0.8.9"
483
+ source = "registry+https://github.com/rust-lang/crates.io-index"
484
+ checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55"
485
+ dependencies = [
486
+ "cfg-if",
487
+ "windows-link",
488
+ ]
489
+
490
+ [[package]]
491
+ name = "libloading"
492
+ version = "0.9.0"
493
+ source = "registry+https://github.com/rust-lang/crates.io-index"
494
+ checksum = "754ca22de805bb5744484a5b151a9e1a8e837d5dc232c2d7d8c2e3492edc8b60"
495
+ dependencies = [
496
+ "cfg-if",
497
+ "windows-link",
498
+ ]
499
+
500
+ [[package]]
501
+ name = "libm"
502
+ version = "0.2.16"
503
+ source = "registry+https://github.com/rust-lang/crates.io-index"
504
+ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981"
505
+
506
+ [[package]]
507
+ name = "lock_api"
508
+ version = "0.4.14"
509
+ source = "registry+https://github.com/rust-lang/crates.io-index"
510
+ checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
511
+ dependencies = [
512
+ "scopeguard",
513
+ ]
514
+
515
+ [[package]]
516
+ name = "log"
517
+ version = "0.4.34"
518
+ source = "registry+https://github.com/rust-lang/crates.io-index"
519
+ checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6"
520
+
521
+ [[package]]
522
+ name = "luminamoon-dataloader"
523
+ version = "1.1.0-post.3"
524
+ dependencies = [
525
+ "arrow-array",
526
+ "arrow-ipc",
527
+ "arrow-schema",
528
+ "crc32fast",
529
+ "hashbrown",
530
+ "parking_lot",
531
+ "parquet",
532
+ "pyo3",
533
+ "rayon",
534
+ "serde",
535
+ "serde_json",
536
+ "thiserror",
537
+ "xxhash-rust",
538
+ ]
539
+
540
+ [[package]]
541
+ name = "luminamoon-optimizer"
542
+ version = "1.1.0-post.3"
543
+ dependencies = [
544
+ "luminamoon-dataloader",
545
+ "luminav-core",
546
+ "luminav-dispatch",
547
+ "luminav-simd",
548
+ "luminav-triton-aot",
549
+ "luminav-vulkan",
550
+ "pyo3",
551
+ "rayon",
552
+ "thiserror",
553
+ ]
554
+
555
+ [[package]]
556
+ name = "luminav-core"
557
+ version = "1.1.0-post.3"
558
+ dependencies = [
559
+ "thiserror",
560
+ ]
561
+
562
+ [[package]]
563
+ name = "luminav-dispatch"
564
+ version = "1.1.0-post.3"
565
+ dependencies = [
566
+ "luminav-core",
567
+ "luminav-simd",
568
+ "luminav-triton-aot",
569
+ "luminav-vulkan",
570
+ "thiserror",
571
+ ]
572
+
573
+ [[package]]
574
+ name = "luminav-simd"
575
+ version = "1.1.0-post.3"
576
+ dependencies = [
577
+ "luminav-core",
578
+ "rayon",
579
+ ]
580
+
581
+ [[package]]
582
+ name = "luminav-triton-aot"
583
+ version = "1.1.0-post.3"
584
+ dependencies = [
585
+ "libloading 0.9.0",
586
+ "luminav-core",
587
+ "serde",
588
+ "serde_json",
589
+ "thiserror",
590
+ ]
591
+
592
+ [[package]]
593
+ name = "luminav-vulkan"
594
+ version = "1.1.0-post.3"
595
+ dependencies = [
596
+ "ash",
597
+ "luminav-core",
598
+ "thiserror",
599
+ ]
600
+
601
+ [[package]]
602
+ name = "lz4_flex"
603
+ version = "0.14.0"
604
+ source = "registry+https://github.com/rust-lang/crates.io-index"
605
+ checksum = "ecbdfe44b1bd960b68170b417450a628c43f7cf56bb3c5317e61cb230ee7f226"
606
+ dependencies = [
607
+ "twox-hash",
608
+ ]
609
+
610
+ [[package]]
611
+ name = "memchr"
612
+ version = "2.8.3"
613
+ source = "registry+https://github.com/rust-lang/crates.io-index"
614
+ checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
615
+
616
+ [[package]]
617
+ name = "num-bigint"
618
+ version = "0.5.1"
619
+ source = "registry+https://github.com/rust-lang/crates.io-index"
620
+ checksum = "93e7820bc0a80a0238e650327316f929ba18d5be054b647490a3a6a339f3e7c0"
621
+ dependencies = [
622
+ "num-integer",
623
+ "num-traits",
624
+ ]
625
+
626
+ [[package]]
627
+ name = "num-complex"
628
+ version = "0.4.6"
629
+ source = "registry+https://github.com/rust-lang/crates.io-index"
630
+ checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495"
631
+ dependencies = [
632
+ "num-traits",
633
+ ]
634
+
635
+ [[package]]
636
+ name = "num-integer"
637
+ version = "0.1.47"
638
+ source = "registry+https://github.com/rust-lang/crates.io-index"
639
+ checksum = "7ce2d95d4b3734dc35aa2f45e1aa22cd416814592a4f9d9205e11affd5b8e10b"
640
+ dependencies = [
641
+ "num-traits",
642
+ ]
643
+
644
+ [[package]]
645
+ name = "num-traits"
646
+ version = "0.2.19"
647
+ source = "registry+https://github.com/rust-lang/crates.io-index"
648
+ checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
649
+ dependencies = [
650
+ "autocfg",
651
+ "libm",
652
+ ]
653
+
654
+ [[package]]
655
+ name = "once_cell"
656
+ version = "1.21.4"
657
+ source = "registry+https://github.com/rust-lang/crates.io-index"
658
+ checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
659
+
660
+ [[package]]
661
+ name = "parking_lot"
662
+ version = "0.12.5"
663
+ source = "registry+https://github.com/rust-lang/crates.io-index"
664
+ checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
665
+ dependencies = [
666
+ "lock_api",
667
+ "parking_lot_core",
668
+ ]
669
+
670
+ [[package]]
671
+ name = "parking_lot_core"
672
+ version = "0.9.12"
673
+ source = "registry+https://github.com/rust-lang/crates.io-index"
674
+ checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
675
+ dependencies = [
676
+ "cfg-if",
677
+ "libc",
678
+ "redox_syscall",
679
+ "smallvec",
680
+ "windows-link",
681
+ ]
682
+
683
+ [[package]]
684
+ name = "parquet"
685
+ version = "60.1.0"
686
+ source = "registry+https://github.com/rust-lang/crates.io-index"
687
+ checksum = "806eaa0f1c0436dcb43bee8e6a82bf2efff90521a60172f8281cb9ef577b43a4"
688
+ dependencies = [
689
+ "ahash",
690
+ "arrow-array",
691
+ "arrow-buffer",
692
+ "arrow-data",
693
+ "arrow-ipc",
694
+ "arrow-schema",
695
+ "arrow-select",
696
+ "base64",
697
+ "brotli",
698
+ "bytes",
699
+ "chrono",
700
+ "flate2",
701
+ "half",
702
+ "hashbrown",
703
+ "lz4_flex",
704
+ "num-bigint",
705
+ "num-integer",
706
+ "num-traits",
707
+ "seq-macro",
708
+ "simdutf8",
709
+ "snap",
710
+ "twox-hash",
711
+ "zstd",
712
+ ]
713
+
714
+ [[package]]
715
+ name = "pin-project-lite"
716
+ version = "0.2.17"
717
+ source = "registry+https://github.com/rust-lang/crates.io-index"
718
+ checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
719
+
720
+ [[package]]
721
+ name = "pkg-config"
722
+ version = "0.3.34"
723
+ source = "registry+https://github.com/rust-lang/crates.io-index"
724
+ checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548"
725
+
726
+ [[package]]
727
+ name = "portable-atomic"
728
+ version = "1.15.0"
729
+ source = "registry+https://github.com/rust-lang/crates.io-index"
730
+ checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85"
731
+
732
+ [[package]]
733
+ name = "proc-macro2"
734
+ version = "1.0.107"
735
+ source = "registry+https://github.com/rust-lang/crates.io-index"
736
+ checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
737
+ dependencies = [
738
+ "unicode-ident",
739
+ ]
740
+
741
+ [[package]]
742
+ name = "pyo3"
743
+ version = "0.30.1"
744
+ source = "registry+https://github.com/rust-lang/crates.io-index"
745
+ checksum = "229ca13b8a15bb0d1487b81ab975bfcd97513416499a8312011b6036e42e0de4"
746
+ dependencies = [
747
+ "libc",
748
+ "once_cell",
749
+ "portable-atomic",
750
+ "pyo3-build-config",
751
+ "pyo3-ffi",
752
+ "pyo3-macros",
753
+ ]
754
+
755
+ [[package]]
756
+ name = "pyo3-build-config"
757
+ version = "0.30.1"
758
+ source = "registry+https://github.com/rust-lang/crates.io-index"
759
+ checksum = "70130e2b6dd1ee589a8788026a429561ca5c40fa7a2fa7914c2282eddae24700"
760
+ dependencies = [
761
+ "target-lexicon",
762
+ ]
763
+
764
+ [[package]]
765
+ name = "pyo3-ffi"
766
+ version = "0.30.1"
767
+ source = "registry+https://github.com/rust-lang/crates.io-index"
768
+ checksum = "5e7ff73c555c286d212d4ce7e5fbda845a973c4f61bd5f160f5f2c3c2a04d1c4"
769
+ dependencies = [
770
+ "libc",
771
+ "pyo3-build-config",
772
+ ]
773
+
774
+ [[package]]
775
+ name = "pyo3-macros"
776
+ version = "0.30.1"
777
+ source = "registry+https://github.com/rust-lang/crates.io-index"
778
+ checksum = "c46d2e5862c7bb8207e3b639f713dda3c499933444784154c11d013c5a691b60"
779
+ dependencies = [
780
+ "proc-macro2",
781
+ "pyo3-macros-backend",
782
+ "quote",
783
+ "syn 3.0.9",
784
+ ]
785
+
786
+ [[package]]
787
+ name = "pyo3-macros-backend"
788
+ version = "0.30.1"
789
+ source = "registry+https://github.com/rust-lang/crates.io-index"
790
+ checksum = "38b89f066f2ae72a2aef52d1537ca2fcd104440ebfadee06e97aa90738a25b9b"
791
+ dependencies = [
792
+ "heck",
793
+ "proc-macro2",
794
+ "quote",
795
+ "syn 3.0.9",
796
+ ]
797
+
798
+ [[package]]
799
+ name = "quote"
800
+ version = "1.0.47"
801
+ source = "registry+https://github.com/rust-lang/crates.io-index"
802
+ checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
803
+ dependencies = [
804
+ "proc-macro2",
805
+ ]
806
+
807
+ [[package]]
808
+ name = "r-efi"
809
+ version = "5.3.0"
810
+ source = "registry+https://github.com/rust-lang/crates.io-index"
811
+ checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
812
+
813
+ [[package]]
814
+ name = "r-efi"
815
+ version = "6.0.0"
816
+ source = "registry+https://github.com/rust-lang/crates.io-index"
817
+ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
818
+
819
+ [[package]]
820
+ name = "rayon"
821
+ version = "1.12.0"
822
+ source = "registry+https://github.com/rust-lang/crates.io-index"
823
+ checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d"
824
+ dependencies = [
825
+ "either",
826
+ "rayon-core",
827
+ ]
828
+
829
+ [[package]]
830
+ name = "rayon-core"
831
+ version = "1.13.0"
832
+ source = "registry+https://github.com/rust-lang/crates.io-index"
833
+ checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
834
+ dependencies = [
835
+ "crossbeam-deque",
836
+ "crossbeam-utils",
837
+ ]
838
+
839
+ [[package]]
840
+ name = "redox_syscall"
841
+ version = "0.5.18"
842
+ source = "registry+https://github.com/rust-lang/crates.io-index"
843
+ checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
844
+ dependencies = [
845
+ "bitflags",
846
+ ]
847
+
848
+ [[package]]
849
+ name = "rustc_version"
850
+ version = "0.4.1"
851
+ source = "registry+https://github.com/rust-lang/crates.io-index"
852
+ checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
853
+ dependencies = [
854
+ "semver",
855
+ ]
856
+
857
+ [[package]]
858
+ name = "rustversion"
859
+ version = "1.0.23"
860
+ source = "registry+https://github.com/rust-lang/crates.io-index"
861
+ checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
862
+
863
+ [[package]]
864
+ name = "scopeguard"
865
+ version = "1.2.0"
866
+ source = "registry+https://github.com/rust-lang/crates.io-index"
867
+ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
868
+
869
+ [[package]]
870
+ name = "semver"
871
+ version = "1.0.28"
872
+ source = "registry+https://github.com/rust-lang/crates.io-index"
873
+ checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
874
+
875
+ [[package]]
876
+ name = "seq-macro"
877
+ version = "0.3.6"
878
+ source = "registry+https://github.com/rust-lang/crates.io-index"
879
+ checksum = "1bc711410fbe7399f390ca1c3b60ad0f53f80e95c5eb935e52268a0e2cd49acc"
880
+
881
+ [[package]]
882
+ name = "serde"
883
+ version = "1.0.229"
884
+ source = "registry+https://github.com/rust-lang/crates.io-index"
885
+ checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
886
+ dependencies = [
887
+ "serde_core",
888
+ "serde_derive",
889
+ ]
890
+
891
+ [[package]]
892
+ name = "serde_core"
893
+ version = "1.0.229"
894
+ source = "registry+https://github.com/rust-lang/crates.io-index"
895
+ checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
896
+ dependencies = [
897
+ "serde_derive",
898
+ ]
899
+
900
+ [[package]]
901
+ name = "serde_derive"
902
+ version = "1.0.229"
903
+ source = "registry+https://github.com/rust-lang/crates.io-index"
904
+ checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
905
+ dependencies = [
906
+ "proc-macro2",
907
+ "quote",
908
+ "syn 3.0.9",
909
+ ]
910
+
911
+ [[package]]
912
+ name = "serde_json"
913
+ version = "1.0.154"
914
+ source = "registry+https://github.com/rust-lang/crates.io-index"
915
+ checksum = "e7e9cc8b1b85264074fbcc02a88680c4096b1e47df8f739dceb03bf482f04bd6"
916
+ dependencies = [
917
+ "itoa",
918
+ "memchr",
919
+ "serde",
920
+ "serde_core",
921
+ "zmij",
922
+ ]
923
+
924
+ [[package]]
925
+ name = "shlex"
926
+ version = "2.0.1"
927
+ source = "registry+https://github.com/rust-lang/crates.io-index"
928
+ checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
929
+
930
+ [[package]]
931
+ name = "simdutf8"
932
+ version = "0.1.5"
933
+ source = "registry+https://github.com/rust-lang/crates.io-index"
934
+ checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
935
+
936
+ [[package]]
937
+ name = "slab"
938
+ version = "0.4.12"
939
+ source = "registry+https://github.com/rust-lang/crates.io-index"
940
+ checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
941
+
942
+ [[package]]
943
+ name = "smallvec"
944
+ version = "1.16.3"
945
+ source = "registry+https://github.com/rust-lang/crates.io-index"
946
+ checksum = "5b3dc8af474f516a851ff4bd12db780f948b9250ad37211e4eec0bccea54e01b"
947
+
948
+ [[package]]
949
+ name = "snap"
950
+ version = "1.1.2"
951
+ source = "registry+https://github.com/rust-lang/crates.io-index"
952
+ checksum = "199905e6153d6405f9728fe44daace35f8f837bbf830bb6e85fbd5828709a886"
953
+
954
+ [[package]]
955
+ name = "syn"
956
+ version = "2.0.119"
957
+ source = "registry+https://github.com/rust-lang/crates.io-index"
958
+ checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
959
+ dependencies = [
960
+ "proc-macro2",
961
+ "quote",
962
+ "unicode-ident",
963
+ ]
964
+
965
+ [[package]]
966
+ name = "syn"
967
+ version = "3.0.9"
968
+ source = "registry+https://github.com/rust-lang/crates.io-index"
969
+ checksum = "d78c8dee4c7bf0e14673097256fed6142ce9d3b85a408189d07482442145823b"
970
+ dependencies = [
971
+ "proc-macro2",
972
+ "quote",
973
+ "unicode-ident",
974
+ ]
975
+
976
+ [[package]]
977
+ name = "target-lexicon"
978
+ version = "0.13.5"
979
+ source = "registry+https://github.com/rust-lang/crates.io-index"
980
+ checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
981
+
982
+ [[package]]
983
+ name = "thiserror"
984
+ version = "2.0.21"
985
+ source = "registry+https://github.com/rust-lang/crates.io-index"
986
+ checksum = "09e52cb86a36cede5cb101bf8908837b3e4c6e5e59fe7fd85c23fb56200d189e"
987
+ dependencies = [
988
+ "thiserror-impl",
989
+ ]
990
+
991
+ [[package]]
992
+ name = "thiserror-impl"
993
+ version = "2.0.21"
994
+ source = "registry+https://github.com/rust-lang/crates.io-index"
995
+ checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524"
996
+ dependencies = [
997
+ "proc-macro2",
998
+ "quote",
999
+ "syn 3.0.9",
1000
+ ]
1001
+
1002
+ [[package]]
1003
+ name = "tiny-keccak"
1004
+ version = "2.0.2"
1005
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1006
+ checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
1007
+ dependencies = [
1008
+ "crunchy",
1009
+ ]
1010
+
1011
+ [[package]]
1012
+ name = "twox-hash"
1013
+ version = "2.1.5"
1014
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1015
+ checksum = "86a801b3cea342a06d468c8710662aa29e5e05e4f5c0d62f00bbb7f2ad7941c2"
1016
+
1017
+ [[package]]
1018
+ name = "unicode-ident"
1019
+ version = "1.0.27"
1020
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1021
+ checksum = "a2c754d6c33795a1c324727428e5a7dedb5b06195f9890bdbcba760d3e246563"
1022
+
1023
+ [[package]]
1024
+ name = "version_check"
1025
+ version = "0.9.5"
1026
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1027
+ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
1028
+
1029
+ [[package]]
1030
+ name = "wasi"
1031
+ version = "0.11.1+wasi-snapshot-preview1"
1032
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1033
+ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
1034
+
1035
+ [[package]]
1036
+ name = "wasip2"
1037
+ version = "1.0.4+wasi-0.2.12"
1038
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1039
+ checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487"
1040
+ dependencies = [
1041
+ "wit-bindgen",
1042
+ ]
1043
+
1044
+ [[package]]
1045
+ name = "wasm-bindgen"
1046
+ version = "0.2.129"
1047
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1048
+ checksum = "9bb54f33acc68fd454578d9820b0bde1a1a3d17aa17bb7b6595806d02886d409"
1049
+ dependencies = [
1050
+ "cfg-if",
1051
+ "once_cell",
1052
+ "rustversion",
1053
+ "wasm-bindgen-macro",
1054
+ "wasm-bindgen-shared",
1055
+ ]
1056
+
1057
+ [[package]]
1058
+ name = "wasm-bindgen-macro"
1059
+ version = "0.2.129"
1060
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1061
+ checksum = "2e29d0c35b16e224a7eeb5cd2d25e3e1968fbd65604117b44d3b789d00ee8535"
1062
+ dependencies = [
1063
+ "quote",
1064
+ "wasm-bindgen-macro-support",
1065
+ ]
1066
+
1067
+ [[package]]
1068
+ name = "wasm-bindgen-macro-support"
1069
+ version = "0.2.129"
1070
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1071
+ checksum = "6f501a8bc3719dba86ef8ae4728879c08001bea749eb1333ac5b91e040e2a6b7"
1072
+ dependencies = [
1073
+ "bumpalo",
1074
+ "proc-macro2",
1075
+ "quote",
1076
+ "syn 3.0.9",
1077
+ "wasm-bindgen-shared",
1078
+ ]
1079
+
1080
+ [[package]]
1081
+ name = "wasm-bindgen-shared"
1082
+ version = "0.2.129"
1083
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1084
+ checksum = "23f0c9c52aa7cd7d77769a4cfe2a9adb1b331f489a41d912ce14513d5ab995c6"
1085
+ dependencies = [
1086
+ "unicode-ident",
1087
+ ]
1088
+
1089
+ [[package]]
1090
+ name = "windows-core"
1091
+ version = "0.62.2"
1092
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1093
+ checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
1094
+ dependencies = [
1095
+ "windows-implement",
1096
+ "windows-interface",
1097
+ "windows-link",
1098
+ "windows-result",
1099
+ "windows-strings",
1100
+ ]
1101
+
1102
+ [[package]]
1103
+ name = "windows-implement"
1104
+ version = "0.60.2"
1105
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1106
+ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
1107
+ dependencies = [
1108
+ "proc-macro2",
1109
+ "quote",
1110
+ "syn 2.0.119",
1111
+ ]
1112
+
1113
+ [[package]]
1114
+ name = "windows-interface"
1115
+ version = "0.59.3"
1116
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1117
+ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
1118
+ dependencies = [
1119
+ "proc-macro2",
1120
+ "quote",
1121
+ "syn 2.0.119",
1122
+ ]
1123
+
1124
+ [[package]]
1125
+ name = "windows-link"
1126
+ version = "0.2.1"
1127
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1128
+ checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
1129
+
1130
+ [[package]]
1131
+ name = "windows-result"
1132
+ version = "0.4.1"
1133
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1134
+ checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
1135
+ dependencies = [
1136
+ "windows-link",
1137
+ ]
1138
+
1139
+ [[package]]
1140
+ name = "windows-strings"
1141
+ version = "0.5.1"
1142
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1143
+ checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
1144
+ dependencies = [
1145
+ "windows-link",
1146
+ ]
1147
+
1148
+ [[package]]
1149
+ name = "wit-bindgen"
1150
+ version = "0.57.1"
1151
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1152
+ checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
1153
+
1154
+ [[package]]
1155
+ name = "xxhash-rust"
1156
+ version = "0.8.19"
1157
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1158
+ checksum = "550a2b930b62486a393c52d5c3b84bff264b28aa437ed64694d31e93b1757af7"
1159
+
1160
+ [[package]]
1161
+ name = "zerocopy"
1162
+ version = "0.8.63"
1163
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1164
+ checksum = "e5fe1f8f1b06191a00962174c61aa5005e0bb391a6d80d07e24d115c01a92ed8"
1165
+ dependencies = [
1166
+ "zerocopy-derive",
1167
+ ]
1168
+
1169
+ [[package]]
1170
+ name = "zerocopy-derive"
1171
+ version = "0.8.63"
1172
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1173
+ checksum = "863ad3ac83293fb4d740aedbfdc9240dd8d1a50c1099acd76ce80ce7c7230c7f"
1174
+ dependencies = [
1175
+ "proc-macro2",
1176
+ "quote",
1177
+ "syn 2.0.119",
1178
+ ]
1179
+
1180
+ [[package]]
1181
+ name = "zlib-rs"
1182
+ version = "0.6.8"
1183
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1184
+ checksum = "b268e58e7c693d7c271f93ffc4ba3b380412554231c85bf61ca7af91042a4112"
1185
+
1186
+ [[package]]
1187
+ name = "zmij"
1188
+ version = "1.0.23"
1189
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1190
+ checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
1191
+
1192
+ [[package]]
1193
+ name = "zstd"
1194
+ version = "0.14.3"
1195
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1196
+ checksum = "db3ae9bf90af6e40197bef767eeefc46ad6397420fe873830bd85e5fbbfb3c70"
1197
+ dependencies = [
1198
+ "zstd-safe",
1199
+ ]
1200
+
1201
+ [[package]]
1202
+ name = "zstd-safe"
1203
+ version = "8.2.0"
1204
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1205
+ checksum = "ded520f16b29e1a9b771c722221b2b0a0398c2316d9bb3c42b458a5414d7434b"
1206
+ dependencies = [
1207
+ "zstd-sys",
1208
+ ]
1209
+
1210
+ [[package]]
1211
+ name = "zstd-sys"
1212
+ version = "2.1.2+zstd.1.5.7"
1213
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1214
+ checksum = "481ada9c543b2323cbb7bbdbb5249177fbf0188ed959ed2c15e4b22421c84ed3"
1215
+ dependencies = [
1216
+ "cc",
1217
+ "pkg-config",
1218
+ ]
Cargo.toml CHANGED
@@ -1,39 +1,39 @@
1
- [workspace]
2
- resolver = "3"
3
- members = [
4
- "optimizer",
5
- "optimizer/crates/*",
6
- "data",
7
- ]
8
-
9
- [workspace.package]
10
- version = "1.1.0-post.3"
11
- edition = "2024"
12
- license = "Apache-2.0"
13
- authors = ["Silver Moon <leafmoon.idn@gmail.com>", "Lumina Moon (cloverx-id) Contributors"]
14
- repository = "https://github.com/cloverx-id/luminamoon"
15
- homepage = "https://github.com/cloverx-id/luminamoon"
16
-
17
- [workspace.dependencies]
18
- pyo3 = { version = "0.30.1", features = ["abi3-py310", "abi3t"] }
19
- ash = "0.38.0"
20
- libloading = "0.9.0"
21
- rayon = "1.12.0"
22
- thiserror = "2.0.21"
23
- serde = { version = "1.0.229", features = ["derive"] }
24
- serde_json = "1.0.154"
25
- parquet = "60.1.0"
26
- arrow-array = "60.1.0"
27
- arrow-schema = "60.1.0"
28
- arrow-ipc = "60.1.0"
29
- xxhash-rust = { version = "0.8.19", features = ["xxh3"] }
30
- crc32fast = "1.5.2"
31
- hashbrown = "0.17.1"
32
- parking_lot = "0.12.5"
33
-
34
- [profile.release]
35
- opt-level = 3
36
- lto = "fat"
37
- codegen-units = 16
38
- panic = "abort"
39
- strip = true
 
1
+ [workspace]
2
+ resolver = "3"
3
+ members = [
4
+ "optimizer",
5
+ "optimizer/crates/*",
6
+ "data",
7
+ ]
8
+
9
+ [workspace.package]
10
+ version = "1.1.0-post.3"
11
+ edition = "2024"
12
+ license = "Apache-2.0"
13
+ authors = ["Silver Moon <leafmoon.idn@gmail.com>", "Lumina Moon (cloverx-id) Contributors"]
14
+ repository = "https://github.com/cloverx-id/luminamoon"
15
+ homepage = "https://github.com/cloverx-id/luminamoon"
16
+
17
+ [workspace.dependencies]
18
+ pyo3 = { version = "0.30.1", features = ["abi3-py310", "abi3t"] }
19
+ ash = "0.38.0"
20
+ libloading = "0.9.0"
21
+ rayon = "1.12.0"
22
+ thiserror = "2.0.21"
23
+ serde = { version = "1.0.229", features = ["derive"] }
24
+ serde_json = "1.0.154"
25
+ parquet = "60.1.0"
26
+ arrow-array = "60.1.0"
27
+ arrow-schema = "60.1.0"
28
+ arrow-ipc = "60.1.0"
29
+ xxhash-rust = { version = "0.8.19", features = ["xxh3"] }
30
+ crc32fast = "1.5.2"
31
+ hashbrown = "0.17.1"
32
+ parking_lot = "0.12.5"
33
+
34
+ [profile.release]
35
+ opt-level = 3
36
+ lto = "fat"
37
+ codegen-units = 16
38
+ panic = "abort"
39
+ strip = true
LICENSE CHANGED
@@ -1,13 +1,13 @@
1
- Copyright 2026 Silver Moon (cloverxion) and Lumina Moon (cloverx-id) Contributors
2
-
3
- Licensed under the Apache License, Version 2.0 (the "License");
4
- you may not use this file except in compliance with the License.
5
- You may obtain a copy of the License at
6
-
7
- https://www.apache.org/licenses/LICENSE-2.0
8
-
9
- Unless required by applicable law or agreed to in writing, software
10
- distributed under the License is distributed on an "AS IS" BASIS,
11
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- See the License for the specific language governing permissions and
13
- limitations under the License.
 
1
+ Copyright 2026 Silver Moon (cloverxion) and Lumina Moon (cloverx-id) Contributors
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ https://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
README.md CHANGED
@@ -1,334 +1,334 @@
1
- ---
2
- license: apache-2.0
3
- library_name: pytorch
4
- language:
5
- - en
6
- tags:
7
- - pytorch
8
- - optimizer
9
- - custom-optimizer
10
- - luminamoon
11
- - luminav
12
- - clm
13
- - td-clm
14
- - lso-eval
15
- - rust
16
- - simd
17
- - avx512
18
- - vulkan
19
- - triton
20
- - memory-efficient
21
- - stochastic-rounding
22
- - low-vram
23
- - bf16
24
- - fp16
25
- - cuda
26
- - rocm
27
- - xpu
28
- - llm
29
- - transformer
30
- - deep-learning
31
- - research
32
- ---
33
-
34
- # Loomy
35
-
36
- <p align="left">
37
- <a href="https://doi.org/10.5281/zenodo.23973326"><img src="https://zenodo.org/badge/DOI/10.5281/zenodo.23973326.svg" alt="DOI"></a>
38
- <a href="https://pypi.org/project/luminamoon/"><img src="https://img.shields.io/pypi/v/luminamoon.svg?color=4f46e5" alt="PyPI version"></a>
39
- <a href="https://github.com/cloverx-id/luminamoon/actions/workflows/ci.yml"><img src="https://github.com/cloverx-id/luminamoon/actions/workflows/ci.yml/badge.svg" alt="CI Status"></a>
40
- <a href="eval/README.md"><img src="https://img.shields.io/badge/LSO--Eval-Verified-10b981" alt="LSO-Eval"></a>
41
- <a href="eval/LSO_Eval.pdf"><img src="https://img.shields.io/badge/Eval_Docs-PDF-b31b1b" alt="Eval Docs"></a>
42
- <a href="https://cloverx-id.github.io/luminamoon/"><img src="https://img.shields.io/badge/docs-GitHub_Pages-06b6d4" alt="Documentation"></a>
43
- <a href="https://pypi.org/project/luminamoon/"><img src="https://img.shields.io/badge/python-3.10%20%E2%86%92%203.15t%20(free--threaded)-10b981" alt="Python Versions"></a>
44
- <a href="LICENSE"><img src="https://img.shields.io/badge/license-Apache--2.0-blue" alt="License"></a>
45
- </p>
46
-
47
- **Loomy (`luminamoon`)** provides low-precision numerical optimization, rational neural network layers, and dataset text preprocessing utilities developed by **Silver Moon ([@cloverxion](https://github.com/cloverxion))** and **Lumina Moon ([@cloverx-id](https://github.com/cloverx-id)) contributors**.
48
-
49
- Built with a modular Rust and Python architecture, `luminamoon` provides components for CPU/GPU parameter updates and text dataset preprocessing:
50
-
51
- > **LSO-Eval Evaluation & Verification Notebook**: [**Open in Google Colab**](https://colab.research.google.com/drive/1UepDza5Wm3KRNooCA2Bn69GIeKB21a4a?usp=sharing) — *Stage II–IV benchmarks executed on `LuminaV v1.5.3` under native `torch.bfloat16` (`centered_defect=False`).*
52
-
53
- ## Projects & Components
54
-
55
- | Project / Component | Path | Status | Description | Documentation |
56
- | :--- | :--- | :--- | :--- | :--- |
57
- | **LuminaV Optimizer** | [`optimizer/`](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/optimizer/README.md) | `Active` (Math `v1.6.0`) | LuminaV v1.6.0 optimizer implementation with CPU SIMD (AVX-512/AVX2), Vulkan UMA, Triton AOT/JIT kernels, Triangle Family (`buffer="T1"`, `"T2"`), optional contiguous bucket fusion, and Triton JIT pre-warmup. | [Optimizer Guide](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/optimizer/README.md) |
58
- | **LuminaMoon Data Engine** | [`data/`](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/data/README.md) | `Active` (`v1.1.0`) | Modular text preprocessing library: AVX-512/AVX2/NEON SIMD text normalization, dual-layout StringTensor (CPU StringView & GPU contiguous buffers), Shannon entropy filtering, MinHash/XXH3-64 deduplication, PII masking, and 128B Ping-Pong checkpoint recovery. | [Data Engine Guide](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/data/README.md) |
59
- | **LSO-Eval** | [`eval/`](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/eval/README.md) | `Active` | Algorithm-agnostic optimizer evaluation & verification: Z3 SMT solver checks, 2D trajectory benchmarks, numerical parity tests, and Two-Class 4B LLM & Vision evaluation. | [LSO-Eval Guide](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/eval/README.md) |
60
- | **Changelogs** | [`changelogs/`](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/changelogs/README.md) | `Active` | Version histories separating Software Package releases (`v1.0.0`–`v1.1.0`) and LuminaV Algorithm Math versions (`v1.0.0`–`v1.6.0`). | [Changelogs](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/changelogs/README.md) |
61
- | **Documentation** | [`docs/`](https://cloverx-id.github.io/luminamoon/) | `Active` | Documentation index, developer guides (`docs/optimize/`, `docs/dataloader/`), and dependency version notes. | [Docs Index](https://cloverx-id.github.io/luminamoon/) |
62
- | **Ra-Family Architecture** | [`python/luminamoon/nn/`](https://huggingface.co/cloverx-id/LuminaV-Optimizer/tree/main/python/luminamoon/nn) | `Active` (`v1.1.0`) | Rational, tanh-free & square-root-free neural components: RaNorm, RaBatchNorm2d (with Conv2d weight folding), RaTanh, RaGELU, PosRaGLU (bounded gating in `(0, 1)`), and RaAttention with Cayley-Padé rational softmax. Triton GPU and Rust AVX2/AVX-512 SIMD acceleration. | [Ra-Family Guide](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/docs/nn/README.md) |
63
- | **Python Package** | [`python/luminamoon/`](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/python/luminamoon/__init__.py) | `Active` (`v1.1.0`) | Unified free-threaded (`abi3t`) Python namespace exposing LuminaV Optimizer, DataStreamLoader, Ra-Family neural architecture, and PyTorch/NumPy/HuggingFace integration. | [API Reference](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/docs/en/03_configuration_api.md) |
64
- | **Theoretical References** | [`references/`](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/references/README.md) | `Active` | Mathematical formulas, component mapping, literature comparisons, and BibTeX citations for LuminaV, Ra-Family, and Data Engine. | [References Guide](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/references/README.md) |
65
-
66
- ## Quickstart
67
-
68
- ### Installation
69
-
70
- Install the compiled package wheel from PyPI:
71
-
72
- ```bash
73
- pip install luminamoon
74
- ```
75
-
76
- Or build from source using Maturin and the Rust 2024 toolchain:
77
-
78
- ```bash
79
- git clone https://github.com/cloverx-id/luminamoon.git
80
- cd luminamoon
81
- pip install maturin
82
- maturin develop --release
83
- ```
84
-
85
- ### Basic Usage (LuminaV Optimizer)
86
-
87
- ```python
88
- import numpy as np
89
- from luminamoon import LuminaV
90
-
91
- # Initialize parameters
92
- weights = np.random.randn(64, 10).astype(np.float32)
93
- biases = np.zeros(10, dtype=np.float32)
94
-
95
- # Instantiate LuminaV optimizer (uses default hyperparameters)
96
- optimizer = LuminaV([weights, biases], lr=8e-4, weight_decay=0.08, buffer=2)
97
-
98
- # Execute optimization step with explicit gradients
99
- grad_w = np.random.randn(64, 10).astype(np.float32)
100
- grad_b = np.random.randn(10, dtype=np.float32)
101
- optimizer.step(grads=[grad_w, grad_b])
102
- ```
103
-
104
- For PyTorch deep learning workflows, see the [PyTorch Integration Guide](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/docs/en/01_getting_started.md#quickstart-pytorch-integration).
105
-
106
- ### LuminaV Hyperparameter Quick Reference
107
-
108
- All default values below are exposed in IDE autocompletion and `LuminaV.__init__` docstrings:
109
-
110
- | Parameter | Type | Default | Description |
111
- | :--- | :--- | :--- | :--- |
112
- | `lr` | `float` | `8e-4` (`0.0008`) | Learning rate (`η`). |
113
- | `betas` | `Tuple[float, float]` | `(0.9, 0.999)` | Exponential moving average factors `(β₁, β₂)` for first-moment momentum and second-moment variance. |
114
- | `eps` | `float` | `1e-8` | Numerical stability constant (`ε`) in the adaptive denominator. |
115
- | `weight_decay` | `float` | `0.08` (`8e-2`) | Decoupled weight decay coefficient. |
116
- | `tau` | `float` | `0.8` | Denominator temperature scaling factor (`τ`) for 2-buffer variance damping. |
117
- | `lam` | `float` | `0.3` | Triangle-defect penalty weight (`λ`) on `\|d_t\| = \|g_t - m_{t-1}\|` in 1-Buffer TD-CLM (`buffer=1`) and Triangle Family (`buffer="T1"`, `"T2"`). |
118
- | `cautious` | `bool` | `True` | Enables sign-aligned cautious update masking and lagged EMA scaling. |
119
- | `cautious_clamp_min` | `float` | `0.5` | Lower bound in `(0.0, 1.0]` for cautious alignment scaling. |
120
- | `buffer` | `int \| str` | `2` | State mode: `2` (`"dual"`, `"clm"`) for 2-Buffer CLM, `1` (`"single"`, `"td-clm"`, `"tdclm"`) for 1-Buffer TD-CLM, `"T2"` for 2-Buffer Triangle Family (`tanh`-free), or `"T1"` for 1-Buffer Triangle Family (`tanh`-free). |
121
- | `stochastic_rounding` | `bool` | `True` | Applies stateless 32-bit MurmurHash3 stochastic rounding when writing to `bfloat16` / `float16` weights. |
122
- | `bound` | `bool` | `True` | Enables adaptive update bounding. |
123
- | `bound_type` | `str \| int` | `"coordinate"` | Clipping mode: `"coordinate"` (element-wise clip) or `"radial"` (global norm ratio). |
124
- | `bound_ratio` | `float` | `0.05` | Maximum update magnitude relative to parameter scale. |
125
- | `master_weights` | `str \| bool` | `"none"` | Mixed-precision master weight mode for `bfloat16`/`float16`: `"none"` (`False`), `"semi"`, or `"full"` (`True`). |
126
- | `execution` | `str \| int` | `"auto"` | Hardware backend: `"auto"`, `"triton"`, `"vulkan"`, `"simd"`, or `"scalar"`. |
127
- | `fused_single_pass` | `bool` | `True` | Fuses parameter update and scalar reduction statistics into a single kernel pass. |
128
- | `fuse_tensors` | `bool` | `False` | Coalesces parameters into contiguous 1D memory buffers to reduce kernel launch latency on fragmented architectures (e.g. Vision models / ViT). Not recommended for large LLMs or distributed FSDP training. |
129
- | `bucket_cap_mb` | `float` | `500.0` | Maximum memory capacity per contiguous bucket chunk in megabytes when `fuse_tensors=True`. Partitions large models into bounded chunks. Emits a `UserWarning` if explicitly specified when `fuse_tensors=False`. |
130
- | `prewarm_jit` | `bool` | `True` | Pre-compiles Triton GPU kernels on 64-element dummy buffers (`lr=0.0`) during initialization to avoid first-step JIT compilation delay. |
131
- | `ema_decay` | `str \| float` | `"auto"` | EMA decay for lagged cautious/radial scalars (`"auto"` resolves to `0.80` for `buffer=2`/`"T2"` and `0.95` for `buffer=1`/`"T1"`). |
132
- | `fsdp_sync` | `bool` | `False` | Synchronizes global reduction scratch buffers across `torch.distributed` FSDP shards. |
133
- | `seed` | `int` | `1337` | Deterministic PRNG seed for stateless stochastic rounding. |
134
- | `version` | `Optional[str]` | `"1.6.0"` | Selects the active LuminaV mathematical specification version. |
135
-
136
- ### Software vs. Algorithm Versioning (`v1.1.0` / Math `v1.6.0`)
137
-
138
- `luminamoon` maintains two distinct, decoupled version lineages (documented in [`changelogs/`](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/changelogs/README.md)):
139
-
140
- 1. **Software Package Version (`v1.1.0`)**:
141
- - Tracks the monorepo distribution, CPython FFI bindings, Rust SIMD crates, neural architecture (`luminamoon.nn`), and Data Engine utilities on PyPI (`pip install luminamoon`).
142
- - To reproduce or execute older software package releases in Python, install the desired release directly via standard package management (`pip install luminamoon==1.0.9`) or check out official git release tags.
143
- - See [`changelogs/SOFTWARE_CHANGELOG.md`](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/changelogs/SOFTWARE_CHANGELOG.md).
144
-
145
- 2. **LuminaV Algorithm Math Version (`v1.6.0`)**:
146
- - Tracks the mathematical optimization formulas, convergence envelopes, and hardware kernels of the **LuminaV Optimizer** independently from pip package updates.
147
- - Four mathematical versions are supported: `1.6.0` (default with Triangle Family `T1`/`T2` and Padé rational bounds), `1.5.5` (bucket fusion and Triton pre-warmup), `1.5.4` (Bitwise-BF16 state promotion), and `1.5.3` (single-buffer TD-CLM).
148
- - Features **strict capability enforcement**: locking to an older mathematical version (such as `version="1.5.4"`) clearly rejects features introduced in later versions (such as `buffer="T2"` or `fuse_tensors=True`), helping ensure exact reproducibility for benchmarks and publications.
149
- - See [`changelogs/LUMINAV_ALGORITHM_CHANGELOG.md`](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/CHANGELOG.md).
150
-
151
- ```python
152
- import luminamoon
153
- from luminamoon import LuminaV
154
- import luminamoon.data as lmd
155
-
156
- # 1. Inspect Software Package Version vs. Algorithm Math Version
157
- print(luminamoon.__version__) # "1.1.0" (Software package release)
158
- print(lmd.get_version()) # "1.1.0" (Data Engine package module)
159
- print(LuminaV.ALGORITHM_MATH) # "1.6.0" (Algorithm mathematical version)
160
- print(LuminaV.ALGORITHM_SPEC) # "1.6.0" (Backward-compatible alias)
161
- print(LuminaV.getVersion()) # "1.6.0"
162
-
163
- # 2. Inspect capability matrix of any supported specification
164
- caps = LuminaV.get_capabilities("1.5.5")
165
- print(caps["allowed_buffers"]) # (1, 2)
166
- print(caps["supports_triangle_family"]) # False (introduced in 1.6.0)
167
-
168
- # 3. Lock algorithm version for scientific checkpoint reproducibility
169
- opt = LuminaV([weights, biases], lr=8e-4, version="1.5.5")
170
- opt.setVersion("1.5.5") # Instance-level version lock
171
- LuminaV.setVersion("1.5.5") # Class-level default specification
172
- ```
173
-
174
- ### Basic Usage (LuminaMoon Data Engine: Fused Text Preprocessing Pipeline)
175
-
176
- ```python
177
- from luminamoon.data import FusedPipeline, StringTensor, create_resumable_collate, ResumablePipeline
178
-
179
- # 1. Fused single-pass text preprocessing in Rust
180
- pipeline = FusedPipeline(
181
- trim_whitespace=True,
182
- normalize_newlines=True, # AVX-512 / AVX2 / NEON SIMD
183
- min_chars=10,
184
- min_entropy=2.0,
185
- )
186
- string_tensor = pipeline([" sample 1\r\n", "sample 2 with proper prose text"])
187
-
188
- # 2. Export to Apache Arrow, Polars, and PyTorch buffers
189
- polars_series = string_tensor.to_polars()
190
- arrow_array = string_tensor.to_arrow()
191
- offsets_tensor, payload_tensor = string_tensor.to_torch_gpu(device="cuda")
192
-
193
- # 3. Stream dataset with background prefetching & 128B Ping-Pong checkpointing
194
- pipe = ResumablePipeline(
195
- source=raw_data,
196
- fused_pipeline=pipeline,
197
- batch_size=64,
198
- prefetch_batches=4,
199
- checkpoint_path="./cache/checkpoint.meta",
200
- )
201
- for batch in pipe:
202
- # Training step...
203
- pipe.commit() # Saves Dual-Slot A/B checkpoint and deduplication WAL
204
- ```
205
-
206
- For detailed architecture, Shannon entropy filtering, MinHash deduplication, and PyTorch `collate_fn` adapters, see the [Data Engine Guide](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/data/README.md).
207
-
208
- ### Basic Usage (Ra-Family Rational Neural Architecture)
209
-
210
- ```python
211
- import torch
212
- from luminamoon.nn import RaNorm, PosRaGLU, RaAttention
213
-
214
- # 1. Square-root-free token normalization (L1-mean)
215
- norm = RaNorm(512)
216
- x = torch.randn(2, 64, 512)
217
- x_norm = norm(x)
218
-
219
- # 2. Rational gated linear unit with strictly positive bounds in (0, 1)
220
- mlp = PosRaGLU(512, hidden_features=2048)
221
- x_ffn = mlp(x_norm)
222
-
223
- # 3. Cayley-Padé rational multi-head attention (SFU-free softmax)
224
- attn = RaAttention(dim=512, num_heads=8, qk_norm=True, causal=True)
225
- out = attn(x_norm)
226
- ```
227
-
228
- For complete architecture, parameter folding for Conv2d, and standalone NumPy usage, see the [Ra-Family Guide](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/docs/nn/README.md).
229
-
230
- ## Verification & CI/CD
231
-
232
- All pull requests and releases are tested across Linux, Windows, and macOS:
233
-
234
- ```bash
235
- # Run static analysis across the Rust workspace
236
- cargo clippy --workspace --all-targets -- -D warnings
237
-
238
- # Run Rust workspace unit and parity tests (Optimizer + Data Engine)
239
- cargo test --workspace
240
-
241
- # Run Python static type check
242
- npx pyright python/luminamoon
243
-
244
- # Run standalone NumPy, PyTorch, and Data Engine tests
245
- python optimizer/tests/test_standalone_numpy.py
246
- python optimizer/tests/test_end_to_end_training.py
247
- python -B tests/test_data_modular.py
248
- python -B tests/test_gpu_vs_cpu_strings.py
249
-
250
- # Run LSO-Eval verification
251
- python -m eval
252
- ```
253
-
254
- ## Developer Resources
255
-
256
- For contributors working on **Loomy (`luminamoon`)**:
257
-
258
- - **Changelogs (Software & Algorithm)**: [`changelogs/`](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/changelogs/README.md)
259
- - **Optimizer Modular Documentation**: [`docs/optimize/`](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/docs/optimize/README.md)
260
- - **Data Engine Modular Documentation**: [`data/`](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/data/README.md)
261
- - **Optimizer Library Notes**: [`docs/library_audit/`](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/docs/library_audit/README.md)
262
- - **Data Standalone Library Notes**: [`data/docs/library_audit/`](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/data/docs/library_audit/README.md)
263
- - **Data Engine Architecture & User Guide**: [`data/README.md`](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/data/README.md)
264
- - **User Documentation**: [`docs/en/`](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/docs/en/README.md)
265
-
266
- ## Theoretical References & Citation
267
-
268
- LuminaMoon's algorithms are built upon published mathematical principles, including Maligranda's triangle inequality envelopes, rational neural networks, and decoupled optimization mechanics.
269
-
270
- For the full component mapping, literature comparisons, and BibTeX entries, see [`references/README.md`](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/references/README.md) and [`references/bibliography.bib`](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/references/bibliography.bib).
271
-
272
- To cite this repository in academic work:
273
-
274
- ```bibtex
275
- @misc{loomy2026,
276
- author = {Silver Moon and {Lumina Moon contributors}},
277
- title = {Loomy: Low-Precision Numerical Optimization, Rational Neural Architecture, and Data Processing Repository},
278
- year = {2026},
279
- publisher = {GitHub},
280
- howpublished = {\url{https://github.com/cloverx-id/luminamoon}},
281
- doi = {10.5281/zenodo.23973326},
282
- note = {Loomy repository}
283
- }
284
- ```
285
-
286
- ## Authors & Maintenance
287
-
288
- **Loomy (`luminamoon`)** is developed and maintained by:
289
-
290
- - **Silver Moon ([@cloverxion](https://github.com/cloverxion))**
291
- - **Lumina Moon ([@cloverx-id](https://github.com/cloverx-id)) contributors**
292
-
293
- ## License
294
-
295
- Licensed under the Apache License, Version 2.0. See [LICENSE](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/LICENSE) for details.
296
-
297
- ---
298
-
299
- ## LSO-Eval: Verification & Pretraining Benchmarks (`Qwen3.5-4B-Base`)
300
-
301
- Summary of verification checks and pretraining benchmarks evaluated on **LuminaV (`v1.5.3`)** using the **LSO-Eval** suite, covering invariant checks (Z3 SMT solver and precision tests), 2D test functions, and pretraining runs on `Qwen3.5-4B-Base` in `bfloat16`:
302
-
303
- > **Benchmark Note (`LuminaV v1.5.3`)**: Results below were evaluated on `LuminaV v1.5.3` under `torch.bfloat16` (`centered_defect=False`). In the evaluation loader, records were sampled by joint `(source, domain)` pairs; all candidate and baseline optimizers shared the same deterministic stream schedule.
304
-
305
- ### Stage IV-B: Context 2048 (20,000 Steps)
306
-
307
- <div align="center">
308
- <img src="https://huggingface.co/cloverx-id/LuminaV-Optimizer/resolve/main/eval/artifacts/v1.5.3/stage4b_lso_eval_results.png" alt="LSO-Eval Stage IV-B Qwen3.5-4B (ctx=2048)" width="100%" />
309
- </div>
310
-
311
- | Optimizer Configuration | Class | Final EMA | Val PPL | ΔPPL | VRAM<sub>peak</sub> | Max R<sub>t,l</sub> | Step / Opt Time |
312
- | :--- | :--- | :---: | :---: | :---: | :---: | :---: | :---: |
313
- | **`LuminaV_Buffer2_Dual` (`8e-4`)** | Class I (Dual) | **`4.5549`** | **`89.14`** | **`-43.43`** | `43.89 GB` | `0.0189` | `297.1 / 48.31 ms` |
314
- | **`LuminaV_Buffer1_TDCLM_3e4` (`3e-4`)** | Class II (Single) | `4.9173` | `165.13` | `+32.56` | **`35.97 GB`** | `0.0087` | **`283.5 / 36.99 ms`** |
315
- | `AdamW` (Native BF16, `3e-4`) | Baseline | `4.7621` | `132.57` | `+0.00` | `43.89 GB` | `0.0037` | `288.0 / 41.48 ms` |
316
- | `AdamW_FP32_Master` (`3e-4`) | Master Ref | `4.8116` | `124.43` | `-8.14` | `80.11 GB` | `0.0045` | `370.1 / 123.33 ms` |
317
-
318
- ### Stage IV-A: Context 512 (4,800 Steps, Grad Accum 8x)
319
-
320
- <div align="center">
321
- <img src="https://huggingface.co/cloverx-id/LuminaV-Optimizer/resolve/main/eval/artifacts/v1.5.3/stage4a_lso_eval_results.png" alt="LSO-Eval Stage IV-A Qwen3.5-4B (ctx=512)" width="100%" />
322
- </div>
323
-
324
- | Optimizer Configuration | Class | Final EMA | Val PPL | ΔPPL | VRAM<sub>peak</sub> | Max R<sub>t,l</sub> | Step / Opt Time |
325
- | :--- | :--- | :---: | :---: | :---: | :---: | :---: | :---: |
326
- | **`LuminaV_Buffer2_Dual` (`8e-4`)** | Class I (Dual) | **`4.7621`** | **`141.96`** | **`-39.21`** | `36.75 GB` | `0.0047` | `841.6 / 48.37 ms` |
327
- | **`LuminaV_Buffer1_TDCLM_3e4` (`3e-4`)** | Class II (Single) | `5.2569` | `195.58` | `+14.41` | **`28.81 GB`** | `0.0120` | **`829.4 / 36.77 ms`** |
328
- | **`LuminaV_Buffer1_TDCLM` (`8e-4`)** | Class II (Single) | `5.2953` | `221.07` | `+39.90` | **`28.81 GB`** | `0.0191` | **`830.7 / 36.46 ms`** |
329
- | `AdamW` (Native BF16, `3e-4`) | Baseline | `5.4497` | `181.17` | `+0.00` | `36.75 GB` | `0.0034` | `835.4 / 41.58 ms` |
330
- | `AdamW_FP32_Master` (`3e-4`) | Master Ref | `5.0234` | `153.78` | `-27.39` | `75.36 GB` | `0.0040` | `919.5 / 123.35 ms` |
331
-
332
- - **[LSO-Eval Formal Specification Manuscript (`eval/LSO_Eval.pdf`)](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/eval/LSO_Eval.pdf)**
333
- - **[Full LSO-Eval Verification Summary & Suite Guide (`eval/README.md`)](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/eval/README.md)**
334
- - **[Interactive Reproduction Notebook in Google Colab](https://colab.research.google.com/drive/1UepDza5Wm3KRNooCA2Bn69GIeKB21a4a?usp=sharing)**
 
1
+ ---
2
+ license: apache-2.0
3
+ library_name: pytorch
4
+ language:
5
+ - en
6
+ tags:
7
+ - pytorch
8
+ - optimizer
9
+ - custom-optimizer
10
+ - luminamoon
11
+ - luminav
12
+ - clm
13
+ - td-clm
14
+ - lso-eval
15
+ - rust
16
+ - simd
17
+ - avx512
18
+ - vulkan
19
+ - triton
20
+ - memory-efficient
21
+ - stochastic-rounding
22
+ - low-vram
23
+ - bf16
24
+ - fp16
25
+ - cuda
26
+ - rocm
27
+ - xpu
28
+ - llm
29
+ - transformer
30
+ - deep-learning
31
+ - research
32
+ ---
33
+
34
+ # Loomy
35
+
36
+ <p align="left">
37
+ <a href="https://doi.org/10.5281/zenodo.23973326"><img src="https://zenodo.org/badge/DOI/10.5281/zenodo.23973326.svg" alt="DOI"></a>
38
+ <a href="https://pypi.org/project/luminamoon/"><img src="https://img.shields.io/pypi/v/luminamoon.svg?color=4f46e5" alt="PyPI version"></a>
39
+ <a href="https://github.com/cloverx-id/luminamoon/actions/workflows/ci.yml"><img src="https://github.com/cloverx-id/luminamoon/actions/workflows/ci.yml/badge.svg" alt="CI Status"></a>
40
+ <a href="eval/README.md"><img src="https://img.shields.io/badge/LSO--Eval-Verified-10b981" alt="LSO-Eval"></a>
41
+ <a href="eval/LSO_Eval.pdf"><img src="https://img.shields.io/badge/Eval_Docs-PDF-b31b1b" alt="Eval Docs"></a>
42
+ <a href="https://cloverx-id.github.io/luminamoon/"><img src="https://img.shields.io/badge/docs-GitHub_Pages-06b6d4" alt="Documentation"></a>
43
+ <a href="https://pypi.org/project/luminamoon/"><img src="https://img.shields.io/badge/python-3.10%20%E2%86%92%203.15t%20(free--threaded)-10b981" alt="Python Versions"></a>
44
+ <a href="LICENSE"><img src="https://img.shields.io/badge/license-Apache--2.0-blue" alt="License"></a>
45
+ </p>
46
+
47
+ **Loomy (`luminamoon`)** provides low-precision numerical optimization, rational neural network layers, and dataset text preprocessing utilities developed by **Silver Moon ([@cloverxion](https://github.com/cloverxion))** and **Lumina Moon ([@cloverx-id](https://github.com/cloverx-id)) contributors**.
48
+
49
+ Built with a modular Rust and Python architecture, `luminamoon` provides components for CPU/GPU parameter updates and text dataset preprocessing:
50
+
51
+ > **LSO-Eval Evaluation & Verification Notebook**: [**Open in Google Colab**](https://colab.research.google.com/drive/1UepDza5Wm3KRNooCA2Bn69GIeKB21a4a?usp=sharing) — *Stage II–IV benchmarks executed on `LuminaV v1.5.3` under native `torch.bfloat16` (`centered_defect=False`).*
52
+
53
+ ## Projects & Components
54
+
55
+ | Project / Component | Path | Status | Description | Documentation |
56
+ | :--- | :--- | :--- | :--- | :--- |
57
+ | **LuminaV Optimizer** | [`optimizer/`](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/optimizer/README.md) | `Active` (Math `v1.6.0`) | LuminaV v1.6.0 optimizer implementation with CPU SIMD (AVX-512/AVX2), Vulkan UMA, Triton AOT/JIT kernels, Triangle Family (`buffer="T1"`, `"T2"`), optional contiguous bucket fusion, and Triton JIT pre-warmup. | [Optimizer Guide](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/optimizer/README.md) |
58
+ | **LuminaMoon Data Engine** | [`data/`](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/data/README.md) | `Active` (`v1.1.0`) | Modular text preprocessing library: AVX-512/AVX2/NEON SIMD text normalization, dual-layout StringTensor (CPU StringView & GPU contiguous buffers), Shannon entropy filtering, MinHash/XXH3-64 deduplication, PII masking, and 128B Ping-Pong checkpoint recovery. | [Data Engine Guide](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/data/README.md) |
59
+ | **LSO-Eval** | [`eval/`](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/eval/README.md) | `Active` | Algorithm-agnostic optimizer evaluation & verification: Z3 SMT solver checks, 2D trajectory benchmarks, numerical parity tests, and Two-Class 4B LLM & Vision evaluation. | [LSO-Eval Guide](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/eval/README.md) |
60
+ | **Changelogs** | [`changelogs/`](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/changelogs/README.md) | `Active` | Version histories separating Software Package releases (`v1.0.0`–`v1.1.0`) and LuminaV Algorithm Math versions (`v1.0.0`–`v1.6.0`). | [Changelogs](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/changelogs/README.md) |
61
+ | **Documentation** | [`docs/`](https://cloverx-id.github.io/luminamoon/) | `Active` | Documentation index, developer guides (`docs/optimize/`, `docs/dataloader/`), and dependency version notes. | [Docs Index](https://cloverx-id.github.io/luminamoon/) |
62
+ | **Ra-Family Architecture** | [`python/luminamoon/nn/`](https://huggingface.co/cloverx-id/LuminaV-Optimizer/tree/main/python/luminamoon/nn) | `Active` (`v1.1.0`) | Rational, tanh-free & square-root-free neural components: RaNorm, RaBatchNorm2d (with Conv2d weight folding), RaTanh, RaGELU, PosRaGLU (bounded gating in `(0, 1)`), and RaAttention with Cayley-Padé rational softmax. Triton GPU and Rust AVX2/AVX-512 SIMD acceleration. | [Ra-Family Guide](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/docs/nn/README.md) |
63
+ | **Python Package** | [`python/luminamoon/`](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/python/luminamoon/__init__.py) | `Active` (`v1.1.0`) | Unified free-threaded (`abi3t`) Python namespace exposing LuminaV Optimizer, DataStreamLoader, Ra-Family neural architecture, and PyTorch/NumPy/HuggingFace integration. | [API Reference](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/docs/en/03_configuration_api.md) |
64
+ | **Theoretical References** | [`references/`](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/references/README.md) | `Active` | Mathematical formulas, component mapping, literature comparisons, and BibTeX citations for LuminaV, Ra-Family, and Data Engine. | [References Guide](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/references/README.md) |
65
+
66
+ ## Quickstart
67
+
68
+ ### Installation
69
+
70
+ Install the compiled package wheel from PyPI:
71
+
72
+ ```bash
73
+ pip install luminamoon
74
+ ```
75
+
76
+ Or build from source using Maturin and the Rust 2024 toolchain:
77
+
78
+ ```bash
79
+ git clone https://github.com/cloverx-id/luminamoon.git
80
+ cd luminamoon
81
+ pip install maturin
82
+ maturin develop --release
83
+ ```
84
+
85
+ ### Basic Usage (LuminaV Optimizer)
86
+
87
+ ```python
88
+ import numpy as np
89
+ from luminamoon import LuminaV
90
+
91
+ # Initialize parameters
92
+ weights = np.random.randn(64, 10).astype(np.float32)
93
+ biases = np.zeros(10, dtype=np.float32)
94
+
95
+ # Instantiate LuminaV optimizer (uses default hyperparameters)
96
+ optimizer = LuminaV([weights, biases], lr=8e-4, weight_decay=0.08, buffer=2)
97
+
98
+ # Execute optimization step with explicit gradients
99
+ grad_w = np.random.randn(64, 10).astype(np.float32)
100
+ grad_b = np.random.randn(10, dtype=np.float32)
101
+ optimizer.step(grads=[grad_w, grad_b])
102
+ ```
103
+
104
+ For PyTorch deep learning workflows, see the [PyTorch Integration Guide](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/docs/en/01_getting_started.md#quickstart-pytorch-integration).
105
+
106
+ ### LuminaV Hyperparameter Quick Reference
107
+
108
+ All default values below are exposed in IDE autocompletion and `LuminaV.__init__` docstrings:
109
+
110
+ | Parameter | Type | Default | Description |
111
+ | :--- | :--- | :--- | :--- |
112
+ | `lr` | `float` | `8e-4` (`0.0008`) | Learning rate (`η`). |
113
+ | `betas` | `Tuple[float, float]` | `(0.9, 0.999)` | Exponential moving average factors `(β₁, β₂)` for first-moment momentum and second-moment variance. |
114
+ | `eps` | `float` | `1e-8` | Numerical stability constant (`ε`) in the adaptive denominator. |
115
+ | `weight_decay` | `float` | `0.08` (`8e-2`) | Decoupled weight decay coefficient. |
116
+ | `tau` | `float` | `0.8` | Denominator temperature scaling factor (`τ`) for 2-buffer variance damping. |
117
+ | `lam` | `float` | `0.3` | Triangle-defect penalty weight (`λ`) on `\|d_t\| = \|g_t - m_{t-1}\|` in 1-Buffer TD-CLM (`buffer=1`) and Triangle Family (`buffer="T1"`, `"T2"`). |
118
+ | `cautious` | `bool` | `True` | Enables sign-aligned cautious update masking and lagged EMA scaling. |
119
+ | `cautious_clamp_min` | `float` | `0.5` | Lower bound in `(0.0, 1.0]` for cautious alignment scaling. |
120
+ | `buffer` | `int \| str` | `2` | State mode: `2` (`"dual"`, `"clm"`) for 2-Buffer CLM, `1` (`"single"`, `"td-clm"`, `"tdclm"`) for 1-Buffer TD-CLM, `"T2"` for 2-Buffer Triangle Family (`tanh`-free), or `"T1"` for 1-Buffer Triangle Family (`tanh`-free). |
121
+ | `stochastic_rounding` | `bool` | `True` | Applies stateless 32-bit MurmurHash3 stochastic rounding when writing to `bfloat16` / `float16` weights. |
122
+ | `bound` | `bool` | `True` | Enables adaptive update bounding. |
123
+ | `bound_type` | `str \| int` | `"coordinate"` | Clipping mode: `"coordinate"` (element-wise clip) or `"radial"` (global norm ratio). |
124
+ | `bound_ratio` | `float` | `0.05` | Maximum update magnitude relative to parameter scale. |
125
+ | `master_weights` | `str \| bool` | `"none"` | Mixed-precision master weight mode for `bfloat16`/`float16`: `"none"` (`False`), `"semi"`, or `"full"` (`True`). |
126
+ | `execution` | `str \| int` | `"auto"` | Hardware backend: `"auto"`, `"triton"`, `"vulkan"`, `"simd"`, or `"scalar"`. |
127
+ | `fused_single_pass` | `bool` | `True` | Fuses parameter update and scalar reduction statistics into a single kernel pass. |
128
+ | `fuse_tensors` | `bool` | `False` | Coalesces parameters into contiguous 1D memory buffers to reduce kernel launch latency on fragmented architectures (e.g. Vision models / ViT). Not recommended for large LLMs or distributed FSDP training. |
129
+ | `bucket_cap_mb` | `float` | `500.0` | Maximum memory capacity per contiguous bucket chunk in megabytes when `fuse_tensors=True`. Partitions large models into bounded chunks. Emits a `UserWarning` if explicitly specified when `fuse_tensors=False`. |
130
+ | `prewarm_jit` | `bool` | `True` | Pre-compiles Triton GPU kernels on 64-element dummy buffers (`lr=0.0`) during initialization to avoid first-step JIT compilation delay. |
131
+ | `ema_decay` | `str \| float` | `"auto"` | EMA decay for lagged cautious/radial scalars (`"auto"` resolves to `0.80` for `buffer=2`/`"T2"` and `0.95` for `buffer=1`/`"T1"`). |
132
+ | `fsdp_sync` | `bool` | `False` | Synchronizes global reduction scratch buffers across `torch.distributed` FSDP shards. |
133
+ | `seed` | `int` | `1337` | Deterministic PRNG seed for stateless stochastic rounding. |
134
+ | `version` | `Optional[str]` | `"1.6.0"` | Selects the active LuminaV mathematical specification version. |
135
+
136
+ ### Software vs. Algorithm Versioning (`v1.1.0` / Math `v1.6.0`)
137
+
138
+ `luminamoon` maintains two distinct, decoupled version lineages (documented in [`changelogs/`](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/changelogs/README.md)):
139
+
140
+ 1. **Software Package Version (`v1.1.0`)**:
141
+ - Tracks the monorepo distribution, CPython FFI bindings, Rust SIMD crates, neural architecture (`luminamoon.nn`), and Data Engine utilities on PyPI (`pip install luminamoon`).
142
+ - To reproduce or execute older software package releases in Python, install the desired release directly via standard package management (`pip install luminamoon==1.0.9`) or check out official git release tags.
143
+ - See [`changelogs/SOFTWARE_CHANGELOG.md`](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/changelogs/SOFTWARE_CHANGELOG.md).
144
+
145
+ 2. **LuminaV Algorithm Math Version (`v1.6.0`)**:
146
+ - Tracks the mathematical optimization formulas, convergence envelopes, and hardware kernels of the **LuminaV Optimizer** independently from pip package updates.
147
+ - Four mathematical versions are supported: `1.6.0` (default with Triangle Family `T1`/`T2` and Padé rational bounds), `1.5.5` (bucket fusion and Triton pre-warmup), `1.5.4` (Bitwise-BF16 state promotion), and `1.5.3` (single-buffer TD-CLM).
148
+ - Features **strict capability enforcement**: locking to an older mathematical version (such as `version="1.5.4"`) clearly rejects features introduced in later versions (such as `buffer="T2"` or `fuse_tensors=True`), helping ensure exact reproducibility for benchmarks and publications.
149
+ - See [`changelogs/LUMINAV_ALGORITHM_CHANGELOG.md`](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/CHANGELOG.md).
150
+
151
+ ```python
152
+ import luminamoon
153
+ from luminamoon import LuminaV
154
+ import luminamoon.data as lmd
155
+
156
+ # 1. Inspect Software Package Version vs. Algorithm Math Version
157
+ print(luminamoon.__version__) # "1.1.0" (Software package release)
158
+ print(lmd.get_version()) # "1.1.0" (Data Engine package module)
159
+ print(LuminaV.ALGORITHM_MATH) # "1.6.0" (Algorithm mathematical version)
160
+ print(LuminaV.ALGORITHM_SPEC) # "1.6.0" (Backward-compatible alias)
161
+ print(LuminaV.getVersion()) # "1.6.0"
162
+
163
+ # 2. Inspect capability matrix of any supported specification
164
+ caps = LuminaV.get_capabilities("1.5.5")
165
+ print(caps["allowed_buffers"]) # (1, 2)
166
+ print(caps["supports_triangle_family"]) # False (introduced in 1.6.0)
167
+
168
+ # 3. Lock algorithm version for scientific checkpoint reproducibility
169
+ opt = LuminaV([weights, biases], lr=8e-4, version="1.5.5")
170
+ opt.setVersion("1.5.5") # Instance-level version lock
171
+ LuminaV.setVersion("1.5.5") # Class-level default specification
172
+ ```
173
+
174
+ ### Basic Usage (LuminaMoon Data Engine: Fused Text Preprocessing Pipeline)
175
+
176
+ ```python
177
+ from luminamoon.data import FusedPipeline, StringTensor, create_resumable_collate, ResumablePipeline
178
+
179
+ # 1. Fused single-pass text preprocessing in Rust
180
+ pipeline = FusedPipeline(
181
+ trim_whitespace=True,
182
+ normalize_newlines=True, # AVX-512 / AVX2 / NEON SIMD
183
+ min_chars=10,
184
+ min_entropy=2.0,
185
+ )
186
+ string_tensor = pipeline([" sample 1\r\n", "sample 2 with proper prose text"])
187
+
188
+ # 2. Export to Apache Arrow, Polars, and PyTorch buffers
189
+ polars_series = string_tensor.to_polars()
190
+ arrow_array = string_tensor.to_arrow()
191
+ offsets_tensor, payload_tensor = string_tensor.to_torch_gpu(device="cuda")
192
+
193
+ # 3. Stream dataset with background prefetching & 128B Ping-Pong checkpointing
194
+ pipe = ResumablePipeline(
195
+ source=raw_data,
196
+ fused_pipeline=pipeline,
197
+ batch_size=64,
198
+ prefetch_batches=4,
199
+ checkpoint_path="./cache/checkpoint.meta",
200
+ )
201
+ for batch in pipe:
202
+ # Training step...
203
+ pipe.commit() # Saves Dual-Slot A/B checkpoint and deduplication WAL
204
+ ```
205
+
206
+ For detailed architecture, Shannon entropy filtering, MinHash deduplication, and PyTorch `collate_fn` adapters, see the [Data Engine Guide](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/data/README.md).
207
+
208
+ ### Basic Usage (Ra-Family Rational Neural Architecture)
209
+
210
+ ```python
211
+ import torch
212
+ from luminamoon.nn import RaNorm, PosRaGLU, RaAttention
213
+
214
+ # 1. Square-root-free token normalization (L1-mean)
215
+ norm = RaNorm(512)
216
+ x = torch.randn(2, 64, 512)
217
+ x_norm = norm(x)
218
+
219
+ # 2. Rational gated linear unit with strictly positive bounds in (0, 1)
220
+ mlp = PosRaGLU(512, hidden_features=2048)
221
+ x_ffn = mlp(x_norm)
222
+
223
+ # 3. Cayley-Padé rational multi-head attention (SFU-free softmax)
224
+ attn = RaAttention(dim=512, num_heads=8, qk_norm=True, causal=True)
225
+ out = attn(x_norm)
226
+ ```
227
+
228
+ For complete architecture, parameter folding for Conv2d, and standalone NumPy usage, see the [Ra-Family Guide](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/docs/nn/README.md).
229
+
230
+ ## Verification & CI/CD
231
+
232
+ All pull requests and releases are tested across Linux, Windows, and macOS:
233
+
234
+ ```bash
235
+ # Run static analysis across the Rust workspace
236
+ cargo clippy --workspace --all-targets -- -D warnings
237
+
238
+ # Run Rust workspace unit and parity tests (Optimizer + Data Engine)
239
+ cargo test --workspace
240
+
241
+ # Run Python static type check
242
+ npx pyright python/luminamoon
243
+
244
+ # Run standalone NumPy, PyTorch, and Data Engine tests
245
+ python optimizer/tests/test_standalone_numpy.py
246
+ python optimizer/tests/test_end_to_end_training.py
247
+ python -B tests/test_data_modular.py
248
+ python -B tests/test_gpu_vs_cpu_strings.py
249
+
250
+ # Run LSO-Eval verification
251
+ python -m eval
252
+ ```
253
+
254
+ ## Developer Resources
255
+
256
+ For contributors working on **Loomy (`luminamoon`)**:
257
+
258
+ - **Changelogs (Software & Algorithm)**: [`changelogs/`](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/changelogs/README.md)
259
+ - **Optimizer Modular Documentation**: [`docs/optimize/`](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/docs/optimize/README.md)
260
+ - **Data Engine Modular Documentation**: [`data/`](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/data/README.md)
261
+ - **Optimizer Library Notes**: [`docs/library_audit/`](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/docs/library_audit/README.md)
262
+ - **Data Standalone Library Notes**: [`data/docs/library_audit/`](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/data/docs/library_audit/README.md)
263
+ - **Data Engine Architecture & User Guide**: [`data/README.md`](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/data/README.md)
264
+ - **User Documentation**: [`docs/en/`](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/docs/en/README.md)
265
+
266
+ ## Theoretical References & Citation
267
+
268
+ LuminaMoon's algorithms are built upon published mathematical principles, including Maligranda's triangle inequality envelopes, rational neural networks, and decoupled optimization mechanics.
269
+
270
+ For the full component mapping, literature comparisons, and BibTeX entries, see [`references/README.md`](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/references/README.md) and [`references/bibliography.bib`](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/references/bibliography.bib).
271
+
272
+ To cite this repository in academic work:
273
+
274
+ ```bibtex
275
+ @misc{loomy2026,
276
+ author = {Silver Moon and {Lumina Moon contributors}},
277
+ title = {Loomy: Low-Precision Numerical Optimization, Rational Neural Architecture, and Data Processing Repository},
278
+ year = {2026},
279
+ publisher = {GitHub},
280
+ howpublished = {\url{https://github.com/cloverx-id/luminamoon}},
281
+ doi = {10.5281/zenodo.23973326},
282
+ note = {Loomy repository}
283
+ }
284
+ ```
285
+
286
+ ## Authors & Maintenance
287
+
288
+ **Loomy (`luminamoon`)** is developed and maintained by:
289
+
290
+ - **Silver Moon ([@cloverxion](https://github.com/cloverxion))**
291
+ - **Lumina Moon ([@cloverx-id](https://github.com/cloverx-id)) contributors**
292
+
293
+ ## License
294
+
295
+ Licensed under the Apache License, Version 2.0. See [LICENSE](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/LICENSE) for details.
296
+
297
+ ---
298
+
299
+ ## LSO-Eval: Verification & Pretraining Benchmarks (`Qwen3.5-4B-Base`)
300
+
301
+ Summary of verification checks and pretraining benchmarks evaluated on **LuminaV (`v1.5.3`)** using the **LSO-Eval** suite, covering invariant checks (Z3 SMT solver and precision tests), 2D test functions, and pretraining runs on `Qwen3.5-4B-Base` in `bfloat16`:
302
+
303
+ > **Benchmark Note (`LuminaV v1.5.3`)**: Results below were evaluated on `LuminaV v1.5.3` under `torch.bfloat16` (`centered_defect=False`). In the evaluation loader, records were sampled by joint `(source, domain)` pairs; all candidate and baseline optimizers shared the same deterministic stream schedule.
304
+
305
+ ### Stage IV-B: Context 2048 (20,000 Steps)
306
+
307
+ <div align="center">
308
+ <img src="https://huggingface.co/cloverx-id/LuminaV-Optimizer/resolve/main/eval/artifacts/v1.5.3/stage4b_lso_eval_results.png" alt="LSO-Eval Stage IV-B Qwen3.5-4B (ctx=2048)" width="100%" />
309
+ </div>
310
+
311
+ | Optimizer Configuration | Class | Final EMA | Val PPL | ΔPPL | VRAM<sub>peak</sub> | Max R<sub>t,l</sub> | Step / Opt Time |
312
+ | :--- | :--- | :---: | :---: | :---: | :---: | :---: | :---: |
313
+ | **`LuminaV_Buffer2_Dual` (`8e-4`)** | Class I (Dual) | **`4.5549`** | **`89.14`** | **`-43.43`** | `43.89 GB` | `0.0189` | `297.1 / 48.31 ms` |
314
+ | **`LuminaV_Buffer1_TDCLM_3e4` (`3e-4`)** | Class II (Single) | `4.9173` | `165.13` | `+32.56` | **`35.97 GB`** | `0.0087` | **`283.5 / 36.99 ms`** |
315
+ | `AdamW` (Native BF16, `3e-4`) | Baseline | `4.7621` | `132.57` | `+0.00` | `43.89 GB` | `0.0037` | `288.0 / 41.48 ms` |
316
+ | `AdamW_FP32_Master` (`3e-4`) | Master Ref | `4.8116` | `124.43` | `-8.14` | `80.11 GB` | `0.0045` | `370.1 / 123.33 ms` |
317
+
318
+ ### Stage IV-A: Context 512 (4,800 Steps, Grad Accum 8x)
319
+
320
+ <div align="center">
321
+ <img src="https://huggingface.co/cloverx-id/LuminaV-Optimizer/resolve/main/eval/artifacts/v1.5.3/stage4a_lso_eval_results.png" alt="LSO-Eval Stage IV-A Qwen3.5-4B (ctx=512)" width="100%" />
322
+ </div>
323
+
324
+ | Optimizer Configuration | Class | Final EMA | Val PPL | ΔPPL | VRAM<sub>peak</sub> | Max R<sub>t,l</sub> | Step / Opt Time |
325
+ | :--- | :--- | :---: | :---: | :---: | :---: | :---: | :---: |
326
+ | **`LuminaV_Buffer2_Dual` (`8e-4`)** | Class I (Dual) | **`4.7621`** | **`141.96`** | **`-39.21`** | `36.75 GB` | `0.0047` | `841.6 / 48.37 ms` |
327
+ | **`LuminaV_Buffer1_TDCLM_3e4` (`3e-4`)** | Class II (Single) | `5.2569` | `195.58` | `+14.41` | **`28.81 GB`** | `0.0120` | **`829.4 / 36.77 ms`** |
328
+ | **`LuminaV_Buffer1_TDCLM` (`8e-4`)** | Class II (Single) | `5.2953` | `221.07` | `+39.90` | **`28.81 GB`** | `0.0191` | **`830.7 / 36.46 ms`** |
329
+ | `AdamW` (Native BF16, `3e-4`) | Baseline | `5.4497` | `181.17` | `+0.00` | `36.75 GB` | `0.0034` | `835.4 / 41.58 ms` |
330
+ | `AdamW_FP32_Master` (`3e-4`) | Master Ref | `5.0234` | `153.78` | `-27.39` | `75.36 GB` | `0.0040` | `919.5 / 123.35 ms` |
331
+
332
+ - **[LSO-Eval Formal Specification Manuscript (`eval/LSO_Eval.pdf`)](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/eval/LSO_Eval.pdf)**
333
+ - **[Full LSO-Eval Verification Summary & Suite Guide (`eval/README.md`)](https://huggingface.co/cloverx-id/LuminaV-Optimizer/blob/main/eval/README.md)**
334
+ - **[Interactive Reproduction Notebook in Google Colab](https://colab.research.google.com/drive/1UepDza5Wm3KRNooCA2Bn69GIeKB21a4a?usp=sharing)**
SECURITY.md CHANGED
@@ -1,50 +1,50 @@
1
- # Security Policy
2
-
3
- ## Supported Versions
4
-
5
- Security updates and bug fixes are maintained for the following versions of **LuminaMoon** (`luminamoon`) and the **LuminaV Optimizer** (`luminav`):
6
-
7
- | Component / Package | Version | Security Support Status |
8
- | :--- | :--- | :--- |
9
- | **`luminamoon`** (Rust and Python Package) | `>= 1.1.0` | Supported (Active) |
10
- | **`luminav`** (Algorithm Math) | `>= 1.6.0` | Supported (Active) |
11
- | **`luminamoon`** (Maintenance Line) | `1.0.8` – `1.0.9` | Supported (Maintenance) |
12
- | **`luminav`** (Maintenance Line) | `1.5.5` | Supported (Maintenance) |
13
- | **`luminamoon`** | `< 1.0.8` | Unsupported (Upgrade recommended) |
14
- | **`luminav`** | `< 1.5.5` | Unsupported (Upgrade recommended) |
15
-
16
- ---
17
-
18
- ## Reporting a Vulnerability
19
-
20
- If you discover a security vulnerability, memory-safety issue in FFI/SIMD/Vulkan/Triton boundaries, or dependency flaw:
21
-
22
- 1. **Private GitHub Security Advisory (Preferred)**:
23
- Submit a confidential report through [GitHub Private Vulnerability Reporting](https://github.com/cloverx-id/luminamoon/security/advisories/new).
24
- 2. **Direct Contact**:
25
- Email **Silver Moon** (`@cloverxion`) directly at [`leafmoon.idn@gmail.com`](mailto:leafmoon.idn@gmail.com) with the subject prefix `[SECURITY][luminamoon]`.
26
-
27
- ### Response & Disclosure Timeline
28
- - **Initial Acknowledgment**: Within 48 hours of report submission.
29
- - **Triage & Verification**: Within 5 business days.
30
- - **Fix & Disclosure**: Critical fixes are prioritized for release on GitHub, PyPI, and Hugging Face Hub alongside a published GitHub Security Advisory (GHSA / CVE).
31
-
32
- Please do not open public GitHub issues for undisclosed security vulnerabilities.
33
-
34
- ---
35
-
36
- ## Automated Security Checks
37
-
38
- Every commit and pull request in `cloverx-id/luminamoon` is checked across automated workflows (`.github/workflows/security.yml`):
39
-
40
- 1. **Static Analysis (GitHub CodeQL)**:
41
- Automated dataflow analysis (`github/codeql-action@v4`) across Python modules and GitHub Actions workflows to catch injection, traversal, and unsafe deserialization issues.
42
- 2. **Dependency & Supply-Chain Scanning (`tools/security_audit.py` & `pip-audit`)**:
43
- Locked Rust crates in `Cargo.lock` and Python dependencies in `pyproject.toml` are checked against the RustSec Advisory Database, Open Source Vulnerabilities (OSV), PyPA, and the National Vulnerability Database.
44
- 3. **Dependency Review Gate (`actions/dependency-review-action@v5`)**:
45
- Blocks pull requests that introduce dependencies with known vulnerabilities.
46
- 4. **Secret Scanning & Push Protection**:
47
- GitHub Secret Scanning and Push Protection are active to prevent accidental credential commits.
48
- 5. **Memory & FFI Checks**:
49
- - Pointer alignment and contiguity verification across PyO3 (`abi3-py310` / `abi3t`) and NumPy/PyTorch buffers.
50
- - Minimal dependency footprint: only actively required crates are locked in `Cargo.lock` (see [`docs/library_audit/`](docs/library_audit/README.md)).
 
1
+ # Security Policy
2
+
3
+ ## Supported Versions
4
+
5
+ Security updates and bug fixes are maintained for the following versions of **LuminaMoon** (`luminamoon`) and the **LuminaV Optimizer** (`luminav`):
6
+
7
+ | Component / Package | Version | Security Support Status |
8
+ | :--- | :--- | :--- |
9
+ | **`luminamoon`** (Rust and Python Package) | `>= 1.1.0` | Supported (Active) |
10
+ | **`luminav`** (Algorithm Math) | `>= 1.6.0` | Supported (Active) |
11
+ | **`luminamoon`** (Maintenance Line) | `1.0.8` – `1.0.9` | Supported (Maintenance) |
12
+ | **`luminav`** (Maintenance Line) | `1.5.5` | Supported (Maintenance) |
13
+ | **`luminamoon`** | `< 1.0.8` | Unsupported (Upgrade recommended) |
14
+ | **`luminav`** | `< 1.5.5` | Unsupported (Upgrade recommended) |
15
+
16
+ ---
17
+
18
+ ## Reporting a Vulnerability
19
+
20
+ If you discover a security vulnerability, memory-safety issue in FFI/SIMD/Vulkan/Triton boundaries, or dependency flaw:
21
+
22
+ 1. **Private GitHub Security Advisory (Preferred)**:
23
+ Submit a confidential report through [GitHub Private Vulnerability Reporting](https://github.com/cloverx-id/luminamoon/security/advisories/new).
24
+ 2. **Direct Contact**:
25
+ Email **Silver Moon** (`@cloverxion`) directly at [`leafmoon.idn@gmail.com`](mailto:leafmoon.idn@gmail.com) with the subject prefix `[SECURITY][luminamoon]`.
26
+
27
+ ### Response & Disclosure Timeline
28
+ - **Initial Acknowledgment**: Within 48 hours of report submission.
29
+ - **Triage & Verification**: Within 5 business days.
30
+ - **Fix & Disclosure**: Critical fixes are prioritized for release on GitHub, PyPI, and Hugging Face Hub alongside a published GitHub Security Advisory (GHSA / CVE).
31
+
32
+ Please do not open public GitHub issues for undisclosed security vulnerabilities.
33
+
34
+ ---
35
+
36
+ ## Automated Security Checks
37
+
38
+ Every commit and pull request in `cloverx-id/luminamoon` is checked across automated workflows (`.github/workflows/security.yml`):
39
+
40
+ 1. **Static Analysis (GitHub CodeQL)**:
41
+ Automated dataflow analysis (`github/codeql-action@v4`) across Python modules and GitHub Actions workflows to catch injection, traversal, and unsafe deserialization issues.
42
+ 2. **Dependency & Supply-Chain Scanning (`tools/security_audit.py` & `pip-audit`)**:
43
+ Locked Rust crates in `Cargo.lock` and Python dependencies in `pyproject.toml` are checked against the RustSec Advisory Database, Open Source Vulnerabilities (OSV), PyPA, and the National Vulnerability Database.
44
+ 3. **Dependency Review Gate (`actions/dependency-review-action@v5`)**:
45
+ Blocks pull requests that introduce dependencies with known vulnerabilities.
46
+ 4. **Secret Scanning & Push Protection**:
47
+ GitHub Secret Scanning and Push Protection are active to prevent accidental credential commits.
48
+ 5. **Memory & FFI Checks**:
49
+ - Pointer alignment and contiguity verification across PyO3 (`abi3-py310` / `abi3t`) and NumPy/PyTorch buffers.
50
+ - Minimal dependency footprint: only actively required crates are locked in `Cargo.lock` (see [`docs/library_audit/`](docs/library_audit/README.md)).
benchmarks/bench_data_processing.py CHANGED
@@ -1,411 +1,411 @@
1
- """LuminaMoon Data Processing Engine Benchmark.
2
-
3
- Compares Native Rust SIMD Accelerator (AVX-512 / AVX2 / Rayon) against
4
- Pure Python Fallbacks across micro-benchmarks and end-to-end pipelines.
5
- """
6
-
7
- from __future__ import annotations
8
-
9
- import json
10
- import math
11
- import os
12
- import random
13
- import sys
14
- import tempfile
15
- import time
16
- from dataclasses import asdict, dataclass
17
- from pathlib import Path
18
- from typing import Any, Callable, Dict, List, Tuple
19
-
20
- # Ensure local python package is discoverable
21
- sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "python"))
22
-
23
- from luminamoon.optimizer import _luminamoon_optimizer_rs as rs
24
- from luminamoon.data import fallbacks as fb
25
-
26
-
27
- @dataclass
28
- class BenchmarkResult:
29
- benchmark_name: str
30
- workload_size: int
31
- data_bytes: int
32
- rust_duration_sec: float
33
- rust_throughput_items_per_sec: float
34
- rust_throughput_mb_per_sec: float
35
- fallback_duration_sec: float
36
- fallback_throughput_items_per_sec: float
37
- fallback_throughput_mb_per_sec: float
38
- speedup_ratio: float
39
-
40
-
41
- def generate_synthetic_dataset(num_records: int = 10000, seed: int = 42) -> Tuple[List[str], int]:
42
- rng = random.Random(seed)
43
- templates = [
44
- " The quick brown fox jumps over the lazy dog.\r\n This is line 2 with carriage returns.\r\n",
45
- "Deep learning optimization requires efficient gradient descent with momentum and adaptive learning rates.\n",
46
- '{"text": "{\"key\": \"value\", \"nested\": [1, 2, 3]}", "meta": "test record"}\r\n',
47
- "System security alert: detected unauthorized access attempt from 192.168.1.105 on port 443.\n",
48
- "Contact user support at alice.walker@enterprise-domain.org or call +1-800-555-0199 for assistance.\r\n",
49
- "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n", # high repetition
50
- "Short text.\n",
51
- "Mathematics: Pade [13/6] approximant ensures subnormal FP16 RNE preservation without overflow.\n",
52
- "High symbol noise: #$%@!&*()!@#$%^&*()_+{}|:<>?~`!@#$%^&*()_+{}|:<>?~`\n",
53
- "Large document block: " + ("lorem ipsum dolor sit amet " * 20) + "\r\n",
54
- ]
55
- records = []
56
- total_bytes = 0
57
- for i in range(num_records):
58
- base = templates[i % len(templates)]
59
- noise = f" [doc_id={i}_{rng.randint(100, 999)}]"
60
- rec = base + noise
61
- records.append(rec)
62
- total_bytes += len(rec.encode("utf-8"))
63
- return records, total_bytes
64
-
65
-
66
- def run_timed_benchmark(
67
- name: str,
68
- rust_fn: Callable[[], Any],
69
- fallback_fn: Callable[[], Any],
70
- workload_size: int,
71
- data_bytes: int,
72
- warmup_runs: int = 2,
73
- test_runs: int = 5,
74
- ) -> BenchmarkResult:
75
- # Warmup Rust
76
- for _ in range(warmup_runs):
77
- rust_fn()
78
- # Timed Rust
79
- rust_times = []
80
- for _ in range(test_runs):
81
- t0 = time.perf_counter_ns()
82
- rust_fn()
83
- t1 = time.perf_counter_ns()
84
- rust_times.append((t1 - t0) / 1e9)
85
- rust_dur = min(rust_times) # best of N runs
86
-
87
- # Warmup Fallback
88
- for _ in range(warmup_runs):
89
- fallback_fn()
90
- # Timed Fallback
91
- fallback_times = []
92
- for _ in range(test_runs):
93
- t0 = time.perf_counter_ns()
94
- fallback_fn()
95
- t1 = time.perf_counter_ns()
96
- fallback_times.append((t1 - t0) / 1e9)
97
- fallback_dur = min(fallback_times)
98
-
99
- rust_items_per_sec = workload_size / max(rust_dur, 1e-9)
100
- rust_mb_per_sec = (data_bytes / (1024 * 1024)) / max(rust_dur, 1e-9)
101
-
102
- fallback_items_per_sec = workload_size / max(fallback_dur, 1e-9)
103
- fallback_mb_per_sec = (data_bytes / (1024 * 1024)) / max(fallback_dur, 1e-9)
104
-
105
- speedup = fallback_dur / max(rust_dur, 1e-9)
106
-
107
- return BenchmarkResult(
108
- benchmark_name=name,
109
- workload_size=workload_size,
110
- data_bytes=data_bytes,
111
- rust_duration_sec=rust_dur,
112
- rust_throughput_items_per_sec=rust_items_per_sec,
113
- rust_throughput_mb_per_sec=rust_mb_per_sec,
114
- fallback_duration_sec=fallback_dur,
115
- fallback_throughput_items_per_sec=fallback_items_per_sec,
116
- fallback_throughput_mb_per_sec=fallback_mb_per_sec,
117
- speedup_ratio=speedup,
118
- )
119
-
120
-
121
- def benchmark_text_cleaner(records: List[str], total_bytes: int) -> BenchmarkResult:
122
- def rust_call():
123
- return rs.clean_batch_records(records, True, True, 10, 10000, False, None)
124
-
125
- def fallback_call():
126
- return fb.clean_batch_records(records, True, True, 10, 10000, False, None)
127
-
128
- return run_timed_benchmark(
129
- name="TextCleaner.clean_batch (CRLF + Trimming + Bounds)",
130
- rust_fn=rust_call,
131
- fallback_fn=fallback_call,
132
- workload_size=len(records),
133
- data_bytes=total_bytes,
134
- )
135
-
136
-
137
- def benchmark_quality_filter(records: List[str], total_bytes: int) -> BenchmarkResult:
138
- rust_filter = rs.QualityFilter(
139
- min_chars=10,
140
- max_chars=20000,
141
- min_words=3,
142
- min_entropy=2.0,
143
- max_repetition_ratio=0.3,
144
- max_symbol_ratio=0.25,
145
- )
146
- fallback_filter = fb.QualityFilter(
147
- min_chars=10,
148
- max_chars=20000,
149
- min_words=3,
150
- min_entropy=2.0,
151
- max_repetition_ratio=0.3,
152
- max_symbol_ratio=0.25,
153
- )
154
-
155
- def rust_call():
156
- return rust_filter.filter_batch(records)
157
-
158
- def fallback_call():
159
- return fallback_filter.filter_batch(records)
160
-
161
- return run_timed_benchmark(
162
- name="QualityFilter.filter_batch (Entropy + Repetition + Symbols)",
163
- rust_fn=rust_call,
164
- fallback_fn=fallback_call,
165
- workload_size=len(records),
166
- data_bytes=total_bytes,
167
- )
168
-
169
-
170
- def benchmark_stream_dedup(records: List[str], total_bytes: int) -> BenchmarkResult:
171
- # Inject 25% duplicate records
172
- dup_records = list(records)
173
- for i in range(len(dup_records) // 4):
174
- dup_records[i * 4] = dup_records[i * 4 + 1]
175
-
176
- def rust_call():
177
- dedup = rs.StreamDeduplicator()
178
- passed = dedup.filter_batch(dup_records)
179
- dedup.commit()
180
- return passed
181
-
182
- def fallback_call():
183
- dedup = fb.StreamDeduplicator()
184
- passed = dedup.filter_batch(dup_records)
185
- dedup.commit()
186
- return passed
187
-
188
- return run_timed_benchmark(
189
- name="StreamDeduplicator.filter_batch (Exact XXH3-64 Staging & Commit)",
190
- rust_fn=rust_call,
191
- fallback_fn=fallback_call,
192
- workload_size=len(dup_records),
193
- data_bytes=total_bytes,
194
- )
195
-
196
-
197
- def benchmark_minhash_dedup(records: List[str], total_bytes: int) -> BenchmarkResult:
198
- # Use 500 documents for MinHash permutation benchmarking
199
- sample_records = records[:500]
200
- sample_bytes = sum(len(r.encode("utf-8")) for r in sample_records)
201
-
202
- def rust_call():
203
- dedup = rs.MinHashDeduplicator(0.8, 5, 64)
204
- for r in sample_records:
205
- dedup.is_unique(r)
206
- return dedup.count()
207
-
208
- def fallback_call():
209
- dedup = fb.MinHashDeduplicator(0.8, 5, 64)
210
- for r in sample_records:
211
- dedup.is_unique(r)
212
- return dedup.count()
213
-
214
- return run_timed_benchmark(
215
- name="MinHashDeduplicator.is_unique (64-Permutation MinHash Signatures)",
216
- rust_fn=rust_call,
217
- fallback_fn=fallback_call,
218
- workload_size=len(sample_records),
219
- data_bytes=sample_bytes,
220
- )
221
-
222
-
223
- def benchmark_state_tracker() -> BenchmarkResult:
224
- with tempfile.TemporaryDirectory() as tmpdir:
225
- rust_wal = Path(tmpdir) / "rust_tracker.wal"
226
- fb_wal = Path(tmpdir) / "fb_tracker.wal"
227
-
228
- rust_tracker = rs.StateTracker(str(rust_wal))
229
- fallback_tracker = fb.StateTracker(str(fb_wal))
230
-
231
- num_commits = 2000
232
-
233
- def rust_call():
234
- for i in range(num_commits):
235
- rust_tracker.update(1, 1, i * 32, i * 1024, 0)
236
- rust_tracker.commit()
237
-
238
- def fallback_call():
239
- for i in range(num_commits):
240
- fallback_tracker.update(1, 1, i * 32, i * 1024, 0)
241
- fallback_tracker.commit()
242
-
243
- return run_timed_benchmark(
244
- name="StateTracker.update & commit (128-Byte Atomic CRC32 WAL)",
245
- rust_fn=rust_call,
246
- fallback_fn=fallback_call,
247
- workload_size=num_commits,
248
- data_bytes=num_commits * 128,
249
- )
250
-
251
-
252
- def benchmark_string_tensor(records: List[str], total_bytes: int) -> BenchmarkResult:
253
- def rust_call():
254
- st = rs.StringTensor.from_list(records)
255
- hashes = st.hash_all()
256
- filtered = st.filter_by_length(20, 1000)
257
- return len(filtered.to_list()), len(hashes)
258
-
259
- def fallback_call():
260
- st = fb.StringTensor.from_list(records)
261
- hashes = st.hash_all()
262
- filtered = st.filter_by_length(20, 1000)
263
- return len(filtered.to_list()), len(hashes)
264
-
265
- return run_timed_benchmark(
266
- name="StringTensor (Umbra-style Layout, Hash All & Length Filter)",
267
- rust_fn=rust_call,
268
- fallback_fn=fallback_call,
269
- workload_size=len(records),
270
- data_bytes=total_bytes,
271
- )
272
-
273
-
274
- def benchmark_fused_pipeline(records: List[str], total_bytes: int) -> BenchmarkResult:
275
- def rust_call():
276
- dedup = rs.StreamDeduplicator()
277
- st = rs.fused_pipeline_process_batch(
278
- records=records,
279
- trim_whitespace=True,
280
- normalize_newlines=True,
281
- min_byte_len=10,
282
- max_byte_len=10000,
283
- truncate_over_max=False,
284
- json_extract_key=None,
285
- filter_quality=True,
286
- min_chars=10,
287
- max_chars=10000,
288
- min_words=3,
289
- min_entropy=2.0,
290
- max_repetition_ratio=0.3,
291
- max_symbol_ratio=0.25,
292
- deduplicator=dedup,
293
- commit_dedup=True,
294
- )
295
- return st.total_bytes()
296
-
297
- def fallback_call():
298
- dedup = fb.StreamDeduplicator()
299
- st = fb.fused_pipeline_process_batch(
300
- records=records,
301
- trim_whitespace=True,
302
- normalize_newlines=True,
303
- min_byte_len=10,
304
- max_byte_len=10000,
305
- truncate_over_max=False,
306
- json_extract_key=None,
307
- filter_quality=True,
308
- min_chars=10,
309
- max_chars=10000,
310
- min_words=3,
311
- min_entropy=2.0,
312
- max_repetition_ratio=0.3,
313
- max_symbol_ratio=0.25,
314
- deduplicator=dedup,
315
- commit_dedup=True,
316
- )
317
- return st.total_bytes()
318
-
319
- return run_timed_benchmark(
320
- name="FusedPipeline.fused_process_batch (End-to-End SIMD Single Pass)",
321
- rust_fn=rust_call,
322
- fallback_fn=fallback_call,
323
- workload_size=len(records),
324
- data_bytes=total_bytes,
325
- )
326
-
327
-
328
- def benchmark_serialization(records: List[str]) -> List[BenchmarkResult]:
329
- cols = {
330
- "id": [str(i) for i in range(len(records))],
331
- "text": records,
332
- "score": [str(round(math.sin(i) * 10, 4)) for i in range(len(records))],
333
- }
334
- total_data_bytes = sum(len(c.encode("utf-8")) for col in cols.values() for c in col)
335
-
336
- results = []
337
- for fmt in ["parquet", "arrow", "jsonl", "csv"]:
338
- def rust_call(fmt=fmt):
339
- return rs.serialize_table_to_bytes(cols, fmt, False)
340
-
341
- def fallback_call(fmt=fmt):
342
- return fb.serialize_table_to_bytes(cols, fmt, False)
343
-
344
- res = run_timed_benchmark(
345
- name=f"Serialization: {fmt.upper()}",
346
- rust_fn=rust_call,
347
- fallback_fn=fallback_call,
348
- workload_size=len(records),
349
- data_bytes=total_data_bytes,
350
- )
351
- results.append(res)
352
- return results
353
-
354
-
355
- def main() -> None:
356
- print("=================================================================")
357
- print(" LuminaMoon Data Processing Engine Benchmark Suite")
358
- print(f" CPU Architecture / SIMD ISA: {rs.dataloader_cpu_isa()}")
359
- print("=================================================================\n")
360
-
361
- num_records = 10000
362
- print(f"Generating synthetic workload ({num_records:,} records)...")
363
- records, total_bytes = generate_synthetic_dataset(num_records=num_records)
364
- print(f"Generated {len(records):,} records, {total_bytes / (1024*1024):.2f} MB payload.\n")
365
-
366
- benchmarks = [
367
- ("Text Cleaner", lambda: benchmark_text_cleaner(records, total_bytes)),
368
- ("Quality Filter", lambda: benchmark_quality_filter(records, total_bytes)),
369
- ("Stream Deduplicator", lambda: benchmark_stream_dedup(records, total_bytes)),
370
- ("MinHash Deduplicator", lambda: benchmark_minhash_dedup(records, total_bytes)),
371
- ("State Tracker", lambda: benchmark_state_tracker()),
372
- ("String Tensor", lambda: benchmark_string_tensor(records, total_bytes)),
373
- ("Fused Pipeline", lambda: benchmark_fused_pipeline(records, total_bytes)),
374
- ]
375
-
376
- all_results: List[BenchmarkResult] = []
377
-
378
- for label, bench_fn in benchmarks:
379
- print(f"Running benchmark: {label}...")
380
- res = bench_fn()
381
- all_results.append(res)
382
- print(f" -> Rust: {res.rust_throughput_items_per_sec:>10,.0f} items/s ({res.rust_throughput_mb_per_sec:>8.2f} MB/s, {res.rust_duration_sec*1000:>7.2f} ms)")
383
- print(f" -> Fallback: {res.fallback_throughput_items_per_sec:>10,.0f} items/s ({res.fallback_throughput_mb_per_sec:>8.2f} MB/s, {res.fallback_duration_sec*1000:>7.2f} ms)")
384
- print(f" -> Speedup: {res.speedup_ratio:>7.2f}x\n")
385
-
386
- print("Running Serialization Benchmarks...")
387
- ser_results = benchmark_serialization(records[:5000])
388
- for res in ser_results:
389
- all_results.append(res)
390
- print(f" -> {res.benchmark_name:<28}: Rust {res.rust_throughput_mb_per_sec:>8.2f} MB/s vs Fallback {res.fallback_throughput_mb_per_sec:>8.2f} MB/s (Speedup: {res.speedup_ratio:>6.2f}x)")
391
-
392
- # Save output json
393
- out_dir = Path(__file__).resolve().parent.parent / "logs"
394
- out_dir.mkdir(parents=True, exist_ok=True)
395
- out_file = out_dir / "benchmark_data_processing_results.json"
396
- with open(out_file, "w", encoding="utf-8") as f:
397
- json.dump(
398
- {
399
- "cpu_isa": rs.dataloader_cpu_isa(),
400
- "num_records": num_records,
401
- "total_mb": total_bytes / (1024 * 1024),
402
- "benchmarks": [asdict(r) for r in all_results],
403
- },
404
- f,
405
- indent=2,
406
- )
407
- print(f"\nSaved raw benchmark metrics to {out_file}")
408
-
409
-
410
- if __name__ == "__main__":
411
- main()
 
1
+ """LuminaMoon Data Processing Engine Benchmark.
2
+
3
+ Compares Native Rust SIMD Accelerator (AVX-512 / AVX2 / Rayon) against
4
+ Pure Python Fallbacks across micro-benchmarks and end-to-end pipelines.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ import math
11
+ import os
12
+ import random
13
+ import sys
14
+ import tempfile
15
+ import time
16
+ from dataclasses import asdict, dataclass
17
+ from pathlib import Path
18
+ from typing import Any, Callable, Dict, List, Tuple
19
+
20
+ # Ensure local python package is discoverable
21
+ sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "python"))
22
+
23
+ from luminamoon.optimizer import _luminamoon_optimizer_rs as rs
24
+ from luminamoon.data import fallbacks as fb
25
+
26
+
27
+ @dataclass
28
+ class BenchmarkResult:
29
+ benchmark_name: str
30
+ workload_size: int
31
+ data_bytes: int
32
+ rust_duration_sec: float
33
+ rust_throughput_items_per_sec: float
34
+ rust_throughput_mb_per_sec: float
35
+ fallback_duration_sec: float
36
+ fallback_throughput_items_per_sec: float
37
+ fallback_throughput_mb_per_sec: float
38
+ speedup_ratio: float
39
+
40
+
41
+ def generate_synthetic_dataset(num_records: int = 10000, seed: int = 42) -> Tuple[List[str], int]:
42
+ rng = random.Random(seed)
43
+ templates = [
44
+ " The quick brown fox jumps over the lazy dog.\r\n This is line 2 with carriage returns.\r\n",
45
+ "Deep learning optimization requires efficient gradient descent with momentum and adaptive learning rates.\n",
46
+ '{"text": "{\"key\": \"value\", \"nested\": [1, 2, 3]}", "meta": "test record"}\r\n',
47
+ "System security alert: detected unauthorized access attempt from 192.168.1.105 on port 443.\n",
48
+ "Contact user support at alice.walker@enterprise-domain.org or call +1-800-555-0199 for assistance.\r\n",
49
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n", # high repetition
50
+ "Short text.\n",
51
+ "Mathematics: Pade [13/6] approximant ensures subnormal FP16 RNE preservation without overflow.\n",
52
+ "High symbol noise: #$%@!&*()!@#$%^&*()_+{}|:<>?~`!@#$%^&*()_+{}|:<>?~`\n",
53
+ "Large document block: " + ("lorem ipsum dolor sit amet " * 20) + "\r\n",
54
+ ]
55
+ records = []
56
+ total_bytes = 0
57
+ for i in range(num_records):
58
+ base = templates[i % len(templates)]
59
+ noise = f" [doc_id={i}_{rng.randint(100, 999)}]"
60
+ rec = base + noise
61
+ records.append(rec)
62
+ total_bytes += len(rec.encode("utf-8"))
63
+ return records, total_bytes
64
+
65
+
66
+ def run_timed_benchmark(
67
+ name: str,
68
+ rust_fn: Callable[[], Any],
69
+ fallback_fn: Callable[[], Any],
70
+ workload_size: int,
71
+ data_bytes: int,
72
+ warmup_runs: int = 2,
73
+ test_runs: int = 5,
74
+ ) -> BenchmarkResult:
75
+ # Warmup Rust
76
+ for _ in range(warmup_runs):
77
+ rust_fn()
78
+ # Timed Rust
79
+ rust_times = []
80
+ for _ in range(test_runs):
81
+ t0 = time.perf_counter_ns()
82
+ rust_fn()
83
+ t1 = time.perf_counter_ns()
84
+ rust_times.append((t1 - t0) / 1e9)
85
+ rust_dur = min(rust_times) # best of N runs
86
+
87
+ # Warmup Fallback
88
+ for _ in range(warmup_runs):
89
+ fallback_fn()
90
+ # Timed Fallback
91
+ fallback_times = []
92
+ for _ in range(test_runs):
93
+ t0 = time.perf_counter_ns()
94
+ fallback_fn()
95
+ t1 = time.perf_counter_ns()
96
+ fallback_times.append((t1 - t0) / 1e9)
97
+ fallback_dur = min(fallback_times)
98
+
99
+ rust_items_per_sec = workload_size / max(rust_dur, 1e-9)
100
+ rust_mb_per_sec = (data_bytes / (1024 * 1024)) / max(rust_dur, 1e-9)
101
+
102
+ fallback_items_per_sec = workload_size / max(fallback_dur, 1e-9)
103
+ fallback_mb_per_sec = (data_bytes / (1024 * 1024)) / max(fallback_dur, 1e-9)
104
+
105
+ speedup = fallback_dur / max(rust_dur, 1e-9)
106
+
107
+ return BenchmarkResult(
108
+ benchmark_name=name,
109
+ workload_size=workload_size,
110
+ data_bytes=data_bytes,
111
+ rust_duration_sec=rust_dur,
112
+ rust_throughput_items_per_sec=rust_items_per_sec,
113
+ rust_throughput_mb_per_sec=rust_mb_per_sec,
114
+ fallback_duration_sec=fallback_dur,
115
+ fallback_throughput_items_per_sec=fallback_items_per_sec,
116
+ fallback_throughput_mb_per_sec=fallback_mb_per_sec,
117
+ speedup_ratio=speedup,
118
+ )
119
+
120
+
121
+ def benchmark_text_cleaner(records: List[str], total_bytes: int) -> BenchmarkResult:
122
+ def rust_call():
123
+ return rs.clean_batch_records(records, True, True, 10, 10000, False, None)
124
+
125
+ def fallback_call():
126
+ return fb.clean_batch_records(records, True, True, 10, 10000, False, None)
127
+
128
+ return run_timed_benchmark(
129
+ name="TextCleaner.clean_batch (CRLF + Trimming + Bounds)",
130
+ rust_fn=rust_call,
131
+ fallback_fn=fallback_call,
132
+ workload_size=len(records),
133
+ data_bytes=total_bytes,
134
+ )
135
+
136
+
137
+ def benchmark_quality_filter(records: List[str], total_bytes: int) -> BenchmarkResult:
138
+ rust_filter = rs.QualityFilter(
139
+ min_chars=10,
140
+ max_chars=20000,
141
+ min_words=3,
142
+ min_entropy=2.0,
143
+ max_repetition_ratio=0.3,
144
+ max_symbol_ratio=0.25,
145
+ )
146
+ fallback_filter = fb.QualityFilter(
147
+ min_chars=10,
148
+ max_chars=20000,
149
+ min_words=3,
150
+ min_entropy=2.0,
151
+ max_repetition_ratio=0.3,
152
+ max_symbol_ratio=0.25,
153
+ )
154
+
155
+ def rust_call():
156
+ return rust_filter.filter_batch(records)
157
+
158
+ def fallback_call():
159
+ return fallback_filter.filter_batch(records)
160
+
161
+ return run_timed_benchmark(
162
+ name="QualityFilter.filter_batch (Entropy + Repetition + Symbols)",
163
+ rust_fn=rust_call,
164
+ fallback_fn=fallback_call,
165
+ workload_size=len(records),
166
+ data_bytes=total_bytes,
167
+ )
168
+
169
+
170
+ def benchmark_stream_dedup(records: List[str], total_bytes: int) -> BenchmarkResult:
171
+ # Inject 25% duplicate records
172
+ dup_records = list(records)
173
+ for i in range(len(dup_records) // 4):
174
+ dup_records[i * 4] = dup_records[i * 4 + 1]
175
+
176
+ def rust_call():
177
+ dedup = rs.StreamDeduplicator()
178
+ passed = dedup.filter_batch(dup_records)
179
+ dedup.commit()
180
+ return passed
181
+
182
+ def fallback_call():
183
+ dedup = fb.StreamDeduplicator()
184
+ passed = dedup.filter_batch(dup_records)
185
+ dedup.commit()
186
+ return passed
187
+
188
+ return run_timed_benchmark(
189
+ name="StreamDeduplicator.filter_batch (Exact XXH3-64 Staging & Commit)",
190
+ rust_fn=rust_call,
191
+ fallback_fn=fallback_call,
192
+ workload_size=len(dup_records),
193
+ data_bytes=total_bytes,
194
+ )
195
+
196
+
197
+ def benchmark_minhash_dedup(records: List[str], total_bytes: int) -> BenchmarkResult:
198
+ # Use 500 documents for MinHash permutation benchmarking
199
+ sample_records = records[:500]
200
+ sample_bytes = sum(len(r.encode("utf-8")) for r in sample_records)
201
+
202
+ def rust_call():
203
+ dedup = rs.MinHashDeduplicator(0.8, 5, 64)
204
+ for r in sample_records:
205
+ dedup.is_unique(r)
206
+ return dedup.count()
207
+
208
+ def fallback_call():
209
+ dedup = fb.MinHashDeduplicator(0.8, 5, 64)
210
+ for r in sample_records:
211
+ dedup.is_unique(r)
212
+ return dedup.count()
213
+
214
+ return run_timed_benchmark(
215
+ name="MinHashDeduplicator.is_unique (64-Permutation MinHash Signatures)",
216
+ rust_fn=rust_call,
217
+ fallback_fn=fallback_call,
218
+ workload_size=len(sample_records),
219
+ data_bytes=sample_bytes,
220
+ )
221
+
222
+
223
+ def benchmark_state_tracker() -> BenchmarkResult:
224
+ with tempfile.TemporaryDirectory() as tmpdir:
225
+ rust_wal = Path(tmpdir) / "rust_tracker.wal"
226
+ fb_wal = Path(tmpdir) / "fb_tracker.wal"
227
+
228
+ rust_tracker = rs.StateTracker(str(rust_wal))
229
+ fallback_tracker = fb.StateTracker(str(fb_wal))
230
+
231
+ num_commits = 2000
232
+
233
+ def rust_call():
234
+ for i in range(num_commits):
235
+ rust_tracker.update(1, 1, i * 32, i * 1024, 0)
236
+ rust_tracker.commit()
237
+
238
+ def fallback_call():
239
+ for i in range(num_commits):
240
+ fallback_tracker.update(1, 1, i * 32, i * 1024, 0)
241
+ fallback_tracker.commit()
242
+
243
+ return run_timed_benchmark(
244
+ name="StateTracker.update & commit (128-Byte Atomic CRC32 WAL)",
245
+ rust_fn=rust_call,
246
+ fallback_fn=fallback_call,
247
+ workload_size=num_commits,
248
+ data_bytes=num_commits * 128,
249
+ )
250
+
251
+
252
+ def benchmark_string_tensor(records: List[str], total_bytes: int) -> BenchmarkResult:
253
+ def rust_call():
254
+ st = rs.StringTensor.from_list(records)
255
+ hashes = st.hash_all()
256
+ filtered = st.filter_by_length(20, 1000)
257
+ return len(filtered.to_list()), len(hashes)
258
+
259
+ def fallback_call():
260
+ st = fb.StringTensor.from_list(records)
261
+ hashes = st.hash_all()
262
+ filtered = st.filter_by_length(20, 1000)
263
+ return len(filtered.to_list()), len(hashes)
264
+
265
+ return run_timed_benchmark(
266
+ name="StringTensor (Umbra-style Layout, Hash All & Length Filter)",
267
+ rust_fn=rust_call,
268
+ fallback_fn=fallback_call,
269
+ workload_size=len(records),
270
+ data_bytes=total_bytes,
271
+ )
272
+
273
+
274
+ def benchmark_fused_pipeline(records: List[str], total_bytes: int) -> BenchmarkResult:
275
+ def rust_call():
276
+ dedup = rs.StreamDeduplicator()
277
+ st = rs.fused_pipeline_process_batch(
278
+ records=records,
279
+ trim_whitespace=True,
280
+ normalize_newlines=True,
281
+ min_byte_len=10,
282
+ max_byte_len=10000,
283
+ truncate_over_max=False,
284
+ json_extract_key=None,
285
+ filter_quality=True,
286
+ min_chars=10,
287
+ max_chars=10000,
288
+ min_words=3,
289
+ min_entropy=2.0,
290
+ max_repetition_ratio=0.3,
291
+ max_symbol_ratio=0.25,
292
+ deduplicator=dedup,
293
+ commit_dedup=True,
294
+ )
295
+ return st.total_bytes()
296
+
297
+ def fallback_call():
298
+ dedup = fb.StreamDeduplicator()
299
+ st = fb.fused_pipeline_process_batch(
300
+ records=records,
301
+ trim_whitespace=True,
302
+ normalize_newlines=True,
303
+ min_byte_len=10,
304
+ max_byte_len=10000,
305
+ truncate_over_max=False,
306
+ json_extract_key=None,
307
+ filter_quality=True,
308
+ min_chars=10,
309
+ max_chars=10000,
310
+ min_words=3,
311
+ min_entropy=2.0,
312
+ max_repetition_ratio=0.3,
313
+ max_symbol_ratio=0.25,
314
+ deduplicator=dedup,
315
+ commit_dedup=True,
316
+ )
317
+ return st.total_bytes()
318
+
319
+ return run_timed_benchmark(
320
+ name="FusedPipeline.fused_process_batch (End-to-End SIMD Single Pass)",
321
+ rust_fn=rust_call,
322
+ fallback_fn=fallback_call,
323
+ workload_size=len(records),
324
+ data_bytes=total_bytes,
325
+ )
326
+
327
+
328
+ def benchmark_serialization(records: List[str]) -> List[BenchmarkResult]:
329
+ cols = {
330
+ "id": [str(i) for i in range(len(records))],
331
+ "text": records,
332
+ "score": [str(round(math.sin(i) * 10, 4)) for i in range(len(records))],
333
+ }
334
+ total_data_bytes = sum(len(c.encode("utf-8")) for col in cols.values() for c in col)
335
+
336
+ results = []
337
+ for fmt in ["parquet", "arrow", "jsonl", "csv"]:
338
+ def rust_call(fmt=fmt):
339
+ return rs.serialize_table_to_bytes(cols, fmt, False)
340
+
341
+ def fallback_call(fmt=fmt):
342
+ return fb.serialize_table_to_bytes(cols, fmt, False)
343
+
344
+ res = run_timed_benchmark(
345
+ name=f"Serialization: {fmt.upper()}",
346
+ rust_fn=rust_call,
347
+ fallback_fn=fallback_call,
348
+ workload_size=len(records),
349
+ data_bytes=total_data_bytes,
350
+ )
351
+ results.append(res)
352
+ return results
353
+
354
+
355
+ def main() -> None:
356
+ print("=================================================================")
357
+ print(" LuminaMoon Data Processing Engine Benchmark Suite")
358
+ print(f" CPU Architecture / SIMD ISA: {rs.dataloader_cpu_isa()}")
359
+ print("=================================================================\n")
360
+
361
+ num_records = 10000
362
+ print(f"Generating synthetic workload ({num_records:,} records)...")
363
+ records, total_bytes = generate_synthetic_dataset(num_records=num_records)
364
+ print(f"Generated {len(records):,} records, {total_bytes / (1024*1024):.2f} MB payload.\n")
365
+
366
+ benchmarks = [
367
+ ("Text Cleaner", lambda: benchmark_text_cleaner(records, total_bytes)),
368
+ ("Quality Filter", lambda: benchmark_quality_filter(records, total_bytes)),
369
+ ("Stream Deduplicator", lambda: benchmark_stream_dedup(records, total_bytes)),
370
+ ("MinHash Deduplicator", lambda: benchmark_minhash_dedup(records, total_bytes)),
371
+ ("State Tracker", lambda: benchmark_state_tracker()),
372
+ ("String Tensor", lambda: benchmark_string_tensor(records, total_bytes)),
373
+ ("Fused Pipeline", lambda: benchmark_fused_pipeline(records, total_bytes)),
374
+ ]
375
+
376
+ all_results: List[BenchmarkResult] = []
377
+
378
+ for label, bench_fn in benchmarks:
379
+ print(f"Running benchmark: {label}...")
380
+ res = bench_fn()
381
+ all_results.append(res)
382
+ print(f" -> Rust: {res.rust_throughput_items_per_sec:>10,.0f} items/s ({res.rust_throughput_mb_per_sec:>8.2f} MB/s, {res.rust_duration_sec*1000:>7.2f} ms)")
383
+ print(f" -> Fallback: {res.fallback_throughput_items_per_sec:>10,.0f} items/s ({res.fallback_throughput_mb_per_sec:>8.2f} MB/s, {res.fallback_duration_sec*1000:>7.2f} ms)")
384
+ print(f" -> Speedup: {res.speedup_ratio:>7.2f}x\n")
385
+
386
+ print("Running Serialization Benchmarks...")
387
+ ser_results = benchmark_serialization(records[:5000])
388
+ for res in ser_results:
389
+ all_results.append(res)
390
+ print(f" -> {res.benchmark_name:<28}: Rust {res.rust_throughput_mb_per_sec:>8.2f} MB/s vs Fallback {res.fallback_throughput_mb_per_sec:>8.2f} MB/s (Speedup: {res.speedup_ratio:>6.2f}x)")
391
+
392
+ # Save output json
393
+ out_dir = Path(__file__).resolve().parent.parent / "logs"
394
+ out_dir.mkdir(parents=True, exist_ok=True)
395
+ out_file = out_dir / "benchmark_data_processing_results.json"
396
+ with open(out_file, "w", encoding="utf-8") as f:
397
+ json.dump(
398
+ {
399
+ "cpu_isa": rs.dataloader_cpu_isa(),
400
+ "num_records": num_records,
401
+ "total_mb": total_bytes / (1024 * 1024),
402
+ "benchmarks": [asdict(r) for r in all_results],
403
+ },
404
+ f,
405
+ indent=2,
406
+ )
407
+ print(f"\nSaved raw benchmark metrics to {out_file}")
408
+
409
+
410
+ if __name__ == "__main__":
411
+ main()
benchmarks/bench_luminav_vision_modes.py CHANGED
@@ -1,398 +1,398 @@
1
- #!/usr/bin/env python3
2
- """Benchmark and Comparative Suite for LuminaV across all execution and buffer modes.
3
-
4
- Tests training performance on highly complex, pathological synthetic image data
5
- (multi-octave fractal turbulence, non-stationary Moire/Chirp waves, chaotic manifolds)
6
- using a deep convolutional vision architecture (ResNet-style ConvNet with residual blocks).
7
- """
8
-
9
- from __future__ import annotations
10
-
11
- import math
12
- import os
13
- import sys
14
- import time
15
- from typing import Any, Dict, List, Tuple
16
-
17
- import numpy as np
18
- import torch
19
- import torch.nn as nn
20
- import torch.nn.functional as F
21
-
22
- from luminamoon import LuminaV
23
-
24
-
25
- # 1. Pathological / Highly Complex Synthetic Vision Dataset Generation
26
- def generate_hard_synthetic_vision_dataset(
27
- num_samples: int = 256,
28
- height: int = 64,
29
- width: int = 64,
30
- num_classes: int = 8,
31
- seed: int = 2026,
32
- ) -> Tuple[torch.Tensor, torch.Tensor]:
33
- """Generates an extremely difficult synthetic image dataset.
34
-
35
- Features:
36
- - Channel 0: Multi-octave Fractal Brownian Motion (fBm) with non-stationary phase shifts.
37
- - Channel 1: High-frequency chirp & radial Moire vortex with cross-quadrature modulation.
38
- - Channel 2: Chaotic double-well reaction-diffusion attractor pattern with high spatial curvature.
39
- - Target: Non-linear topological invariant coupling cross-channel gradients with label ambiguity.
40
- """
41
- rng = np.random.RandomState(seed)
42
-
43
- # Coordinate grids normalized to [-2, 2]
44
- y_coords, x_coords = np.meshgrid(
45
- np.linspace(-2.0, 2.0, height, dtype=np.float32),
46
- np.linspace(-2.0, 2.0, width, dtype=np.float32),
47
- indexing="ij",
48
- )
49
- r_sq = x_coords**2 + y_coords**2
50
- theta = np.arctan2(y_coords, x_coords)
51
-
52
- images = np.zeros((num_samples, 3, height, width), dtype=np.float32)
53
- labels = np.zeros((num_samples,), dtype=np.int64)
54
-
55
- for i in range(num_samples):
56
- # Sample-specific chaotic random parameters
57
- omega = rng.uniform(8.0, 18.0)
58
- phi = rng.uniform(0.0, 2.0 * math.pi)
59
- k_oct = rng.uniform(3.0, 7.0)
60
-
61
- # Ch 0: Fractal multi-octave turbulence
62
- ch0 = np.zeros((height, width), dtype=np.float32)
63
- for octave in range(1, 5):
64
- freq = 2.0**octave
65
- amp = 1.0 / freq
66
- angle = phi + octave * 0.785
67
- proj = x_coords * math.cos(angle) + y_coords * math.sin(angle)
68
- ch0 += amp * np.sin(freq * k_oct * proj + phi)
69
- # Add high-frequency micro-checkerboard jitter
70
- checker = ((np.arange(height)[:, None] + np.arange(width)[None, :]) % 2) * 2.0 - 1.0
71
- ch0 += 0.25 * checker * np.cos(omega * r_sq)
72
-
73
- # Ch 1: High-frequency Chirp / Moire radial vortex
74
- ch1 = np.sin(omega * r_sq + 4.0 * theta + phi) * np.cos(k_oct * x_coords * y_coords)
75
- # Cross-quadrature modulation
76
- ch1 += 0.35 * np.cos(15.0 * (x_coords + y_coords) + phi)
77
-
78
- # Ch 2: Chaotic double-well / non-linear attractor
79
- poly = (x_coords**3 - 3.0 * x_coords * (y_coords**2)) * 0.5
80
- ch2 = np.tanh(poly * 2.0 + np.cos(omega * y_coords))
81
- ch2 += 0.3 * np.sin(20.0 * x_coords + phi) * np.sin(20.0 * y_coords)
82
-
83
- # Combine channels and normalize
84
- images[i, 0] = (ch0 - ch0.mean()) / (ch0.std() + 1e-6)
85
- images[i, 1] = (ch1 - ch1.mean()) / (ch1.std() + 1e-6)
86
- images[i, 2] = (ch2 - ch2.mean()) / (ch2.std() + 1e-6)
87
-
88
- # Compute complex topological functional for target class:
89
- # Cross-gradient winding energy + multi-channel nonlinear coupling
90
- dx_ch1 = np.gradient(ch1, axis=1)
91
- dy_ch2 = np.gradient(ch2, axis=0)
92
- curl_cross = np.mean(ch0 * (dx_ch1 * dy_ch2))
93
- vol = np.mean(ch0 * ch1 * ch2)
94
- quad = np.mean(np.sin(r_sq * 5.0) * ch0)
95
-
96
- # Map to discrete class [0..num_classes-1] via non-linear hash
97
- hash_val = math.sin(curl_cross * 25.0 + vol * 15.0 + quad * 10.0 + phi)
98
- class_idx = int(math.floor(((hash_val + 1.0) / 2.0) * num_classes)) % num_classes
99
-
100
- # Inject 10% label ambiguity / boundary noise to test cautious clamping & regularization
101
- if rng.rand() < 0.10:
102
- class_idx = rng.randint(0, num_classes)
103
-
104
- labels[i] = class_idx
105
-
106
- return torch.from_numpy(images), torch.from_numpy(labels)
107
-
108
-
109
- # 2. Deep Convolutional Vision Architecture (ResNet-Lite)
110
- class ResidualConvBlock(nn.Module):
111
- def __init__(self, channels: int):
112
- super().__init__()
113
- self.conv1 = nn.Conv2d(channels, channels, kernel_size=3, padding=1, bias=False)
114
- self.bn1 = nn.BatchNorm2d(channels)
115
- self.conv2 = nn.Conv2d(channels, channels, kernel_size=3, padding=1, bias=False)
116
- self.bn2 = nn.BatchNorm2d(channels)
117
-
118
- def forward(self, x: torch.Tensor) -> torch.Tensor:
119
- res = x
120
- out = F.silu(self.bn1(self.conv1(x)))
121
- out = self.bn2(self.conv2(out))
122
- return F.silu(out + res)
123
-
124
-
125
- class HardVisionConvNet(nn.Module):
126
- def __init__(self, in_channels: int = 3, num_classes: int = 8):
127
- super().__init__()
128
- # Initial stem
129
- self.stem = nn.Sequential(
130
- nn.Conv2d(in_channels, 32, kernel_size=3, padding=1, bias=False),
131
- nn.BatchNorm2d(32),
132
- nn.SiLU(),
133
- )
134
- # Stage 1 (32 channels)
135
- self.res1 = ResidualConvBlock(32)
136
- self.pool1 = nn.MaxPool2d(2) # 64 -> 32
137
-
138
- # Stage 2 (64 channels)
139
- self.trans1 = nn.Sequential(
140
- nn.Conv2d(32, 64, kernel_size=3, padding=1, bias=False),
141
- nn.BatchNorm2d(64),
142
- nn.SiLU(),
143
- )
144
- self.res2 = ResidualConvBlock(64)
145
- self.pool2 = nn.MaxPool2d(2) # 32 -> 16
146
-
147
- # Stage 3 (128 channels)
148
- self.trans2 = nn.Sequential(
149
- nn.Conv2d(64, 128, kernel_size=3, padding=1, bias=False),
150
- nn.BatchNorm2d(128),
151
- nn.SiLU(),
152
- )
153
- self.res3 = ResidualConvBlock(128)
154
- self.pool3 = nn.AdaptiveAvgPool2d((2, 2)) # 128 x 2 x 2 = 512
155
-
156
- # Classification Head
157
- self.classifier = nn.Sequential(
158
- nn.Linear(512, 128),
159
- nn.SiLU(),
160
- nn.Dropout(0.1),
161
- nn.Linear(128, num_classes),
162
- )
163
-
164
- def forward(self, x: torch.Tensor) -> torch.Tensor:
165
- x = self.stem(x)
166
- x = self.res1(x)
167
- x = self.pool1(x)
168
- x = self.trans1(x)
169
- x = self.res2(x)
170
- x = self.pool2(x)
171
- x = self.trans2(x)
172
- x = self.res3(x)
173
- x = self.pool3(x)
174
- x = torch.flatten(x, 1)
175
- return self.classifier(x)
176
-
177
-
178
- # 3. Benchmark Training Routine for Single Configuration
179
- def run_benchmark_trial(
180
- config_name: str,
181
- optimizer_fn: Any,
182
- images: torch.Tensor,
183
- labels: torch.Tensor,
184
- batch_size: int = 64,
185
- steps: int = 20,
186
- seed: int = 42,
187
- ) -> Dict[str, Any]:
188
- torch.manual_seed(seed)
189
- np.random.seed(seed)
190
-
191
- model = HardVisionConvNet(in_channels=3, num_classes=8)
192
- criterion = nn.CrossEntropyLoss()
193
- optimizer = optimizer_fn(model.parameters())
194
-
195
- total_params = sum(p.numel() for p in model.parameters())
196
- num_tensors = sum(1 for p in model.parameters())
197
-
198
- losses: List[float] = []
199
- step_times: List[float] = []
200
- accuracies: List[float] = []
201
-
202
- num_samples = images.shape[0]
203
-
204
- # Pre-split into deterministic batches
205
- batch_indices = [
206
- slice(
207
- (s * batch_size) % (num_samples - batch_size),
208
- ((s * batch_size) % (num_samples - batch_size)) + batch_size,
209
- )
210
- for s in range(steps)
211
- ]
212
-
213
- total_start = time.perf_counter()
214
-
215
- for step_idx in range(steps):
216
- b_slice = batch_indices[step_idx]
217
- batch_x = images[b_slice]
218
- batch_y = labels[b_slice]
219
-
220
- t0 = time.perf_counter()
221
-
222
- optimizer.zero_grad(set_to_none=True)
223
- logits = model(batch_x)
224
- loss = criterion(logits, batch_y)
225
- loss.backward()
226
- optimizer.step()
227
-
228
- t1 = time.perf_counter()
229
-
230
- loss_val = float(loss.item())
231
- losses.append(loss_val)
232
- step_times.append((t1 - t0) * 1000.0) # ms
233
-
234
- preds = torch.argmax(logits, dim=1)
235
- acc = float((preds == batch_y).float().mean().item()) * 100.0
236
- accuracies.append(acc)
237
-
238
- total_duration_ms = (time.perf_counter() - total_start) * 1000.0
239
-
240
- # Calculate optimizer state memory footprint
241
- opt_state_bytes = 0
242
- if hasattr(optimizer, "state"):
243
- for p, st in optimizer.state.items():
244
- if isinstance(st, dict):
245
- for k, v in st.items():
246
- if isinstance(v, torch.Tensor):
247
- opt_state_bytes += v.numel() * v.element_size()
248
- elif isinstance(v, np.ndarray):
249
- opt_state_bytes += v.nbytes
250
-
251
- initial_loss = losses[0]
252
- final_loss = losses[-1]
253
- pct_drop = ((initial_loss - final_loss) / initial_loss) * 100.0
254
- avg_step_ms = float(np.mean(step_times))
255
-
256
- return {
257
- "name": config_name,
258
- "initial_loss": initial_loss,
259
- "final_loss": final_loss,
260
- "loss_reduction_pct": pct_drop,
261
- "final_accuracy": accuracies[-1],
262
- "total_time_ms": total_duration_ms,
263
- "avg_step_ms": avg_step_ms,
264
- "state_mem_mb": opt_state_bytes / (1024.0 * 1024.0),
265
- "total_params": total_params,
266
- "num_tensors": num_tensors,
267
- "losses": losses,
268
- }
269
-
270
-
271
- # 4. Main Suite Comparing All Modes
272
- def main():
273
- print("=" * 80)
274
- print("LuminaV Vision Mode Benchmark & Comparative Training Suite")
275
- print("=" * 80)
276
-
277
- hw = LuminaV.detect_hardware()
278
- print("[Hardware Environment]")
279
- print(f" CPU SIMD Vectorization: {hw.get('simd_backend')}")
280
- print(f" Vulkan GPU Acceleration: {hw.get('vulkan_available')} ({hw.get('gpu_device_name')})")
281
- print(f" Triton AOT Available: {hw.get('triton_aot_available')} (arch: {hw.get('triton_arch')})")
282
- print(f" Wave32 Hardware Support: {hw.get('wave32_supported')}")
283
- try:
284
- import triton
285
- print(f" Triton Windows Package: Version {triton.__version__} ({triton.__file__})")
286
- except Exception as e:
287
- print(f" Triton Windows Package: Failed ({e})")
288
- print("-" * 80)
289
-
290
- print("[Dataset Generation] Synthesizing pathological hard vision images (64x64, 3ch)...")
291
- images, labels = generate_hard_synthetic_vision_dataset(
292
- num_samples=256, height=64, width=64, num_classes=8, seed=2026
293
- )
294
- print(f" Generated {images.shape[0]} images | Shape: {images.shape} | Labels: {labels.shape}")
295
- print(f" Label distribution: {torch.bincount(labels).tolist()}")
296
- print("-" * 80)
297
-
298
- # Define test configurations across all LuminaV modes and architectures
299
- configurations = [
300
- # --- Triton Windows Backends ---
301
- (
302
- "LuminaV (triton, Dual-Buffer)",
303
- lambda p: LuminaV(p, lr=1e-3, buffer=2, execution="triton", bound=True, cautious=True),
304
- ),
305
- (
306
- "LuminaV (triton, Single-Buffer TD-CLM)",
307
- lambda p: LuminaV(p, lr=1e-3, buffer=1, execution="triton", bound=True, cautious=True),
308
- ),
309
- # --- Hardware Backends (Vulkan GPU, SIMD AVX-512, Scalar Core) ---
310
- (
311
- "LuminaV (vulkan, Dual-Buffer)",
312
- lambda p: LuminaV(p, lr=1e-3, buffer=2, execution="vulkan", bound=True, cautious=True),
313
- ),
314
- (
315
- "LuminaV (simd AVX-512, Dual-Buffer)",
316
- lambda p: LuminaV(p, lr=1e-3, buffer=2, execution="simd", bound=True, cautious=True),
317
- ),
318
- (
319
- "LuminaV (scalar, Dual-Buffer)",
320
- lambda p: LuminaV(p, lr=1e-3, buffer=2, execution="scalar", bound=True, cautious=True),
321
- ),
322
- # --- Buffer Architectures ---
323
- (
324
- "LuminaV (auto, Single-Buffer TD-CLM)",
325
- lambda p: LuminaV(p, lr=1e-3, buffer=1, execution="auto", bound=True, cautious=True),
326
- ),
327
- (
328
- "LuminaV (auto, Dual-Buffer CLM)",
329
- lambda p: LuminaV(p, lr=1e-3, buffer=2, execution="auto", bound=True, cautious=True),
330
- ),
331
- (
332
- "LuminaV (auto, T1 Single-Buffer Triangle)",
333
- lambda p: LuminaV(p, lr=1e-3, buffer="T1", execution="auto", bound=True, cautious=True),
334
- ),
335
- (
336
- "LuminaV (auto, T2 Dual-Buffer Triangle)",
337
- lambda p: LuminaV(p, lr=1e-3, buffer="T2", execution="auto", bound=True, cautious=True),
338
- ),
339
- # --- Advanced Vision Features ---
340
- (
341
- "LuminaV (auto, fuse_tensors=True)",
342
- lambda p: LuminaV(p, lr=1e-3, buffer=2, execution="auto", fuse_tensors=True),
343
- ),
344
- (
345
- "LuminaV (auto, radial bound)",
346
- lambda p: LuminaV(p, lr=1e-3, buffer=2, execution="auto", bound=True, bound_type="radial"),
347
- ),
348
- # --- Baseline Comparison ---
349
- (
350
- "Baseline AdamW (PyTorch)",
351
- lambda p: torch.optim.AdamW(p, lr=1e-3, betas=(0.9, 0.999), weight_decay=0.01),
352
- ),
353
- ]
354
-
355
- results: List[Dict[str, Any]] = []
356
-
357
- print("[Training Phase] Benchmarking 20 optimization steps per configuration on ResNet-Lite...")
358
- for idx, (name, opt_fn) in enumerate(configurations, 1):
359
- print(f"\n[{idx:02d}/{len(configurations):02d}] Running trial: {name} ...", flush=True)
360
- res = run_benchmark_trial(
361
- name, opt_fn, images, labels, batch_size=64, steps=20, seed=42
362
- )
363
- results.append(res)
364
- print(
365
- f" Loss: {res['initial_loss']:.4f} -> {res['final_loss']:.4f} "
366
- f"({res['loss_reduction_pct']:+.2f}%) | "
367
- f"Acc: {res['final_accuracy']:.1f}% | "
368
- f"Step: {res['avg_step_ms']:.2f} ms | "
369
- f"State Mem: {res['state_mem_mb']:.2f} MB"
370
- )
371
-
372
- print("\n" + "=" * 105)
373
- print("COMPREHENSIVE BENCHMARK COMPARISON TABLE")
374
- print("=" * 105)
375
- header = (
376
- f"{'Mode Configuration':<40} | {'Init Loss':<10} | {'Final Loss':<10} | "
377
- f"{'Reduction':<10} | {'Accuracy':<9} | {'Avg Step':<11} | {'State RAM':<9}"
378
- )
379
- print(header)
380
- print("-" * 105)
381
-
382
- for r in results:
383
- row = (
384
- f"{r['name']:<40} | "
385
- f"{r['initial_loss']:<10.4f} | "
386
- f"{r['final_loss']:<10.4f} | "
387
- f"{r['loss_reduction_pct']:>8.2f}% | "
388
- f"{r['final_accuracy']:>7.1f}% | "
389
- f"{r['avg_step_ms']:>8.2f} ms | "
390
- f"{r['state_mem_mb']:>7.2f} MB"
391
- )
392
- print(row)
393
-
394
- print("=" * 105)
395
-
396
-
397
- if __name__ == "__main__":
398
- main()
 
1
+ #!/usr/bin/env python3
2
+ """Benchmark and Comparative Suite for LuminaV across all execution and buffer modes.
3
+
4
+ Tests training performance on highly complex, pathological synthetic image data
5
+ (multi-octave fractal turbulence, non-stationary Moire/Chirp waves, chaotic manifolds)
6
+ using a deep convolutional vision architecture (ResNet-style ConvNet with residual blocks).
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import math
12
+ import os
13
+ import sys
14
+ import time
15
+ from typing import Any, Dict, List, Tuple
16
+
17
+ import numpy as np
18
+ import torch
19
+ import torch.nn as nn
20
+ import torch.nn.functional as F
21
+
22
+ from luminamoon import LuminaV
23
+
24
+
25
+ # 1. Pathological / Highly Complex Synthetic Vision Dataset Generation
26
+ def generate_hard_synthetic_vision_dataset(
27
+ num_samples: int = 256,
28
+ height: int = 64,
29
+ width: int = 64,
30
+ num_classes: int = 8,
31
+ seed: int = 2026,
32
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
33
+ """Generates an extremely difficult synthetic image dataset.
34
+
35
+ Features:
36
+ - Channel 0: Multi-octave Fractal Brownian Motion (fBm) with non-stationary phase shifts.
37
+ - Channel 1: High-frequency chirp & radial Moire vortex with cross-quadrature modulation.
38
+ - Channel 2: Chaotic double-well reaction-diffusion attractor pattern with high spatial curvature.
39
+ - Target: Non-linear topological invariant coupling cross-channel gradients with label ambiguity.
40
+ """
41
+ rng = np.random.RandomState(seed)
42
+
43
+ # Coordinate grids normalized to [-2, 2]
44
+ y_coords, x_coords = np.meshgrid(
45
+ np.linspace(-2.0, 2.0, height, dtype=np.float32),
46
+ np.linspace(-2.0, 2.0, width, dtype=np.float32),
47
+ indexing="ij",
48
+ )
49
+ r_sq = x_coords**2 + y_coords**2
50
+ theta = np.arctan2(y_coords, x_coords)
51
+
52
+ images = np.zeros((num_samples, 3, height, width), dtype=np.float32)
53
+ labels = np.zeros((num_samples,), dtype=np.int64)
54
+
55
+ for i in range(num_samples):
56
+ # Sample-specific chaotic random parameters
57
+ omega = rng.uniform(8.0, 18.0)
58
+ phi = rng.uniform(0.0, 2.0 * math.pi)
59
+ k_oct = rng.uniform(3.0, 7.0)
60
+
61
+ # Ch 0: Fractal multi-octave turbulence
62
+ ch0 = np.zeros((height, width), dtype=np.float32)
63
+ for octave in range(1, 5):
64
+ freq = 2.0**octave
65
+ amp = 1.0 / freq
66
+ angle = phi + octave * 0.785
67
+ proj = x_coords * math.cos(angle) + y_coords * math.sin(angle)
68
+ ch0 += amp * np.sin(freq * k_oct * proj + phi)
69
+ # Add high-frequency micro-checkerboard jitter
70
+ checker = ((np.arange(height)[:, None] + np.arange(width)[None, :]) % 2) * 2.0 - 1.0
71
+ ch0 += 0.25 * checker * np.cos(omega * r_sq)
72
+
73
+ # Ch 1: High-frequency Chirp / Moire radial vortex
74
+ ch1 = np.sin(omega * r_sq + 4.0 * theta + phi) * np.cos(k_oct * x_coords * y_coords)
75
+ # Cross-quadrature modulation
76
+ ch1 += 0.35 * np.cos(15.0 * (x_coords + y_coords) + phi)
77
+
78
+ # Ch 2: Chaotic double-well / non-linear attractor
79
+ poly = (x_coords**3 - 3.0 * x_coords * (y_coords**2)) * 0.5
80
+ ch2 = np.tanh(poly * 2.0 + np.cos(omega * y_coords))
81
+ ch2 += 0.3 * np.sin(20.0 * x_coords + phi) * np.sin(20.0 * y_coords)
82
+
83
+ # Combine channels and normalize
84
+ images[i, 0] = (ch0 - ch0.mean()) / (ch0.std() + 1e-6)
85
+ images[i, 1] = (ch1 - ch1.mean()) / (ch1.std() + 1e-6)
86
+ images[i, 2] = (ch2 - ch2.mean()) / (ch2.std() + 1e-6)
87
+
88
+ # Compute complex topological functional for target class:
89
+ # Cross-gradient winding energy + multi-channel nonlinear coupling
90
+ dx_ch1 = np.gradient(ch1, axis=1)
91
+ dy_ch2 = np.gradient(ch2, axis=0)
92
+ curl_cross = np.mean(ch0 * (dx_ch1 * dy_ch2))
93
+ vol = np.mean(ch0 * ch1 * ch2)
94
+ quad = np.mean(np.sin(r_sq * 5.0) * ch0)
95
+
96
+ # Map to discrete class [0..num_classes-1] via non-linear hash
97
+ hash_val = math.sin(curl_cross * 25.0 + vol * 15.0 + quad * 10.0 + phi)
98
+ class_idx = int(math.floor(((hash_val + 1.0) / 2.0) * num_classes)) % num_classes
99
+
100
+ # Inject 10% label ambiguity / boundary noise to test cautious clamping & regularization
101
+ if rng.rand() < 0.10:
102
+ class_idx = rng.randint(0, num_classes)
103
+
104
+ labels[i] = class_idx
105
+
106
+ return torch.from_numpy(images), torch.from_numpy(labels)
107
+
108
+
109
+ # 2. Deep Convolutional Vision Architecture (ResNet-Lite)
110
+ class ResidualConvBlock(nn.Module):
111
+ def __init__(self, channels: int):
112
+ super().__init__()
113
+ self.conv1 = nn.Conv2d(channels, channels, kernel_size=3, padding=1, bias=False)
114
+ self.bn1 = nn.BatchNorm2d(channels)
115
+ self.conv2 = nn.Conv2d(channels, channels, kernel_size=3, padding=1, bias=False)
116
+ self.bn2 = nn.BatchNorm2d(channels)
117
+
118
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
119
+ res = x
120
+ out = F.silu(self.bn1(self.conv1(x)))
121
+ out = self.bn2(self.conv2(out))
122
+ return F.silu(out + res)
123
+
124
+
125
+ class HardVisionConvNet(nn.Module):
126
+ def __init__(self, in_channels: int = 3, num_classes: int = 8):
127
+ super().__init__()
128
+ # Initial stem
129
+ self.stem = nn.Sequential(
130
+ nn.Conv2d(in_channels, 32, kernel_size=3, padding=1, bias=False),
131
+ nn.BatchNorm2d(32),
132
+ nn.SiLU(),
133
+ )
134
+ # Stage 1 (32 channels)
135
+ self.res1 = ResidualConvBlock(32)
136
+ self.pool1 = nn.MaxPool2d(2) # 64 -> 32
137
+
138
+ # Stage 2 (64 channels)
139
+ self.trans1 = nn.Sequential(
140
+ nn.Conv2d(32, 64, kernel_size=3, padding=1, bias=False),
141
+ nn.BatchNorm2d(64),
142
+ nn.SiLU(),
143
+ )
144
+ self.res2 = ResidualConvBlock(64)
145
+ self.pool2 = nn.MaxPool2d(2) # 32 -> 16
146
+
147
+ # Stage 3 (128 channels)
148
+ self.trans2 = nn.Sequential(
149
+ nn.Conv2d(64, 128, kernel_size=3, padding=1, bias=False),
150
+ nn.BatchNorm2d(128),
151
+ nn.SiLU(),
152
+ )
153
+ self.res3 = ResidualConvBlock(128)
154
+ self.pool3 = nn.AdaptiveAvgPool2d((2, 2)) # 128 x 2 x 2 = 512
155
+
156
+ # Classification Head
157
+ self.classifier = nn.Sequential(
158
+ nn.Linear(512, 128),
159
+ nn.SiLU(),
160
+ nn.Dropout(0.1),
161
+ nn.Linear(128, num_classes),
162
+ )
163
+
164
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
165
+ x = self.stem(x)
166
+ x = self.res1(x)
167
+ x = self.pool1(x)
168
+ x = self.trans1(x)
169
+ x = self.res2(x)
170
+ x = self.pool2(x)
171
+ x = self.trans2(x)
172
+ x = self.res3(x)
173
+ x = self.pool3(x)
174
+ x = torch.flatten(x, 1)
175
+ return self.classifier(x)
176
+
177
+
178
+ # 3. Benchmark Training Routine for Single Configuration
179
+ def run_benchmark_trial(
180
+ config_name: str,
181
+ optimizer_fn: Any,
182
+ images: torch.Tensor,
183
+ labels: torch.Tensor,
184
+ batch_size: int = 64,
185
+ steps: int = 20,
186
+ seed: int = 42,
187
+ ) -> Dict[str, Any]:
188
+ torch.manual_seed(seed)
189
+ np.random.seed(seed)
190
+
191
+ model = HardVisionConvNet(in_channels=3, num_classes=8)
192
+ criterion = nn.CrossEntropyLoss()
193
+ optimizer = optimizer_fn(model.parameters())
194
+
195
+ total_params = sum(p.numel() for p in model.parameters())
196
+ num_tensors = sum(1 for p in model.parameters())
197
+
198
+ losses: List[float] = []
199
+ step_times: List[float] = []
200
+ accuracies: List[float] = []
201
+
202
+ num_samples = images.shape[0]
203
+
204
+ # Pre-split into deterministic batches
205
+ batch_indices = [
206
+ slice(
207
+ (s * batch_size) % (num_samples - batch_size),
208
+ ((s * batch_size) % (num_samples - batch_size)) + batch_size,
209
+ )
210
+ for s in range(steps)
211
+ ]
212
+
213
+ total_start = time.perf_counter()
214
+
215
+ for step_idx in range(steps):
216
+ b_slice = batch_indices[step_idx]
217
+ batch_x = images[b_slice]
218
+ batch_y = labels[b_slice]
219
+
220
+ t0 = time.perf_counter()
221
+
222
+ optimizer.zero_grad(set_to_none=True)
223
+ logits = model(batch_x)
224
+ loss = criterion(logits, batch_y)
225
+ loss.backward()
226
+ optimizer.step()
227
+
228
+ t1 = time.perf_counter()
229
+
230
+ loss_val = float(loss.item())
231
+ losses.append(loss_val)
232
+ step_times.append((t1 - t0) * 1000.0) # ms
233
+
234
+ preds = torch.argmax(logits, dim=1)
235
+ acc = float((preds == batch_y).float().mean().item()) * 100.0
236
+ accuracies.append(acc)
237
+
238
+ total_duration_ms = (time.perf_counter() - total_start) * 1000.0
239
+
240
+ # Calculate optimizer state memory footprint
241
+ opt_state_bytes = 0
242
+ if hasattr(optimizer, "state"):
243
+ for p, st in optimizer.state.items():
244
+ if isinstance(st, dict):
245
+ for k, v in st.items():
246
+ if isinstance(v, torch.Tensor):
247
+ opt_state_bytes += v.numel() * v.element_size()
248
+ elif isinstance(v, np.ndarray):
249
+ opt_state_bytes += v.nbytes
250
+
251
+ initial_loss = losses[0]
252
+ final_loss = losses[-1]
253
+ pct_drop = ((initial_loss - final_loss) / initial_loss) * 100.0
254
+ avg_step_ms = float(np.mean(step_times))
255
+
256
+ return {
257
+ "name": config_name,
258
+ "initial_loss": initial_loss,
259
+ "final_loss": final_loss,
260
+ "loss_reduction_pct": pct_drop,
261
+ "final_accuracy": accuracies[-1],
262
+ "total_time_ms": total_duration_ms,
263
+ "avg_step_ms": avg_step_ms,
264
+ "state_mem_mb": opt_state_bytes / (1024.0 * 1024.0),
265
+ "total_params": total_params,
266
+ "num_tensors": num_tensors,
267
+ "losses": losses,
268
+ }
269
+
270
+
271
+ # 4. Main Suite Comparing All Modes
272
+ def main():
273
+ print("=" * 80)
274
+ print("LuminaV Vision Mode Benchmark & Comparative Training Suite")
275
+ print("=" * 80)
276
+
277
+ hw = LuminaV.detect_hardware()
278
+ print("[Hardware Environment]")
279
+ print(f" CPU SIMD Vectorization: {hw.get('simd_backend')}")
280
+ print(f" Vulkan GPU Acceleration: {hw.get('vulkan_available')} ({hw.get('gpu_device_name')})")
281
+ print(f" Triton AOT Available: {hw.get('triton_aot_available')} (arch: {hw.get('triton_arch')})")
282
+ print(f" Wave32 Hardware Support: {hw.get('wave32_supported')}")
283
+ try:
284
+ import triton
285
+ print(f" Triton Windows Package: Version {triton.__version__} ({triton.__file__})")
286
+ except Exception as e:
287
+ print(f" Triton Windows Package: Failed ({e})")
288
+ print("-" * 80)
289
+
290
+ print("[Dataset Generation] Synthesizing pathological hard vision images (64x64, 3ch)...")
291
+ images, labels = generate_hard_synthetic_vision_dataset(
292
+ num_samples=256, height=64, width=64, num_classes=8, seed=2026
293
+ )
294
+ print(f" Generated {images.shape[0]} images | Shape: {images.shape} | Labels: {labels.shape}")
295
+ print(f" Label distribution: {torch.bincount(labels).tolist()}")
296
+ print("-" * 80)
297
+
298
+ # Define test configurations across all LuminaV modes and architectures
299
+ configurations = [
300
+ # --- Triton Windows Backends ---
301
+ (
302
+ "LuminaV (triton, Dual-Buffer)",
303
+ lambda p: LuminaV(p, lr=1e-3, buffer=2, execution="triton", bound=True, cautious=True),
304
+ ),
305
+ (
306
+ "LuminaV (triton, Single-Buffer TD-CLM)",
307
+ lambda p: LuminaV(p, lr=1e-3, buffer=1, execution="triton", bound=True, cautious=True),
308
+ ),
309
+ # --- Hardware Backends (Vulkan GPU, SIMD AVX-512, Scalar Core) ---
310
+ (
311
+ "LuminaV (vulkan, Dual-Buffer)",
312
+ lambda p: LuminaV(p, lr=1e-3, buffer=2, execution="vulkan", bound=True, cautious=True),
313
+ ),
314
+ (
315
+ "LuminaV (simd AVX-512, Dual-Buffer)",
316
+ lambda p: LuminaV(p, lr=1e-3, buffer=2, execution="simd", bound=True, cautious=True),
317
+ ),
318
+ (
319
+ "LuminaV (scalar, Dual-Buffer)",
320
+ lambda p: LuminaV(p, lr=1e-3, buffer=2, execution="scalar", bound=True, cautious=True),
321
+ ),
322
+ # --- Buffer Architectures ---
323
+ (
324
+ "LuminaV (auto, Single-Buffer TD-CLM)",
325
+ lambda p: LuminaV(p, lr=1e-3, buffer=1, execution="auto", bound=True, cautious=True),
326
+ ),
327
+ (
328
+ "LuminaV (auto, Dual-Buffer CLM)",
329
+ lambda p: LuminaV(p, lr=1e-3, buffer=2, execution="auto", bound=True, cautious=True),
330
+ ),
331
+ (
332
+ "LuminaV (auto, T1 Single-Buffer Triangle)",
333
+ lambda p: LuminaV(p, lr=1e-3, buffer="T1", execution="auto", bound=True, cautious=True),
334
+ ),
335
+ (
336
+ "LuminaV (auto, T2 Dual-Buffer Triangle)",
337
+ lambda p: LuminaV(p, lr=1e-3, buffer="T2", execution="auto", bound=True, cautious=True),
338
+ ),
339
+ # --- Advanced Vision Features ---
340
+ (
341
+ "LuminaV (auto, fuse_tensors=True)",
342
+ lambda p: LuminaV(p, lr=1e-3, buffer=2, execution="auto", fuse_tensors=True),
343
+ ),
344
+ (
345
+ "LuminaV (auto, radial bound)",
346
+ lambda p: LuminaV(p, lr=1e-3, buffer=2, execution="auto", bound=True, bound_type="radial"),
347
+ ),
348
+ # --- Baseline Comparison ---
349
+ (
350
+ "Baseline AdamW (PyTorch)",
351
+ lambda p: torch.optim.AdamW(p, lr=1e-3, betas=(0.9, 0.999), weight_decay=0.01),
352
+ ),
353
+ ]
354
+
355
+ results: List[Dict[str, Any]] = []
356
+
357
+ print("[Training Phase] Benchmarking 20 optimization steps per configuration on ResNet-Lite...")
358
+ for idx, (name, opt_fn) in enumerate(configurations, 1):
359
+ print(f"\n[{idx:02d}/{len(configurations):02d}] Running trial: {name} ...", flush=True)
360
+ res = run_benchmark_trial(
361
+ name, opt_fn, images, labels, batch_size=64, steps=20, seed=42
362
+ )
363
+ results.append(res)
364
+ print(
365
+ f" Loss: {res['initial_loss']:.4f} -> {res['final_loss']:.4f} "
366
+ f"({res['loss_reduction_pct']:+.2f}%) | "
367
+ f"Acc: {res['final_accuracy']:.1f}% | "
368
+ f"Step: {res['avg_step_ms']:.2f} ms | "
369
+ f"State Mem: {res['state_mem_mb']:.2f} MB"
370
+ )
371
+
372
+ print("\n" + "=" * 105)
373
+ print("COMPREHENSIVE BENCHMARK COMPARISON TABLE")
374
+ print("=" * 105)
375
+ header = (
376
+ f"{'Mode Configuration':<40} | {'Init Loss':<10} | {'Final Loss':<10} | "
377
+ f"{'Reduction':<10} | {'Accuracy':<9} | {'Avg Step':<11} | {'State RAM':<9}"
378
+ )
379
+ print(header)
380
+ print("-" * 105)
381
+
382
+ for r in results:
383
+ row = (
384
+ f"{r['name']:<40} | "
385
+ f"{r['initial_loss']:<10.4f} | "
386
+ f"{r['final_loss']:<10.4f} | "
387
+ f"{r['loss_reduction_pct']:>8.2f}% | "
388
+ f"{r['final_accuracy']:>7.1f}% | "
389
+ f"{r['avg_step_ms']:>8.2f} ms | "
390
+ f"{r['state_mem_mb']:>7.2f} MB"
391
+ )
392
+ print(row)
393
+
394
+ print("=" * 105)
395
+
396
+
397
+ if __name__ == "__main__":
398
+ main()
changelogs/LUMINAV_ALGORITHM_CHANGELOG.md CHANGED
@@ -1,430 +1,430 @@
1
- # LuminaV Optimizer Algorithm Changelog
2
-
3
- All notable changes to the LuminaV Optimizer algorithm are documented in this file.
4
-
5
- The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
-
7
- * [Software Package Changelog (`luminamoon`)](SOFTWARE_CHANGELOG.md)
8
-
9
- ---
10
-
11
- ## [1.6.0] - 2026-11-03
12
-
13
- ### Added
14
- - **T-Family (Triangle Family) tanh-free Modes (`buffer="T1"` & `buffer="T2"`)**:
15
- * **`buffer="T2"` (2-Buffer Triangle Family Mode)**: Keeps the 2-buffer state (`exp_avg` and `exp_avg_sq`) without using $\tanh$, computing the gradient-momentum residual $d_t = g_t - m _ {t-1}$ and bounding the lookahead update $\tilde{m}_t$ via the **Triangle Inequality Envelope** $\mathcal{E}_t^{(\text{T2})}$:
16
-
17
- $\displaystyle d_t = g_t - m _ {t-1}, \qquad v_t = \beta_2 v _ {t-1} + (1 - \beta_2) d_t^2$
18
-
19
- $\displaystyle \tilde{m} _ t = w_1 m _ {t-1} + w_2 g_t, \qquad w_1 = \beta_1^2, \qquad w_2 = 1 - \beta_1^2$
20
-
21
- $\displaystyle \mathcal{E} _ t^{(\text{T2})} = w_1 |m _ {t-1}| + w_2 |g_t| + 0.5\lambda |d_t| + 0.5\lambda \sigma_t + \epsilon _ {\text{eff}}, \qquad u_t^{(\text{T2})} = \frac{\tilde{m} _ t}{\mathcal{E} _ t^{(\text{T2})}} \in (-1, 1)$
22
-
23
- By the Triangle Inequality ($|w_1 m _ {t-1} + w_2 g_t| \le w_1 |m _ {t-1}| + w_2 |g_t| \le \mathcal{E} _ t^{(\text{T2})}$), $|u_t^{(\text{T2})}| < 1$ is guaranteed without evaluating $\tanh$. The term $0.5\lambda |d_t|$ acts as a **triangle-defect penalty**, and $0.5\lambda \sigma_t$ provides belief-variance damping.
24
- * **`buffer="T1"` (1-Buffer Triangle Family Mode)**: Combines the single-buffer **Triangle-Defect** denominator $\mathcal{E} _ t^{(\text{T1})} = w_1 |m _ {t-1}| + w_2 |g_t| + \lambda |d_t| + \epsilon _ {\text{eff}}$ (using residual $d_t = g_t - m _ {t-1}$) with the rational update bounds below, reducing optimizer state memory by 50% via the Triangle Inequality.
25
- * **Rational Update Bounds (for `T1` & `T2`)**: Replaces $\tanh$ and $\sqrt{\cdot}$ in the coordinate, radial, and FP16 update bounds with simple rational formulas:
26
-
27
- $\displaystyle b_i = \mathrm{bound} _ {\text{ratio}} \cdot (|w_i| + 10^{-2}), \qquad \Delta w_i = \frac{b_i (b_i + |s_i|) s_i}{b_i (b_i + |s_i|) + s_i^2}, \qquad \mathrm{scale} _ {\text{radial}} = \frac{1 + r}{1 + r + r^2}$
28
- * Keeps 100% backward compatibility and identical outputs for existing modes (`buffer=1`, `buffer=2`, and `version="1.5.5"`).
29
-
30
- ---
31
-
32
- ## [1.5.5] - 2026-10-29
33
-
34
- ### Added
35
- - **Contiguous Bucket Fusion (`fuse_tensors: bool = False`)**:
36
- * Added opt-in contiguous parameter and state coalescing (`_build_contiguous_buckets`) that groups parameters within each `(param_group, device, dtype)` domain $\mathcal{B}_{g,d,\tau}$ into contiguous 1D storage (`flat_p`, `flat_g`, `flat_m`, `flat_v`, `flat_mp`) and rebinds `p.data`, `p.grad`, and `self.state[p]` entries as zero-copy views (`flat_*[offset : offset + n].view(p.shape)`).
37
- * Eliminates per-tensor CPU-to-GPU kernel launch latency on fragmented vision and edge architectures by executing an entire bucket in a single fused kernel dispatch while computing `cautious_scale` and `radial` trust bounds across the bucket domain $\mathcal{B}_{g,d,\tau}$.
38
- * Added in-place `zero_grad()` view preservation (`flat_g.zero_()`) and full `state_dict()` / `load_state_dict()` checkpoint compatibility without leaking temporary buffer keys (LSO-Eval Stage II compliant).
39
- - **Triton JIT Pre-Warmup (`prewarm_jit: bool = True`)**:
40
- * Added automatic initialization-time Triton JIT compilation (`_prewarm_all_configs`) using 64-element dummy buffers (`128–256 bytes` VRAM) with `lr=0.0` and `weight_decay=0.0` across both Step 1 (`pass1` + `pass2`) and Step 2+ (`fused_single_pass` + `post_step_ema`) paths with target `BLOCK_SIZE` specialization (`1024` or `2048`), eliminating first-step compilation stalls.
41
- - **Bounded Bucket Chunking & Sequential In-Place Migration (`bucket_cap_mb: float = 500.0`)**:
42
- * Bound contiguous bucket coalescing by an upper memory capacity ceiling $C_{\text{cap}} = 500\text{ MB}$, partitioning model parameters into ordered contiguous chunks $\mathcal{B}^{(1)}, \mathcal{B}^{(2)}, \dots, \mathcal{B}^{(K)}$ such that $\sum_{p \in \mathcal{B}^{(k)}} \text{bytes}(p) \le C_{\text{cap}}$ (with giant single layers $> C_{\text{cap}}$ isolated into individual buckets).
43
- * Enforced sequential in-place migration with immediate parameter storage recycling (`del old_data`, `del old_grad`) and garbage collection, constraining peak transient memory allocation spikes during optimizer initialization to at most $\min(\text{model bytes}, C _ {\text{cap}})$.
44
-
45
- ---
46
-
47
- ## [1.5.4] - 2026-10-29
48
-
49
- ### Added
50
- - **In-Container Native Bitwise-BF16 State Promotion (`dtype_mode == 2`, `master_weights="none"`)**:
51
- * Promoted 16-bit optimizer state buffers ($m_t$ via stochastic rounding `f32_to_bf16_bits_sr` and $v_t$ via round-to-nearest-even `f32_to_bf16_bits_rne`) to native Bitwise-BF16 stored inside the existing 16-bit container during pure `FP16` execution, expanding state exponent dynamic range to 8 bits ($1.18 \times 10^{-38}$) with zero additional VRAM overhead.
52
-
53
- ### Changed
54
- - **Unified Denominator Stability Constant ($\epsilon_{\text{eff}} = \epsilon = 10^{-8}$)**:
55
- * Unified the effective denominator constant $\epsilon_{\text{eff}} = \epsilon = 10^{-8}$ across all precision modes and hardware backends (replacing the legacy `FP16` floor clamp $\max(\epsilon, 10^{-4})$ introduced in `1.1.5`), preventing update velocity attenuation and late-stage convergence loss resets when $\sqrt{v_t} \ll 10^{-4}$.
56
-
57
- ### Removed
58
- - **Centered Defect Compensation (`centered_defect`, $\mu_d$)**:
59
- * Removed the `centered_defect` hyperparameter and the global defect mean reduction $\mu_d = \frac{1}{N}\sum_{i=1}^N (g_{t,i} - m_{t-1,i})$ across both Dual-Buffer Convex Lookahead Momentum (`buffer=2` / CLM) and Single-Buffer Triangle-Defect Convex Lookahead Momentum (`buffer=1` / TD-CLM).
60
- * Simplified the instantaneous gradient-momentum residual to purely local coordinate-wise differences:
61
-
62
- $\displaystyle d_t^{(\text{B1})} = g_t - m_{t-1}, \qquad d_t^{(\text{B2})} = g_t - m_t$
63
-
64
- * Eliminated the pre-step global reduction pass (`_lumina_diff_sum_kernel` in Triton JIT/AOT, `diff_sum.comp` in Vulkan SPIR-V, `reduce_diff_sum_simd` in x86\_64 AVX-512/AVX2, and slot 5 of the 6-element `ScratchBuffer`), removing cross-lane/cross-rank reduction latency while retaining the 6-element physical `ScratchBuffer` layout (`[mask_sum, u_sq_sum, p_sq_sum, cautious_scale, bound_scale, _reserved]`, 5 active slots + 1 reserved slot for ABI alignment).
65
-
66
- ---
67
-
68
- ## [1.5.3] - 2026-10-20
69
-
70
- No versions were published between 1.3.3 and 1.5.3. This release notes all changes made since version 1.3.3.
71
-
72
- ### Added
73
- - **Single-Buffer Triangle-Defect Convex Lookahead Momentum (TD-CLM, `buffer=1`)**:
74
- * Replaced the legacy 3-pass RMS single-buffer update with a single-pass formulation:
75
-
76
- $\displaystyle \tilde{m} _ t = w_1 m _ {t-1} + w_2 g_t, \quad w_1 = \beta_1^2, \quad w_2 = 1 - \beta_1^2$
77
-
78
- $\displaystyle \mathcal{E} _ t = w_1 |m _ {t-1}| + w_2 |g_t| + \lambda |d_t^{(\text{B1})}| + \epsilon _ {\text{eff}}$
79
-
80
- $\displaystyle u_t^{(\text{B1})} = \frac{\tilde{m} _ t}{\mathcal{E} _ t} \in (-1, 1)$
81
-
82
- * In 1.3.3, single-buffer mode required three sequential GPU kernel launches (`_lumina_v1_pass1_kernel`, `_lumina_v1_pass2_kernel`, `_lumina_v1_pass3_update_kernel`) and global atomic reductions across thread blocks to compute tensor-wide root-mean-square ($\text{rms} = \sqrt{\frac{1}{N}\sum \tilde{m} _ i^2 + \epsilon}$).
83
- * TD-CLM replaces the tensor-wide reduction with coordinate-wise envelope bounding via the Triangle Inequality Envelope $\mathcal{E} _ t$. This reduces single-buffer execution to a single GPU pass and eliminates all $\tanh$ evaluations in the momentum path.
84
- * Calibrated the defect penalty weight $\lambda$ to the symmetric spike-reversal equilibrium:
85
-
86
- $\displaystyle \lambda^* = \frac{2(1 - \beta_1^2)^2}{4\beta_1^2 - 3}$
87
-
88
- For the default $\beta_1 = 0.9$, $\lambda^* \approx 0.3008$, setting the default parameter to `lam=0.3`.
89
-
90
- - **Padé [13/6] Rational Approximation (`tanh_pade_f32`)**:
91
- * Implemented a rational Padé [13/6] polynomial for hyperbolic tangent calculations:
92
-
93
- $\displaystyle \tanh _ {\text{Pade}}(x) = \frac{x(1 + c_1 x^2 + c_2 x^4 + c_3 x^6 + c_4 x^8 + c_5 x^{10} + c_6 x^{12})}{1 + d_1 x^2 + d_2 x^4 + d_3 x^6}$
94
-
95
- * Evaluated over the clamped domain $x \in [-7.9988117, 7.9988117]$ with asymptotic saturation to $\pm 1.0$.
96
- * In 1.3.3, `_triton_tanh_fast` used hardware-dependent SFU instructions (`tl.math.tanh`) or analytical sigmoid fallbacks (`2 / (1 + exp(-2x)) - 1`). Different GPU architectures (e.g. Turing, Ampere, Hopper) evaluate SFU transcendental functions with slightly different mantissa rounding, leading to bitwise divergence during distributed training.
97
- * Padé [13/6] provides uniform precision with $\lVert \tanh _ {\text{Pade}}(x) - \tanh(x) \rVert _ \infty < 1.5 \times 10^{-6}$ across CPU Scalar, AVX2, AVX-512, Vulkan SPIR-V, and Triton GPU kernels.
98
-
99
- - **Centered Innovation Defect (`centered_defect`)**:
100
- * Added batch-mean centering for gradient innovation tracking (`centered_defect=False` by default):
101
-
102
- $\displaystyle \mu_d = \frac{1}{N}\sum _ {i=1}^N (g _ {t,i} - m _ {t-1,i})$
103
-
104
- $\displaystyle d_t^{(\text{B1})} = (g_t - m _ {t-1}) - \mu_d$
105
-
106
- $\displaystyle d_t^{(\text{B2})} = (g_t - m_t) - \beta_1 \mu_d$
107
-
108
- * In 1.3.3, uncentered innovation $g_t - m_t$ was used directly. On layers where gradients have a non-zero mean (such as normalization scales, biases, and token embeddings), the raw difference caused systematic drift in the second-moment accumulator. Centering eliminates this mean shift.
109
-
110
- - **Subnormal-Safe IEEE-754 FP16 Stochastic Rounding**:
111
- * Added `f32_to_f16_bits_rne` to handle IEEE-754 subnormal numbers ($|x| < 2^{-14}$) during FP16 stochastic rounding.
112
- * In 1.3.3, stochastic rounding added 13-bit random noise and masked with `& -8192`. When a floating-point value entered the subnormal range, bitwise addition directly corrupted the exponent field (bit 10), causing values to flip between normal and subnormal representations or trigger hardware flush-to-zero traps.
113
- * The updated routine checks for zero exponent, shifts the significand into the subnormal format, and applies round-to-nearest-even tie-breaking before bit truncation.
114
-
115
- - **Multi-Backend Execution Engine**:
116
- * Replaced the Python-only runtime with a multi-backend execution layer:
117
- * **Rust SIMD (`luminav-simd`)**: AVX-512 (512-bit registers), AVX2 with FMA, portable scalar fallback, and Rayon parallel chunk scheduling (`ELEM_CHUNK_SIZE = 2,097,152`).
118
- * **Vulkan UMA (`luminav-vulkan`)**: SPIR-V compute pipeline for cross-vendor GPUs (AMD, Intel, Apple Silicon via MoltenVK, and NVIDIA) with zero-copy host-visible memory buffers.
119
- * **Triton AOT (`luminav-triton-aot`)**: C-ABI wrapper dispatching ahead-of-time precompiled kernels for 84 GPU architectures (Blackwell SM100/SM120, Hopper SM90, Ada SM89, Ampere SM80/SM86, Turing SM75, and AMD ROCm gfx90a/gfx942/gfx1100).
120
- * **Standalone Runtime (`luminamoon.optimizer.standalone`)**: Pure NumPy and C-ABI execution that runs without requiring PyTorch.
121
-
122
- ### Changed
123
- - **Parameter `alpha_ss`**: Removed. In 1.3.3, `alpha_ss=0.5` was used for soft-sign scaling $z / (1 + \alpha _ {\text{ss}} |z|)$ in the legacy single-buffer path. In 1.5.3, TD-CLM envelope normalization bounds updates directly to $(-1, 1)$, making soft-sign obsolete.
124
- - **Parameter `lam`**: Added with default `0.3`. Governs the defect penalty weight in TD-CLM.
125
- - **Parameter `centered_defect`**: Added with default `False`. Enables batch-mean centering of the innovation defect.
126
- - **Parameter `bound_type`**: Default changed from `"radial"` (1.3.3) to `"coordinate"` (1.5.3). Coordinate-wise hyperbolic bounding $(B_i \tanh(\Delta \theta_i / B_i))$ evaluates locally on each parameter without computing a tensor-wide norm reduction.
127
- - **Parameter `bound_ratio`**: Default changed from `0.03` to `0.05` to provide better update headroom on large model architectures.
128
- - **Parameter `ema_decay`**: Default changed from static `0.8` to `"auto"`. Resolves to `0.80` for Dual-Buffer mode (`buffer=2`) and `0.95` for Single-Buffer mode (`buffer=1`).
129
- - **Scratch Buffer**: Expanded from 5 floats `[mask_sum, u_sq_sum, p_sq_sum, cautious_scale, bound_scale]` to 6 floats, adding `diff_sum` at index 5 to accumulate batch-mean defect statistics.
130
- - **Cautious Scaling Floor**: Enforced `cautious_clamp_min=0.5` across all backends to cap maximum cautious energy scaling at $2.0\times$ ($2^1$).
131
-
132
- ### Fixed
133
- - **Hardware Mantissa Divergence**: Fixed bitwise output differences across GPU families by replacing hardware SFU `tl.math.tanh` with Padé [13/6] rational approximation.
134
- - **FP16 Subnormal Bit Corruption**: Fixed exponent corruption in subnormal FP16 stochastic rounding by separating exponent extraction from significand shift logic.
135
- - **Single-Buffer Kernel Launch Latency**: Reduced single-buffer kernel dispatches from 3 passes to 1 pass, eliminating host-side synchronization bubbles and intermediate L2 atomic reduction overhead.
136
- - **Non-Zero Mean Gradient Drift**: Resolved directional accumulator drift on bias and normalization layers via `centered_defect`.
137
-
138
- ### Parameter Comparison (1.3.3 vs 1.5.3)
139
-
140
- | Parameter | Version 1.3.3 | Version 1.5.3 | Technical Notes |
141
- | :--- | :--- | :--- | :--- |
142
- | `params` | `params` | `params` | Iterable of parameters or parameter groups. |
143
- | `lr` | `8e-4` | `8e-4` | Base learning rate. |
144
- | `betas` | `(0.9, 0.999)` | `(0.9, 0.999)` | First and second moment exponential decay rates. |
145
- | `eps` | `1e-8` | `1e-8` | Numerical stability floor. |
146
- | `weight_decay` | `8e-2` | `8e-2` | Decoupled weight decay rate. |
147
- | `tau` | `0.8` | `0.8` | Noise dispersion scale factor. |
148
- | `alpha_ss` | `0.5` | *(removed)* | Removed; superseded by TD-CLM envelope normalization. |
149
- | `lam` | *(none)* | `0.3` | Defect penalty weight in TD-CLM (calibrated to $\lambda^* \approx 0.3008$). |
150
- | `cautious` | `True` | `True` | Directional verification gate ($u_i g_i > 0$). |
151
- | `cautious_clamp_min` | `0.5` | `0.5` | Maximum cautious scale cap ($2.0\times$). |
152
- | `buffer` | `2` | `2` | `1` = Single-Buffer (TD-CLM), `2` = Dual-Buffer (CLM). |
153
- | `centered_defect` | *(none)* | `False` | Enables subtraction of batch-mean defect $\mu_d$. |
154
- | `stochastic_rounding` | `True` | `True` | Bitwise stochastic rounding on FP16/BF16 weights. |
155
- | `bound` | `True` | `True` | Enables update trust-region bounding. |
156
- | `bound_type` | `"radial"` | `"coordinate"` | Default changed to coordinate-wise bounding (zero reduction overhead). |
157
- | `bound_ratio` | `0.03` | `0.05` | Maximum step magnitude relative to parameter magnitude. |
158
- | `master_weights` | `False` | `"none"` | `"none"`, `"semi"`, or `"full"`. |
159
- | `execution` | `"auto"` | `"auto"` | Dynamic routing: SIMD, Vulkan, Triton, or Foreach. |
160
- | `fused_single_pass` | `False` | `True` | Enables single-pass streaming with EMA scaling. |
161
- | `ema_decay` | `0.8` | `"auto"` | Automatically sets `0.80` for dual-buffer, `0.95` for single-buffer. |
162
- | `fsdp_sync` | `False` | `False` | Cross-rank collective all-reduce synchronization. |
163
- | `seed` | `1337` | `1337` | PRNG seed for deterministic stochastic rounding. |
164
-
165
- ---
166
-
167
- ## [1.3.3] - 2026-10-06
168
-
169
- ### Fixed
170
- * **Triton AST Compiler Early-Return Inlining Error:**
171
- * Resolved fatal MLIR/CFG code generation failure (`at 35:4: _store_state_buffer(...) ^`) triggered by mid-branch `return` statements in `@triton.jit` helper functions (`_store_param` and `_store_state_buffer`).
172
- * Refactored helper control flow into structured, single-exit `if ... elif ... else` branches, ensuring compliant JIT function inlining across all kernel entry points.
173
- * **Bitcast Width Mismatch (Int64 Promotion in Stochastic Rounding):**
174
- * Fixed implicit 64-bit integer promotion caused by Python integer literals (`0x9E3779B9`, `~0x1FFF`, `~0xFFFF`) inside Triton bitwise operations.
175
- * Enforced strict 32-bit signed arithmetic via two's complement constants (`-1640531527`, `-2048173461`, `-1028477387`) with explicit `.to(tl.int32)` coercion, and standardized bitwise masks (`-65536` for BF16, `-8192` for FP16), ensuring type-safe 32-bit bitcasting (`tl.int32` to `tl.float32`).
176
- * **JIT Kernel Call Keyword Arguments:**
177
- * Replaced internal keyword argument invocations (`use_sr=False`) with standard positional boolean parameters inside Triton JIT kernels to eliminate compiler argument binding warnings across Triton runtime versions.
178
-
179
- ### Improved
180
- * **Throttled Fallback Telemetry & Console De-Noising:**
181
- * Added `self._warned_triton_failure` state tracking to prevent multi-line compiler AST dumps from flooding the console across every parameter tensor in a parameter group.
182
- * Formatted fallback notifications into a single concise one-line `WARNING` log on initial encounter, routing full tracebacks to `DEBUG` level.
183
-
184
- ### Verified
185
- * **End-to-End JIT Execution on NVIDIA Architectures (Tesla T4):**
186
- * Validated JIT execution across both Dual-Pass (`fused_single_pass=False`) and Fused Single-Pass (`fused_single_pass=True`) modes in native FP16.
187
- * Confirmed non-zero weight update dynamics ($|\Delta W| > 0$) without gradient underflow or weight freezing in pure master-free training for Version 1.3.3.
188
-
189
- ---
190
-
191
- ## [1.3.2] - 2026-10-06
192
-
193
- ### Added
194
- - **Distributed FSDP Collective Synchronization (`fsdp_sync`)**:
195
- - Introduced the `fsdp_sync: bool = False` configuration flag on optimizer initialization, backed by automated runtime guarding via `torch.distributed.is_initialized()`.
196
- - Integrated cross-GPU collective communications (`torch.distributed.all_reduce(..., op=torch.distributed.ReduceOp.SUM)`) for global reduction metrics (`mask_sum`, `u_sq_sum`, and `p_sq_sum`) across Triton kernels, single-step execution, and vectorized foreach loops.
197
- - Maintains bit-exact scalar consistency for `m_bar`, `cautious_scale`, and `bound_scale` across distributed parameter shards during large-scale pretraining under PyTorch FSDP (Fully Sharded Data Parallel) and DeepSpeed ZeRO-3.
198
- - **Dedicated Low-Precision Stochastic Quantization Routine (`_sr_quantize`)**:
199
- - Factored out bitwise stochastic rounding into a dedicated, modular helper function: `_sr_quantize(target, val_f32, use_sr, generator)`.
200
- - Enabled native stochastic rounding on half-precision momentum states (`exp_avg` in BF16/FP16) across all PyTorch CPU/CUDA fallback paths (`_single_step_v2`, `_foreach_step_v2`, `_single_step_v1`, and `_foreach_step_v1`), systematically eliminating gradient stagnation induced by fractional bit truncation (LSB underflow).
201
- - **Persistent Contiguous Buffer Caching**:
202
- - Introduced persistent state cache tensors within `self.state[p]` (`p_contig`, `exp_avg_contig`, `exp_avg_sq_contig`, and `mp_contig`) to stage strided or sliced parameters before kernel launch.
203
- - Eliminates dynamic memory allocations (`.contiguous()`) during iterative training loops, preserving PyTorch VRAM caching allocator stability and preventing CUDA Graph capture invalidation.
204
- - **Zero-Copy Direct Tensor Aliasing for FP32 Master Weights**:
205
- - When parameter tensors are natively `torch.float32`, `state["master_param"]` now directly aliases the parameter tensor pointer (`state["master_param"] = p`), avoiding additional master weight VRAM overhead for FP32 layers (0 bytes auxiliary memory footprint).
206
- - **Checkpoint Sanitizer via State Dict Filter (`state_dict`)**:
207
- - Overrode `state_dict()` to automatically purge temporary contiguous cache buffers (`p_contig`, `exp_avg_contig`, `exp_avg_sq_contig`, `mp_contig`), ensuring on-disk checkpoint payloads remain clean, compact, and fully backward-compatible.
208
- - **Defensive Device Pipeline Flush on Kernel Fault (`torch.cuda.synchronize`)**:
209
- - Embedded explicit hardware device synchronization (`torch.cuda.synchronize(p.device)`) within Triton exception recovery handlers prior to PyTorch fallback execution, preventing asynchronous CUDA error cascades into subsequent tensor operations.
210
-
211
- ### Changed
212
- - **High-Entropy Bit-Avalanche PRNG Hash Function (`_prng`)**:
213
- - Replaced the standard Linear Congruential Generator (LCG) with a MurmurHash3 / SplitMix32 integer bit-avalanche hashing routine:
214
- ```python
215
- z = idx + seed + 0x9E3779B9
216
- z = (z ^ (z >> 16)) * 0x85EBCA6B
217
- z = (z ^ (z >> 13)) * 0xC2B2AE35
218
- z = z ^ (z >> 16)
219
- ```
220
- - Delivers uniform pseudo-random noise distributions with zero low-order bit periodicity, significantly refining stochastic rounding fidelity on BF16/FP16 parameters.
221
- - **Memory-Contiguous Scratch Buffer Realignment for v1 Triton Kernels**:
222
- - Re-indexed reduction buffer offsets in Triton v1 kernels:
223
- - Pass 1 metrics (`[rms_sum, p_sq_sum]`) mapped to contiguous indices `[0, 1]`.
224
- - Pass 2 metrics (`[mask_sum, u_sq_sum]`) mapped to contiguous indices `[2, 3]`.
225
- - Enables vectorized contiguous slice all-reduces (`scratch_param[:2]` and `scratch_param[2:4]`) under FSDP without requiring non-contiguous memory gather/scatter overhead.
226
- - **Multi-GPU Device Index Resolution in RNG Dispatcher (`_get_generator`)**:
227
- - Fixed device index resolution when `device` is passed as a generic `torch.device("cuda")` without an explicit index by defaulting to `torch.cuda.current_device()`, avoiding cross-GPU generator key collisions during multi-GPU distributed runs.
228
- - **Conditional Atomic Reduction Pruning in v2 Pass 1 Kernel**:
229
- - Guarded `mask_sum` atomic accumulation in `_lumina_v2_pass1_kernel` with `if cautious:`, bypassing CTA reductions and L2 atomic additions when `cautious=False`.
230
- - **Expanded Master Weight Precision Configuration Aliases**:
231
- - Added the `"half"` alias for `"semi"` mode alongside `"fp32"` and `"masterweight"` aliases for `"full"` mode.
232
- - **Metadata & Documentation Header Update**:
233
- - Bumped specification version to `1.3.2`.
234
-
235
- ### Fixed
236
- - **Singular Zero-Division & Step-Zero Bias Correction (`safe_base_step`)**:
237
- - Introduced `safe_base_step = max(base_step, 1)` and `bc2_base = max(1.0 - (beta2 ** safe_base_step), 1e-15)` across Triton, single-step, and foreach loops.
238
- - Fully resolves potential `ZeroDivisionError` and NaN blowups in `c1` / `c2` computations when steps evaluate at `step == 0`.
239
- - **Numerical Overflow Clamp in Sigmoid-Based Tanh Fallback**:
240
- - Applied register clamp bounds `x_clamped = tl.maximum(tl.minimum(x, 20.0), -20.0)` in analytical fallback `_triton_tanh_fast`, preventing `tl.exp(-2.0 * x)` from overflowing to `inf` or `nan` on devices lacking hardware SFU tanh instructions.
241
- - **Missing Argument Forwarding in PyTorch Single-Step v1**:
242
- - Corrected `_single_step_v1` method signature and call-sites to accept and propagate `scratches`, eliminating runtime `TypeError` during fallback invocation.
243
- - **Multi-Rank Synchronization Skew in Sharded Parameter Bounding**:
244
- - Aligned tensor norms and radial bounding factors across sharded ranks via synchronized global scalar reductions, preventing parameter update drift across distributed ranks.
245
-
246
- ### Performance & Engine Refactoring
247
- - **Zero Dynamic VRAM Allocation on Strided / Sliced Tensors**:
248
- - Persistent pre-allocated contiguous staging buffers avoid PyTorch caching allocator churn and eliminate CUDA Graph memory address invalidations.
249
- - Preserves deterministic memory footprints across long pre-training runs.
250
- - **Optimized Distributed Collective Bandwidth**:
251
- - Grouping scalar reductions into contiguous vector slices compresses distributed synchronization overhead into a single collective kernel launch per pass.
252
-
253
- ---
254
-
255
- ## [1.3.0] - 2026-09-23
256
-
257
- ### Added
258
- - **Fused Single-Pass Kernel with Post-Step EMA (`fused_single_pass`, `ema_decay`)**:
259
- - Introduced `_lumina_v2_fused_single_pass_kernel` coupled with an asynchronous CTA micro-kernel `_lumina_v2_post_step_ema_kernel[(1,)]`.
260
- - When `fused_single_pass=True`, avoids two-pass global reductions by leveraging running Exponential Moving Average (EMA) estimates of `cautious_scale` ($1 / \bar{M}$) and `bound_scale` ($\tanh(r) / r$).
261
- - Unifies gradient filtering, momentum accumulation, variance dispersion, cautious masking, radial bounding, decoupled weight decay, stochastic rounding, and block partial reductions into a **single unified VRAM-resident pass**, reducing global GPU memory bandwidth traffic per optimizer step.
262
- - **Hybrid Master Weight Precision Architecture (`master_weights`)**:
263
- - Added multi-tier master weight flexibility via `master_weights: Union[bool, str] = False`:
264
- - `"none"` / `False` *(Default)*: Pure master-free low-precision training leveraging on-chip bitwise stochastic rounding directly on native half-precision weights.
265
- - `"semi"` / `"hybrid"` / `"half"`: 32-bit FP32 master parameters (`state["master_param"]`) coupled with 16-bit low-memory optimizer states (`exp_avg`, `exp_avg_sq` in BF16/FP16), saving ~50% optimizer state VRAM compared to traditional FP32 setups while maintaining full FP32 weight accumulation accuracy.
266
- - `"full"` / `"fp32"` / `"masterweight"`: Traditional full FP32 master parameters with full FP32 optimizer states for standard baseline comparisons.
267
- - **In-Kernel In-Flight NaN / Inf Gradient Sanitization**:
268
- - Embedded register-level defensive sanitization across all Triton GPU kernels: `is_finite_g = (g == g) & (g < 1e38) & (g > -1e38); g = tl.where(is_finite_g, g, 0.0)`.
269
- - Replaces non-finite gradients and subnormal corruptions with zero in registers before they can contaminate momentum (`exp_avg`) or variance (`exp_avg_sq`) state buffers.
270
- - **Low-Precision State Buffer Serialization (`_store_state_buffer`)**:
271
- - Added a dedicated Triton state serialization routine supporting FP32, BF16, and FP16 state representations with optional bitwise stochastic rounding applied directly to momentum buffers.
272
- - **Multi-Device & Cross-Accelerator Hardware Context Isolation (`_get_device_context`)**:
273
- - Unified device stream scoping supporting NVIDIA CUDA (`torch.cuda`), Intel XPU (`torch.xpu`), and modern PyTorch 2.x unified accelerator interfaces (`torch.accelerator.device`), eliminating multi-GPU stream hijacking and cross-device race conditions.
274
- - **Strict Determinism Auto-Routing**:
275
- - Automatically queries `torch.are_deterministic_algorithms_enabled()`. When enabled under `execution="auto"`, dynamically routes execution to C++ vectorized `foreach` to preserve bitwise cross-run reproducibility, circumventing non-deterministic floating-point hardware atomic reordering.
276
- - **Granular Fault Recovery with State Guarding (`completed_indices`)**:
277
- - Implemented per-parameter completion tracking in `_dispatch_triton`. If a kernel encounters an unexpected runtime fault, completed parameters are preserved while uncompleted ones fall back cleanly to `foreach`.
278
- - Added `_single_step_v2_apply_param` and `_single_step_v1_apply_param` to safely finalize parameter updates if Pass 1 had already committed state transitions, strictly preventing double-update state corruption.
279
-
280
- ### Changed
281
- - **Per-Parameter Persistent Scratch Tensors (Elimination of Global Slicing)**:
282
- - Replaced the global dynamic slice allocator (`self._scratch_tensors[dev][:count]`) with isolated per-parameter persistent state allocations (`state["scratch"] = torch.zeros((5,), device=p.device, dtype=torch.float32)`).
283
- - Memory pointers for `[mask_sum, u_sq_sum, p_sq_sum, cautious_scale, bound_scale]` remain completely invariant throughout the entire training lifecycle, preserving pointer stability for **CUDA Graph Capture** (`torch.accelerator.Graph`) and TorchInductor AOT compilation without memory aliasing.
284
- - **Analytically Exact Nesterov Bias Correction ($\beta_1^{\text{step} + 1}$)**:
285
- - Corrected first-moment bias correction from the classical Adam formulation ($1.0 - \beta_1^{\text{step}}$) to the exact analytical expectation of the Nesterov blended momentum: `bc1_nes = 1.0 - beta1^(step + 1)`. Removes residual momentum bias during early warmup iterations.
286
- - **Adaptive CTA Tile Sizing and Dynamic Thread Warp Scaling**:
287
- - Introduced hardware-aware CTA tile scaling based on parameter volume: parameters with $\ge 2,097,152$ elements (2M+) dynamically configure `BLOCK_SIZE = 2048` and `num_warps = 8` (versus `BLOCK_SIZE = 1024`, `num_warps = 4` for smaller tensors).
288
- - Cuts total thread-block grid counts and L2 cache atomic addition pressure by 50% on large MLP, Attention Projection, and Vocabulary layers.
289
- - **Hardware-Native Tanh Instruction Dispatch (`tl.math.tanh` / `tl.tanh`)**:
290
- - Upgraded `_triton_tanh_fast` to dynamically inspect compiler capabilities, binding directly to native PTX Special Function Unit (SFU) instructions (`tanh.approx.f32`) on modern Triton compilers while retaining analytical sigmoid fallbacks for legacy environments.
291
- - **PRNG Decorrelation between Momentum and Parameter Updates**:
292
- - Separated RNG seed streams using bitwise alternating mask shifts: `seed_p = (seed + 0x55555555) & 0x7FFFFFFF`, mathematically eliminating cross-coupling noise correlation between state buffer rounding and parameter update rounding.
293
- - **Bumped specification version metadata to 1.3.0** across `luminav.py`.
294
-
295
- ### Fixed
296
- - **Floating-Point Cancellation in Radial Bounding ($r \to 0$)**:
297
- - Fixed precision jitter and roundoff degradation near zero by adding an asymptotic Taylor-series guard across Triton and PyTorch bounding engines: `torch.where(r < 1e-4, 1.0, tanh(r) / r)`.
298
- - **Artificial Denominator Dilation in Dispersion Scale ($\sigma$)**:
299
- - Replaced additive epsilon inflation (`sigma + 1e-6`) with a non-distorting floor clamp (`tl.maximum(sigma, 1e-12)`), preventing artificial dampening of subtle gradient signals.
300
- - **Singular Zero-Division in Parameter Norm Clamping**:
301
- - Replaced `norm_p + 1e-6` with `torch.clamp_min(norm_p, 1.0)` / `tl.maximum(norm_p, 1.0)`, maintaining numerical stability even when parameters are initialized near zero.
302
- - **Transient Memory Pool Spikes during PyTorch Fallback Stochastic Rounding**:
303
- - Refactored `_sr_update` to stream stochastic noise generation in bounded chunks of 1,048,576 elements (~4 MB chunk size), eliminating multi-hundred megabyte temporary int32 tensor spikes during fallback execution on large parameter matrices.
304
- - **Non-Contiguous Buffer Safety & Strided Memory Copy**:
305
- - Added strict contiguous layout verification for parameters, states, and master weights with in-place synchronization (`p.copy_(p_contig)`), guarded by a one-time developer warning.
306
-
307
- ### Performance & Engine Refactoring
308
- - **~50% Global Memory Bandwidth Reduction via Fused Single-Pass Mode**:
309
- - By unifying parameter loading, gradient evaluation, state transformation, and bounding into one GPU pass under `fused_single_pass=True`, VRAM read/write cycles are halved for cautious and radially bounded steps.
310
- - **Sub-Microsecond L2 Hardware Atomic Reduction Architecture**:
311
- - Formally documented the hybrid Intra-Block Tree Reduction + Inter-Block Hardware Atomic pattern. With `BLOCK_SIZE=2048`, a 10M-parameter tensor issues only 4,883 atomic additions directly into GPU L2 cache crossbars, executing in < 0.5 µs and strictly outperforming multi-tier cascaded reduction micro-kernels by eliminating 3–5 µs driver dispatch bubbles.
312
- - **Static Buffer Address Invariance for CUDA Graphs & TorchInductor AOT**:
313
- - Persistent 5-slot state scratch buffers eliminate all dynamic slicing and memory reallocations during optimization loops, preserving pointer address invariance for CUDA Graph replay.
314
-
315
- ---
316
-
317
- ## [1.2.1] - 2026-09-17
318
-
319
- ### Added
320
- - **In-Kernel GPU Parameter Norm Reduction (`p_sq_sum_ptr`)**: Fused parameter Euclidean norm squared $\sum(p_i^2)$ calculation directly into Pass 1 Triton reduction kernels (`_lumina_v2_pass1_kernel` and `_lumina_v1_pass1_kernel`). Pass 2 and Pass 3 now load and resolve `norm_p` (`tl.sqrt(tl.maximum(p_sq_sum, 0.0))`) entirely inside GPU SRAM/registers without host intervention.
321
- - **Stateless CPU Integer Multiplicative PRNG Hashing**: Replaced dynamic GPU RNG tensor allocation with a pure CPU integer hash using Knuth's 32-bit golden ratio constant: `(step * 0x9E3779B9 + i * 10007) & 0x7FFFFFFF`. Generates uncorrelated 31-bit integer seeds per layer with zero GPU kernel launch overhead and zero PCIe host-device latency.
322
- - **Isolated Generator Cache for Fallback Engine (`self._generators`)**: Added an internal per-device `torch.Generator` cache, ensuring stochastic rounding in `_sr_update` operates in complete isolation from PyTorch's default CUDA generator state.
323
-
324
- ### Changed
325
- - **Consolidated Scratch Buffer Zeroing**: Removed redundant per-parameter slice zeroing inside parameter loops. Buffer zeroing is now batched into a single consolidated `scratch.zero_()` call per step inside `_get_scratch_buffer`.
326
- - **Scratch Buffer Stride Expansion**:
327
- - `buffer=2`: Expanded stride from 2×N to 3×N elements per parameter to host `[mask_sum, u_sq_sum, p_sq_sum]`.
328
- - `buffer=1`: Expanded stride from 3×N to 4×N elements per parameter to host `[rms_sum, mask_sum, u_sq_sum, p_sq_sum]`.
329
- - **Bumped specification version metadata to 1.2.1**.
330
-
331
- ### Fixed
332
- - **Host-Device Synchronization & Pipeline Stalls (`.item()` Bottleneck)**: Removed `norm_p = float(p_contig.float().norm().item())` and `seed = torch.randint(...).item()` from Triton step pipelines. Eliminated 200–400 synchronous PCIe roundtrips and GPU pipeline stalls per step on deep architectures (e.g., 28-layer Transformers), restoring full asynchronous GPU compute utilization.
333
- - **CUDA Graphs & `torch.compile` Graph Breaks**: Fixed fatal graph capture crashes and TorchDynamo graph breaks caused by blocking host-device synchronization during radial bounding and seed generation. The optimizer step is now compatible with CUDA Graphs (`torch.accelerator.Graph`) and TorchInductor compilation.
334
- - **Transient VRAM Allocator Thrashing**: Eliminated repeated temporary FP32 parameter cloning (`p_contig.float()`) during norm evaluation, preventing memory pool fragmentation in tight VRAM regimes.
335
- - **Global RNG Pollution & Determinism Compliance**: Fixed global RNG state mutation in fallback stochastic rounding. Solved reproducibility drift in upstream Dropout, DataLoader shuffling, and RLHF reward modeling, ensuring strict adherence to PyTorch 2.10+ `torch.use_deterministic_algorithms(True)`.
336
-
337
- ### Performance & Engine Refactoring
338
- - **Batch Kernel Launch Overhead Elimination**: Removed up to 400–800 individual `cudaMemsetAsync` micro-calls per step previously triggered by per-slice `.zero_()` invocations on 1-element scratch tensors.
339
- - **Single-Pass Bypass for Coordinate Bounding**: Streamlined `_triton_step_v2` to immediately bypass two-pass reductions and invoke `_lumina_v2_single_pass_kernel` when `cautious=False` and coordinate bounding (`bound_mode == 2`) is selected.
340
-
341
- ---
342
-
343
- ## [1.2.0] - 2026-09-17
344
-
345
- ### Added
346
- - **Automatic Dynamic FP16 Cliff Governor:** Built-in asymptotic boundary governor (`_triton_tanh_fast` in GPU register and PyTorch fallback) that automatically activates when `p.dtype == torch.float16`. Prevents unclipped steps from pushing parameters, activations, and residual streams into the IEEE-754 FP16 overflow cliff (> 65,504). Remains completely dormant (zero overhead) on `bfloat16` (TPU / Ampere+) and `float32`.
347
- - **Smooth Asymptotic Parameter Bounding (`bound`, `bound_type`, `bound_ratio`):** Added non-intrusive step bounding with two mathematically continuous formulations:
348
- - `"radial"`: Direction-Preserving Radial Squashing (`update * tanh(r) / r`) preserving gradient direction (Cosine Similarity = 1.000).
349
- - `"coordinate"`: Dual-Hyperbolic Coordinate Squashing for fine-grained per-element damping.
350
- - Configurable via constructor: `bound: bool = True`, `bound_type: str = "radial"`, `bound_ratio: float = 0.03`.
351
-
352
- ### Changed
353
- - **Increased Default `cautious_clamp_min` from `0.2` to `0.5`:** Enforces an exact power-of-two maximum energy scaling factor (2.00x, $2^1$). Eliminates floating-point mantissa roundoff distortion in IEEE-754 hardware.
354
- - Bumped specification version metadata to `1.2.0`.
355
-
356
- ### Fixed
357
- * **FP16 Step Divergence & NaN Overflow at High Learning Rates:** Solved step divergence and subsequent NaN crashes when training deep 28-layer Transformer architectures (e.g., Qwen3-0.6B) in pure FP16 at nominal learning rates (`lr = 8e-4`).
358
- * **Sparse Embedding & LM-Head Amplification:** Mitigated 5x over-amplification on sparse vocabulary gradients where active token counts are much smaller than total tensor parameters ($N \gg N_{\text{active}}$).
359
- * **Verified stable 1,000-step pretraining from scratch on Qwen3-0.6B** (seq length 512, pure FP16, `lr = 8e-4`) on Nvidia Tesla T4 with zero gradient clipping and zero LR scheduler (loss dropped from 12.09 down to ~5.7 with zero NaNs).
360
- * **Weight Decay Numerical Parity on Fallback Engine:** Resolved a silent precision-stagnation issue in `_single_step` and `_foreach_step`. Previously, decoupled weight decay (`p.mul_(1.0 - lr * weight_decay)`) was executed directly on native FP16/BF16 tensors prior to `_sr_update`. Due to IEEE-754 Round-to-Nearest (RTN) limits, `(1 - lr * weight_decay) ≈ 0.999936` rounded back to `1.0`, causing weight decay to freeze on half-precision parameters. Weight decay is now unified into FP32 registers inside `_sr_update` alongside stochastic rounding, matching the numerical behavior with the Triton kernel.
361
- * **Fallback Foreach Indentation & Scope Alignment:** Corrected tensor iteration scope and indentation for `torch._foreach_add_` in non-SR fallback branches.
362
- * **In-Place GPU Clamping in `_apply_bound`:** Replaced dynamic GPU scalar allocations (`torch.tensor(1.5e-4, device=p.device)`) with native `torch.clamp_min`, eliminating transient CUDA malloc spikes per update step.
363
-
364
- ### Performance & Engine Refactoring
365
- * **Unified Bound Execution Pipeline:** Added an immediate short-circuit guard (`if lr == 0.0: return update`) and streamlined the execution flow: intermediate updates are scaled in FP32 (`step_val = lr * u_float`), passed through radial/coordinate bounding, chained cleanly into the FP16 Cliff Governor, and returned in a single normalized step.
366
-
367
- ---
368
-
369
- ## [1.1.5] - 2026-09-15
370
-
371
- ### Added
372
- - **Native Hardware Capability Detection (`_supports_native_bf16`):** Added automatic device capability detection via `torch.cuda.is_bf16_supported()` to identify Ampere SM80+ architectures and streamline native execution paths.
373
- - **Hugging Face Repository Reference:** Added official Hugging Face repository link in module docstrings.
374
-
375
- ### Changed
376
- - **Dynamic Epsilon Clamping for FP16:** Dynamically floors effective epsilon to `max(eps, 1e-4)` when parameters or states are in `torch.float16`. Prevents bias-corrected scale factor `c2` from underflowing into IEEE-754 subnormal/zero limits on step 1.
377
- - Bumped specification version to `1.1.5`.
378
-
379
- ### Fixed
380
- - **FP16 Step-1 `NaN` from Epsilon Underflow:** Fixed `0.0 / 0.0 = NaN` division occurring on initial training steps when parameters receive zero gradients (e.g., unselected vocabulary embeddings, padding tokens, or dropout paths).
381
- - **Second-Moment Variance Saturation (`+inf`):** Added safe upper-bound register clamping (`tl.minimum(v_new, 65000.0)` in Triton kernels and `.clamp_(0.0, 65000.0)` in PyTorch C++ foreach/single loops). Prevents unclipped initial pretraining gradient surges ($|g| > 256$) from overflowing the FP16 maximum dynamic range (65,504) into irreversible `+inf`.
382
- - **Zero-Sigma Division Guard:** Injected explicit `sigma + 1e-6` denominator guards across all Triton kernel passes and PyTorch update steps to ensure numerical safety during cold-start iterations.
383
- - Verified stable 100-step pretraining from scratch on `SmolLM-135M` using Wikimedia Wikipedia in pure FP16 on Nvidia Tesla T4 GPU with zero gradient clipping.
384
-
385
- ---
386
-
387
- ## [1.1.0] - 2026-09-11
388
-
389
- ### Added
390
- - **Bitwise Stochastic Rounding (SR):** Integrated on-chip PRNG integer bit-manipulation hash in Triton kernels and vectorized PyTorch fallback for native FP16/BF16 training without master weights.
391
- - Unified Triton store routine introducing compile-time branching (`dtype_mode` / `use_sr: tl.constexpr`) with explicit safe casting for FP16 and BF16 pointers.
392
- - Vectorized fallback improvements utilizing C++ multi-tensor operations when stochastic rounding is disabled or tensors are FP32.
393
- - State buffer contiguity validation (`exp_avg_contig`, `exp_avg_sq_contig`) ensuring safe linear memory addressing before kernel dispatch.
394
- - Declarative configuration file (`config.json`) defining hyperparameters and optimizer presets.
395
-
396
- ### Changed
397
- - Refactored PRNG integer arithmetic to strictly stay within signed 32-bit integer limits ($< 2^{31} - 1$), eliminating JIT compilation failures on newer Triton releases.
398
- - State buffers (`exp_avg`, `exp_avg_sq`) now initialize with strict contiguous memory format.
399
-
400
- ### Fixed
401
- - Fixed `ValueError: Scalar out of range for type int32` and Triton LLVM compile errors during unsigned 32-bit bitmask operations.
402
- - Resolved kernel fallback hangs and compilation overhead on CUDA devices.
403
-
404
- ---
405
-
406
- ## [1.0.2] - 2026-09-08
407
-
408
- ### Added
409
- - Official Apache-2.0 license notice and copyright headers.
410
- - Standardized package exports (`__all__`, `__version__`, `__author__`, `__license__`).
411
-
412
- ---
413
-
414
- ## [1.0.1] - 2026-08-31
415
-
416
- ### Fixed
417
- - **Memory Synchronization Fix (`423d171`):** Fixed a critical bug where non-contiguous parameter tensors (e.g., transposed linear layers or sliced weights) were updated on temporary contiguous copies without reflecting changes back to model weights. Added `p.copy_(p_contig)` after Triton execution.
418
-
419
- ---
420
-
421
- ## [1.0.0] - 2026-08-30
422
-
423
- ### Added
424
- - Initial release of the LuminaV optimizer.
425
- - **Zero Master-Weight Copies:** Direct parameter updates in native half-precision without allocating 4-byte FP32 master weights.
426
- - **Hyperbolic Tangent (`tanh`) Bounding Envelope:** Projects normalized momentum ratio into `(-1.0, 1.0)` to eliminate exploding gradient shocks.
427
- - **Directional Alignment Gate:** Cautious update masking that zeroes out momentum steps opposing mini-batch gradient directions.
428
- - **Central Innovation Variance:** Noise dispersion tracking $(g_t - m_t)^2$ decoupling mini-batch turbulence from directional descent.
429
- - Dual-buffer mode (`buffer=2`, LuminaV-2B) and single-buffer mode (`buffer=1`, LuminaV-1).
430
- - Custom fused OpenAI Triton kernels with automated fallback execution.
 
1
+ # LuminaV Optimizer Algorithm Changelog
2
+
3
+ All notable changes to the LuminaV Optimizer algorithm are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ * [Software Package Changelog (`luminamoon`)](SOFTWARE_CHANGELOG.md)
8
+
9
+ ---
10
+
11
+ ## [1.6.0] - 2026-11-03
12
+
13
+ ### Added
14
+ - **T-Family (Triangle Family) tanh-free Modes (`buffer="T1"` & `buffer="T2"`)**:
15
+ * **`buffer="T2"` (2-Buffer Triangle Family Mode)**: Keeps the 2-buffer state (`exp_avg` and `exp_avg_sq`) without using $\tanh$, computing the gradient-momentum residual $d_t = g_t - m _ {t-1}$ and bounding the lookahead update $\tilde{m}_t$ via the **Triangle Inequality Envelope** $\mathcal{E}_t^{(\text{T2})}$:
16
+
17
+ $\displaystyle d_t = g_t - m _ {t-1}, \qquad v_t = \beta_2 v _ {t-1} + (1 - \beta_2) d_t^2$
18
+
19
+ $\displaystyle \tilde{m} _ t = w_1 m _ {t-1} + w_2 g_t, \qquad w_1 = \beta_1^2, \qquad w_2 = 1 - \beta_1^2$
20
+
21
+ $\displaystyle \mathcal{E} _ t^{(\text{T2})} = w_1 |m _ {t-1}| + w_2 |g_t| + 0.5\lambda |d_t| + 0.5\lambda \sigma_t + \epsilon _ {\text{eff}}, \qquad u_t^{(\text{T2})} = \frac{\tilde{m} _ t}{\mathcal{E} _ t^{(\text{T2})}} \in (-1, 1)$
22
+
23
+ By the Triangle Inequality ($|w_1 m _ {t-1} + w_2 g_t| \le w_1 |m _ {t-1}| + w_2 |g_t| \le \mathcal{E} _ t^{(\text{T2})}$), $|u_t^{(\text{T2})}| < 1$ is guaranteed without evaluating $\tanh$. The term $0.5\lambda |d_t|$ acts as a **triangle-defect penalty**, and $0.5\lambda \sigma_t$ provides belief-variance damping.
24
+ * **`buffer="T1"` (1-Buffer Triangle Family Mode)**: Combines the single-buffer **Triangle-Defect** denominator $\mathcal{E} _ t^{(\text{T1})} = w_1 |m _ {t-1}| + w_2 |g_t| + \lambda |d_t| + \epsilon _ {\text{eff}}$ (using residual $d_t = g_t - m _ {t-1}$) with the rational update bounds below, reducing optimizer state memory by 50% via the Triangle Inequality.
25
+ * **Rational Update Bounds (for `T1` & `T2`)**: Replaces $\tanh$ and $\sqrt{\cdot}$ in the coordinate, radial, and FP16 update bounds with simple rational formulas:
26
+
27
+ $\displaystyle b_i = \mathrm{bound} _ {\text{ratio}} \cdot (|w_i| + 10^{-2}), \qquad \Delta w_i = \frac{b_i (b_i + |s_i|) s_i}{b_i (b_i + |s_i|) + s_i^2}, \qquad \mathrm{scale} _ {\text{radial}} = \frac{1 + r}{1 + r + r^2}$
28
+ * Keeps 100% backward compatibility and identical outputs for existing modes (`buffer=1`, `buffer=2`, and `version="1.5.5"`).
29
+
30
+ ---
31
+
32
+ ## [1.5.5] - 2026-10-29
33
+
34
+ ### Added
35
+ - **Contiguous Bucket Fusion (`fuse_tensors: bool = False`)**:
36
+ * Added opt-in contiguous parameter and state coalescing (`_build_contiguous_buckets`) that groups parameters within each `(param_group, device, dtype)` domain $\mathcal{B}_{g,d,\tau}$ into contiguous 1D storage (`flat_p`, `flat_g`, `flat_m`, `flat_v`, `flat_mp`) and rebinds `p.data`, `p.grad`, and `self.state[p]` entries as zero-copy views (`flat_*[offset : offset + n].view(p.shape)`).
37
+ * Eliminates per-tensor CPU-to-GPU kernel launch latency on fragmented vision and edge architectures by executing an entire bucket in a single fused kernel dispatch while computing `cautious_scale` and `radial` trust bounds across the bucket domain $\mathcal{B}_{g,d,\tau}$.
38
+ * Added in-place `zero_grad()` view preservation (`flat_g.zero_()`) and full `state_dict()` / `load_state_dict()` checkpoint compatibility without leaking temporary buffer keys (LSO-Eval Stage II compliant).
39
+ - **Triton JIT Pre-Warmup (`prewarm_jit: bool = True`)**:
40
+ * Added automatic initialization-time Triton JIT compilation (`_prewarm_all_configs`) using 64-element dummy buffers (`128–256 bytes` VRAM) with `lr=0.0` and `weight_decay=0.0` across both Step 1 (`pass1` + `pass2`) and Step 2+ (`fused_single_pass` + `post_step_ema`) paths with target `BLOCK_SIZE` specialization (`1024` or `2048`), eliminating first-step compilation stalls.
41
+ - **Bounded Bucket Chunking & Sequential In-Place Migration (`bucket_cap_mb: float = 500.0`)**:
42
+ * Bound contiguous bucket coalescing by an upper memory capacity ceiling $C_{\text{cap}} = 500\text{ MB}$, partitioning model parameters into ordered contiguous chunks $\mathcal{B}^{(1)}, \mathcal{B}^{(2)}, \dots, \mathcal{B}^{(K)}$ such that $\sum_{p \in \mathcal{B}^{(k)}} \text{bytes}(p) \le C_{\text{cap}}$ (with giant single layers $> C_{\text{cap}}$ isolated into individual buckets).
43
+ * Enforced sequential in-place migration with immediate parameter storage recycling (`del old_data`, `del old_grad`) and garbage collection, constraining peak transient memory allocation spikes during optimizer initialization to at most $\min(\text{model bytes}, C _ {\text{cap}})$.
44
+
45
+ ---
46
+
47
+ ## [1.5.4] - 2026-10-29
48
+
49
+ ### Added
50
+ - **In-Container Native Bitwise-BF16 State Promotion (`dtype_mode == 2`, `master_weights="none"`)**:
51
+ * Promoted 16-bit optimizer state buffers ($m_t$ via stochastic rounding `f32_to_bf16_bits_sr` and $v_t$ via round-to-nearest-even `f32_to_bf16_bits_rne`) to native Bitwise-BF16 stored inside the existing 16-bit container during pure `FP16` execution, expanding state exponent dynamic range to 8 bits ($1.18 \times 10^{-38}$) with zero additional VRAM overhead.
52
+
53
+ ### Changed
54
+ - **Unified Denominator Stability Constant ($\epsilon_{\text{eff}} = \epsilon = 10^{-8}$)**:
55
+ * Unified the effective denominator constant $\epsilon_{\text{eff}} = \epsilon = 10^{-8}$ across all precision modes and hardware backends (replacing the legacy `FP16` floor clamp $\max(\epsilon, 10^{-4})$ introduced in `1.1.5`), preventing update velocity attenuation and late-stage convergence loss resets when $\sqrt{v_t} \ll 10^{-4}$.
56
+
57
+ ### Removed
58
+ - **Centered Defect Compensation (`centered_defect`, $\mu_d$)**:
59
+ * Removed the `centered_defect` hyperparameter and the global defect mean reduction $\mu_d = \frac{1}{N}\sum_{i=1}^N (g_{t,i} - m_{t-1,i})$ across both Dual-Buffer Convex Lookahead Momentum (`buffer=2` / CLM) and Single-Buffer Triangle-Defect Convex Lookahead Momentum (`buffer=1` / TD-CLM).
60
+ * Simplified the instantaneous gradient-momentum residual to purely local coordinate-wise differences:
61
+
62
+ $\displaystyle d_t^{(\text{B1})} = g_t - m_{t-1}, \qquad d_t^{(\text{B2})} = g_t - m_t$
63
+
64
+ * Eliminated the pre-step global reduction pass (`_lumina_diff_sum_kernel` in Triton JIT/AOT, `diff_sum.comp` in Vulkan SPIR-V, `reduce_diff_sum_simd` in x86\_64 AVX-512/AVX2, and slot 5 of the 6-element `ScratchBuffer`), removing cross-lane/cross-rank reduction latency while retaining the 6-element physical `ScratchBuffer` layout (`[mask_sum, u_sq_sum, p_sq_sum, cautious_scale, bound_scale, _reserved]`, 5 active slots + 1 reserved slot for ABI alignment).
65
+
66
+ ---
67
+
68
+ ## [1.5.3] - 2026-10-20
69
+
70
+ No versions were published between 1.3.3 and 1.5.3. This release notes all changes made since version 1.3.3.
71
+
72
+ ### Added
73
+ - **Single-Buffer Triangle-Defect Convex Lookahead Momentum (TD-CLM, `buffer=1`)**:
74
+ * Replaced the legacy 3-pass RMS single-buffer update with a single-pass formulation:
75
+
76
+ $\displaystyle \tilde{m} _ t = w_1 m _ {t-1} + w_2 g_t, \quad w_1 = \beta_1^2, \quad w_2 = 1 - \beta_1^2$
77
+
78
+ $\displaystyle \mathcal{E} _ t = w_1 |m _ {t-1}| + w_2 |g_t| + \lambda |d_t^{(\text{B1})}| + \epsilon _ {\text{eff}}$
79
+
80
+ $\displaystyle u_t^{(\text{B1})} = \frac{\tilde{m} _ t}{\mathcal{E} _ t} \in (-1, 1)$
81
+
82
+ * In 1.3.3, single-buffer mode required three sequential GPU kernel launches (`_lumina_v1_pass1_kernel`, `_lumina_v1_pass2_kernel`, `_lumina_v1_pass3_update_kernel`) and global atomic reductions across thread blocks to compute tensor-wide root-mean-square ($\text{rms} = \sqrt{\frac{1}{N}\sum \tilde{m} _ i^2 + \epsilon}$).
83
+ * TD-CLM replaces the tensor-wide reduction with coordinate-wise envelope bounding via the Triangle Inequality Envelope $\mathcal{E} _ t$. This reduces single-buffer execution to a single GPU pass and eliminates all $\tanh$ evaluations in the momentum path.
84
+ * Calibrated the defect penalty weight $\lambda$ to the symmetric spike-reversal equilibrium:
85
+
86
+ $\displaystyle \lambda^* = \frac{2(1 - \beta_1^2)^2}{4\beta_1^2 - 3}$
87
+
88
+ For the default $\beta_1 = 0.9$, $\lambda^* \approx 0.3008$, setting the default parameter to `lam=0.3`.
89
+
90
+ - **Padé [13/6] Rational Approximation (`tanh_pade_f32`)**:
91
+ * Implemented a rational Padé [13/6] polynomial for hyperbolic tangent calculations:
92
+
93
+ $\displaystyle \tanh _ {\text{Pade}}(x) = \frac{x(1 + c_1 x^2 + c_2 x^4 + c_3 x^6 + c_4 x^8 + c_5 x^{10} + c_6 x^{12})}{1 + d_1 x^2 + d_2 x^4 + d_3 x^6}$
94
+
95
+ * Evaluated over the clamped domain $x \in [-7.9988117, 7.9988117]$ with asymptotic saturation to $\pm 1.0$.
96
+ * In 1.3.3, `_triton_tanh_fast` used hardware-dependent SFU instructions (`tl.math.tanh`) or analytical sigmoid fallbacks (`2 / (1 + exp(-2x)) - 1`). Different GPU architectures (e.g. Turing, Ampere, Hopper) evaluate SFU transcendental functions with slightly different mantissa rounding, leading to bitwise divergence during distributed training.
97
+ * Padé [13/6] provides uniform precision with $\lVert \tanh _ {\text{Pade}}(x) - \tanh(x) \rVert _ \infty < 1.5 \times 10^{-6}$ across CPU Scalar, AVX2, AVX-512, Vulkan SPIR-V, and Triton GPU kernels.
98
+
99
+ - **Centered Innovation Defect (`centered_defect`)**:
100
+ * Added batch-mean centering for gradient innovation tracking (`centered_defect=False` by default):
101
+
102
+ $\displaystyle \mu_d = \frac{1}{N}\sum _ {i=1}^N (g _ {t,i} - m _ {t-1,i})$
103
+
104
+ $\displaystyle d_t^{(\text{B1})} = (g_t - m _ {t-1}) - \mu_d$
105
+
106
+ $\displaystyle d_t^{(\text{B2})} = (g_t - m_t) - \beta_1 \mu_d$
107
+
108
+ * In 1.3.3, uncentered innovation $g_t - m_t$ was used directly. On layers where gradients have a non-zero mean (such as normalization scales, biases, and token embeddings), the raw difference caused systematic drift in the second-moment accumulator. Centering eliminates this mean shift.
109
+
110
+ - **Subnormal-Safe IEEE-754 FP16 Stochastic Rounding**:
111
+ * Added `f32_to_f16_bits_rne` to handle IEEE-754 subnormal numbers ($|x| < 2^{-14}$) during FP16 stochastic rounding.
112
+ * In 1.3.3, stochastic rounding added 13-bit random noise and masked with `& -8192`. When a floating-point value entered the subnormal range, bitwise addition directly corrupted the exponent field (bit 10), causing values to flip between normal and subnormal representations or trigger hardware flush-to-zero traps.
113
+ * The updated routine checks for zero exponent, shifts the significand into the subnormal format, and applies round-to-nearest-even tie-breaking before bit truncation.
114
+
115
+ - **Multi-Backend Execution Engine**:
116
+ * Replaced the Python-only runtime with a multi-backend execution layer:
117
+ * **Rust SIMD (`luminav-simd`)**: AVX-512 (512-bit registers), AVX2 with FMA, portable scalar fallback, and Rayon parallel chunk scheduling (`ELEM_CHUNK_SIZE = 2,097,152`).
118
+ * **Vulkan UMA (`luminav-vulkan`)**: SPIR-V compute pipeline for cross-vendor GPUs (AMD, Intel, Apple Silicon via MoltenVK, and NVIDIA) with zero-copy host-visible memory buffers.
119
+ * **Triton AOT (`luminav-triton-aot`)**: C-ABI wrapper dispatching ahead-of-time precompiled kernels for 84 GPU architectures (Blackwell SM100/SM120, Hopper SM90, Ada SM89, Ampere SM80/SM86, Turing SM75, and AMD ROCm gfx90a/gfx942/gfx1100).
120
+ * **Standalone Runtime (`luminamoon.optimizer.standalone`)**: Pure NumPy and C-ABI execution that runs without requiring PyTorch.
121
+
122
+ ### Changed
123
+ - **Parameter `alpha_ss`**: Removed. In 1.3.3, `alpha_ss=0.5` was used for soft-sign scaling $z / (1 + \alpha _ {\text{ss}} |z|)$ in the legacy single-buffer path. In 1.5.3, TD-CLM envelope normalization bounds updates directly to $(-1, 1)$, making soft-sign obsolete.
124
+ - **Parameter `lam`**: Added with default `0.3`. Governs the defect penalty weight in TD-CLM.
125
+ - **Parameter `centered_defect`**: Added with default `False`. Enables batch-mean centering of the innovation defect.
126
+ - **Parameter `bound_type`**: Default changed from `"radial"` (1.3.3) to `"coordinate"` (1.5.3). Coordinate-wise hyperbolic bounding $(B_i \tanh(\Delta \theta_i / B_i))$ evaluates locally on each parameter without computing a tensor-wide norm reduction.
127
+ - **Parameter `bound_ratio`**: Default changed from `0.03` to `0.05` to provide better update headroom on large model architectures.
128
+ - **Parameter `ema_decay`**: Default changed from static `0.8` to `"auto"`. Resolves to `0.80` for Dual-Buffer mode (`buffer=2`) and `0.95` for Single-Buffer mode (`buffer=1`).
129
+ - **Scratch Buffer**: Expanded from 5 floats `[mask_sum, u_sq_sum, p_sq_sum, cautious_scale, bound_scale]` to 6 floats, adding `diff_sum` at index 5 to accumulate batch-mean defect statistics.
130
+ - **Cautious Scaling Floor**: Enforced `cautious_clamp_min=0.5` across all backends to cap maximum cautious energy scaling at $2.0\times$ ($2^1$).
131
+
132
+ ### Fixed
133
+ - **Hardware Mantissa Divergence**: Fixed bitwise output differences across GPU families by replacing hardware SFU `tl.math.tanh` with Padé [13/6] rational approximation.
134
+ - **FP16 Subnormal Bit Corruption**: Fixed exponent corruption in subnormal FP16 stochastic rounding by separating exponent extraction from significand shift logic.
135
+ - **Single-Buffer Kernel Launch Latency**: Reduced single-buffer kernel dispatches from 3 passes to 1 pass, eliminating host-side synchronization bubbles and intermediate L2 atomic reduction overhead.
136
+ - **Non-Zero Mean Gradient Drift**: Resolved directional accumulator drift on bias and normalization layers via `centered_defect`.
137
+
138
+ ### Parameter Comparison (1.3.3 vs 1.5.3)
139
+
140
+ | Parameter | Version 1.3.3 | Version 1.5.3 | Technical Notes |
141
+ | :--- | :--- | :--- | :--- |
142
+ | `params` | `params` | `params` | Iterable of parameters or parameter groups. |
143
+ | `lr` | `8e-4` | `8e-4` | Base learning rate. |
144
+ | `betas` | `(0.9, 0.999)` | `(0.9, 0.999)` | First and second moment exponential decay rates. |
145
+ | `eps` | `1e-8` | `1e-8` | Numerical stability floor. |
146
+ | `weight_decay` | `8e-2` | `8e-2` | Decoupled weight decay rate. |
147
+ | `tau` | `0.8` | `0.8` | Noise dispersion scale factor. |
148
+ | `alpha_ss` | `0.5` | *(removed)* | Removed; superseded by TD-CLM envelope normalization. |
149
+ | `lam` | *(none)* | `0.3` | Defect penalty weight in TD-CLM (calibrated to $\lambda^* \approx 0.3008$). |
150
+ | `cautious` | `True` | `True` | Directional verification gate ($u_i g_i > 0$). |
151
+ | `cautious_clamp_min` | `0.5` | `0.5` | Maximum cautious scale cap ($2.0\times$). |
152
+ | `buffer` | `2` | `2` | `1` = Single-Buffer (TD-CLM), `2` = Dual-Buffer (CLM). |
153
+ | `centered_defect` | *(none)* | `False` | Enables subtraction of batch-mean defect $\mu_d$. |
154
+ | `stochastic_rounding` | `True` | `True` | Bitwise stochastic rounding on FP16/BF16 weights. |
155
+ | `bound` | `True` | `True` | Enables update trust-region bounding. |
156
+ | `bound_type` | `"radial"` | `"coordinate"` | Default changed to coordinate-wise bounding (zero reduction overhead). |
157
+ | `bound_ratio` | `0.03` | `0.05` | Maximum step magnitude relative to parameter magnitude. |
158
+ | `master_weights` | `False` | `"none"` | `"none"`, `"semi"`, or `"full"`. |
159
+ | `execution` | `"auto"` | `"auto"` | Dynamic routing: SIMD, Vulkan, Triton, or Foreach. |
160
+ | `fused_single_pass` | `False` | `True` | Enables single-pass streaming with EMA scaling. |
161
+ | `ema_decay` | `0.8` | `"auto"` | Automatically sets `0.80` for dual-buffer, `0.95` for single-buffer. |
162
+ | `fsdp_sync` | `False` | `False` | Cross-rank collective all-reduce synchronization. |
163
+ | `seed` | `1337` | `1337` | PRNG seed for deterministic stochastic rounding. |
164
+
165
+ ---
166
+
167
+ ## [1.3.3] - 2026-10-06
168
+
169
+ ### Fixed
170
+ * **Triton AST Compiler Early-Return Inlining Error:**
171
+ * Resolved fatal MLIR/CFG code generation failure (`at 35:4: _store_state_buffer(...) ^`) triggered by mid-branch `return` statements in `@triton.jit` helper functions (`_store_param` and `_store_state_buffer`).
172
+ * Refactored helper control flow into structured, single-exit `if ... elif ... else` branches, ensuring compliant JIT function inlining across all kernel entry points.
173
+ * **Bitcast Width Mismatch (Int64 Promotion in Stochastic Rounding):**
174
+ * Fixed implicit 64-bit integer promotion caused by Python integer literals (`0x9E3779B9`, `~0x1FFF`, `~0xFFFF`) inside Triton bitwise operations.
175
+ * Enforced strict 32-bit signed arithmetic via two's complement constants (`-1640531527`, `-2048173461`, `-1028477387`) with explicit `.to(tl.int32)` coercion, and standardized bitwise masks (`-65536` for BF16, `-8192` for FP16), ensuring type-safe 32-bit bitcasting (`tl.int32` to `tl.float32`).
176
+ * **JIT Kernel Call Keyword Arguments:**
177
+ * Replaced internal keyword argument invocations (`use_sr=False`) with standard positional boolean parameters inside Triton JIT kernels to eliminate compiler argument binding warnings across Triton runtime versions.
178
+
179
+ ### Improved
180
+ * **Throttled Fallback Telemetry & Console De-Noising:**
181
+ * Added `self._warned_triton_failure` state tracking to prevent multi-line compiler AST dumps from flooding the console across every parameter tensor in a parameter group.
182
+ * Formatted fallback notifications into a single concise one-line `WARNING` log on initial encounter, routing full tracebacks to `DEBUG` level.
183
+
184
+ ### Verified
185
+ * **End-to-End JIT Execution on NVIDIA Architectures (Tesla T4):**
186
+ * Validated JIT execution across both Dual-Pass (`fused_single_pass=False`) and Fused Single-Pass (`fused_single_pass=True`) modes in native FP16.
187
+ * Confirmed non-zero weight update dynamics ($|\Delta W| > 0$) without gradient underflow or weight freezing in pure master-free training for Version 1.3.3.
188
+
189
+ ---
190
+
191
+ ## [1.3.2] - 2026-10-06
192
+
193
+ ### Added
194
+ - **Distributed FSDP Collective Synchronization (`fsdp_sync`)**:
195
+ - Introduced the `fsdp_sync: bool = False` configuration flag on optimizer initialization, backed by automated runtime guarding via `torch.distributed.is_initialized()`.
196
+ - Integrated cross-GPU collective communications (`torch.distributed.all_reduce(..., op=torch.distributed.ReduceOp.SUM)`) for global reduction metrics (`mask_sum`, `u_sq_sum`, and `p_sq_sum`) across Triton kernels, single-step execution, and vectorized foreach loops.
197
+ - Maintains bit-exact scalar consistency for `m_bar`, `cautious_scale`, and `bound_scale` across distributed parameter shards during large-scale pretraining under PyTorch FSDP (Fully Sharded Data Parallel) and DeepSpeed ZeRO-3.
198
+ - **Dedicated Low-Precision Stochastic Quantization Routine (`_sr_quantize`)**:
199
+ - Factored out bitwise stochastic rounding into a dedicated, modular helper function: `_sr_quantize(target, val_f32, use_sr, generator)`.
200
+ - Enabled native stochastic rounding on half-precision momentum states (`exp_avg` in BF16/FP16) across all PyTorch CPU/CUDA fallback paths (`_single_step_v2`, `_foreach_step_v2`, `_single_step_v1`, and `_foreach_step_v1`), systematically eliminating gradient stagnation induced by fractional bit truncation (LSB underflow).
201
+ - **Persistent Contiguous Buffer Caching**:
202
+ - Introduced persistent state cache tensors within `self.state[p]` (`p_contig`, `exp_avg_contig`, `exp_avg_sq_contig`, and `mp_contig`) to stage strided or sliced parameters before kernel launch.
203
+ - Eliminates dynamic memory allocations (`.contiguous()`) during iterative training loops, preserving PyTorch VRAM caching allocator stability and preventing CUDA Graph capture invalidation.
204
+ - **Zero-Copy Direct Tensor Aliasing for FP32 Master Weights**:
205
+ - When parameter tensors are natively `torch.float32`, `state["master_param"]` now directly aliases the parameter tensor pointer (`state["master_param"] = p`), avoiding additional master weight VRAM overhead for FP32 layers (0 bytes auxiliary memory footprint).
206
+ - **Checkpoint Sanitizer via State Dict Filter (`state_dict`)**:
207
+ - Overrode `state_dict()` to automatically purge temporary contiguous cache buffers (`p_contig`, `exp_avg_contig`, `exp_avg_sq_contig`, `mp_contig`), ensuring on-disk checkpoint payloads remain clean, compact, and fully backward-compatible.
208
+ - **Defensive Device Pipeline Flush on Kernel Fault (`torch.cuda.synchronize`)**:
209
+ - Embedded explicit hardware device synchronization (`torch.cuda.synchronize(p.device)`) within Triton exception recovery handlers prior to PyTorch fallback execution, preventing asynchronous CUDA error cascades into subsequent tensor operations.
210
+
211
+ ### Changed
212
+ - **High-Entropy Bit-Avalanche PRNG Hash Function (`_prng`)**:
213
+ - Replaced the standard Linear Congruential Generator (LCG) with a MurmurHash3 / SplitMix32 integer bit-avalanche hashing routine:
214
+ ```python
215
+ z = idx + seed + 0x9E3779B9
216
+ z = (z ^ (z >> 16)) * 0x85EBCA6B
217
+ z = (z ^ (z >> 13)) * 0xC2B2AE35
218
+ z = z ^ (z >> 16)
219
+ ```
220
+ - Delivers uniform pseudo-random noise distributions with zero low-order bit periodicity, significantly refining stochastic rounding fidelity on BF16/FP16 parameters.
221
+ - **Memory-Contiguous Scratch Buffer Realignment for v1 Triton Kernels**:
222
+ - Re-indexed reduction buffer offsets in Triton v1 kernels:
223
+ - Pass 1 metrics (`[rms_sum, p_sq_sum]`) mapped to contiguous indices `[0, 1]`.
224
+ - Pass 2 metrics (`[mask_sum, u_sq_sum]`) mapped to contiguous indices `[2, 3]`.
225
+ - Enables vectorized contiguous slice all-reduces (`scratch_param[:2]` and `scratch_param[2:4]`) under FSDP without requiring non-contiguous memory gather/scatter overhead.
226
+ - **Multi-GPU Device Index Resolution in RNG Dispatcher (`_get_generator`)**:
227
+ - Fixed device index resolution when `device` is passed as a generic `torch.device("cuda")` without an explicit index by defaulting to `torch.cuda.current_device()`, avoiding cross-GPU generator key collisions during multi-GPU distributed runs.
228
+ - **Conditional Atomic Reduction Pruning in v2 Pass 1 Kernel**:
229
+ - Guarded `mask_sum` atomic accumulation in `_lumina_v2_pass1_kernel` with `if cautious:`, bypassing CTA reductions and L2 atomic additions when `cautious=False`.
230
+ - **Expanded Master Weight Precision Configuration Aliases**:
231
+ - Added the `"half"` alias for `"semi"` mode alongside `"fp32"` and `"masterweight"` aliases for `"full"` mode.
232
+ - **Metadata & Documentation Header Update**:
233
+ - Bumped specification version to `1.3.2`.
234
+
235
+ ### Fixed
236
+ - **Singular Zero-Division & Step-Zero Bias Correction (`safe_base_step`)**:
237
+ - Introduced `safe_base_step = max(base_step, 1)` and `bc2_base = max(1.0 - (beta2 ** safe_base_step), 1e-15)` across Triton, single-step, and foreach loops.
238
+ - Fully resolves potential `ZeroDivisionError` and NaN blowups in `c1` / `c2` computations when steps evaluate at `step == 0`.
239
+ - **Numerical Overflow Clamp in Sigmoid-Based Tanh Fallback**:
240
+ - Applied register clamp bounds `x_clamped = tl.maximum(tl.minimum(x, 20.0), -20.0)` in analytical fallback `_triton_tanh_fast`, preventing `tl.exp(-2.0 * x)` from overflowing to `inf` or `nan` on devices lacking hardware SFU tanh instructions.
241
+ - **Missing Argument Forwarding in PyTorch Single-Step v1**:
242
+ - Corrected `_single_step_v1` method signature and call-sites to accept and propagate `scratches`, eliminating runtime `TypeError` during fallback invocation.
243
+ - **Multi-Rank Synchronization Skew in Sharded Parameter Bounding**:
244
+ - Aligned tensor norms and radial bounding factors across sharded ranks via synchronized global scalar reductions, preventing parameter update drift across distributed ranks.
245
+
246
+ ### Performance & Engine Refactoring
247
+ - **Zero Dynamic VRAM Allocation on Strided / Sliced Tensors**:
248
+ - Persistent pre-allocated contiguous staging buffers avoid PyTorch caching allocator churn and eliminate CUDA Graph memory address invalidations.
249
+ - Preserves deterministic memory footprints across long pre-training runs.
250
+ - **Optimized Distributed Collective Bandwidth**:
251
+ - Grouping scalar reductions into contiguous vector slices compresses distributed synchronization overhead into a single collective kernel launch per pass.
252
+
253
+ ---
254
+
255
+ ## [1.3.0] - 2026-09-23
256
+
257
+ ### Added
258
+ - **Fused Single-Pass Kernel with Post-Step EMA (`fused_single_pass`, `ema_decay`)**:
259
+ - Introduced `_lumina_v2_fused_single_pass_kernel` coupled with an asynchronous CTA micro-kernel `_lumina_v2_post_step_ema_kernel[(1,)]`.
260
+ - When `fused_single_pass=True`, avoids two-pass global reductions by leveraging running Exponential Moving Average (EMA) estimates of `cautious_scale` ($1 / \bar{M}$) and `bound_scale` ($\tanh(r) / r$).
261
+ - Unifies gradient filtering, momentum accumulation, variance dispersion, cautious masking, radial bounding, decoupled weight decay, stochastic rounding, and block partial reductions into a **single unified VRAM-resident pass**, reducing global GPU memory bandwidth traffic per optimizer step.
262
+ - **Hybrid Master Weight Precision Architecture (`master_weights`)**:
263
+ - Added multi-tier master weight flexibility via `master_weights: Union[bool, str] = False`:
264
+ - `"none"` / `False` *(Default)*: Pure master-free low-precision training leveraging on-chip bitwise stochastic rounding directly on native half-precision weights.
265
+ - `"semi"` / `"hybrid"` / `"half"`: 32-bit FP32 master parameters (`state["master_param"]`) coupled with 16-bit low-memory optimizer states (`exp_avg`, `exp_avg_sq` in BF16/FP16), saving ~50% optimizer state VRAM compared to traditional FP32 setups while maintaining full FP32 weight accumulation accuracy.
266
+ - `"full"` / `"fp32"` / `"masterweight"`: Traditional full FP32 master parameters with full FP32 optimizer states for standard baseline comparisons.
267
+ - **In-Kernel In-Flight NaN / Inf Gradient Sanitization**:
268
+ - Embedded register-level defensive sanitization across all Triton GPU kernels: `is_finite_g = (g == g) & (g < 1e38) & (g > -1e38); g = tl.where(is_finite_g, g, 0.0)`.
269
+ - Replaces non-finite gradients and subnormal corruptions with zero in registers before they can contaminate momentum (`exp_avg`) or variance (`exp_avg_sq`) state buffers.
270
+ - **Low-Precision State Buffer Serialization (`_store_state_buffer`)**:
271
+ - Added a dedicated Triton state serialization routine supporting FP32, BF16, and FP16 state representations with optional bitwise stochastic rounding applied directly to momentum buffers.
272
+ - **Multi-Device & Cross-Accelerator Hardware Context Isolation (`_get_device_context`)**:
273
+ - Unified device stream scoping supporting NVIDIA CUDA (`torch.cuda`), Intel XPU (`torch.xpu`), and modern PyTorch 2.x unified accelerator interfaces (`torch.accelerator.device`), eliminating multi-GPU stream hijacking and cross-device race conditions.
274
+ - **Strict Determinism Auto-Routing**:
275
+ - Automatically queries `torch.are_deterministic_algorithms_enabled()`. When enabled under `execution="auto"`, dynamically routes execution to C++ vectorized `foreach` to preserve bitwise cross-run reproducibility, circumventing non-deterministic floating-point hardware atomic reordering.
276
+ - **Granular Fault Recovery with State Guarding (`completed_indices`)**:
277
+ - Implemented per-parameter completion tracking in `_dispatch_triton`. If a kernel encounters an unexpected runtime fault, completed parameters are preserved while uncompleted ones fall back cleanly to `foreach`.
278
+ - Added `_single_step_v2_apply_param` and `_single_step_v1_apply_param` to safely finalize parameter updates if Pass 1 had already committed state transitions, strictly preventing double-update state corruption.
279
+
280
+ ### Changed
281
+ - **Per-Parameter Persistent Scratch Tensors (Elimination of Global Slicing)**:
282
+ - Replaced the global dynamic slice allocator (`self._scratch_tensors[dev][:count]`) with isolated per-parameter persistent state allocations (`state["scratch"] = torch.zeros((5,), device=p.device, dtype=torch.float32)`).
283
+ - Memory pointers for `[mask_sum, u_sq_sum, p_sq_sum, cautious_scale, bound_scale]` remain completely invariant throughout the entire training lifecycle, preserving pointer stability for **CUDA Graph Capture** (`torch.accelerator.Graph`) and TorchInductor AOT compilation without memory aliasing.
284
+ - **Analytically Exact Nesterov Bias Correction ($\beta_1^{\text{step} + 1}$)**:
285
+ - Corrected first-moment bias correction from the classical Adam formulation ($1.0 - \beta_1^{\text{step}}$) to the exact analytical expectation of the Nesterov blended momentum: `bc1_nes = 1.0 - beta1^(step + 1)`. Removes residual momentum bias during early warmup iterations.
286
+ - **Adaptive CTA Tile Sizing and Dynamic Thread Warp Scaling**:
287
+ - Introduced hardware-aware CTA tile scaling based on parameter volume: parameters with $\ge 2,097,152$ elements (2M+) dynamically configure `BLOCK_SIZE = 2048` and `num_warps = 8` (versus `BLOCK_SIZE = 1024`, `num_warps = 4` for smaller tensors).
288
+ - Cuts total thread-block grid counts and L2 cache atomic addition pressure by 50% on large MLP, Attention Projection, and Vocabulary layers.
289
+ - **Hardware-Native Tanh Instruction Dispatch (`tl.math.tanh` / `tl.tanh`)**:
290
+ - Upgraded `_triton_tanh_fast` to dynamically inspect compiler capabilities, binding directly to native PTX Special Function Unit (SFU) instructions (`tanh.approx.f32`) on modern Triton compilers while retaining analytical sigmoid fallbacks for legacy environments.
291
+ - **PRNG Decorrelation between Momentum and Parameter Updates**:
292
+ - Separated RNG seed streams using bitwise alternating mask shifts: `seed_p = (seed + 0x55555555) & 0x7FFFFFFF`, mathematically eliminating cross-coupling noise correlation between state buffer rounding and parameter update rounding.
293
+ - **Bumped specification version metadata to 1.3.0** across `luminav.py`.
294
+
295
+ ### Fixed
296
+ - **Floating-Point Cancellation in Radial Bounding ($r \to 0$)**:
297
+ - Fixed precision jitter and roundoff degradation near zero by adding an asymptotic Taylor-series guard across Triton and PyTorch bounding engines: `torch.where(r < 1e-4, 1.0, tanh(r) / r)`.
298
+ - **Artificial Denominator Dilation in Dispersion Scale ($\sigma$)**:
299
+ - Replaced additive epsilon inflation (`sigma + 1e-6`) with a non-distorting floor clamp (`tl.maximum(sigma, 1e-12)`), preventing artificial dampening of subtle gradient signals.
300
+ - **Singular Zero-Division in Parameter Norm Clamping**:
301
+ - Replaced `norm_p + 1e-6` with `torch.clamp_min(norm_p, 1.0)` / `tl.maximum(norm_p, 1.0)`, maintaining numerical stability even when parameters are initialized near zero.
302
+ - **Transient Memory Pool Spikes during PyTorch Fallback Stochastic Rounding**:
303
+ - Refactored `_sr_update` to stream stochastic noise generation in bounded chunks of 1,048,576 elements (~4 MB chunk size), eliminating multi-hundred megabyte temporary int32 tensor spikes during fallback execution on large parameter matrices.
304
+ - **Non-Contiguous Buffer Safety & Strided Memory Copy**:
305
+ - Added strict contiguous layout verification for parameters, states, and master weights with in-place synchronization (`p.copy_(p_contig)`), guarded by a one-time developer warning.
306
+
307
+ ### Performance & Engine Refactoring
308
+ - **~50% Global Memory Bandwidth Reduction via Fused Single-Pass Mode**:
309
+ - By unifying parameter loading, gradient evaluation, state transformation, and bounding into one GPU pass under `fused_single_pass=True`, VRAM read/write cycles are halved for cautious and radially bounded steps.
310
+ - **Sub-Microsecond L2 Hardware Atomic Reduction Architecture**:
311
+ - Formally documented the hybrid Intra-Block Tree Reduction + Inter-Block Hardware Atomic pattern. With `BLOCK_SIZE=2048`, a 10M-parameter tensor issues only 4,883 atomic additions directly into GPU L2 cache crossbars, executing in < 0.5 µs and strictly outperforming multi-tier cascaded reduction micro-kernels by eliminating 3–5 µs driver dispatch bubbles.
312
+ - **Static Buffer Address Invariance for CUDA Graphs & TorchInductor AOT**:
313
+ - Persistent 5-slot state scratch buffers eliminate all dynamic slicing and memory reallocations during optimization loops, preserving pointer address invariance for CUDA Graph replay.
314
+
315
+ ---
316
+
317
+ ## [1.2.1] - 2026-09-17
318
+
319
+ ### Added
320
+ - **In-Kernel GPU Parameter Norm Reduction (`p_sq_sum_ptr`)**: Fused parameter Euclidean norm squared $\sum(p_i^2)$ calculation directly into Pass 1 Triton reduction kernels (`_lumina_v2_pass1_kernel` and `_lumina_v1_pass1_kernel`). Pass 2 and Pass 3 now load and resolve `norm_p` (`tl.sqrt(tl.maximum(p_sq_sum, 0.0))`) entirely inside GPU SRAM/registers without host intervention.
321
+ - **Stateless CPU Integer Multiplicative PRNG Hashing**: Replaced dynamic GPU RNG tensor allocation with a pure CPU integer hash using Knuth's 32-bit golden ratio constant: `(step * 0x9E3779B9 + i * 10007) & 0x7FFFFFFF`. Generates uncorrelated 31-bit integer seeds per layer with zero GPU kernel launch overhead and zero PCIe host-device latency.
322
+ - **Isolated Generator Cache for Fallback Engine (`self._generators`)**: Added an internal per-device `torch.Generator` cache, ensuring stochastic rounding in `_sr_update` operates in complete isolation from PyTorch's default CUDA generator state.
323
+
324
+ ### Changed
325
+ - **Consolidated Scratch Buffer Zeroing**: Removed redundant per-parameter slice zeroing inside parameter loops. Buffer zeroing is now batched into a single consolidated `scratch.zero_()` call per step inside `_get_scratch_buffer`.
326
+ - **Scratch Buffer Stride Expansion**:
327
+ - `buffer=2`: Expanded stride from 2×N to 3×N elements per parameter to host `[mask_sum, u_sq_sum, p_sq_sum]`.
328
+ - `buffer=1`: Expanded stride from 3×N to 4×N elements per parameter to host `[rms_sum, mask_sum, u_sq_sum, p_sq_sum]`.
329
+ - **Bumped specification version metadata to 1.2.1**.
330
+
331
+ ### Fixed
332
+ - **Host-Device Synchronization & Pipeline Stalls (`.item()` Bottleneck)**: Removed `norm_p = float(p_contig.float().norm().item())` and `seed = torch.randint(...).item()` from Triton step pipelines. Eliminated 200–400 synchronous PCIe roundtrips and GPU pipeline stalls per step on deep architectures (e.g., 28-layer Transformers), restoring full asynchronous GPU compute utilization.
333
+ - **CUDA Graphs & `torch.compile` Graph Breaks**: Fixed fatal graph capture crashes and TorchDynamo graph breaks caused by blocking host-device synchronization during radial bounding and seed generation. The optimizer step is now compatible with CUDA Graphs (`torch.accelerator.Graph`) and TorchInductor compilation.
334
+ - **Transient VRAM Allocator Thrashing**: Eliminated repeated temporary FP32 parameter cloning (`p_contig.float()`) during norm evaluation, preventing memory pool fragmentation in tight VRAM regimes.
335
+ - **Global RNG Pollution & Determinism Compliance**: Fixed global RNG state mutation in fallback stochastic rounding. Solved reproducibility drift in upstream Dropout, DataLoader shuffling, and RLHF reward modeling, ensuring strict adherence to PyTorch 2.10+ `torch.use_deterministic_algorithms(True)`.
336
+
337
+ ### Performance & Engine Refactoring
338
+ - **Batch Kernel Launch Overhead Elimination**: Removed up to 400–800 individual `cudaMemsetAsync` micro-calls per step previously triggered by per-slice `.zero_()` invocations on 1-element scratch tensors.
339
+ - **Single-Pass Bypass for Coordinate Bounding**: Streamlined `_triton_step_v2` to immediately bypass two-pass reductions and invoke `_lumina_v2_single_pass_kernel` when `cautious=False` and coordinate bounding (`bound_mode == 2`) is selected.
340
+
341
+ ---
342
+
343
+ ## [1.2.0] - 2026-09-17
344
+
345
+ ### Added
346
+ - **Automatic Dynamic FP16 Cliff Governor:** Built-in asymptotic boundary governor (`_triton_tanh_fast` in GPU register and PyTorch fallback) that automatically activates when `p.dtype == torch.float16`. Prevents unclipped steps from pushing parameters, activations, and residual streams into the IEEE-754 FP16 overflow cliff (> 65,504). Remains completely dormant (zero overhead) on `bfloat16` (TPU / Ampere+) and `float32`.
347
+ - **Smooth Asymptotic Parameter Bounding (`bound`, `bound_type`, `bound_ratio`):** Added non-intrusive step bounding with two mathematically continuous formulations:
348
+ - `"radial"`: Direction-Preserving Radial Squashing (`update * tanh(r) / r`) preserving gradient direction (Cosine Similarity = 1.000).
349
+ - `"coordinate"`: Dual-Hyperbolic Coordinate Squashing for fine-grained per-element damping.
350
+ - Configurable via constructor: `bound: bool = True`, `bound_type: str = "radial"`, `bound_ratio: float = 0.03`.
351
+
352
+ ### Changed
353
+ - **Increased Default `cautious_clamp_min` from `0.2` to `0.5`:** Enforces an exact power-of-two maximum energy scaling factor (2.00x, $2^1$). Eliminates floating-point mantissa roundoff distortion in IEEE-754 hardware.
354
+ - Bumped specification version metadata to `1.2.0`.
355
+
356
+ ### Fixed
357
+ * **FP16 Step Divergence & NaN Overflow at High Learning Rates:** Solved step divergence and subsequent NaN crashes when training deep 28-layer Transformer architectures (e.g., Qwen3-0.6B) in pure FP16 at nominal learning rates (`lr = 8e-4`).
358
+ * **Sparse Embedding & LM-Head Amplification:** Mitigated 5x over-amplification on sparse vocabulary gradients where active token counts are much smaller than total tensor parameters ($N \gg N_{\text{active}}$).
359
+ * **Verified stable 1,000-step pretraining from scratch on Qwen3-0.6B** (seq length 512, pure FP16, `lr = 8e-4`) on Nvidia Tesla T4 with zero gradient clipping and zero LR scheduler (loss dropped from 12.09 down to ~5.7 with zero NaNs).
360
+ * **Weight Decay Numerical Parity on Fallback Engine:** Resolved a silent precision-stagnation issue in `_single_step` and `_foreach_step`. Previously, decoupled weight decay (`p.mul_(1.0 - lr * weight_decay)`) was executed directly on native FP16/BF16 tensors prior to `_sr_update`. Due to IEEE-754 Round-to-Nearest (RTN) limits, `(1 - lr * weight_decay) ≈ 0.999936` rounded back to `1.0`, causing weight decay to freeze on half-precision parameters. Weight decay is now unified into FP32 registers inside `_sr_update` alongside stochastic rounding, matching the numerical behavior with the Triton kernel.
361
+ * **Fallback Foreach Indentation & Scope Alignment:** Corrected tensor iteration scope and indentation for `torch._foreach_add_` in non-SR fallback branches.
362
+ * **In-Place GPU Clamping in `_apply_bound`:** Replaced dynamic GPU scalar allocations (`torch.tensor(1.5e-4, device=p.device)`) with native `torch.clamp_min`, eliminating transient CUDA malloc spikes per update step.
363
+
364
+ ### Performance & Engine Refactoring
365
+ * **Unified Bound Execution Pipeline:** Added an immediate short-circuit guard (`if lr == 0.0: return update`) and streamlined the execution flow: intermediate updates are scaled in FP32 (`step_val = lr * u_float`), passed through radial/coordinate bounding, chained cleanly into the FP16 Cliff Governor, and returned in a single normalized step.
366
+
367
+ ---
368
+
369
+ ## [1.1.5] - 2026-09-15
370
+
371
+ ### Added
372
+ - **Native Hardware Capability Detection (`_supports_native_bf16`):** Added automatic device capability detection via `torch.cuda.is_bf16_supported()` to identify Ampere SM80+ architectures and streamline native execution paths.
373
+ - **Hugging Face Repository Reference:** Added official Hugging Face repository link in module docstrings.
374
+
375
+ ### Changed
376
+ - **Dynamic Epsilon Clamping for FP16:** Dynamically floors effective epsilon to `max(eps, 1e-4)` when parameters or states are in `torch.float16`. Prevents bias-corrected scale factor `c2` from underflowing into IEEE-754 subnormal/zero limits on step 1.
377
+ - Bumped specification version to `1.1.5`.
378
+
379
+ ### Fixed
380
+ - **FP16 Step-1 `NaN` from Epsilon Underflow:** Fixed `0.0 / 0.0 = NaN` division occurring on initial training steps when parameters receive zero gradients (e.g., unselected vocabulary embeddings, padding tokens, or dropout paths).
381
+ - **Second-Moment Variance Saturation (`+inf`):** Added safe upper-bound register clamping (`tl.minimum(v_new, 65000.0)` in Triton kernels and `.clamp_(0.0, 65000.0)` in PyTorch C++ foreach/single loops). Prevents unclipped initial pretraining gradient surges ($|g| > 256$) from overflowing the FP16 maximum dynamic range (65,504) into irreversible `+inf`.
382
+ - **Zero-Sigma Division Guard:** Injected explicit `sigma + 1e-6` denominator guards across all Triton kernel passes and PyTorch update steps to ensure numerical safety during cold-start iterations.
383
+ - Verified stable 100-step pretraining from scratch on `SmolLM-135M` using Wikimedia Wikipedia in pure FP16 on Nvidia Tesla T4 GPU with zero gradient clipping.
384
+
385
+ ---
386
+
387
+ ## [1.1.0] - 2026-09-11
388
+
389
+ ### Added
390
+ - **Bitwise Stochastic Rounding (SR):** Integrated on-chip PRNG integer bit-manipulation hash in Triton kernels and vectorized PyTorch fallback for native FP16/BF16 training without master weights.
391
+ - Unified Triton store routine introducing compile-time branching (`dtype_mode` / `use_sr: tl.constexpr`) with explicit safe casting for FP16 and BF16 pointers.
392
+ - Vectorized fallback improvements utilizing C++ multi-tensor operations when stochastic rounding is disabled or tensors are FP32.
393
+ - State buffer contiguity validation (`exp_avg_contig`, `exp_avg_sq_contig`) ensuring safe linear memory addressing before kernel dispatch.
394
+ - Declarative configuration file (`config.json`) defining hyperparameters and optimizer presets.
395
+
396
+ ### Changed
397
+ - Refactored PRNG integer arithmetic to strictly stay within signed 32-bit integer limits ($< 2^{31} - 1$), eliminating JIT compilation failures on newer Triton releases.
398
+ - State buffers (`exp_avg`, `exp_avg_sq`) now initialize with strict contiguous memory format.
399
+
400
+ ### Fixed
401
+ - Fixed `ValueError: Scalar out of range for type int32` and Triton LLVM compile errors during unsigned 32-bit bitmask operations.
402
+ - Resolved kernel fallback hangs and compilation overhead on CUDA devices.
403
+
404
+ ---
405
+
406
+ ## [1.0.2] - 2026-09-08
407
+
408
+ ### Added
409
+ - Official Apache-2.0 license notice and copyright headers.
410
+ - Standardized package exports (`__all__`, `__version__`, `__author__`, `__license__`).
411
+
412
+ ---
413
+
414
+ ## [1.0.1] - 2026-08-31
415
+
416
+ ### Fixed
417
+ - **Memory Synchronization Fix (`423d171`):** Fixed a critical bug where non-contiguous parameter tensors (e.g., transposed linear layers or sliced weights) were updated on temporary contiguous copies without reflecting changes back to model weights. Added `p.copy_(p_contig)` after Triton execution.
418
+
419
+ ---
420
+
421
+ ## [1.0.0] - 2026-08-30
422
+
423
+ ### Added
424
+ - Initial release of the LuminaV optimizer.
425
+ - **Zero Master-Weight Copies:** Direct parameter updates in native half-precision without allocating 4-byte FP32 master weights.
426
+ - **Hyperbolic Tangent (`tanh`) Bounding Envelope:** Projects normalized momentum ratio into `(-1.0, 1.0)` to eliminate exploding gradient shocks.
427
+ - **Directional Alignment Gate:** Cautious update masking that zeroes out momentum steps opposing mini-batch gradient directions.
428
+ - **Central Innovation Variance:** Noise dispersion tracking $(g_t - m_t)^2$ decoupling mini-batch turbulence from directional descent.
429
+ - Dual-buffer mode (`buffer=2`, LuminaV-2B) and single-buffer mode (`buffer=1`, LuminaV-1).
430
+ - Custom fused OpenAI Triton kernels with automated fallback execution.
changelogs/README.md CHANGED
@@ -1,25 +1,25 @@
1
- # LuminaMoon Changelogs
2
-
3
- This directory separates release tracking into two distinct lineages to eliminate version ambiguity between the software distribution and the underlying mathematical specification:
4
-
5
- | Document | Current Version | Scope |
6
- | :--- | :--- | :--- |
7
- | [**Software Changelog**](SOFTWARE_CHANGELOG.md) | **`v1.1.0`** | Tracks the `luminamoon` package distributed on PyPI and GitHub (`pip install luminamoon`), covering the Rust workspace crates, CPython FFI bindings, Data Engine, LSO-Eval, and CI/CD infrastructure. |
8
- | [**LuminaV Algorithm Changelog**](LUMINAV_ALGORITHM_CHANGELOG.md) | **`v1.6.0`** | Tracks the mathematical formulation, numerical invariants, stochastic rounding algorithms, and GPU/SIMD kernel evolution of the **LuminaV Optimizer** from `v1.0.0` through the `v1.6.0` specification. |
9
-
10
- ## Version Introspection in Python
11
-
12
- ```python
13
- import luminamoon
14
- from luminamoon import LuminaV
15
- import luminamoon.data as lmd
16
-
17
- # 1. Software Package Version (PyPI / Cargo / Data Engine)
18
- print(luminamoon.__version__) # "1.1.0"
19
- print(lmd.get_version()) # "1.1.0"
20
-
21
- # 2. LuminaV Algorithm Math Version
22
- print(LuminaV.ALGORITHM_MATH) # "1.6.0"
23
- print(LuminaV.ALGORITHM_SPEC) # "1.6.0" (Backward-compatible alias)
24
- print(LuminaV.getVersion()) # "1.6.0"
25
- ```
 
1
+ # LuminaMoon Changelogs
2
+
3
+ This directory separates release tracking into two distinct lineages to eliminate version ambiguity between the software distribution and the underlying mathematical specification:
4
+
5
+ | Document | Current Version | Scope |
6
+ | :--- | :--- | :--- |
7
+ | [**Software Changelog**](SOFTWARE_CHANGELOG.md) | **`v1.1.0`** | Tracks the `luminamoon` package distributed on PyPI and GitHub (`pip install luminamoon`), covering the Rust workspace crates, CPython FFI bindings, Data Engine, LSO-Eval, and CI/CD infrastructure. |
8
+ | [**LuminaV Algorithm Changelog**](LUMINAV_ALGORITHM_CHANGELOG.md) | **`v1.6.0`** | Tracks the mathematical formulation, numerical invariants, stochastic rounding algorithms, and GPU/SIMD kernel evolution of the **LuminaV Optimizer** from `v1.0.0` through the `v1.6.0` specification. |
9
+
10
+ ## Version Introspection in Python
11
+
12
+ ```python
13
+ import luminamoon
14
+ from luminamoon import LuminaV
15
+ import luminamoon.data as lmd
16
+
17
+ # 1. Software Package Version (PyPI / Cargo / Data Engine)
18
+ print(luminamoon.__version__) # "1.1.0"
19
+ print(lmd.get_version()) # "1.1.0"
20
+
21
+ # 2. LuminaV Algorithm Math Version
22
+ print(LuminaV.ALGORITHM_MATH) # "1.6.0"
23
+ print(LuminaV.ALGORITHM_SPEC) # "1.6.0" (Backward-compatible alias)
24
+ print(LuminaV.getVersion()) # "1.6.0"
25
+ ```
changelogs/SOFTWARE_CHANGELOG.md CHANGED
@@ -1,261 +1,261 @@
1
- # LuminaMoon Software Changelog
2
-
3
- All notable software, packaging, FFI, and infrastructure changes to the `luminamoon` monorepo and PyPI distribution (`pip install luminamoon`) are documented in this file.
4
-
5
- The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
-
7
- For the mathematical and kernel evolution of the **LuminaV Optimizer** specification (`v1.0.0` to `v1.6.0`), see [`LUMINAV_ALGORITHM_CHANGELOG.md`](LUMINAV_ALGORITHM_CHANGELOG.md).
8
-
9
- ---
10
-
11
- ## [1.1.0.post3] - 2026-11-07
12
-
13
- ### Changed
14
- - **Academic Citation Consolidation**:
15
- * Removed redundant `CITATION.cff` metadata file to consolidate citation records directly into Zenodo (DOI: [`10.5281/zenodo.23978801`](https://doi.org/10.5281/zenodo.23978801)) and repository `README.md`.
16
- - **Data Engine Version Introspection (`python/luminamoon/data/version.py`)**:
17
- * Streamlined data version checking so `get_version()` directly returns the active package version string without disk or mutable state side effects.
18
- * Maintained full backward compatibility for `list_versions()` and `set_version()` with informative `DeprecationWarning` notices explaining that the data engine operates on the unified native SIMD implementation.
19
- * Removed internal cache directory cloning and dynamic module loading routines from `~/.cache/luminamoon/versions`.
20
- * Updated `data/README.md` to document direct version inspection via `luminamoon.data.get_version()`.
21
- - **Documentation & Technical Guides**:
22
- * Rewrote technical guides, component documentation, and reference overviews across `docs/` and `data/docs/` in direct, humble, and accessible language for human readability.
23
- * Preserved core algorithmic identities including the Triangle Family, Triangle Inequality Envelope, Gradient-Momentum Residual, and Triangle-Defect Penalty without inflated descriptors.
24
- * Normalized data engine documentation in `python/luminamoon/data` and `data/` to consistent English terminology.
25
- * Aligned the MinHash documentation in `docs/dataloader/sources_and_streaming/README.md` with the Rust implementation using XXH3-64 seed multipliers.
26
-
27
- ### Refined
28
- - **Evaluation Specification Artifacts (`eval/`)**:
29
- * Updated `eval/lso_eval_specification.tex` with refined layout and mathematical notation.
30
- * Added updated compiled specification manuscript `eval/LSO_Eval.pdf` and removed legacy PDF files.
31
- * Aligned `tools/sync_huggingface.py` and `eval/README.md` to track `eval/LSO_Eval.pdf`.
32
- - **PosRaGLU Activation Gating (`luminamoon.nn`)**:
33
- * Added explicit positive anchor support ($b > 0$) for `pos_ra_gate` and `PosRaGLU`, preserving unit derivative at the origin while bounding output in $(0, 1)$.
34
-
35
- ### Fixed
36
- - **Markdown Parameter Table Pipe Escaping**:
37
- * Escaped pipe characters in inline code notation (`\|d_t\| = \|g_t - m_{t-1}\|`) within the README parameter table to prevent GitHub Flavored Markdown (GFM) table parsers from prematurely splitting table cells.
38
- - **Static Type Analysis (`luminamoon.nn._compat`)**:
39
- * Declared `_rs_engine` at module scope with multi-tier import fallbacks to eliminate Pyright unresolved import warnings.
40
-
41
- ### Infrastructure
42
- - **Continuous Integration & Release Automation**:
43
- * Added Python runtime setup to the Rust test workflow in `.github/workflows/ci.yml` so linkers reliably locate shared Python libraries during PyO3 FFI tests on updated runner images.
44
- * Streamlined GitHub Release note generation by eliminating redundant boilerplate headers and prioritizing direct changelog entries.
45
-
46
- ---
47
-
48
- ## [1.1.0.post2] - 2026-11-05
49
-
50
- ### Fixed
51
- - **Rust Arrow & Parquet Dependencies (`RUSTSEC-2026-0442`)**:
52
- * Updated `parquet`, `arrow-array`, `arrow-schema`, and `arrow-ipc` to `60.1.0` in workspace manifests (`Cargo.toml` and `Cargo.lock`).
53
- * Resolves advisory **RUSTSEC-2026-0442** (CVE-2026-111959) in `arrow-data`, preventing potential out-of-bounds reads when validating RunEndEncoded arrays during dataset ingestion.
54
-
55
- ---
56
-
57
- ## [1.1.0] - 2026-11-04
58
-
59
- ### Added
60
- - **Hardware-Accelerated Triangle Family Fused Triton Kernels (GPU JIT & Rust AOT)**:
61
- * **Python JIT (`_lumina_t1_single_pass_kernel`, `_lumina_t2_fused_single_pass_kernel`)**: Added dedicated fused register kernels for the Triangle Family (`buffer="T1"` and `buffer="T2"`), replacing eager PyTorch chunking and eliminating CPU-GPU `.item()` sync stalls, bringing T2 execution latency from 20.7 ms down to native GPU speed (~0.9–1.0 ms).
62
- * **Rust Triton AOT Crate (`luminav-triton-aot`)**: Added Ahead-Of-Time compiled kernels for `T1` and `T2` across 12 architectures (Turing, Ampere, Ada, Hopper, Blackwell, CDNA, RDNA3), expanding the embedded manifest to 96 binary artifacts.
63
- - **Strict Version Capability Enforcement**:
64
- * Enforced specification capabilities across historical LuminaV algorithm versions (`1.6.0`, `1.5.5`, `1.5.4`, `1.5.3`): locking to `1.5.4` or `1.5.5` strictly rejects features introduced in later versions (such as `buffer="T2"` or `fuse_tensors=True`), guaranteeing reproducibility for academic benchmarks.
65
-
66
- ### Added
67
- - **Ra-Family (Rational, Tanh-Free & Square-Root-Free Neural Architecture)**:
68
- * **Zero Mandatory PyTorch Dependency**: The `luminamoon` package remains lightweight (~2.4 MB) with zero required dependencies (`dependencies = []`). The new `luminamoon.nn` module automatically detects the runtime environment (`_compat.py`), inheriting from `torch.nn.Module` when PyTorch is installed, or falling back cleanly to standalone NumPy buffers when PyTorch is absent.
69
- * **`RaNorm` (`luminamoon.nn.RaNorm`)**: Token normalization using $L_1$ mean absolute deviation instead of root-mean-square. Eliminates square-root evaluations and amortizes a single scalar reciprocal per token vector.
70
- * **`RaBatchNorm2d` (`luminamoon.nn.RaBatchNorm2d`)**: 2D batch normalization using cross-batch mean absolute deviation (MAD). Includes inference weight folding (`fold_into_conv`) that merges running statistics into preceding `Conv2d` layers with zero additional inference latency.
71
- * **`RaTanh` & `ra_tanh`**: Rational hyperbolic tangent with non-vanishing algebraic gradient tails $O(1 / |x|^3)$ and unit derivative at origin.
72
- * **`RaGELU` & `ra_gelu`**: Rational GELU activation using rational polynomial approximations.
73
- * **`PosRaGLU` (`luminamoon.nn.PosRaGLU`)**: Gated Linear Unit with strictly positive rational gating bounded in $(0, 1)$. Eliminates quadratic activation outlier explosion for stable low-precision (INT4/FP8) quantization.
74
- * **`RaGLU` & `TriGLU`**: Standard and triple rational gated linear unit variants.
75
- * **`RaAttention` & `RaAttn` (`luminamoon.nn.RaAttention`, `luminamoon.nn.RaAttn`)**: Multi-head attention using Cayley-Padé rational softmax. Supports causal masking, QK-Norm (`qk_norm=True`), dropout, and custom head dimension scaling.
76
- * **`ra_softmax` (`luminamoon.nn.ra_softmax`)**: Rational softmax with exact compact support for $\Delta \ge 16$, guaranteeing sum of numerators $\ge 1.0$ without requiring an epsilon floor.
77
- * **Dual Hardware Acceleration (Triton JIT GPU & Rust AVX-512/AVX2 SIMD CPU)**:
78
- - Added Triton GPU kernels (`_ra_norm_fwd_kernel`, `_pos_ra_glu_fwd_kernel`, `_ra_softmax_fwd_kernel`) in `luminamoon.nn.triton_kernels` for register-fused execution without intermediate global memory roundtrips.
79
- - Added native Rust SIMD kernels in `luminav-simd` with AVX2/FMA vectorization and PyO3 bindings for high-throughput CPU token normalization and rational softmax.
80
- - **Academic Aliases (`luminamoon.nn`)**:
81
- * Exported standardized academic aliases across neural components: `L1Norm`, `L1LayerNorm`, `L1BatchNorm2d`, `RationalTanh`, `rational_tanh`, `RationalGELU`, `rational_gelu`, `RationalGLU`, `BoundedRationalGLU`, `rational_softmax`, `pade_softmax`, `RationalAttention`, and `PadeAttention`.
82
- - **Theoretical References & Literature Documentation**:
83
- * Added `references/README.md` and `references/bibliography.bib` documenting literature mappings and rational approximations, paired with `docs/references.md` in the documentation.
84
- - **Empirical Evaluation**:
85
- * Updated `eval/LSO-Eval.pdf` with the latest benchmark convergence findings and empirical evaluation report.
86
- - **Unit Tests**: Added 32 unit tests in `tests/test_nn_ra_family.py` covering mathematical invariants, convolution folding, isolated zero-torch subprocess execution, and hardware introspection.
87
-
88
- ### Changed
89
- - **Security Policy**: Streamlined `SECURITY.md` supported version matrix (`luminamoon >= 1.1.0`, `luminav >= 1.6.0`) with simplified reporting guidelines.
90
-
91
- ---
92
-
93
- ## [1.0.9] - 2026-11-03
94
-
95
- ### Added
96
- - **LuminaV v1.6.0 — T-FAMILY (Triangle Family) tanh-free Modes (`buffer="T1"` & `buffer="T2"`)**:
97
- * **`buffer="T2"` (2-Buffer Triangle Family Mode)**: Keeps the 2-buffer state (`exp_avg` and `exp_avg_sq`) without using $\tanh$. Computes the gradient-momentum residual $d_t = g_t - m _ {t-1}$ and bounds the lookahead update via the **Triangle Inequality Envelope**:
98
-
99
- $\displaystyle \mathcal{E} _ t^{(\text{T2})} = w_1 |m _ {t-1}| + w_2 |g_t| + 0.5\lambda |d_t| + 0.5\lambda \sigma_t + \epsilon _ {\text{eff}}, \qquad u_t^{(\text{T2})} = \frac{\tilde{m} _ t}{\mathcal{E} _ t^{(\text{T2})}} \in (-1, 1)$
100
-
101
- By the Triangle Inequality, $|w_1 m _ {t-1} + w_2 g_t| \le w_1 |m _ {t-1}| + w_2 |g_t| \le \mathcal{E} _ t^{(\text{T2})}$, which guarantees the update step $u_t^{(\text{T2})}$ strictly remains within $(-1, 1)$ without evaluating $\tanh$. The term $0.5\lambda |d_t|$ acts as a **triangle-defect penalty**, while $0.5\lambda \sigma_t$ dampens belief-variance dispersion.
102
- * **`buffer="T1"` (1-Buffer Triangle Family Mode)**: Pairs the single-buffer **Triangle-Defect** denominator ($\mathcal{E} _ t^{(\text{T1})} = w_1 |m _ {t-1}| + w_2 |g_t| + \lambda |d_t| + \epsilon _ {\text{eff}}$ with residual $d_t = g_t - m _ {t-1}$) with the rational update bounds below via the Triangle Inequality, eliminating the second-moment accumulator ($v_t$) and reducing optimizer state memory by 50%.
103
- * **Rational Update Bounds (for `T1` & `T2`)**: Replaces $\tanh$ and $\sqrt{\cdot}$ in the coordinate, radial, and FP16 update bounds with simple rational formulas across Rust, PyTorch, FSDP, and NumPy:
104
-
105
- $\displaystyle b_i = \mathrm{bound} _ {\text{ratio}} \cdot (|w_i| + 10^{-2}), \qquad \Delta w_i = \frac{b_i (b_i + |s_i|) s_i}{b_i (b_i + |s_i|) + s_i^2}, \qquad \mathrm{scale} _ {\text{radial}} = \frac{1 + r}{1 + r + r^2}$
106
- - **100% Backward Compatibility & Zero Breaking Changes**:
107
- * Existing code and checkpoints using `buffer=2` (default), `buffer=1`, or `version="1.5.5"` continue to work as before with identical outputs and `state_dict()` compatibility.
108
-
109
- ---
110
-
111
- ## [1.0.8.post1] - 2026-10-30
112
-
113
- ### Fixed
114
- - **PyPI Discovery Description & Package Metadata**:
115
- * Updated `pyproject.toml` distribution `description` to prominently highlight LuminaV low-precision BF16/FP16 optimization, zero-copy multimodal dataset streaming, and native Rust SIMD, Vulkan, and Triton acceleration.
116
- - **Un-fused Bucket Cap Configuration Warning**:
117
- * Added protective `UserWarning` in `LuminaV.__init__` when `bucket_cap_mb` is explicitly configured while `fuse_tensors=False`, preventing developer confusion regarding memory bounding scope on uncoalesced parameter tensors.
118
-
119
- ---
120
-
121
- ## [1.0.8] - 2026-10-29
122
-
123
- ### Added
124
- - **LuminaV v1.5.5 Contiguous Bucket Fusion (`fuse_tensors=False`) & Triton JIT Pre-Warmup (`prewarm_jit=True`)**:
125
- * Added opt-in `fuse_tensors: bool = False` in `LuminaV` to coalesce fragmented parameter groups into contiguous 1D buffers (`flat_p`, `flat_g`, `flat_m`, `flat_v`, `flat_mp`) with zero-copy tensor views, in-place `zero_grad()` pointer preservation, and LSO-Eval Stage II compliant `state_dict()` / `load_state_dict()` round-tripping.
126
- * Added `prewarm_jit: bool = True` (enabled by default) in `LuminaVGpuMixin._prewarm_all_configs` to pre-compile Triton GPU kernels (`step=1` two-pass and `step=2+` fused single-pass + post-step EMA) on 64-element dummy tensors (`lr=0.0`) during `__init__()`.
127
- * Isolated `_prepare_triton_buffers` state lookup via `self.state.get(p)` to prevent contiguous bucket or warmup buffers from polluting `self.state`.
128
- - **Bounded Bucket Chunking (`bucket_cap_mb = 500.0`) & Sequential In-Place Migration**:
129
- * Added `bucket_cap_mb: float = 500.0` in `LuminaV` to partition contiguous parameter coalescing into bounded chunk buckets (maximum 500 MB per bucket), preventing monolithic VRAM allocation spikes on large models (e.g. 4B+).
130
- * Implemented `_partition_into_bounded_chunks` with isolated single-tensor handling for huge layers (e.g. large embedding matrices).
131
- * Implemented sequential in-place migration per parameter inside chunks with immediate dereferencing (`del old_data`, `del old_grad`, `del conv_m`, `del conv_v`) and `gc.collect()`, ensuring that peak transient memory spikes during initialization never exceed 1 chunk (max 500 MB).
132
-
133
- ---
134
-
135
- ## [1.0.7] - 2026-10-29
136
-
137
- ### Added
138
- - **LuminaV v1.5.4 Specification Upgrade**: Upgraded the bundled LuminaV Optimizer specification to `v1.5.4`, removing `centered_defect` and its global reduction kernels (`_lumina_diff_sum_kernel`, `diff_sum.comp`, `reduce_diff_sum_simd`) across Rust core, SIMD, Vulkan 1.3/1.4, Triton AOT/JIT, PyTorch, and NumPy backends to reduce per-step latency.
139
- - **In-Container Native Bitwise-BF16 State Promotion**: Added native Bitwise-BF16 state promotion for pure `FP16` execution (`dtype_mode == 2`, `master_weights=False`) across Rust core, AVX-512/AVX2 SIMD, Vulkan 1.3/1.4 compute shaders, Triton kernels, PyTorch fallback, and standalone NumPy, preserving the 16-bit container memory footprint (zero extra VRAM) while expanding state dynamic range to 8-bit exponent ($1.18 \times 10^{-38}$).
140
- - **Cross-Precision Checkpoint Handshake**: Added `_convert_torch_state_buffer` in `luminamoon.optimizer.luminav` for bit-exact state conversions during `load_state_dict` and `step` between `master_weights=False` (`torch.float16` Bitwise-BF16) and `master_weights="full"` (`torch.float32` / `torch.bfloat16`).
141
- - **Comprehensive Hardware Parity & Regression Tests**: Added `test_vulkan_parity_dual_fp16_native_bf16_state_131072_5steps` and `test_vulkan_parity_tdclm_fp16_native_bf16_state_131072_5steps` in `vulkan_parity.rs` and `run_pure_fp16_native_bf16_state_tests` in `test_end_to_end_training.py`.
142
-
143
- ### Fixed
144
- - **FP16 Convergence Stability & Loss Reset Mitigation**: Resolved sudden loss reset to $\ln(V)$ during convergence in pure `FP16` mode by eliminating second-moment underflow and unifying adaptive denominator epsilon (`effective_eps = eps = 1e-8`) across all execution backends (fixes #65).
145
- - **Scalar Reference Parity Synchronization**: Fixed state quantization mode propagation in CPU scalar reference `fused_single_pass` path (`dual.rs` and `tdclm.rs`), maintaining mathematical parity between CPU scalar and GPU Vulkan shaders.
146
- - **Headless CI Vulkan Detection Guard**: Added `LuminaV.detect_hardware()["vulkan_available"]` check in end-to-end tests to prevent false failures on headless CPU runners.
147
-
148
- ---
149
-
150
- ## [1.0.6.post1] - 2026-10-26
151
-
152
- ### Fixed
153
- - **Prefetcher Batch ID Synchronization**: Fixed batch ID drift in `ResumablePipeline` during async prefetching (`prefetch_batches > 0`) by tagging each queue item with `(batch_payload, batch_id)` and synchronizing `self._active_batch_id` on consumption, eliminating premature coordinate commits and `UnknownBatchId` errors.
154
- - **Data Engine Fallback Parity**: Fixed `StreamDeduplicator.count` callable signature in `luminamoon.data.fallbacks.dedup` (and high-level wrapper polymorphic dispatch), restored RAM staging in `StateTracker.update(..., batch_id=1)`, and aligned `fused_pipeline_process_batch` with two-stage staging and `commit_dedup` gating.
155
- - **Optimizer Hyperparameter Constants**: Explicitly populated complete default hyperparameter constants in `luminamoon.optimizer.constants`.
156
- - **Version Synchronization Tooling**: Added `--check` mode and PEP 440 validation in `tools/sync_version.py` to prevent accidental version string corruption.
157
-
158
- ---
159
-
160
- ## [1.0.6] - 2026-10-26
161
-
162
- ### Added
163
- - **Python 3.15 Classifier Support**: Added `Programming Language :: Python :: 3.15` to package metadata in `pyproject.toml`.
164
- - **O(1) Deduplication Membership Check**: Added `TwoStageDeduplicator::is_seen` in `data/src/dedup.rs` and wired `PyStreamDeduplicator::is_seen` to avoid $O(N)$ hash-vector exports.
165
-
166
- ### Changed
167
- - **PROJECT LOOMY Rebranding & Author Standardization**: Standardized project branding to **PROJECT LOOMY** (`Loomy(luminamoon)`) with **Silver Moon (`@cloverxion`)** listed first as lead author followed by **Lumina Moon (`@cloverx-id`) contributors** across documentation and manifests.
168
- - **Optional Dependency Import Diagnostics**: Wrapped `numpy`, `torch`, `pyarrow`, and `polars` imports in `StringTensor` export methods with actionable installation hints.
169
- - **Fallback Signature Parity**: Aligned `suggest_closest_matches` (with Damerau-Levenshtein edit-distance ranking) and `postprocess_and_dedup_records` return tuple `(cleaned_unique, newly_committed)` in `luminamoon.data.fallbacks` with Rust FFI and `_stubs.py`.
170
-
171
- ### Fixed
172
- - **WAL & GPU Buffer Corruption Hardening**: Added byte-alignment validation (`% 8 == 0` for `.dedup` WAL files, `% 4 == 0` for GPU `uint32` offset buffers), exact pre-commit offset seeking in `DualMetadataManager::commit_gpu_batch`, and `StringView16` field encapsulation (`pub(crate)`), inline length clamping, and saturating `u32` offset arithmetic in `data/src/string_tensor.rs`.
173
- - **Data Pipeline & Collate Hardening**: Fixed WAL double-commit skip in `fused_process_batch`, added missing column typo suggestions and `bytes` decoding in `_extract_text`, auto-populated `tokenizer.pad_token = tokenizer.eos_token` when missing, restored resume coordinates in `ResumablePipeline`, and eliminated background prefetcher queue timeout drops.
174
-
175
- ---
176
-
177
- ## [1.0.5.post1] - 2026-10-25
178
-
179
- ### Changed
180
- - **Modular Data Processing Engine**: Decomposed the monolithic data processing fallback layer in `luminamoon.data` into dedicated submodules under `fallbacks/` (`cleaner.py`, `quality.py`, `dedup.py`, `minhash.py`, `masker.py`, `coordinate.py`, `tracker.py`, `string_tensor.py`, `fused.py`, `serialization.py`, `helpers.py`), preserving backward compatibility.
181
- - **Updated Algorithm Changelog**: Updated `LUMINAV_ALGORITHM_CHANGELOG.md` with detailed mathematical formulation and parameter comparisons between 1.3.3 and 1.5.3.
182
- - **PEP 440 Post-Release Synchronization**: Bumped package distribution version to `1.0.5.post1`.
183
-
184
- ---
185
-
186
- ## [1.0.5] - 2026-10-24
187
-
188
- ### Added
189
- - **Comprehensive In-Editor Docstrings (`luminav.py`)**: Added Google-style docstrings across `LuminaV.__init__`, `step`, `zero_grad`, `state_dict`, `load_state_dict`, and `detect_hardware`, providing full IDE autocomplete tooltips and explicit default hyperparameter values.
190
- - **Hyperparameter Quick Reference (`README.md`)**: Added a structured default parameter reference table directly to the main repository README.
191
- - **Dedicated Dual-Track Changelog Directory (`changelogs/`)**: Separated release documentation into `SOFTWARE_CHANGELOG.md` (package releases) and `LUMINAV_ALGORITHM_CHANGELOG.md` (algorithm specification history).
192
- - **Explicit Algorithm Specification Constant (`LuminaV.ALGORITHM_SPEC`)**: Added `LuminaV.ALGORITHM_SPEC = "1.5.3"` to clearly distinguish the mathematical specification version from the software package version.
193
-
194
- ### Changed
195
- - **Unified Software Version Synchronization (`1.0.5`)**: Synchronized `luminamoon.__version__`, `luminamoon.data.get_version()`, `pyproject.toml`, and `Cargo.toml` to a single source of truth (`1.0.5`).
196
- - **Offline Version Resolution (`version.py`)**: Removed runtime HTTP GitHub file fetching from `luminamoon.optimizer.version` and `luminamoon.data.version`, allowing offline execution in air-gapped HPC clusters.
197
-
198
- ### Security
199
- - **Upgraded `setuptools` (`84.0.0` -> `84.1.0`)**: Patched development dependency `setuptools` in `requirements.txt` to resolve `GHSA-xmj3-9gf7-h52w` (`CWE-22`).
200
-
201
- ---
202
-
203
- ## [1.0.4] - 2026-10-24
204
-
205
- ### Added
206
- - **Free-Threaded CPython (No-GIL / PEP 703) Support**: Verified compilation and runtime execution of native `abi3t` / free-threaded wheels on Python `3.14t` and `3.15t`.
207
- - **Rust Core Invariant Tests**: Added `optimizer/crates/luminav-core/tests/core_tests.rs` covering PRNG seed determinism, Padé rational $\tanh$ bounds, scratch buffer EMA smoothing, and Levenshtein typo suggestions.
208
- - **PyPI Project URLs & Contributors Metadata**: Added `Homepage`, `Repository`, `Issues`, `Documentation`, and `Changelog` links alongside `Lumina Moon (cloverx-id) Contributors` to `pyproject.toml`, `Cargo.toml`, and `LICENSE`.
209
-
210
- ### Changed
211
- - **Rust Workspace Modularization**:
212
- - Decomposed `luminav-core/src/reference_cpu.rs` into `reference_cpu/{mod, dual, tdclm}.rs`.
213
- - Decomposed `luminav-simd` (`avx2.rs`, `avx512.rs`, `parallel.rs`) into dedicated submodule directories (`dual.rs`, `tdclm.rs`, `mod.rs`).
214
- - Extracted Vulkan UMA step dispatches into `luminav-vulkan/src/{dual, tdclm}.rs`.
215
- - Decomposed `optimizer/src/python_ffi.rs` into `python_ffi/{config_parser, extract, worker, mod}.rs`.
216
- - **Python Package Modularization**:
217
- - Split `luminav.py` into `standalone.py`, `validation.py`, and `numpy_backend.py`.
218
- - Split `gpu_backend.py` into `fsdp_sync.py`, `torch_ops.py`, and `triton_kernels.py`.
219
- - Added cross-platform cache directory resolution (`LOCALAPPDATA` on Windows, `XDG_CACHE_HOME` on POSIX).
220
- - **Reference Implementation Restructuring**:
221
- - Decomposed monolithic `optimizer/reference/luminav_v153.py` (3,996 lines) into `core/`, `kernels/`, `single_backend/`, `triton_backend/`, and `utils/`.
222
-
223
- ---
224
-
225
- ## [1.0.3] - 2026-10-21
226
-
227
- ### Added
228
- - **Two-Stage Deferred Release Pipeline**: Implemented draft GitHub release artifact staging on tag push (`v*`) with automated PyPI publication triggered upon release publication.
229
-
230
- ### Fixed
231
- - **SIMD Finite BF16 Clamps**: Hardened AVX-512 and AVX2 finite clamp boundaries prior to BFloat16 stochastic rounding.
232
- - **Build Wheel Privacy Sanitizer**: Added automated local path scrubbing in `tools/build_wheel.py` without modifying compiled ELF/PE/Mach-O relocation tables.
233
-
234
- ---
235
-
236
- ## [1.0.2.post2] - 2026-10-20
237
-
238
- ### Fixed
239
- - **Dynamic Tag Versioning**: Added `tools/sync_version.py` to synchronize `pyproject.toml` and `_version.py` directly from Git release tags during CI wheel builds.
240
-
241
- ---
242
-
243
- ## [1.0.2.post1] - 2026-10-20
244
-
245
- ### Fixed
246
- - **PEP 440 Post-Release Compliance**: Standardized version regex and enabled `skip-existing: true` on PyPI publishing workflows.
247
-
248
- ---
249
-
250
- ## [1.0.2] - 2026-10-20
251
-
252
- ### Added
253
- - **Modular `luminamoon.data` Engine**: Added SIMD text normalization (`TextCleaner`), Shannon entropy & symbol heuristic filtering (`QualityFilter`), two-stage XXH3-64 exact deduplication (`StreamDeduplicator`), 64-permutation MinHash LSH fuzzy deduplication (`MinHashDeduplicator`), PII masking (`PIIMasker`), 128-byte Ping-Pong crash recovery (`StateTracker`), and dual-layout `StringTensor` (CPU 16-byte Umbra `StringView` & GPU contiguous offset+payload buffers).
254
- - **LuminaV v1.5.3 Engine Integration**: Integrated the Rust AVX-512/AVX2 SIMD, Vulkan 1.3/1.4 UMA compute, and Triton AOT/JIT backends.
255
-
256
- ---
257
-
258
- ## [1.0.1] - 2026-10-20
259
-
260
- ### Added
261
- - Initial monorepo release unifying the `luminamoon` Universal DataLoader and `LuminaV` Optimizer under a single Rust + Python workspace.
 
1
+ # LuminaMoon Software Changelog
2
+
3
+ All notable software, packaging, FFI, and infrastructure changes to the `luminamoon` monorepo and PyPI distribution (`pip install luminamoon`) are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ For the mathematical and kernel evolution of the **LuminaV Optimizer** specification (`v1.0.0` to `v1.6.0`), see [`LUMINAV_ALGORITHM_CHANGELOG.md`](LUMINAV_ALGORITHM_CHANGELOG.md).
8
+
9
+ ---
10
+
11
+ ## [1.1.0.post3] - 2026-11-07
12
+
13
+ ### Changed
14
+ - **Academic Citation Consolidation**:
15
+ * Removed redundant `CITATION.cff` metadata file to consolidate citation records directly into Zenodo (DOI: [`10.5281/zenodo.23978801`](https://doi.org/10.5281/zenodo.23978801)) and repository `README.md`.
16
+ - **Data Engine Version Introspection (`python/luminamoon/data/version.py`)**:
17
+ * Streamlined data version checking so `get_version()` directly returns the active package version string without disk or mutable state side effects.
18
+ * Maintained full backward compatibility for `list_versions()` and `set_version()` with informative `DeprecationWarning` notices explaining that the data engine operates on the unified native SIMD implementation.
19
+ * Removed internal cache directory cloning and dynamic module loading routines from `~/.cache/luminamoon/versions`.
20
+ * Updated `data/README.md` to document direct version inspection via `luminamoon.data.get_version()`.
21
+ - **Documentation & Technical Guides**:
22
+ * Rewrote technical guides, component documentation, and reference overviews across `docs/` and `data/docs/` in direct, humble, and accessible language for human readability.
23
+ * Preserved core algorithmic identities including the Triangle Family, Triangle Inequality Envelope, Gradient-Momentum Residual, and Triangle-Defect Penalty without inflated descriptors.
24
+ * Normalized data engine documentation in `python/luminamoon/data` and `data/` to consistent English terminology.
25
+ * Aligned the MinHash documentation in `docs/dataloader/sources_and_streaming/README.md` with the Rust implementation using XXH3-64 seed multipliers.
26
+
27
+ ### Refined
28
+ - **Evaluation Specification Artifacts (`eval/`)**:
29
+ * Updated `eval/lso_eval_specification.tex` with refined layout and mathematical notation.
30
+ * Added updated compiled specification manuscript `eval/LSO_Eval.pdf` and removed legacy PDF files.
31
+ * Aligned `tools/sync_huggingface.py` and `eval/README.md` to track `eval/LSO_Eval.pdf`.
32
+ - **PosRaGLU Activation Gating (`luminamoon.nn`)**:
33
+ * Added explicit positive anchor support ($b > 0$) for `pos_ra_gate` and `PosRaGLU`, preserving unit derivative at the origin while bounding output in $(0, 1)$.
34
+
35
+ ### Fixed
36
+ - **Markdown Parameter Table Pipe Escaping**:
37
+ * Escaped pipe characters in inline code notation (`\|d_t\| = \|g_t - m_{t-1}\|`) within the README parameter table to prevent GitHub Flavored Markdown (GFM) table parsers from prematurely splitting table cells.
38
+ - **Static Type Analysis (`luminamoon.nn._compat`)**:
39
+ * Declared `_rs_engine` at module scope with multi-tier import fallbacks to eliminate Pyright unresolved import warnings.
40
+
41
+ ### Infrastructure
42
+ - **Continuous Integration & Release Automation**:
43
+ * Added Python runtime setup to the Rust test workflow in `.github/workflows/ci.yml` so linkers reliably locate shared Python libraries during PyO3 FFI tests on updated runner images.
44
+ * Streamlined GitHub Release note generation by eliminating redundant boilerplate headers and prioritizing direct changelog entries.
45
+
46
+ ---
47
+
48
+ ## [1.1.0.post2] - 2026-11-05
49
+
50
+ ### Fixed
51
+ - **Rust Arrow & Parquet Dependencies (`RUSTSEC-2026-0442`)**:
52
+ * Updated `parquet`, `arrow-array`, `arrow-schema`, and `arrow-ipc` to `60.1.0` in workspace manifests (`Cargo.toml` and `Cargo.lock`).
53
+ * Resolves advisory **RUSTSEC-2026-0442** (CVE-2026-111959) in `arrow-data`, preventing potential out-of-bounds reads when validating RunEndEncoded arrays during dataset ingestion.
54
+
55
+ ---
56
+
57
+ ## [1.1.0] - 2026-11-04
58
+
59
+ ### Added
60
+ - **Hardware-Accelerated Triangle Family Fused Triton Kernels (GPU JIT & Rust AOT)**:
61
+ * **Python JIT (`_lumina_t1_single_pass_kernel`, `_lumina_t2_fused_single_pass_kernel`)**: Added dedicated fused register kernels for the Triangle Family (`buffer="T1"` and `buffer="T2"`), replacing eager PyTorch chunking and eliminating CPU-GPU `.item()` sync stalls, bringing T2 execution latency from 20.7 ms down to native GPU speed (~0.9–1.0 ms).
62
+ * **Rust Triton AOT Crate (`luminav-triton-aot`)**: Added Ahead-Of-Time compiled kernels for `T1` and `T2` across 12 architectures (Turing, Ampere, Ada, Hopper, Blackwell, CDNA, RDNA3), expanding the embedded manifest to 96 binary artifacts.
63
+ - **Strict Version Capability Enforcement**:
64
+ * Enforced specification capabilities across historical LuminaV algorithm versions (`1.6.0`, `1.5.5`, `1.5.4`, `1.5.3`): locking to `1.5.4` or `1.5.5` strictly rejects features introduced in later versions (such as `buffer="T2"` or `fuse_tensors=True`), guaranteeing reproducibility for academic benchmarks.
65
+
66
+ ### Added
67
+ - **Ra-Family (Rational, Tanh-Free & Square-Root-Free Neural Architecture)**:
68
+ * **Zero Mandatory PyTorch Dependency**: The `luminamoon` package remains lightweight (~2.4 MB) with zero required dependencies (`dependencies = []`). The new `luminamoon.nn` module automatically detects the runtime environment (`_compat.py`), inheriting from `torch.nn.Module` when PyTorch is installed, or falling back cleanly to standalone NumPy buffers when PyTorch is absent.
69
+ * **`RaNorm` (`luminamoon.nn.RaNorm`)**: Token normalization using $L_1$ mean absolute deviation instead of root-mean-square. Eliminates square-root evaluations and amortizes a single scalar reciprocal per token vector.
70
+ * **`RaBatchNorm2d` (`luminamoon.nn.RaBatchNorm2d`)**: 2D batch normalization using cross-batch mean absolute deviation (MAD). Includes inference weight folding (`fold_into_conv`) that merges running statistics into preceding `Conv2d` layers with zero additional inference latency.
71
+ * **`RaTanh` & `ra_tanh`**: Rational hyperbolic tangent with non-vanishing algebraic gradient tails $O(1 / |x|^3)$ and unit derivative at origin.
72
+ * **`RaGELU` & `ra_gelu`**: Rational GELU activation using rational polynomial approximations.
73
+ * **`PosRaGLU` (`luminamoon.nn.PosRaGLU`)**: Gated Linear Unit with strictly positive rational gating bounded in $(0, 1)$. Eliminates quadratic activation outlier explosion for stable low-precision (INT4/FP8) quantization.
74
+ * **`RaGLU` & `TriGLU`**: Standard and triple rational gated linear unit variants.
75
+ * **`RaAttention` & `RaAttn` (`luminamoon.nn.RaAttention`, `luminamoon.nn.RaAttn`)**: Multi-head attention using Cayley-Padé rational softmax. Supports causal masking, QK-Norm (`qk_norm=True`), dropout, and custom head dimension scaling.
76
+ * **`ra_softmax` (`luminamoon.nn.ra_softmax`)**: Rational softmax with exact compact support for $\Delta \ge 16$, guaranteeing sum of numerators $\ge 1.0$ without requiring an epsilon floor.
77
+ * **Dual Hardware Acceleration (Triton JIT GPU & Rust AVX-512/AVX2 SIMD CPU)**:
78
+ - Added Triton GPU kernels (`_ra_norm_fwd_kernel`, `_pos_ra_glu_fwd_kernel`, `_ra_softmax_fwd_kernel`) in `luminamoon.nn.triton_kernels` for register-fused execution without intermediate global memory roundtrips.
79
+ - Added native Rust SIMD kernels in `luminav-simd` with AVX2/FMA vectorization and PyO3 bindings for high-throughput CPU token normalization and rational softmax.
80
+ - **Academic Aliases (`luminamoon.nn`)**:
81
+ * Exported standardized academic aliases across neural components: `L1Norm`, `L1LayerNorm`, `L1BatchNorm2d`, `RationalTanh`, `rational_tanh`, `RationalGELU`, `rational_gelu`, `RationalGLU`, `BoundedRationalGLU`, `rational_softmax`, `pade_softmax`, `RationalAttention`, and `PadeAttention`.
82
+ - **Theoretical References & Literature Documentation**:
83
+ * Added `references/README.md` and `references/bibliography.bib` documenting literature mappings and rational approximations, paired with `docs/references.md` in the documentation.
84
+ - **Empirical Evaluation**:
85
+ * Updated `eval/LSO-Eval.pdf` with the latest benchmark convergence findings and empirical evaluation report.
86
+ - **Unit Tests**: Added 32 unit tests in `tests/test_nn_ra_family.py` covering mathematical invariants, convolution folding, isolated zero-torch subprocess execution, and hardware introspection.
87
+
88
+ ### Changed
89
+ - **Security Policy**: Streamlined `SECURITY.md` supported version matrix (`luminamoon >= 1.1.0`, `luminav >= 1.6.0`) with simplified reporting guidelines.
90
+
91
+ ---
92
+
93
+ ## [1.0.9] - 2026-11-03
94
+
95
+ ### Added
96
+ - **LuminaV v1.6.0 — T-FAMILY (Triangle Family) tanh-free Modes (`buffer="T1"` & `buffer="T2"`)**:
97
+ * **`buffer="T2"` (2-Buffer Triangle Family Mode)**: Keeps the 2-buffer state (`exp_avg` and `exp_avg_sq`) without using $\tanh$. Computes the gradient-momentum residual $d_t = g_t - m _ {t-1}$ and bounds the lookahead update via the **Triangle Inequality Envelope**:
98
+
99
+ $\displaystyle \mathcal{E} _ t^{(\text{T2})} = w_1 |m _ {t-1}| + w_2 |g_t| + 0.5\lambda |d_t| + 0.5\lambda \sigma_t + \epsilon _ {\text{eff}}, \qquad u_t^{(\text{T2})} = \frac{\tilde{m} _ t}{\mathcal{E} _ t^{(\text{T2})}} \in (-1, 1)$
100
+
101
+ By the Triangle Inequality, $|w_1 m _ {t-1} + w_2 g_t| \le w_1 |m _ {t-1}| + w_2 |g_t| \le \mathcal{E} _ t^{(\text{T2})}$, which guarantees the update step $u_t^{(\text{T2})}$ strictly remains within $(-1, 1)$ without evaluating $\tanh$. The term $0.5\lambda |d_t|$ acts as a **triangle-defect penalty**, while $0.5\lambda \sigma_t$ dampens belief-variance dispersion.
102
+ * **`buffer="T1"` (1-Buffer Triangle Family Mode)**: Pairs the single-buffer **Triangle-Defect** denominator ($\mathcal{E} _ t^{(\text{T1})} = w_1 |m _ {t-1}| + w_2 |g_t| + \lambda |d_t| + \epsilon _ {\text{eff}}$ with residual $d_t = g_t - m _ {t-1}$) with the rational update bounds below via the Triangle Inequality, eliminating the second-moment accumulator ($v_t$) and reducing optimizer state memory by 50%.
103
+ * **Rational Update Bounds (for `T1` & `T2`)**: Replaces $\tanh$ and $\sqrt{\cdot}$ in the coordinate, radial, and FP16 update bounds with simple rational formulas across Rust, PyTorch, FSDP, and NumPy:
104
+
105
+ $\displaystyle b_i = \mathrm{bound} _ {\text{ratio}} \cdot (|w_i| + 10^{-2}), \qquad \Delta w_i = \frac{b_i (b_i + |s_i|) s_i}{b_i (b_i + |s_i|) + s_i^2}, \qquad \mathrm{scale} _ {\text{radial}} = \frac{1 + r}{1 + r + r^2}$
106
+ - **100% Backward Compatibility & Zero Breaking Changes**:
107
+ * Existing code and checkpoints using `buffer=2` (default), `buffer=1`, or `version="1.5.5"` continue to work as before with identical outputs and `state_dict()` compatibility.
108
+
109
+ ---
110
+
111
+ ## [1.0.8.post1] - 2026-10-30
112
+
113
+ ### Fixed
114
+ - **PyPI Discovery Description & Package Metadata**:
115
+ * Updated `pyproject.toml` distribution `description` to prominently highlight LuminaV low-precision BF16/FP16 optimization, zero-copy multimodal dataset streaming, and native Rust SIMD, Vulkan, and Triton acceleration.
116
+ - **Un-fused Bucket Cap Configuration Warning**:
117
+ * Added protective `UserWarning` in `LuminaV.__init__` when `bucket_cap_mb` is explicitly configured while `fuse_tensors=False`, preventing developer confusion regarding memory bounding scope on uncoalesced parameter tensors.
118
+
119
+ ---
120
+
121
+ ## [1.0.8] - 2026-10-29
122
+
123
+ ### Added
124
+ - **LuminaV v1.5.5 Contiguous Bucket Fusion (`fuse_tensors=False`) & Triton JIT Pre-Warmup (`prewarm_jit=True`)**:
125
+ * Added opt-in `fuse_tensors: bool = False` in `LuminaV` to coalesce fragmented parameter groups into contiguous 1D buffers (`flat_p`, `flat_g`, `flat_m`, `flat_v`, `flat_mp`) with zero-copy tensor views, in-place `zero_grad()` pointer preservation, and LSO-Eval Stage II compliant `state_dict()` / `load_state_dict()` round-tripping.
126
+ * Added `prewarm_jit: bool = True` (enabled by default) in `LuminaVGpuMixin._prewarm_all_configs` to pre-compile Triton GPU kernels (`step=1` two-pass and `step=2+` fused single-pass + post-step EMA) on 64-element dummy tensors (`lr=0.0`) during `__init__()`.
127
+ * Isolated `_prepare_triton_buffers` state lookup via `self.state.get(p)` to prevent contiguous bucket or warmup buffers from polluting `self.state`.
128
+ - **Bounded Bucket Chunking (`bucket_cap_mb = 500.0`) & Sequential In-Place Migration**:
129
+ * Added `bucket_cap_mb: float = 500.0` in `LuminaV` to partition contiguous parameter coalescing into bounded chunk buckets (maximum 500 MB per bucket), preventing monolithic VRAM allocation spikes on large models (e.g. 4B+).
130
+ * Implemented `_partition_into_bounded_chunks` with isolated single-tensor handling for huge layers (e.g. large embedding matrices).
131
+ * Implemented sequential in-place migration per parameter inside chunks with immediate dereferencing (`del old_data`, `del old_grad`, `del conv_m`, `del conv_v`) and `gc.collect()`, ensuring that peak transient memory spikes during initialization never exceed 1 chunk (max 500 MB).
132
+
133
+ ---
134
+
135
+ ## [1.0.7] - 2026-10-29
136
+
137
+ ### Added
138
+ - **LuminaV v1.5.4 Specification Upgrade**: Upgraded the bundled LuminaV Optimizer specification to `v1.5.4`, removing `centered_defect` and its global reduction kernels (`_lumina_diff_sum_kernel`, `diff_sum.comp`, `reduce_diff_sum_simd`) across Rust core, SIMD, Vulkan 1.3/1.4, Triton AOT/JIT, PyTorch, and NumPy backends to reduce per-step latency.
139
+ - **In-Container Native Bitwise-BF16 State Promotion**: Added native Bitwise-BF16 state promotion for pure `FP16` execution (`dtype_mode == 2`, `master_weights=False`) across Rust core, AVX-512/AVX2 SIMD, Vulkan 1.3/1.4 compute shaders, Triton kernels, PyTorch fallback, and standalone NumPy, preserving the 16-bit container memory footprint (zero extra VRAM) while expanding state dynamic range to 8-bit exponent ($1.18 \times 10^{-38}$).
140
+ - **Cross-Precision Checkpoint Handshake**: Added `_convert_torch_state_buffer` in `luminamoon.optimizer.luminav` for bit-exact state conversions during `load_state_dict` and `step` between `master_weights=False` (`torch.float16` Bitwise-BF16) and `master_weights="full"` (`torch.float32` / `torch.bfloat16`).
141
+ - **Comprehensive Hardware Parity & Regression Tests**: Added `test_vulkan_parity_dual_fp16_native_bf16_state_131072_5steps` and `test_vulkan_parity_tdclm_fp16_native_bf16_state_131072_5steps` in `vulkan_parity.rs` and `run_pure_fp16_native_bf16_state_tests` in `test_end_to_end_training.py`.
142
+
143
+ ### Fixed
144
+ - **FP16 Convergence Stability & Loss Reset Mitigation**: Resolved sudden loss reset to $\ln(V)$ during convergence in pure `FP16` mode by eliminating second-moment underflow and unifying adaptive denominator epsilon (`effective_eps = eps = 1e-8`) across all execution backends (fixes #65).
145
+ - **Scalar Reference Parity Synchronization**: Fixed state quantization mode propagation in CPU scalar reference `fused_single_pass` path (`dual.rs` and `tdclm.rs`), maintaining mathematical parity between CPU scalar and GPU Vulkan shaders.
146
+ - **Headless CI Vulkan Detection Guard**: Added `LuminaV.detect_hardware()["vulkan_available"]` check in end-to-end tests to prevent false failures on headless CPU runners.
147
+
148
+ ---
149
+
150
+ ## [1.0.6.post1] - 2026-10-26
151
+
152
+ ### Fixed
153
+ - **Prefetcher Batch ID Synchronization**: Fixed batch ID drift in `ResumablePipeline` during async prefetching (`prefetch_batches > 0`) by tagging each queue item with `(batch_payload, batch_id)` and synchronizing `self._active_batch_id` on consumption, eliminating premature coordinate commits and `UnknownBatchId` errors.
154
+ - **Data Engine Fallback Parity**: Fixed `StreamDeduplicator.count` callable signature in `luminamoon.data.fallbacks.dedup` (and high-level wrapper polymorphic dispatch), restored RAM staging in `StateTracker.update(..., batch_id=1)`, and aligned `fused_pipeline_process_batch` with two-stage staging and `commit_dedup` gating.
155
+ - **Optimizer Hyperparameter Constants**: Explicitly populated complete default hyperparameter constants in `luminamoon.optimizer.constants`.
156
+ - **Version Synchronization Tooling**: Added `--check` mode and PEP 440 validation in `tools/sync_version.py` to prevent accidental version string corruption.
157
+
158
+ ---
159
+
160
+ ## [1.0.6] - 2026-10-26
161
+
162
+ ### Added
163
+ - **Python 3.15 Classifier Support**: Added `Programming Language :: Python :: 3.15` to package metadata in `pyproject.toml`.
164
+ - **O(1) Deduplication Membership Check**: Added `TwoStageDeduplicator::is_seen` in `data/src/dedup.rs` and wired `PyStreamDeduplicator::is_seen` to avoid $O(N)$ hash-vector exports.
165
+
166
+ ### Changed
167
+ - **PROJECT LOOMY Rebranding & Author Standardization**: Standardized project branding to **PROJECT LOOMY** (`Loomy(luminamoon)`) with **Silver Moon (`@cloverxion`)** listed first as lead author followed by **Lumina Moon (`@cloverx-id`) contributors** across documentation and manifests.
168
+ - **Optional Dependency Import Diagnostics**: Wrapped `numpy`, `torch`, `pyarrow`, and `polars` imports in `StringTensor` export methods with actionable installation hints.
169
+ - **Fallback Signature Parity**: Aligned `suggest_closest_matches` (with Damerau-Levenshtein edit-distance ranking) and `postprocess_and_dedup_records` return tuple `(cleaned_unique, newly_committed)` in `luminamoon.data.fallbacks` with Rust FFI and `_stubs.py`.
170
+
171
+ ### Fixed
172
+ - **WAL & GPU Buffer Corruption Hardening**: Added byte-alignment validation (`% 8 == 0` for `.dedup` WAL files, `% 4 == 0` for GPU `uint32` offset buffers), exact pre-commit offset seeking in `DualMetadataManager::commit_gpu_batch`, and `StringView16` field encapsulation (`pub(crate)`), inline length clamping, and saturating `u32` offset arithmetic in `data/src/string_tensor.rs`.
173
+ - **Data Pipeline & Collate Hardening**: Fixed WAL double-commit skip in `fused_process_batch`, added missing column typo suggestions and `bytes` decoding in `_extract_text`, auto-populated `tokenizer.pad_token = tokenizer.eos_token` when missing, restored resume coordinates in `ResumablePipeline`, and eliminated background prefetcher queue timeout drops.
174
+
175
+ ---
176
+
177
+ ## [1.0.5.post1] - 2026-10-25
178
+
179
+ ### Changed
180
+ - **Modular Data Processing Engine**: Decomposed the monolithic data processing fallback layer in `luminamoon.data` into dedicated submodules under `fallbacks/` (`cleaner.py`, `quality.py`, `dedup.py`, `minhash.py`, `masker.py`, `coordinate.py`, `tracker.py`, `string_tensor.py`, `fused.py`, `serialization.py`, `helpers.py`), preserving backward compatibility.
181
+ - **Updated Algorithm Changelog**: Updated `LUMINAV_ALGORITHM_CHANGELOG.md` with detailed mathematical formulation and parameter comparisons between 1.3.3 and 1.5.3.
182
+ - **PEP 440 Post-Release Synchronization**: Bumped package distribution version to `1.0.5.post1`.
183
+
184
+ ---
185
+
186
+ ## [1.0.5] - 2026-10-24
187
+
188
+ ### Added
189
+ - **Comprehensive In-Editor Docstrings (`luminav.py`)**: Added Google-style docstrings across `LuminaV.__init__`, `step`, `zero_grad`, `state_dict`, `load_state_dict`, and `detect_hardware`, providing full IDE autocomplete tooltips and explicit default hyperparameter values.
190
+ - **Hyperparameter Quick Reference (`README.md`)**: Added a structured default parameter reference table directly to the main repository README.
191
+ - **Dedicated Dual-Track Changelog Directory (`changelogs/`)**: Separated release documentation into `SOFTWARE_CHANGELOG.md` (package releases) and `LUMINAV_ALGORITHM_CHANGELOG.md` (algorithm specification history).
192
+ - **Explicit Algorithm Specification Constant (`LuminaV.ALGORITHM_SPEC`)**: Added `LuminaV.ALGORITHM_SPEC = "1.5.3"` to clearly distinguish the mathematical specification version from the software package version.
193
+
194
+ ### Changed
195
+ - **Unified Software Version Synchronization (`1.0.5`)**: Synchronized `luminamoon.__version__`, `luminamoon.data.get_version()`, `pyproject.toml`, and `Cargo.toml` to a single source of truth (`1.0.5`).
196
+ - **Offline Version Resolution (`version.py`)**: Removed runtime HTTP GitHub file fetching from `luminamoon.optimizer.version` and `luminamoon.data.version`, allowing offline execution in air-gapped HPC clusters.
197
+
198
+ ### Security
199
+ - **Upgraded `setuptools` (`84.0.0` -> `84.1.0`)**: Patched development dependency `setuptools` in `requirements.txt` to resolve `GHSA-xmj3-9gf7-h52w` (`CWE-22`).
200
+
201
+ ---
202
+
203
+ ## [1.0.4] - 2026-10-24
204
+
205
+ ### Added
206
+ - **Free-Threaded CPython (No-GIL / PEP 703) Support**: Verified compilation and runtime execution of native `abi3t` / free-threaded wheels on Python `3.14t` and `3.15t`.
207
+ - **Rust Core Invariant Tests**: Added `optimizer/crates/luminav-core/tests/core_tests.rs` covering PRNG seed determinism, Padé rational $\tanh$ bounds, scratch buffer EMA smoothing, and Levenshtein typo suggestions.
208
+ - **PyPI Project URLs & Contributors Metadata**: Added `Homepage`, `Repository`, `Issues`, `Documentation`, and `Changelog` links alongside `Lumina Moon (cloverx-id) Contributors` to `pyproject.toml`, `Cargo.toml`, and `LICENSE`.
209
+
210
+ ### Changed
211
+ - **Rust Workspace Modularization**:
212
+ - Decomposed `luminav-core/src/reference_cpu.rs` into `reference_cpu/{mod, dual, tdclm}.rs`.
213
+ - Decomposed `luminav-simd` (`avx2.rs`, `avx512.rs`, `parallel.rs`) into dedicated submodule directories (`dual.rs`, `tdclm.rs`, `mod.rs`).
214
+ - Extracted Vulkan UMA step dispatches into `luminav-vulkan/src/{dual, tdclm}.rs`.
215
+ - Decomposed `optimizer/src/python_ffi.rs` into `python_ffi/{config_parser, extract, worker, mod}.rs`.
216
+ - **Python Package Modularization**:
217
+ - Split `luminav.py` into `standalone.py`, `validation.py`, and `numpy_backend.py`.
218
+ - Split `gpu_backend.py` into `fsdp_sync.py`, `torch_ops.py`, and `triton_kernels.py`.
219
+ - Added cross-platform cache directory resolution (`LOCALAPPDATA` on Windows, `XDG_CACHE_HOME` on POSIX).
220
+ - **Reference Implementation Restructuring**:
221
+ - Decomposed monolithic `optimizer/reference/luminav_v153.py` (3,996 lines) into `core/`, `kernels/`, `single_backend/`, `triton_backend/`, and `utils/`.
222
+
223
+ ---
224
+
225
+ ## [1.0.3] - 2026-10-21
226
+
227
+ ### Added
228
+ - **Two-Stage Deferred Release Pipeline**: Implemented draft GitHub release artifact staging on tag push (`v*`) with automated PyPI publication triggered upon release publication.
229
+
230
+ ### Fixed
231
+ - **SIMD Finite BF16 Clamps**: Hardened AVX-512 and AVX2 finite clamp boundaries prior to BFloat16 stochastic rounding.
232
+ - **Build Wheel Privacy Sanitizer**: Added automated local path scrubbing in `tools/build_wheel.py` without modifying compiled ELF/PE/Mach-O relocation tables.
233
+
234
+ ---
235
+
236
+ ## [1.0.2.post2] - 2026-10-20
237
+
238
+ ### Fixed
239
+ - **Dynamic Tag Versioning**: Added `tools/sync_version.py` to synchronize `pyproject.toml` and `_version.py` directly from Git release tags during CI wheel builds.
240
+
241
+ ---
242
+
243
+ ## [1.0.2.post1] - 2026-10-20
244
+
245
+ ### Fixed
246
+ - **PEP 440 Post-Release Compliance**: Standardized version regex and enabled `skip-existing: true` on PyPI publishing workflows.
247
+
248
+ ---
249
+
250
+ ## [1.0.2] - 2026-10-20
251
+
252
+ ### Added
253
+ - **Modular `luminamoon.data` Engine**: Added SIMD text normalization (`TextCleaner`), Shannon entropy & symbol heuristic filtering (`QualityFilter`), two-stage XXH3-64 exact deduplication (`StreamDeduplicator`), 64-permutation MinHash LSH fuzzy deduplication (`MinHashDeduplicator`), PII masking (`PIIMasker`), 128-byte Ping-Pong crash recovery (`StateTracker`), and dual-layout `StringTensor` (CPU 16-byte Umbra `StringView` & GPU contiguous offset+payload buffers).
254
+ - **LuminaV v1.5.3 Engine Integration**: Integrated the Rust AVX-512/AVX2 SIMD, Vulkan 1.3/1.4 UMA compute, and Triton AOT/JIT backends.
255
+
256
+ ---
257
+
258
+ ## [1.0.1] - 2026-10-20
259
+
260
+ ### Added
261
+ - Initial monorepo release unifying the `luminamoon` Universal DataLoader and `LuminaV` Optimizer under a single Rust + Python workspace.
config.json CHANGED
@@ -1,122 +1,122 @@
1
- {
2
- "name": "LuminaV",
3
- "version": "1.6.0",
4
- "package": "luminamoon",
5
- "package_version": "1.0.9",
6
- "description": "Master-free, low-precision BF16/FP16 deep learning optimizer with Dual-Buffer Convex Lookahead Momentum (CLM), Single-Buffer Triangle-Defect Convex Lookahead Momentum (TD-CLM) via the Triangle Inequality Envelope, rational Pade [13/6] bounding, stateless 32-bit MurmurHash3 stochastic rounding, and native Rust SIMD / Vulkan / Triton execution.",
7
- "repository": "https://github.com/cloverx-id/luminamoon",
8
- "documentation": "https://cloverx-id.github.io/luminamoon/",
9
- "default_params": {
10
- "lr": 0.0008,
11
- "betas": [
12
- 0.9,
13
- 0.999
14
- ],
15
- "eps": 1e-08,
16
- "weight_decay": 0.08,
17
- "tau": 0.8,
18
- "lam": 0.3,
19
- "cautious": true,
20
- "cautious_clamp_min": 0.5,
21
- "buffer": 2,
22
- "stochastic_rounding": true,
23
- "bound": true,
24
- "bound_type": "coordinate",
25
- "bound_ratio": 0.05,
26
- "master_weights": "none",
27
- "execution": "auto",
28
- "fused_single_pass": true,
29
- "fuse_tensors": false,
30
- "bucket_cap_mb": 500.0,
31
- "prewarm_jit": true,
32
- "ema_decay": "auto",
33
- "fsdp_sync": false,
34
- "seed": 1337
35
- },
36
- "presets": {
37
- "dual_buffer_clm": {
38
- "buffer": 2,
39
- "tau": 0.8,
40
- "cautious": true,
41
- "cautious_clamp_min": 0.5,
42
- "bound": true,
43
- "bound_type": "coordinate",
44
- "bound_ratio": 0.05,
45
- "stochastic_rounding": true,
46
- "master_weights": "none",
47
- "fused_single_pass": true,
48
- "ema_decay": "auto",
49
- "fsdp_sync": false,
50
- "description": "Default Dual-Buffer Convex Lookahead Momentum (CLM) tracking m_t and innovation variance v_t with coordinate trust-region bounding."
51
- },
52
- "single_buffer_td_clm": {
53
- "buffer": 1,
54
- "lam": 0.3,
55
- "cautious": true,
56
- "cautious_clamp_min": 0.5,
57
- "bound": true,
58
- "bound_type": "coordinate",
59
- "bound_ratio": 0.05,
60
- "stochastic_rounding": true,
61
- "master_weights": "none",
62
- "fused_single_pass": true,
63
- "ema_decay": "auto",
64
- "fsdp_sync": false,
65
- "description": "Single-Buffer Triangle-Defect Convex Lookahead Momentum (TD-CLM) using the Triangle Inequality Envelope (cuts optimizer state RAM by 50% with zero global reductions in coordinate mode)."
66
- },
67
- "radial_trust_region_bound": {
68
- "buffer": 2,
69
- "tau": 0.8,
70
- "cautious": true,
71
- "cautious_clamp_min": 0.5,
72
- "bound": true,
73
- "bound_type": "radial",
74
- "bound_ratio": 0.03,
75
- "stochastic_rounding": true,
76
- "master_weights": "none",
77
- "fused_single_pass": true,
78
- "ema_decay": "auto",
79
- "fsdp_sync": false,
80
- "description": "Direction-preserving radial trust-region bounding (tanh(r)/r) preserving 100% update angular geometry."
81
- },
82
- "semi_hybrid_fp32_master": {
83
- "buffer": 2,
84
- "cautious": true,
85
- "cautious_clamp_min": 0.5,
86
- "bound": true,
87
- "bound_type": "coordinate",
88
- "bound_ratio": 0.05,
89
- "stochastic_rounding": true,
90
- "master_weights": "semi",
91
- "fused_single_pass": true,
92
- "fsdp_sync": false,
93
- "description": "Hybrid precision pairing 32-bit FP32 master weights with 16-bit BF16/FP16 optimizer moment buffers."
94
- },
95
- "full_fp32_baseline": {
96
- "buffer": 2,
97
- "cautious": true,
98
- "cautious_clamp_min": 0.5,
99
- "bound": true,
100
- "bound_type": "coordinate",
101
- "bound_ratio": 0.05,
102
- "stochastic_rounding": true,
103
- "master_weights": "full",
104
- "fused_single_pass": true,
105
- "fsdp_sync": false,
106
- "description": "Full-precision FP32 master weights and FP32 optimizer states for controlled baseline evaluation."
107
- },
108
- "fsdp_distributed_sync": {
109
- "buffer": 2,
110
- "cautious": true,
111
- "cautious_clamp_min": 0.5,
112
- "bound": true,
113
- "bound_type": "radial",
114
- "bound_ratio": 0.03,
115
- "stochastic_rounding": true,
116
- "master_weights": "none",
117
- "fused_single_pass": true,
118
- "fsdp_sync": true,
119
- "description": "Distributed multi-GPU training mode under PyTorch FSDP or DeepSpeed ZeRO-3 with cross-rank collective scratch synchronization."
120
- }
121
- }
122
- }
 
1
+ {
2
+ "name": "LuminaV",
3
+ "version": "1.6.0",
4
+ "package": "luminamoon",
5
+ "package_version": "1.0.9",
6
+ "description": "Master-free, low-precision BF16/FP16 deep learning optimizer with Dual-Buffer Convex Lookahead Momentum (CLM), Single-Buffer Triangle-Defect Convex Lookahead Momentum (TD-CLM) via the Triangle Inequality Envelope, rational Pade [13/6] bounding, stateless 32-bit MurmurHash3 stochastic rounding, and native Rust SIMD / Vulkan / Triton execution.",
7
+ "repository": "https://github.com/cloverx-id/luminamoon",
8
+ "documentation": "https://cloverx-id.github.io/luminamoon/",
9
+ "default_params": {
10
+ "lr": 0.0008,
11
+ "betas": [
12
+ 0.9,
13
+ 0.999
14
+ ],
15
+ "eps": 1e-08,
16
+ "weight_decay": 0.08,
17
+ "tau": 0.8,
18
+ "lam": 0.3,
19
+ "cautious": true,
20
+ "cautious_clamp_min": 0.5,
21
+ "buffer": 2,
22
+ "stochastic_rounding": true,
23
+ "bound": true,
24
+ "bound_type": "coordinate",
25
+ "bound_ratio": 0.05,
26
+ "master_weights": "none",
27
+ "execution": "auto",
28
+ "fused_single_pass": true,
29
+ "fuse_tensors": false,
30
+ "bucket_cap_mb": 500.0,
31
+ "prewarm_jit": true,
32
+ "ema_decay": "auto",
33
+ "fsdp_sync": false,
34
+ "seed": 1337
35
+ },
36
+ "presets": {
37
+ "dual_buffer_clm": {
38
+ "buffer": 2,
39
+ "tau": 0.8,
40
+ "cautious": true,
41
+ "cautious_clamp_min": 0.5,
42
+ "bound": true,
43
+ "bound_type": "coordinate",
44
+ "bound_ratio": 0.05,
45
+ "stochastic_rounding": true,
46
+ "master_weights": "none",
47
+ "fused_single_pass": true,
48
+ "ema_decay": "auto",
49
+ "fsdp_sync": false,
50
+ "description": "Default Dual-Buffer Convex Lookahead Momentum (CLM) tracking m_t and innovation variance v_t with coordinate trust-region bounding."
51
+ },
52
+ "single_buffer_td_clm": {
53
+ "buffer": 1,
54
+ "lam": 0.3,
55
+ "cautious": true,
56
+ "cautious_clamp_min": 0.5,
57
+ "bound": true,
58
+ "bound_type": "coordinate",
59
+ "bound_ratio": 0.05,
60
+ "stochastic_rounding": true,
61
+ "master_weights": "none",
62
+ "fused_single_pass": true,
63
+ "ema_decay": "auto",
64
+ "fsdp_sync": false,
65
+ "description": "Single-Buffer Triangle-Defect Convex Lookahead Momentum (TD-CLM) using the Triangle Inequality Envelope (cuts optimizer state RAM by 50% with zero global reductions in coordinate mode)."
66
+ },
67
+ "radial_trust_region_bound": {
68
+ "buffer": 2,
69
+ "tau": 0.8,
70
+ "cautious": true,
71
+ "cautious_clamp_min": 0.5,
72
+ "bound": true,
73
+ "bound_type": "radial",
74
+ "bound_ratio": 0.03,
75
+ "stochastic_rounding": true,
76
+ "master_weights": "none",
77
+ "fused_single_pass": true,
78
+ "ema_decay": "auto",
79
+ "fsdp_sync": false,
80
+ "description": "Direction-preserving radial trust-region bounding (tanh(r)/r) preserving 100% update angular geometry."
81
+ },
82
+ "semi_hybrid_fp32_master": {
83
+ "buffer": 2,
84
+ "cautious": true,
85
+ "cautious_clamp_min": 0.5,
86
+ "bound": true,
87
+ "bound_type": "coordinate",
88
+ "bound_ratio": 0.05,
89
+ "stochastic_rounding": true,
90
+ "master_weights": "semi",
91
+ "fused_single_pass": true,
92
+ "fsdp_sync": false,
93
+ "description": "Hybrid precision pairing 32-bit FP32 master weights with 16-bit BF16/FP16 optimizer moment buffers."
94
+ },
95
+ "full_fp32_baseline": {
96
+ "buffer": 2,
97
+ "cautious": true,
98
+ "cautious_clamp_min": 0.5,
99
+ "bound": true,
100
+ "bound_type": "coordinate",
101
+ "bound_ratio": 0.05,
102
+ "stochastic_rounding": true,
103
+ "master_weights": "full",
104
+ "fused_single_pass": true,
105
+ "fsdp_sync": false,
106
+ "description": "Full-precision FP32 master weights and FP32 optimizer states for controlled baseline evaluation."
107
+ },
108
+ "fsdp_distributed_sync": {
109
+ "buffer": 2,
110
+ "cautious": true,
111
+ "cautious_clamp_min": 0.5,
112
+ "bound": true,
113
+ "bound_type": "radial",
114
+ "bound_ratio": 0.03,
115
+ "stochastic_rounding": true,
116
+ "master_weights": "none",
117
+ "fused_single_pass": true,
118
+ "fsdp_sync": true,
119
+ "description": "Distributed multi-GPU training mode under PyTorch FSDP or DeepSpeed ZeRO-3 with cross-rank collective scratch synchronization."
120
+ }
121
+ }
122
+ }
data/Cargo.toml CHANGED
@@ -1,24 +1,24 @@
1
- [package]
2
- name = "luminamoon-dataloader"
3
- version.workspace = true
4
- edition.workspace = true
5
- license.workspace = true
6
-
7
- [lib]
8
- name = "luminamoon_dataloader"
9
- path = "src/lib.rs"
10
-
11
- [dependencies]
12
- pyo3.workspace = true
13
- thiserror.workspace = true
14
- serde.workspace = true
15
- serde_json.workspace = true
16
- rayon.workspace = true
17
- parquet.workspace = true
18
- arrow-array.workspace = true
19
- arrow-schema.workspace = true
20
- arrow-ipc.workspace = true
21
- xxhash-rust.workspace = true
22
- crc32fast.workspace = true
23
- hashbrown.workspace = true
24
- parking_lot.workspace = true
 
1
+ [package]
2
+ name = "luminamoon-dataloader"
3
+ version.workspace = true
4
+ edition.workspace = true
5
+ license.workspace = true
6
+
7
+ [lib]
8
+ name = "luminamoon_dataloader"
9
+ path = "src/lib.rs"
10
+
11
+ [dependencies]
12
+ pyo3.workspace = true
13
+ thiserror.workspace = true
14
+ serde.workspace = true
15
+ serde_json.workspace = true
16
+ rayon.workspace = true
17
+ parquet.workspace = true
18
+ arrow-array.workspace = true
19
+ arrow-schema.workspace = true
20
+ arrow-ipc.workspace = true
21
+ xxhash-rust.workspace = true
22
+ crc32fast.workspace = true
23
+ hashbrown.workspace = true
24
+ parking_lot.workspace = true
data/README.md CHANGED
@@ -1,206 +1,206 @@
1
- # LuminaMoon Data Engine v1.1.0
2
-
3
- LuminaMoon Data (`luminamoon.data`) provides text cleaning, SIMD normalization, heuristic quality filtering, exact and fuzzy deduplication, and crash-resilient checkpointing for Python and Rust data pipelines, including PyTorch, Hugging Face datasets, Ray, and WebDataset.
4
-
5
- ---
6
-
7
- ## 1. Modular Architecture & Responsibilities
8
-
9
- LuminaMoon Data focuses on in-memory text processing and deterministic state tracking, integrating smoothly with existing dataset readers:
10
-
11
- ```mermaid
12
- flowchart LR
13
- subgraph External ["External Data Sources"]
14
- HF["Hugging Face datasets.load_dataset(streaming=True)"]
15
- Torch["PyTorch torch.utils.data.DataLoader"]
16
- Custom["Custom Python Generator / Iterables"]
17
- end
18
-
19
- subgraph Data_Engine ["LuminaMoon Data Engine (Rust Core)"]
20
- direction TB
21
- Cleaner["1. TextCleaner\n(AVX-512/AVX2/NEON SIMD CRLF & JSON path extraction)"]
22
- Quality["2. QualityFilter\n(Shannon entropy, repetition loop, symbol ratio)"]
23
- Dedup["3. StreamDeduplicator & MinHash\n(XXH3-64, SwissTable WAL, MinHash fuzzy LSH)"]
24
- Masker["4. PIIMasker\n(Email, IP, Bearer/API Key, Card pattern sanitizer)"]
25
- Tracker["5. StateTracker\n(Dual-Slot 128B Ping-Pong Slot A/B CRC32)"]
26
- Glue["6. Collate & Pipeline Glue\n(PyTorch collate_fn, ResumablePipeline)"]
27
- end
28
-
29
- HF --> Cleaner
30
- Torch --> Cleaner
31
- Custom --> Cleaner
32
- Cleaner --> Quality
33
- Quality --> Dedup
34
- Dedup --> Masker
35
- Masker --> Tracker
36
- Tracker --> Glue
37
- Glue --> Consumer["GPU Model Training Step"]
38
- ```
39
-
40
- ---
41
-
42
- ## 2. Component Breakdown
43
-
44
- | Module | Core Responsibility | Underlying Implementation |
45
- | :--- | :--- | :--- |
46
- | **`luminamoon.data.cleaner`** | Whitespace trimming, SIMD newline normalization (`\r\n` $\rightarrow$ `\n`), byte length boundaries, and JSON path extraction. | `simd_cpu.rs`, `postprocess.rs` (Rayon parallel multi-threading, AVX-512BW, AVX2, NEON) |
47
- | **`luminamoon.data.quality`** | Shannon Character Entropy, loop repetition detector, and symbol-to-word ratio filtering. | `quality.rs` (Parallel Rayon heuristic quality filter) |
48
- | **`luminamoon.data.dedup`** | Stateful 64-bit exact hashing and deduplication with write-ahead log (`.dedup`) persistence. | `dedup.rs` (`xxh3_64` + `IdentityBuildHasher` SwissTable) |
49
- | **`luminamoon.data.minhash`** | Fuzzy near-duplicate detection via MinHash shingling and Jaccard similarity estimation. | `minhash.rs` (Permuted XXH3-64 MinHash signatures) |
50
- | **`luminamoon.data.masker`** | Pattern-based PII masking for emails, IPs, API keys/tokens, and payment cards. | `masker.py` (Compiled multi-pattern regex sanitizer) |
51
- | **`luminamoon.data.checkpoint`** | Dual-slot atomic coordinate checkpointing (`shard_id`, `row_offset`, `byte_offset`, `global_idx`). | `metadata.rs`, `coord.rs` (128-byte Dual-Slot Ping-Pong Slot A/B CRC32) |
52
- | **`luminamoon.data.string_tensor`** | Columnar string representation with CPU inlined StringView and GPU contiguous offset/payload buffers. | `string_tensor.rs` (16-byte Umbra/DuckDB StringView, SIMD prefix filter, contiguous u32 offsets + u8 payload) |
53
- | **`luminamoon.data.adapters`** | PyTorch `collate_fn` adapter and batch `ResumablePipeline`. | `adapters.py` (Dynamic token padding & tensor generation) |
54
- | **`luminamoon.data.serialization`** | Conversion of Python dictionaries to Parquet, JSONL, or Arrow IPC bytes. | `serialize.rs` (Arrow RecordBatch & Parquet ArrowWriter with ZSTD) |
55
-
56
- ---
57
-
58
- ## 3. Quickstart & Usage
59
-
60
- ### 3.1. Text Cleaning with SIMD & Rayon
61
- ```python
62
- from luminamoon.data import TextCleaner
63
-
64
- cleaner = TextCleaner(
65
- trim_whitespace=True,
66
- normalize_newlines=True, # AVX-512 / AVX2 / NEON SIMD
67
- min_byte_len=10,
68
- max_byte_len=2048,
69
- truncate_over_max=True,
70
- json_extract_key="data.text", # Extracts dotted path in Rust
71
- )
72
-
73
- cleaned = cleaner.clean_batch([" sample 1\r\n", "sample 2 "])
74
- ```
75
-
76
- ### 3.2. Quality Filtering (Shannon Entropy & Repetition Detector)
77
- ```python
78
- from luminamoon.data import QualityFilter
79
-
80
- qf = QualityFilter(
81
- min_entropy=2.5, # Filters low-entropy noise
82
- max_repetition_ratio=0.35, # Filters repetitive text loops
83
- max_symbol_ratio=0.30, # Filters symbol-heavy dumps
84
- )
85
-
86
- if qf.is_high_quality("Sample prose text for language model training."):
87
- ...
88
- ```
89
-
90
- ### 3.3. Exact & Fuzzy Near-Deduplication
91
- ```python
92
- from luminamoon.data import StreamDeduplicator, MinHashDeduplicator
93
-
94
- # Exact dedup with WAL
95
- dedup = StreamDeduplicator(wal_path="./cache/dataset.dedup")
96
- unique_batch = dedup.filter_batch(cleaned)
97
- dedup.commit()
98
-
99
- # Fuzzy near-dedup (> 85% similarity)
100
- fuzzy = MinHashDeduplicator(threshold=0.85)
101
- if fuzzy.is_unique("A document with slight punctuation differences."):
102
- ...
103
- ```
104
-
105
- ### 3.4. PII Sanitization
106
- ```python
107
- from luminamoon.data import PIIMasker
108
-
109
- masker = PIIMasker(mask_emails=True, mask_ips=True, mask_api_keys=True)
110
- safe_text = masker.mask("Contact support@lumi.ai with key Bearer sk-1234567890abcdef1234567890abcdef")
111
- # Result: "Contact [EMAIL] with key [API_KEY]"
112
- ```
113
-
114
- ### 3.5. Dual-Layout StringTensor (CPU StringView & GPU Buffers)
115
- ```python
116
- from luminamoon.data import StringTensor
117
-
118
- # 1. CPU Layout (Pattern 2 - Umbra / DuckDB / Arrow 2.0 StringView)
119
- # Strings <= 12 bytes are stored inline in a 16-byte struct
120
- st = StringTensor(["short", "this is a longer sample exceeding 12 bytes", "pref_sample"])
121
- print(st.is_inlined_mask()) # [True, False, True]
122
-
123
- # SIMD prefix and length filtering
124
- filtered = st.filter_by_prefix("pref_")
125
- hashes = st.hash_all() # Multi-threaded XXH3-64 hashing in Rust
126
-
127
- # 2. GPU Layout (Pattern 1 - Apache Arrow & TensorFlow Contiguous Offsets + Data Buffer)
128
- offsets, payload = st.to_gpu_buffers() # Raw uint32 offsets and uint8 payload
129
- off_tensor, pay_tensor = st.to_torch_gpu() # PyTorch tensors
130
- off_arr, pay_arr = st.to_numpy_gpu() # NumPy buffers
131
- arrow_array = st.to_arrow() # Apache Arrow 2.0 StringArray
132
- polars_series = st.to_polars() # Polars Series
133
- ```
134
-
135
- ### 3.6. Fused Single-Pass Pipeline
136
- ```python
137
- from luminamoon.data import FusedPipeline, StreamDeduplicator
138
-
139
- pipeline = FusedPipeline(
140
- trim_whitespace=True,
141
- normalize_newlines=True,
142
- min_chars=15,
143
- min_entropy=2.0,
144
- deduplicator=StreamDeduplicator(),
145
- commit_dedup=True,
146
- )
147
-
148
- # Process a batch in a single multi-threaded Rayon pass
149
- string_tensor = pipeline(raw_records)
150
- ```
151
-
152
- ### 3.7. PyTorch Collate & Async Prefetch Pipeline
153
- ```python
154
- from torch.utils.data import DataLoader
155
- from luminamoon.data import create_resumable_collate, ResumablePipeline, FusedPipeline
156
-
157
- # 1. As PyTorch collate_fn
158
- collate_fn = create_resumable_collate(
159
- fused_pipeline=FusedPipeline(),
160
- return_string_tensor=True,
161
- tokenizer=my_tokenizer,
162
- max_length=512,
163
- )
164
- loader = DataLoader(raw_dataset, batch_size=64, collate_fn=collate_fn)
165
-
166
- # 2. Or as batch ResumablePipeline with background prefetcher
167
- resumable_pipe = ResumablePipeline(
168
- source=raw_dataset,
169
- fused_pipeline=FusedPipeline(),
170
- batch_size=64,
171
- prefetch_batches=4, # Background prefetch worker for processed batches
172
- return_string_tensor=True,
173
- )
174
- for batch in resumable_pipe:
175
- ...
176
- ```
177
-
178
- ---
179
-
180
- ## 4. Verification & Testing
181
-
182
- Run Rust tests:
183
-
184
- ```bash
185
- cargo test -p luminamoon-dataloader
186
- ```
187
-
188
- Run Python modular test suite:
189
-
190
- ```bash
191
- python -B tests/test_data_modular.py
192
- ```
193
-
194
- ---
195
-
196
- ## 5. Version Inspection
197
-
198
- Query the current data engine module version:
199
-
200
- ```python
201
- import luminamoon.data as lmd
202
-
203
- print(lmd.get_version()) # Active package release version string
204
- ```
205
-
206
- > **Note**: LuminaMoon Data operates on a single unified native SIMD engine. Package releases follow `luminamoon.__version__`.
 
1
+ # LuminaMoon Data Engine v1.1.0
2
+
3
+ LuminaMoon Data (`luminamoon.data`) provides text cleaning, SIMD normalization, heuristic quality filtering, exact and fuzzy deduplication, and crash-resilient checkpointing for Python and Rust data pipelines, including PyTorch, Hugging Face datasets, Ray, and WebDataset.
4
+
5
+ ---
6
+
7
+ ## 1. Modular Architecture & Responsibilities
8
+
9
+ LuminaMoon Data focuses on in-memory text processing and deterministic state tracking, integrating smoothly with existing dataset readers:
10
+
11
+ ```mermaid
12
+ flowchart LR
13
+ subgraph External ["External Data Sources"]
14
+ HF["Hugging Face datasets.load_dataset(streaming=True)"]
15
+ Torch["PyTorch torch.utils.data.DataLoader"]
16
+ Custom["Custom Python Generator / Iterables"]
17
+ end
18
+
19
+ subgraph Data_Engine ["LuminaMoon Data Engine (Rust Core)"]
20
+ direction TB
21
+ Cleaner["1. TextCleaner\n(AVX-512/AVX2/NEON SIMD CRLF & JSON path extraction)"]
22
+ Quality["2. QualityFilter\n(Shannon entropy, repetition loop, symbol ratio)"]
23
+ Dedup["3. StreamDeduplicator & MinHash\n(XXH3-64, SwissTable WAL, MinHash fuzzy LSH)"]
24
+ Masker["4. PIIMasker\n(Email, IP, Bearer/API Key, Card pattern sanitizer)"]
25
+ Tracker["5. StateTracker\n(Dual-Slot 128B Ping-Pong Slot A/B CRC32)"]
26
+ Glue["6. Collate & Pipeline Glue\n(PyTorch collate_fn, ResumablePipeline)"]
27
+ end
28
+
29
+ HF --> Cleaner
30
+ Torch --> Cleaner
31
+ Custom --> Cleaner
32
+ Cleaner --> Quality
33
+ Quality --> Dedup
34
+ Dedup --> Masker
35
+ Masker --> Tracker
36
+ Tracker --> Glue
37
+ Glue --> Consumer["GPU Model Training Step"]
38
+ ```
39
+
40
+ ---
41
+
42
+ ## 2. Component Breakdown
43
+
44
+ | Module | Core Responsibility | Underlying Implementation |
45
+ | :--- | :--- | :--- |
46
+ | **`luminamoon.data.cleaner`** | Whitespace trimming, SIMD newline normalization (`\r\n` $\rightarrow$ `\n`), byte length boundaries, and JSON path extraction. | `simd_cpu.rs`, `postprocess.rs` (Rayon parallel multi-threading, AVX-512BW, AVX2, NEON) |
47
+ | **`luminamoon.data.quality`** | Shannon Character Entropy, loop repetition detector, and symbol-to-word ratio filtering. | `quality.rs` (Parallel Rayon heuristic quality filter) |
48
+ | **`luminamoon.data.dedup`** | Stateful 64-bit exact hashing and deduplication with write-ahead log (`.dedup`) persistence. | `dedup.rs` (`xxh3_64` + `IdentityBuildHasher` SwissTable) |
49
+ | **`luminamoon.data.minhash`** | Fuzzy near-duplicate detection via MinHash shingling and Jaccard similarity estimation. | `minhash.rs` (Permuted XXH3-64 MinHash signatures) |
50
+ | **`luminamoon.data.masker`** | Pattern-based PII masking for emails, IPs, API keys/tokens, and payment cards. | `masker.py` (Compiled multi-pattern regex sanitizer) |
51
+ | **`luminamoon.data.checkpoint`** | Dual-slot atomic coordinate checkpointing (`shard_id`, `row_offset`, `byte_offset`, `global_idx`). | `metadata.rs`, `coord.rs` (128-byte Dual-Slot Ping-Pong Slot A/B CRC32) |
52
+ | **`luminamoon.data.string_tensor`** | Columnar string representation with CPU inlined StringView and GPU contiguous offset/payload buffers. | `string_tensor.rs` (16-byte Umbra/DuckDB StringView, SIMD prefix filter, contiguous u32 offsets + u8 payload) |
53
+ | **`luminamoon.data.adapters`** | PyTorch `collate_fn` adapter and batch `ResumablePipeline`. | `adapters.py` (Dynamic token padding & tensor generation) |
54
+ | **`luminamoon.data.serialization`** | Conversion of Python dictionaries to Parquet, JSONL, or Arrow IPC bytes. | `serialize.rs` (Arrow RecordBatch & Parquet ArrowWriter with ZSTD) |
55
+
56
+ ---
57
+
58
+ ## 3. Quickstart & Usage
59
+
60
+ ### 3.1. Text Cleaning with SIMD & Rayon
61
+ ```python
62
+ from luminamoon.data import TextCleaner
63
+
64
+ cleaner = TextCleaner(
65
+ trim_whitespace=True,
66
+ normalize_newlines=True, # AVX-512 / AVX2 / NEON SIMD
67
+ min_byte_len=10,
68
+ max_byte_len=2048,
69
+ truncate_over_max=True,
70
+ json_extract_key="data.text", # Extracts dotted path in Rust
71
+ )
72
+
73
+ cleaned = cleaner.clean_batch([" sample 1\r\n", "sample 2 "])
74
+ ```
75
+
76
+ ### 3.2. Quality Filtering (Shannon Entropy & Repetition Detector)
77
+ ```python
78
+ from luminamoon.data import QualityFilter
79
+
80
+ qf = QualityFilter(
81
+ min_entropy=2.5, # Filters low-entropy noise
82
+ max_repetition_ratio=0.35, # Filters repetitive text loops
83
+ max_symbol_ratio=0.30, # Filters symbol-heavy dumps
84
+ )
85
+
86
+ if qf.is_high_quality("Sample prose text for language model training."):
87
+ ...
88
+ ```
89
+
90
+ ### 3.3. Exact & Fuzzy Near-Deduplication
91
+ ```python
92
+ from luminamoon.data import StreamDeduplicator, MinHashDeduplicator
93
+
94
+ # Exact dedup with WAL
95
+ dedup = StreamDeduplicator(wal_path="./cache/dataset.dedup")
96
+ unique_batch = dedup.filter_batch(cleaned)
97
+ dedup.commit()
98
+
99
+ # Fuzzy near-dedup (> 85% similarity)
100
+ fuzzy = MinHashDeduplicator(threshold=0.85)
101
+ if fuzzy.is_unique("A document with slight punctuation differences."):
102
+ ...
103
+ ```
104
+
105
+ ### 3.4. PII Sanitization
106
+ ```python
107
+ from luminamoon.data import PIIMasker
108
+
109
+ masker = PIIMasker(mask_emails=True, mask_ips=True, mask_api_keys=True)
110
+ safe_text = masker.mask("Contact support@lumi.ai with key Bearer sk-1234567890abcdef1234567890abcdef")
111
+ # Result: "Contact [EMAIL] with key [API_KEY]"
112
+ ```
113
+
114
+ ### 3.5. Dual-Layout StringTensor (CPU StringView & GPU Buffers)
115
+ ```python
116
+ from luminamoon.data import StringTensor
117
+
118
+ # 1. CPU Layout (Pattern 2 - Umbra / DuckDB / Arrow 2.0 StringView)
119
+ # Strings <= 12 bytes are stored inline in a 16-byte struct
120
+ st = StringTensor(["short", "this is a longer sample exceeding 12 bytes", "pref_sample"])
121
+ print(st.is_inlined_mask()) # [True, False, True]
122
+
123
+ # SIMD prefix and length filtering
124
+ filtered = st.filter_by_prefix("pref_")
125
+ hashes = st.hash_all() # Multi-threaded XXH3-64 hashing in Rust
126
+
127
+ # 2. GPU Layout (Pattern 1 - Apache Arrow & TensorFlow Contiguous Offsets + Data Buffer)
128
+ offsets, payload = st.to_gpu_buffers() # Raw uint32 offsets and uint8 payload
129
+ off_tensor, pay_tensor = st.to_torch_gpu() # PyTorch tensors
130
+ off_arr, pay_arr = st.to_numpy_gpu() # NumPy buffers
131
+ arrow_array = st.to_arrow() # Apache Arrow 2.0 StringArray
132
+ polars_series = st.to_polars() # Polars Series
133
+ ```
134
+
135
+ ### 3.6. Fused Single-Pass Pipeline
136
+ ```python
137
+ from luminamoon.data import FusedPipeline, StreamDeduplicator
138
+
139
+ pipeline = FusedPipeline(
140
+ trim_whitespace=True,
141
+ normalize_newlines=True,
142
+ min_chars=15,
143
+ min_entropy=2.0,
144
+ deduplicator=StreamDeduplicator(),
145
+ commit_dedup=True,
146
+ )
147
+
148
+ # Process a batch in a single multi-threaded Rayon pass
149
+ string_tensor = pipeline(raw_records)
150
+ ```
151
+
152
+ ### 3.7. PyTorch Collate & Async Prefetch Pipeline
153
+ ```python
154
+ from torch.utils.data import DataLoader
155
+ from luminamoon.data import create_resumable_collate, ResumablePipeline, FusedPipeline
156
+
157
+ # 1. As PyTorch collate_fn
158
+ collate_fn = create_resumable_collate(
159
+ fused_pipeline=FusedPipeline(),
160
+ return_string_tensor=True,
161
+ tokenizer=my_tokenizer,
162
+ max_length=512,
163
+ )
164
+ loader = DataLoader(raw_dataset, batch_size=64, collate_fn=collate_fn)
165
+
166
+ # 2. Or as batch ResumablePipeline with background prefetcher
167
+ resumable_pipe = ResumablePipeline(
168
+ source=raw_dataset,
169
+ fused_pipeline=FusedPipeline(),
170
+ batch_size=64,
171
+ prefetch_batches=4, # Background prefetch worker for processed batches
172
+ return_string_tensor=True,
173
+ )
174
+ for batch in resumable_pipe:
175
+ ...
176
+ ```
177
+
178
+ ---
179
+
180
+ ## 4. Verification & Testing
181
+
182
+ Run Rust tests:
183
+
184
+ ```bash
185
+ cargo test -p luminamoon-dataloader
186
+ ```
187
+
188
+ Run Python modular test suite:
189
+
190
+ ```bash
191
+ python -B tests/test_data_modular.py
192
+ ```
193
+
194
+ ---
195
+
196
+ ## 5. Version Inspection
197
+
198
+ Query the current data engine module version:
199
+
200
+ ```python
201
+ import luminamoon.data as lmd
202
+
203
+ print(lmd.get_version()) # Active package release version string
204
+ ```
205
+
206
+ > **Note**: LuminaMoon Data operates on a single unified native SIMD engine. Package releases follow `luminamoon.__version__`.
data/docs/README.md CHANGED
@@ -1,62 +1,62 @@
1
- # LuminaMoon Data Engine Documentation
2
-
3
- Documentation index for the **LuminaMoon Data Engine (`luminamoon.data` / `luminamoon-dataloader`)**.
4
-
5
- ---
6
-
7
- ## 1. Architecture, User Guide & Developer Specs
8
-
9
- - **[LuminaMoon Data Engine Guide (`data/README.md`)](../README.md)**: Architecture overview, crate module breakdown, `FusedPipeline` single-pass Rayon execution, dual-layout `StringTensor` (CPU 16-byte `StringView16` & GPU contiguous buffers), 128-byte Ping-Pong checkpoint recovery (`Slot A` / `Slot B`), two-stage `xxh3_64` deduplication, 64-permutation MinHash LSH, `AVX-512BW` / `AVX2` / `NEON` CPU SIMD normalization, and PyTorch `ResumablePipeline` / `create_resumable_collate` integration.
10
- - **[Developer Modular Documentation Index (`docs/dataloader/README.md`)](../../docs/dataloader/README.md)**:
11
- - **[01. Coordinates & Dual Metadata (`coord.rs`, `metadata.rs`, `constants.rs`, `error.rs`)](../../docs/dataloader/coord_and_metadata/README.md)**
12
- - **[02. Two-Stage XXH3-64 Deduplication (`dedup.rs`)](../../docs/dataloader/dedup/README.md)**
13
- - **[03. CPU SIMD & Parallel Post-Processing (`simd_cpu.rs`, `postprocess.rs`)](../../docs/dataloader/simd_and_postprocess/README.md)**
14
- - **[04. Heuristic Quality Filter & MinHash LSH (`quality.rs`, `minhash.rs`)](../../docs/dataloader/sources_and_streaming/README.md)**
15
- - **[05. Dual-Layout `StringTensor` & Resumable Adapters (`string_tensor.rs`, `adapters.py`)](../../docs/dataloader/preram/README.md)**
16
- - **[06. Columnar Table Serialization (`serialize.rs`, `serialization.py`)](../../docs/dataloader/multimodal_and_hf_hub/README.md)**
17
- - **[07. PyO3 `abi3t` FFI Bridge & Modular Runtime (`py_bridge.rs`, `python/luminamoon/data/`)](../../docs/dataloader/python_ffi_and_runtime/README.md)**
18
-
19
- ---
20
-
21
- ## 2. External Library Compatibility Notes (`data/docs/library_audit/`)
22
-
23
- Version verification, changelog tracking, and compatibility notes for external dependencies used by `luminamoon-dataloader`:
24
-
25
- - **[Library Compatibility Index](library_audit/README.md)**
26
- - **[01. Rust Toolchain, SIMD & Memory Alignment (`rayon`)](library_audit/01_rust_toolchain_simd_and_mmap.md)**: Rust 1.99.1, Edition 2024 `unsafe_op_in_unsafe_fn`, `OnceLock` ISA caching, and `AVX-512BW` / `AVX2` / `NEON` intrinsics.
27
- - **[02. Python, PyO3 (`abi3t`), and Maturin](library_audit/02_python_pyo3_abi3t_and_maturin.md)**: Free-threaded Python (`abi3t`), `py.detach` GIL release, `Bound::cast::<T>()`, `PyBool` vs `PyInt` type ordering, and table/postprocess FFI functions.
28
- - **[03. Apache Arrow & Parquet (`arrow-array`, `arrow-schema`, `parquet`)](library_audit/03_parquet_arrow_and_http206_streaming.md)**: Columnar `RecordBatch` construction, ZSTD Parquet compression, and HuggingFace Dataset Viewer `Struct<bytes, path>` schema generation.
29
- - **[04. `xxhash-rust`, `hashbrown`, `crc32fast`, and `parking_lot`](library_audit/04_xxh3_dedup_crc32_and_hf_hub_security.md)**: `xxh3_64` + `IdentityBuildHasher` SwissTable, `crc32fast` Ping-Pong checkpoint slots, and synchronization primitives.
30
- - **[05. Universal Formats (`arrow-ipc`, `serde_json`) and Typo Diagnostics](library_audit/05_universal_formats_compression_and_diagnostics.md)**: Arrow IPC (`ARROW1`), NDJSON serialization, and Damerau-Levenshtein parameter suggestions (`did_you_mean`).
31
-
32
- ---
33
-
34
- ## 3. Module Summary & Verification
35
-
36
- ### 3.1. Module Matrix
37
-
38
- | Module / File | Core Responsibility | Status |
39
- | :--- | :--- | :--- |
40
- | [`data/src/constants.rs`](../src/constants.rs) | Magic bytes (`LMDL`, `PAR1`, `ARROW1`), slot sizes, and default bounds. | Verified |
41
- | [`data/src/coord.rs`](../src/coord.rs) | 32-byte little-endian `StreamCoordinate` (`shard_id`, `byte_offset`, `row_group_idx`, `page_idx`, `row_offset_in_page`, `global_row_idx`). | Verified |
42
- | [`data/src/dedup.rs`](../src/dedup.rs) | `TwoStageDeduplicator` using `xxh3_64` with `IdentityBuildHasher` SwissTable. | Verified |
43
- | [`data/src/error.rs`](../src/error.rs) | Unified `DataLoaderError` with Damerau-Levenshtein edit-distance suggestions (`did_you_mean`). | Verified |
44
- | [`data/src/metadata.rs`](../src/metadata.rs) | 128-byte Dual-Slot Ping-Pong (`Slot A` / `Slot B`) checkpoint file with IEEE CRC32 (`crc32fast`) and `.dedup` WAL. | Verified |
45
- | [`data/src/minhash.rs`](../src/minhash.rs) | 64-permutation MinHash LSH fuzzy deduplicator and Jaccard similarity estimation. | Verified |
46
- | [`data/src/quality.rs`](../src/quality.rs) | Shannon Character Entropy, loop repetition detector, and symbol-to-word ratio filters. | Verified |
47
- | [`data/src/simd_cpu.rs`](../src/simd_cpu.rs) | `AVX-512BW`, `AVX2`, `NEON`, and Scalar kernels (`find_byte_fast`, `rfind_byte_fast`) + 64-byte `CacheAlignedBuffer`. | Verified |
48
- | [`data/src/string_tensor.rs`](../src/string_tensor.rs) | Dual-layout container for CPU `StringView16` (<=12B inline) and GPU contiguous offsets/payload buffers. | Verified |
49
- | [`data/src/postprocess.rs`](../src/postprocess.rs) | Rayon parallel UTF-8 validation, SIMD `\r` detection, JSON dotted-path extraction, and whitespace normalization. | Verified |
50
- | [`data/src/serialize.rs`](../src/serialize.rs) | Table serializers for Apache Arrow IPC, Parquet (ZSTD), and NDJSON (`DataValue`). | Verified |
51
- | [`data/src/py_bridge.rs`](../src/py_bridge.rs) | `PyO3 0.30.1` (`abi3t`) bindings for `FusedPipeline`, `StringTensor`, `StateTracker`, `StreamDeduplicator`, `QualityFilter`, and `MinHashDeduplicator`. | Verified |
52
- | [`python/luminamoon/data/`](../../python/luminamoon/data/__init__.py) | Modular Python package (`fused.py`, `cleaner.py`, `quality.py`, `dedup.py`, `minhash.py`, `masker.py`, `string_tensor.py`, `checkpoint.py`, `adapters.py`, `serialization.py`, `version.py`, and `fallbacks/`). | Verified |
53
-
54
- ### 3.2. Verification Commands
55
-
56
- | Check | Command | Result |
57
- | :--- | :--- | :--- |
58
- | Rust Workspace Linter | `cargo clippy --workspace --all-targets -- -D warnings` | **PASSED** (0 Warnings) |
59
- | Rust Release Tests | `cargo test --workspace` | **PASSED** |
60
- | Python Static Type Check | `npx pyright python/luminamoon` | **PASSED** (0 Errors, 0 Warnings) |
61
- | Python Modular Data Tests | `python -B tests/test_data_modular.py` | **PASSED** |
62
- | Python StringTensor CPU/GPU Tests | `python -B tests/test_gpu_vs_cpu_strings.py` | **PASSED** |
 
1
+ # LuminaMoon Data Engine Documentation
2
+
3
+ Documentation index for the **LuminaMoon Data Engine (`luminamoon.data` / `luminamoon-dataloader`)**.
4
+
5
+ ---
6
+
7
+ ## 1. Architecture, User Guide & Developer Specs
8
+
9
+ - **[LuminaMoon Data Engine Guide (`data/README.md`)](../README.md)**: Architecture overview, crate module breakdown, `FusedPipeline` single-pass Rayon execution, dual-layout `StringTensor` (CPU 16-byte `StringView16` & GPU contiguous buffers), 128-byte Ping-Pong checkpoint recovery (`Slot A` / `Slot B`), two-stage `xxh3_64` deduplication, 64-permutation MinHash LSH, `AVX-512BW` / `AVX2` / `NEON` CPU SIMD normalization, and PyTorch `ResumablePipeline` / `create_resumable_collate` integration.
10
+ - **[Developer Modular Documentation Index (`docs/dataloader/README.md`)](../../docs/dataloader/README.md)**:
11
+ - **[01. Coordinates & Dual Metadata (`coord.rs`, `metadata.rs`, `constants.rs`, `error.rs`)](../../docs/dataloader/coord_and_metadata/README.md)**
12
+ - **[02. Two-Stage XXH3-64 Deduplication (`dedup.rs`)](../../docs/dataloader/dedup/README.md)**
13
+ - **[03. CPU SIMD & Parallel Post-Processing (`simd_cpu.rs`, `postprocess.rs`)](../../docs/dataloader/simd_and_postprocess/README.md)**
14
+ - **[04. Heuristic Quality Filter & MinHash LSH (`quality.rs`, `minhash.rs`)](../../docs/dataloader/sources_and_streaming/README.md)**
15
+ - **[05. Dual-Layout `StringTensor` & Resumable Adapters (`string_tensor.rs`, `adapters.py`)](../../docs/dataloader/preram/README.md)**
16
+ - **[06. Columnar Table Serialization (`serialize.rs`, `serialization.py`)](../../docs/dataloader/multimodal_and_hf_hub/README.md)**
17
+ - **[07. PyO3 `abi3t` FFI Bridge & Modular Runtime (`py_bridge.rs`, `python/luminamoon/data/`)](../../docs/dataloader/python_ffi_and_runtime/README.md)**
18
+
19
+ ---
20
+
21
+ ## 2. External Library Compatibility Notes (`data/docs/library_audit/`)
22
+
23
+ Version verification, changelog tracking, and compatibility notes for external dependencies used by `luminamoon-dataloader`:
24
+
25
+ - **[Library Compatibility Index](library_audit/README.md)**
26
+ - **[01. Rust Toolchain, SIMD & Memory Alignment (`rayon`)](library_audit/01_rust_toolchain_simd_and_mmap.md)**: Rust 1.99.1, Edition 2024 `unsafe_op_in_unsafe_fn`, `OnceLock` ISA caching, and `AVX-512BW` / `AVX2` / `NEON` intrinsics.
27
+ - **[02. Python, PyO3 (`abi3t`), and Maturin](library_audit/02_python_pyo3_abi3t_and_maturin.md)**: Free-threaded Python (`abi3t`), `py.detach` GIL release, `Bound::cast::<T>()`, `PyBool` vs `PyInt` type ordering, and table/postprocess FFI functions.
28
+ - **[03. Apache Arrow & Parquet (`arrow-array`, `arrow-schema`, `parquet`)](library_audit/03_parquet_arrow_and_http206_streaming.md)**: Columnar `RecordBatch` construction, ZSTD Parquet compression, and HuggingFace Dataset Viewer `Struct<bytes, path>` schema generation.
29
+ - **[04. `xxhash-rust`, `hashbrown`, `crc32fast`, and `parking_lot`](library_audit/04_xxh3_dedup_crc32_and_hf_hub_security.md)**: `xxh3_64` + `IdentityBuildHasher` SwissTable, `crc32fast` Ping-Pong checkpoint slots, and synchronization primitives.
30
+ - **[05. Universal Formats (`arrow-ipc`, `serde_json`) and Typo Diagnostics](library_audit/05_universal_formats_compression_and_diagnostics.md)**: Arrow IPC (`ARROW1`), NDJSON serialization, and Damerau-Levenshtein parameter suggestions (`did_you_mean`).
31
+
32
+ ---
33
+
34
+ ## 3. Module Summary & Verification
35
+
36
+ ### 3.1. Module Matrix
37
+
38
+ | Module / File | Core Responsibility | Status |
39
+ | :--- | :--- | :--- |
40
+ | [`data/src/constants.rs`](../src/constants.rs) | Magic bytes (`LMDL`, `PAR1`, `ARROW1`), slot sizes, and default bounds. | Verified |
41
+ | [`data/src/coord.rs`](../src/coord.rs) | 32-byte little-endian `StreamCoordinate` (`shard_id`, `byte_offset`, `row_group_idx`, `page_idx`, `row_offset_in_page`, `global_row_idx`). | Verified |
42
+ | [`data/src/dedup.rs`](../src/dedup.rs) | `TwoStageDeduplicator` using `xxh3_64` with `IdentityBuildHasher` SwissTable. | Verified |
43
+ | [`data/src/error.rs`](../src/error.rs) | Unified `DataLoaderError` with Damerau-Levenshtein edit-distance suggestions (`did_you_mean`). | Verified |
44
+ | [`data/src/metadata.rs`](../src/metadata.rs) | 128-byte Dual-Slot Ping-Pong (`Slot A` / `Slot B`) checkpoint file with IEEE CRC32 (`crc32fast`) and `.dedup` WAL. | Verified |
45
+ | [`data/src/minhash.rs`](../src/minhash.rs) | 64-permutation MinHash LSH fuzzy deduplicator and Jaccard similarity estimation. | Verified |
46
+ | [`data/src/quality.rs`](../src/quality.rs) | Shannon Character Entropy, loop repetition detector, and symbol-to-word ratio filters. | Verified |
47
+ | [`data/src/simd_cpu.rs`](../src/simd_cpu.rs) | `AVX-512BW`, `AVX2`, `NEON`, and Scalar kernels (`find_byte_fast`, `rfind_byte_fast`) + 64-byte `CacheAlignedBuffer`. | Verified |
48
+ | [`data/src/string_tensor.rs`](../src/string_tensor.rs) | Dual-layout container for CPU `StringView16` (<=12B inline) and GPU contiguous offsets/payload buffers. | Verified |
49
+ | [`data/src/postprocess.rs`](../src/postprocess.rs) | Rayon parallel UTF-8 validation, SIMD `\r` detection, JSON dotted-path extraction, and whitespace normalization. | Verified |
50
+ | [`data/src/serialize.rs`](../src/serialize.rs) | Table serializers for Apache Arrow IPC, Parquet (ZSTD), and NDJSON (`DataValue`). | Verified |
51
+ | [`data/src/py_bridge.rs`](../src/py_bridge.rs) | `PyO3 0.30.1` (`abi3t`) bindings for `FusedPipeline`, `StringTensor`, `StateTracker`, `StreamDeduplicator`, `QualityFilter`, and `MinHashDeduplicator`. | Verified |
52
+ | [`python/luminamoon/data/`](../../python/luminamoon/data/__init__.py) | Modular Python package (`fused.py`, `cleaner.py`, `quality.py`, `dedup.py`, `minhash.py`, `masker.py`, `string_tensor.py`, `checkpoint.py`, `adapters.py`, `serialization.py`, `version.py`, and `fallbacks/`). | Verified |
53
+
54
+ ### 3.2. Verification Commands
55
+
56
+ | Check | Command | Result |
57
+ | :--- | :--- | :--- |
58
+ | Rust Workspace Linter | `cargo clippy --workspace --all-targets -- -D warnings` | **PASSED** (0 Warnings) |
59
+ | Rust Release Tests | `cargo test --workspace` | **PASSED** |
60
+ | Python Static Type Check | `npx pyright python/luminamoon` | **PASSED** (0 Errors, 0 Warnings) |
61
+ | Python Modular Data Tests | `python -B tests/test_data_modular.py` | **PASSED** |
62
+ | Python StringTensor CPU/GPU Tests | `python -B tests/test_gpu_vs_cpu_strings.py` | **PASSED** |
data/docs/library_audit/01_rust_toolchain_simd_and_mmap.md CHANGED
@@ -1,56 +1,56 @@
1
- # 1. Rust Toolchain, `std::arch` SIMD Intrinsics, `memmap2`, and Core Crates
2
-
3
- ## 1.1 Latest Stable Versions
4
- - **Rust Compiler (`rustc` & `cargo`)**: `1.99.1` (`edition = "2024"`, `resolver = "3"`)
5
- - **`memmap2`**: `0.9.11`
6
- - **`rayon`**: `1.12.0`
7
- - **`thiserror`**: `2.0.21`
8
- - **`serde`**: `1.0.229`
9
- - **`serde_json`**: `1.0.154`
10
-
11
- ---
12
-
13
- ## 1.2 Version Range Changelog (`Rust 1.85.0` - `1.99.1` & Supporting Crates)
14
-
15
- ### A. Rust 1.85.0 (`edition = "2024"`)
16
- - **What changed**: The `unsafe_op_in_unsafe_fn` lint is enabled by default in Rust Edition 2024. Declaring a function as `unsafe fn` no longer implicitly permits calling `unsafe` operations inside the function body without an explicit `unsafe { ... }` block.
17
- - **Syntax changes**: Stabilized `let-chains` (`if let Some(a) = opt && a > 0`) inside `if` and `while` conditions, now enforced by `clippy::collapsible_if`.
18
-
19
- ### B. Rust 1.86.0 - 1.89.0 (`std::arch` Target Features & `avx512bw` Stabilization)
20
- - **Rust 1.86.0 - 1.87.0**: Value-based SIMD intrinsics (such as `_mm512_set1_epi8`, `_mm512_cmpeq_epi8_mask`, `_mm256_set1_epi8`, `_mm256_cmpeq_epi8`, `_mm256_movemask_epi8`, `vdupq_n_u8`, `vceqq_u8`, `vmaxvq_u8`) no longer require an `unsafe` block when called inside a `#[target_feature]` function. Wrapping value intrinsics in `unsafe` triggers an `unused_unsafe` compiler warning. Only raw pointer dereference intrinsics (`_mm512_loadu_si512`, `_mm256_loadu_si256`, `vld1q_u8`) and pointer arithmetic (`ptr.add(offset)`) remain `unsafe`.
21
- - **Rust 1.89.0**: All `avx512f` and `avx512bw` target features and 512-bit byte intrinsics (`_mm512_cmpeq_epi8_mask`) were stabilized on the stable compiler channel without requiring `#![feature(stdarch_x86_avx512)]`.
22
-
23
- ### C. Rust 1.88.0 - 1.95.0 (`[T]::as_chunks` & `std::cfg_select!`)
24
- - **Rust 1.88.0**: Stabilized the standard library method `slice::as_chunks::<N>()` to partition slices into fixed-size array chunks `&[[T; N]]` plus a remainder slice, replacing legacy `chunks_exact(N)` patterns (enforced by `clippy::chunks_exact_to_as_chunks`).
25
- - **Rust 1.95.0**: Added the standard macro `std::cfg_select!` for cross-architecture conditional compilation without requiring the external `cfg-if` crate.
26
-
27
- ### D. `memmap2` (`0.9.0` - `0.9.11`): Mitigation of `RUSTSEC-2026-0186`
28
- - **What changed**: In `memmap2 < 0.9.11`, internal memory-mapping end-boundary calculations used `ptr.add(len)`. Under modern Rust/LLVM pointer provenance rules, calling `ptr.add` on an empty mapping or at the edge of the address space violates `inbounds` invariants and triggers Undefined Behavior (UB).
29
- - **Upstream fix (`0.9.11`)**: Replaced internal pointer boundary calculations with `wrapping_add` and stabilized `Advice::PopulateRead` (`MADV_POPULATE_READ` on Linux) for page pre-faulting.
30
-
31
- ### E. `thiserror` (`1.0` - `2.0.21`), `rayon` (`1.10` - `1.12.0`), and `serde_json` (`1.0.120` - `1.0.154`)
32
- - **`thiserror 2.0.21`**: Migrated to the stabilized `core::error::Error` trait in `core`, removing mandatory reliance on internal `std::error::Error` paths and improving variable format expansion in `#[error("...")]` macros.
33
- - **`rayon 1.12.0`**: Improved work-stealing scheduling efficiency on indexed parallel iterators (`into_par_iter().enumerate()`).
34
- - **`serde_json 1.0.154`**: Optimized direct byte-buffer serialization (`serde_json::to_writer(&mut Vec<u8>, ...)`) without intermediate `String` allocations.
35
-
36
- ---
37
-
38
- ## 1.3 Adaptations in LuminaMoon Source Code
39
-
40
- 1. **Architecture Detection via `std::cfg_select!` and Scoped `unsafe` Intrinsic Blocks (`data/src/simd_cpu.rs`)**:
41
- - Uses `std::cfg_select!` inside `detect_cpu_isa()` to select `x86`/`x86_64`, `aarch64`, or `Scalar` fallback branches without any `cfg-if` dependency.
42
- - In `find_byte_avx512`, `rfind_byte_avx512`, `find_byte_avx2`, `rfind_byte_avx2`, `find_byte_neon`, and `rfind_byte_neon`, `unsafe { ... }` blocks are restricted strictly to `_mm512_loadu_si512`, `_mm256_loadu_si256`, `vld1q_u8`, and `ptr.add(...)`:
43
- ```rust
44
- while offset + 64 <= len {
45
- let chunk = unsafe { _mm512_loadu_si512(ptr.add(offset).cast()) };
46
- let mask = _mm512_cmpeq_epi8_mask(chunk, v_needle);
47
- if mask != 0 {
48
- return Some(offset + mask.trailing_zeros() as usize);
49
- }
50
- offset += 64;
51
- }
52
- ```
53
- 2. **Adoption of `slice::as_chunks::<3>()` (`data/src/multimodal_serialize.rs`)**:
54
- - The `encode_base64` function uses `let (chunks, rem) = input.as_chunks::<3>();` to process 3-byte blocks directly without per-iteration bounds checks.
55
- 3. **Safe Memory Mapping with `memmap2 0.9.11` (`data/src/source_local.rs`)**:
56
- - `LocalMmapLineSource::open` maps local files using `memmap2 0.9.11` (patched against `RUSTSEC-2026-0186`) and enforces `start_coord.byte_offset.min(total_len)` bounds checks prior to slicing.
 
1
+ # 1. Rust Toolchain, `std::arch` SIMD Intrinsics, `memmap2`, and Core Crates
2
+
3
+ ## 1.1 Latest Stable Versions
4
+ - **Rust Compiler (`rustc` & `cargo`)**: `1.99.1` (`edition = "2024"`, `resolver = "3"`)
5
+ - **`memmap2`**: `0.9.11`
6
+ - **`rayon`**: `1.12.0`
7
+ - **`thiserror`**: `2.0.21`
8
+ - **`serde`**: `1.0.229`
9
+ - **`serde_json`**: `1.0.154`
10
+
11
+ ---
12
+
13
+ ## 1.2 Version Range Changelog (`Rust 1.85.0` - `1.99.1` & Supporting Crates)
14
+
15
+ ### A. Rust 1.85.0 (`edition = "2024"`)
16
+ - **What changed**: The `unsafe_op_in_unsafe_fn` lint is enabled by default in Rust Edition 2024. Declaring a function as `unsafe fn` no longer implicitly permits calling `unsafe` operations inside the function body without an explicit `unsafe { ... }` block.
17
+ - **Syntax changes**: Stabilized `let-chains` (`if let Some(a) = opt && a > 0`) inside `if` and `while` conditions, now enforced by `clippy::collapsible_if`.
18
+
19
+ ### B. Rust 1.86.0 - 1.89.0 (`std::arch` Target Features & `avx512bw` Stabilization)
20
+ - **Rust 1.86.0 - 1.87.0**: Value-based SIMD intrinsics (such as `_mm512_set1_epi8`, `_mm512_cmpeq_epi8_mask`, `_mm256_set1_epi8`, `_mm256_cmpeq_epi8`, `_mm256_movemask_epi8`, `vdupq_n_u8`, `vceqq_u8`, `vmaxvq_u8`) no longer require an `unsafe` block when called inside a `#[target_feature]` function. Wrapping value intrinsics in `unsafe` triggers an `unused_unsafe` compiler warning. Only raw pointer dereference intrinsics (`_mm512_loadu_si512`, `_mm256_loadu_si256`, `vld1q_u8`) and pointer arithmetic (`ptr.add(offset)`) remain `unsafe`.
21
+ - **Rust 1.89.0**: All `avx512f` and `avx512bw` target features and 512-bit byte intrinsics (`_mm512_cmpeq_epi8_mask`) were stabilized on the stable compiler channel without requiring `#![feature(stdarch_x86_avx512)]`.
22
+
23
+ ### C. Rust 1.88.0 - 1.95.0 (`[T]::as_chunks` & `std::cfg_select!`)
24
+ - **Rust 1.88.0**: Stabilized the standard library method `slice::as_chunks::<N>()` to partition slices into fixed-size array chunks `&[[T; N]]` plus a remainder slice, replacing legacy `chunks_exact(N)` patterns (enforced by `clippy::chunks_exact_to_as_chunks`).
25
+ - **Rust 1.95.0**: Added the standard macro `std::cfg_select!` for cross-architecture conditional compilation without requiring the external `cfg-if` crate.
26
+
27
+ ### D. `memmap2` (`0.9.0` - `0.9.11`): Mitigation of `RUSTSEC-2026-0186`
28
+ - **What changed**: In `memmap2 < 0.9.11`, internal memory-mapping end-boundary calculations used `ptr.add(len)`. Under modern Rust/LLVM pointer provenance rules, calling `ptr.add` on an empty mapping or at the edge of the address space violates `inbounds` invariants and triggers Undefined Behavior (UB).
29
+ - **Upstream fix (`0.9.11`)**: Replaced internal pointer boundary calculations with `wrapping_add` and stabilized `Advice::PopulateRead` (`MADV_POPULATE_READ` on Linux) for page pre-faulting.
30
+
31
+ ### E. `thiserror` (`1.0` - `2.0.21`), `rayon` (`1.10` - `1.12.0`), and `serde_json` (`1.0.120` - `1.0.154`)
32
+ - **`thiserror 2.0.21`**: Migrated to the stabilized `core::error::Error` trait in `core`, removing mandatory reliance on internal `std::error::Error` paths and improving variable format expansion in `#[error("...")]` macros.
33
+ - **`rayon 1.12.0`**: Improved work-stealing scheduling efficiency on indexed parallel iterators (`into_par_iter().enumerate()`).
34
+ - **`serde_json 1.0.154`**: Optimized direct byte-buffer serialization (`serde_json::to_writer(&mut Vec<u8>, ...)`) without intermediate `String` allocations.
35
+
36
+ ---
37
+
38
+ ## 1.3 Adaptations in LuminaMoon Source Code
39
+
40
+ 1. **Architecture Detection via `std::cfg_select!` and Scoped `unsafe` Intrinsic Blocks (`data/src/simd_cpu.rs`)**:
41
+ - Uses `std::cfg_select!` inside `detect_cpu_isa()` to select `x86`/`x86_64`, `aarch64`, or `Scalar` fallback branches without any `cfg-if` dependency.
42
+ - In `find_byte_avx512`, `rfind_byte_avx512`, `find_byte_avx2`, `rfind_byte_avx2`, `find_byte_neon`, and `rfind_byte_neon`, `unsafe { ... }` blocks are restricted strictly to `_mm512_loadu_si512`, `_mm256_loadu_si256`, `vld1q_u8`, and `ptr.add(...)`:
43
+ ```rust
44
+ while offset + 64 <= len {
45
+ let chunk = unsafe { _mm512_loadu_si512(ptr.add(offset).cast()) };
46
+ let mask = _mm512_cmpeq_epi8_mask(chunk, v_needle);
47
+ if mask != 0 {
48
+ return Some(offset + mask.trailing_zeros() as usize);
49
+ }
50
+ offset += 64;
51
+ }
52
+ ```
53
+ 2. **Adoption of `slice::as_chunks::<3>()` (`data/src/multimodal_serialize.rs`)**:
54
+ - The `encode_base64` function uses `let (chunks, rem) = input.as_chunks::<3>();` to process 3-byte blocks directly without per-iteration bounds checks.
55
+ 3. **Safe Memory Mapping with `memmap2 0.9.11` (`data/src/source_local.rs`)**:
56
+ - `LocalMmapLineSource::open` maps local files using `memmap2 0.9.11` (patched against `RUSTSEC-2026-0186`) and enforces `start_coord.byte_offset.min(total_len)` bounds checks prior to slicing.
data/docs/library_audit/02_python_pyo3_abi3t_and_maturin.md CHANGED
@@ -1,53 +1,53 @@
1
- # 2. Python, PyO3 (`abi3t`), and Maturin
2
-
3
- ## 2.1 Latest Stable Versions
4
- - **Python**: `3.15.0` (supports both standard CPython and free-threaded `Py_GIL_DISABLED` builds)
5
- - **`pyo3`**: `0.30.1` (`features = ["abi3-py310", "abi3t"]`)
6
- - **`maturin`**: `1.15.0` (`requires = ["maturin>=1.15,<2.0"]`)
7
-
8
- ---
9
-
10
- ## 2.2 Version Range Changelog (`pyo3 0.24.0` - `0.30.1` & `maturin 1.8` - `1.15.0`)
11
-
12
- ### A. PyO3 0.25.0 - 0.26.0: Deprecation of `extension-module` Feature
13
- - **What changed**: The Cargo feature `pyo3/extension-module` was deprecated starting in `pyo3 0.26.0`.
14
- - **Upstream technical rationale**: Cargo features are additive across an entire workspace. Enabling `extension-module` in `Cargo.toml` caused `cargo test` to fail during linking because `libpython` symbols were intentionally omitted when that feature was active.
15
- - **New idiom**: Extension module linking is controlled directly by `maturin >= 1.8` (or the environment variable `PYO3_BUILD_EXTENSION_MODULE=1`), rather than static declarations in `Cargo.toml`.
16
-
17
- ### B. PyO3 0.26.0 - 0.28.0: Removal of `Python::allow_threads` $\rightarrow$ `Python::detach`
18
- - **What changed**: `py.allow_threads(|| ...)` was deprecated in `pyo3 0.26.0` and **completely removed** in `pyo3 0.28.0`.
19
- - **Upstream technical rationale**: On free-threaded Python runtimes (PEP 703 / PEP 803 without the GIL), Python threads already execute in parallel; releasing the context actually detaches the OS thread from the CPython runtime state.
20
- - **New idiom**: Use `py.detach(|| ...)`.
21
-
22
- ### C. PyO3 0.27.0 - 0.30.1: Removal of `Bound::downcast` $\rightarrow$ `Bound::cast::<T>()`
23
- - **What changed**: `Bound<'py, PyAny>::downcast::<T>()` and `downcast_into::<T>()` were deprecated and removed in `pyo3 0.30.1`, replaced by `Bound<'py, PyAny>::cast::<T>()` and `cast_into::<T>()`.
24
- - **Upstream technical rationale**: Unified smart-reference type conversion naming across the `Bound<'py, T>` API.
25
-
26
- ### D. PyO3 0.29.0 - 0.30.1 & Python 3.15: Stable ABI `abi3t` (PEP 803) and `#[pyclass(frozen, subclass)]`
27
- - **What changed**:
28
- 1. Added the `abi3t` feature alongside `abi3-py310` to support the cross-version Stable ABI across Python 3.10–3.15 on both standard GIL builds and free-threaded builds (`python3.14t` / `python3.15t`).
29
- 2. Rust classes exposed to Python via `#[pyclass]` no longer permit Python-side subclassing by default unless the `subclass` attribute is explicitly specified, and `frozen` is recommended for lock-free concurrency without `PyCell` borrow-checking overhead in free-threaded mode.
30
- 3. Inside declarative `#[pymodule] mod ...` blocks, external sub-components are registered using functions annotated with `#[pymodule_init]`.
31
-
32
- ---
33
-
34
- ## 2.3 Adaptations in LuminaMoon Source Code
35
-
36
- 1. **`Cargo.toml` & `data/Cargo.toml`**:
37
- - Configured `pyo3 = { version = "0.30.1", features = ["abi3-py310", "abi3t"] }` in `[workspace.dependencies]` without `"extension-module"`, allowing `cargo test -p luminamoon-dataloader` to link and run standalone.
38
- 2. **Runtime Detachment via `py.detach` (`data/src/py_bridge.rs`)**:
39
- - All disk I/O, `mmap` reads, HTTP 206 network streaming, Parquet schema inference, and HuggingFace Hub NDJSON uploads execute inside `py.detach(|| ...)`:
40
- ```rust
41
- let batch_opt = py
42
- .detach(|| self.inner.next_batch())
43
- .map_err(to_py_err)?;
44
- ```
45
- 3. **Type Casting via `Bound::cast::<T>()` and `PyBool` vs `PyInt` Hierarchy (`data/src/py_bridge.rs`)**:
46
- - Uses `columns.cast::<PyDict>()`, `v.cast::<PySequence>()`, and `pair.cast::<PyTuple>()` inside `extract_py_columns_table`.
47
- - Because `bool` is a subclass of `int` in CPython, `py_any_to_multimodal_value` always checks `item.is_instance_of::<PyBool>()` before `item.is_instance_of::<PyInt>()` so boolean columns are never coerced into `1`/`0` integers.
48
- 4. **`#[pyclass(frozen, subclass)]` Declarations and `#[pymodule_init]` Registration (`data/src/py_bridge.rs` & `optimizer/src/lib.rs`)**:
49
- - `PyDataStreamLoader`, `PyMultimodalParquetLoader`, and `PyHfHubUploader` are declared with `#[pyclass(frozen, subclass, ...)]` and synchronized on the Rust side using `parking_lot::Mutex`.
50
- - `register_dataloader_classes(m)` is invoked from `#[pymodule_init] fn init(m: &Bound<'_, PyModule>)` inside `optimizer/src/lib.rs`, registering all classes and module-level functions (`serialize_table_to_bytes`, `postprocess_and_dedup_records`, `suggest_closest_matches`).
51
- 5. **Automatic Row-to-Column Transposition & GIL-Free Table/Postprocess Functions (`data/src/py_bridge.rs`)**:
52
- - `extract_py_columns_table` supports three Python input layouts simultaneously: `dict[str, Sequence[Any]]`, `Sequence[tuple[str, Sequence[Any]]]`, and `Sequence[dict[str, Any]]` (transposing row dictionaries into ordered columns by first-seen key order and filling missing keys with `MultimodalValue::Null`).
53
- - `serialize_table_to_bytes` and `postprocess_and_dedup_records` (with `deduplicate: bool = true`) execute Parquet/Arrow IPC/CSV/JSONL/JSON serialization and Rayon + SIMD normalization + `xxh3_64` deduplication completely outside the GIL via `py.detach(|| ...)`.
 
1
+ # 2. Python, PyO3 (`abi3t`), and Maturin
2
+
3
+ ## 2.1 Latest Stable Versions
4
+ - **Python**: `3.15.0` (supports both standard CPython and free-threaded `Py_GIL_DISABLED` builds)
5
+ - **`pyo3`**: `0.30.1` (`features = ["abi3-py310", "abi3t"]`)
6
+ - **`maturin`**: `1.15.0` (`requires = ["maturin>=1.15,<2.0"]`)
7
+
8
+ ---
9
+
10
+ ## 2.2 Version Range Changelog (`pyo3 0.24.0` - `0.30.1` & `maturin 1.8` - `1.15.0`)
11
+
12
+ ### A. PyO3 0.25.0 - 0.26.0: Deprecation of `extension-module` Feature
13
+ - **What changed**: The Cargo feature `pyo3/extension-module` was deprecated starting in `pyo3 0.26.0`.
14
+ - **Upstream technical rationale**: Cargo features are additive across an entire workspace. Enabling `extension-module` in `Cargo.toml` caused `cargo test` to fail during linking because `libpython` symbols were intentionally omitted when that feature was active.
15
+ - **New idiom**: Extension module linking is controlled directly by `maturin >= 1.8` (or the environment variable `PYO3_BUILD_EXTENSION_MODULE=1`), rather than static declarations in `Cargo.toml`.
16
+
17
+ ### B. PyO3 0.26.0 - 0.28.0: Removal of `Python::allow_threads` $\rightarrow$ `Python::detach`
18
+ - **What changed**: `py.allow_threads(|| ...)` was deprecated in `pyo3 0.26.0` and **completely removed** in `pyo3 0.28.0`.
19
+ - **Upstream technical rationale**: On free-threaded Python runtimes (PEP 703 / PEP 803 without the GIL), Python threads already execute in parallel; releasing the context actually detaches the OS thread from the CPython runtime state.
20
+ - **New idiom**: Use `py.detach(|| ...)`.
21
+
22
+ ### C. PyO3 0.27.0 - 0.30.1: Removal of `Bound::downcast` $\rightarrow$ `Bound::cast::<T>()`
23
+ - **What changed**: `Bound<'py, PyAny>::downcast::<T>()` and `downcast_into::<T>()` were deprecated and removed in `pyo3 0.30.1`, replaced by `Bound<'py, PyAny>::cast::<T>()` and `cast_into::<T>()`.
24
+ - **Upstream technical rationale**: Unified smart-reference type conversion naming across the `Bound<'py, T>` API.
25
+
26
+ ### D. PyO3 0.29.0 - 0.30.1 & Python 3.15: Stable ABI `abi3t` (PEP 803) and `#[pyclass(frozen, subclass)]`
27
+ - **What changed**:
28
+ 1. Added the `abi3t` feature alongside `abi3-py310` to support the cross-version Stable ABI across Python 3.10–3.15 on both standard GIL builds and free-threaded builds (`python3.14t` / `python3.15t`).
29
+ 2. Rust classes exposed to Python via `#[pyclass]` no longer permit Python-side subclassing by default unless the `subclass` attribute is explicitly specified, and `frozen` is recommended for lock-free concurrency without `PyCell` borrow-checking overhead in free-threaded mode.
30
+ 3. Inside declarative `#[pymodule] mod ...` blocks, external sub-components are registered using functions annotated with `#[pymodule_init]`.
31
+
32
+ ---
33
+
34
+ ## 2.3 Adaptations in LuminaMoon Source Code
35
+
36
+ 1. **`Cargo.toml` & `data/Cargo.toml`**:
37
+ - Configured `pyo3 = { version = "0.30.1", features = ["abi3-py310", "abi3t"] }` in `[workspace.dependencies]` without `"extension-module"`, allowing `cargo test -p luminamoon-dataloader` to link and run standalone.
38
+ 2. **Runtime Detachment via `py.detach` (`data/src/py_bridge.rs`)**:
39
+ - All disk I/O, `mmap` reads, HTTP 206 network streaming, Parquet schema inference, and HuggingFace Hub NDJSON uploads execute inside `py.detach(|| ...)`:
40
+ ```rust
41
+ let batch_opt = py
42
+ .detach(|| self.inner.next_batch())
43
+ .map_err(to_py_err)?;
44
+ ```
45
+ 3. **Type Casting via `Bound::cast::<T>()` and `PyBool` vs `PyInt` Hierarchy (`data/src/py_bridge.rs`)**:
46
+ - Uses `columns.cast::<PyDict>()`, `v.cast::<PySequence>()`, and `pair.cast::<PyTuple>()` inside `extract_py_columns_table`.
47
+ - Because `bool` is a subclass of `int` in CPython, `py_any_to_multimodal_value` always checks `item.is_instance_of::<PyBool>()` before `item.is_instance_of::<PyInt>()` so boolean columns are never coerced into `1`/`0` integers.
48
+ 4. **`#[pyclass(frozen, subclass)]` Declarations and `#[pymodule_init]` Registration (`data/src/py_bridge.rs` & `optimizer/src/lib.rs`)**:
49
+ - `PyDataStreamLoader`, `PyMultimodalParquetLoader`, and `PyHfHubUploader` are declared with `#[pyclass(frozen, subclass, ...)]` and synchronized on the Rust side using `parking_lot::Mutex`.
50
+ - `register_dataloader_classes(m)` is invoked from `#[pymodule_init] fn init(m: &Bound<'_, PyModule>)` inside `optimizer/src/lib.rs`, registering all classes and module-level functions (`serialize_table_to_bytes`, `postprocess_and_dedup_records`, `suggest_closest_matches`).
51
+ 5. **Automatic Row-to-Column Transposition & GIL-Free Table/Postprocess Functions (`data/src/py_bridge.rs`)**:
52
+ - `extract_py_columns_table` supports three Python input layouts simultaneously: `dict[str, Sequence[Any]]`, `Sequence[tuple[str, Sequence[Any]]]`, and `Sequence[dict[str, Any]]` (transposing row dictionaries into ordered columns by first-seen key order and filling missing keys with `MultimodalValue::Null`).
53
+ - `serialize_table_to_bytes` and `postprocess_and_dedup_records` (with `deduplicate: bool = true`) execute Parquet/Arrow IPC/CSV/JSONL/JSON serialization and Rayon + SIMD normalization + `xxh3_64` deduplication completely outside the GIL via `py.detach(|| ...)`.
data/docs/library_audit/03_parquet_arrow_and_http206_streaming.md CHANGED
@@ -1,48 +1,48 @@
1
- # 3. Apache Arrow/Parquet, `reqwest`, `tokio`, `bytes`, and `futures`
2
-
3
- ## 3.1 Latest Stable Versions
4
- - **`parquet`**: `60.0.0` (`features = ["async"]`)
5
- - **`arrow-array`**: `60.0.0`
6
- - **`arrow-schema`**: `60.0.0`
7
- - **`reqwest`**: `0.13.5` (`default-features = false`, `features = ["default-tls", "http2", "system-proxy", "json", "stream", "gzip"]`)
8
- - **`tokio`**: `1.53.3` (`features = ["rt-multi-thread", "fs", "io-util", "sync", "time", "macros"]`)
9
- - **`bytes`**: `1.12.1`
10
- - **`futures`**: `0.3.34`
11
-
12
- ---
13
-
14
- ## 3.2 Version Range Changelog (`arrow-rs 53.0.0` - `60.0.0`, `reqwest 0.12` - `0.13.5`, `bytes 1.6` - `1.12.1`)
15
-
16
- ### A. Apache Arrow & Parquet (`53.0.0` - `60.0.0`)
17
- - **`Utf8View` (`StringViewArray`) and `BinaryView` (`BinaryViewArray`) Type Promotion**:
18
- Across `arrow 54.0`–`60.0.0`, modern Parquet readers can map string and binary columns into *ByteView* representations (12-byte inline prefix + offset buffer) alongside `StringArray`/`LargeStringArray` and `BinaryArray`/`LargeBinaryArray`.
19
- - **Separation of `ArrowReaderMetadata` for Repeated Reads**:
20
- `ParquetRecordBatchReaderBuilder::try_new(file)` re-reads and re-parses the Parquet footer metadata on every invocation. The `ArrowReaderMetadata::load(&file, options)` API paired with `ParquetRecordBatchReaderBuilder::new_with_metadata(file, metadata)` allows sharing cached metadata in memory in $O(1)$ time.
21
- - **`ParquetMetaDataReader::load_and_finish` Signature Change (`parquet 58.0` - `60.0.0`)**:
22
- The async method `ParquetMetaDataReader::load_and_finish` now takes a generic parameter `F: MetadataSuffixFetch` (blanket-implemented for `&mut T` where `T: AsyncFileReader`). Calling `.load_and_finish(self, file_size)` directly inside `fn get_metadata(&'a mut self, ...)` fails trait-bound resolution because `self` has type `&'a mut Self`, requiring an explicit reborrow `&mut *self`.
23
-
24
- ### B. `reqwest` (`0.12.0` - `0.13.5`) & `tokio` (`1.38.0` - `1.53.3`)
25
- - **Default TLS Backend Change in `reqwest 0.13.x`**:
26
- Starting in `reqwest 0.13.0`, default features (`default-features = true`) switched from `native-tls` to `rustls` backed by `aws-lc-sys`. On Windows, compiling `aws-lc-sys` requires external CMake and NASM installations, causing build failures when absent.
27
- - **Modularization of `json`, `stream`, and `gzip` Features**:
28
- `.json(...)`, `.bytes_stream()`, and automatic response decompression in `reqwest 0.13.5` are gated behind explicit feature flags.
29
-
30
- ### C. `bytes` (`1.6.0` - `1.12.1`): Mitigation of `RUSTSEC-2026-0007` / `CVE-2026-25541`
31
- - **What changed**: In `bytes < 1.11.1`, an integer overflow vulnerability existed in capacity calculations inside `BytesMut::reserve` when reservation sizes approached `usize::MAX`, potentially allocating a smaller buffer than requested (*heap buffer overflow*).
32
- - **Upstream fix (`1.11.1` - `1.12.1`)**: Added checked arithmetic across all `BytesMut` capacity expansion paths and zero-copy `Bytes::slice` operations.
33
-
34
- ---
35
-
36
- ## 3.3 Adaptations in LuminaMoon Source Code
37
-
38
- 1. **`StringViewArray` and `BinaryViewArray` Support (`data/src/source_local.rs` & `data/src/multimodal.rs`)**:
39
- - `extract_strings_from_batch` and `extract_array_values` handle all three Arrow string/binary type families (`StringArray` / `LargeStringArray` / `StringViewArray` and `BinaryArray` / `LargeBinaryArray` / `BinaryViewArray`).
40
- 2. **`ArrowReaderMetadata` Caching and `RowSelection` (`data/src/source_local.rs` & `data/src/multimodal.rs`)**:
41
- - `LocalParquetSource` and `LocalMultimodalParquetSource` load `ArrowReaderMetadata` once during `open()` and combine it with `ProjectionMask::roots(...)` and `RowSelection::from(vec![RowSelector::skip(...), RowSelector::select(...)])`.
42
- 3. **Explicit `&mut *self` Reborrow on `HttpParquetReader` (`data/src/source_hf.rs`)**:
43
- - `AsyncFileReader::get_metadata` for `HttpParquetReader` calls `ParquetMetaDataReader::new().with_arrow_reader_options(options).load_and_finish(&mut *self, file_size).await?` and caches the result in `cached_metadata: Option<Arc<ParquetMetaData>>`.
44
- 4. **`reqwest 0.13.5` and `bytes 1.12.1` Configuration (`Cargo.toml`)**:
45
- - Configured `reqwest = { version = "0.13.5", default-features = false, features = ["default-tls", "http2", "system-proxy", "json", "stream", "gzip"] }` and `bytes = "1.12.1"` so cross-platform builds remain free of external `aws-lc-sys` CMake/NASM dependencies while mitigating `CVE-2026-25541`.
46
- 5. **Concurrent Multi-Range Downloads (`fetch_ranges_concurrent`) & Multi-Thread Tokio Pool (`data/src/source_hf.rs` & `data/src/preram.rs`)**:
47
- - `HttpRangeClient::fetch_ranges_concurrent` uses `futures::stream::iter(...).buffered(concurrency)` to download multiple HTTP 206 byte ranges in parallel across workers while preserving range ordering.
48
- - `HttpParquetReader::get_byte_ranges` forwards Arrow/Parquet column range lists to `fetch_ranges_concurrent`, while `RemoteLineSource` and `PreRamDataLoader` run a multi-threaded Tokio runtime (`Builder::new_multi_thread().worker_threads(download_workers)`) for parallel multi-chunk prefetching.
 
1
+ # 3. Apache Arrow/Parquet, `reqwest`, `tokio`, `bytes`, and `futures`
2
+
3
+ ## 3.1 Latest Stable Versions
4
+ - **`parquet`**: `60.0.0` (`features = ["async"]`)
5
+ - **`arrow-array`**: `60.0.0`
6
+ - **`arrow-schema`**: `60.0.0`
7
+ - **`reqwest`**: `0.13.5` (`default-features = false`, `features = ["default-tls", "http2", "system-proxy", "json", "stream", "gzip"]`)
8
+ - **`tokio`**: `1.53.3` (`features = ["rt-multi-thread", "fs", "io-util", "sync", "time", "macros"]`)
9
+ - **`bytes`**: `1.12.1`
10
+ - **`futures`**: `0.3.34`
11
+
12
+ ---
13
+
14
+ ## 3.2 Version Range Changelog (`arrow-rs 53.0.0` - `60.0.0`, `reqwest 0.12` - `0.13.5`, `bytes 1.6` - `1.12.1`)
15
+
16
+ ### A. Apache Arrow & Parquet (`53.0.0` - `60.0.0`)
17
+ - **`Utf8View` (`StringViewArray`) and `BinaryView` (`BinaryViewArray`) Type Promotion**:
18
+ Across `arrow 54.0`–`60.0.0`, modern Parquet readers can map string and binary columns into *ByteView* representations (12-byte inline prefix + offset buffer) alongside `StringArray`/`LargeStringArray` and `BinaryArray`/`LargeBinaryArray`.
19
+ - **Separation of `ArrowReaderMetadata` for Repeated Reads**:
20
+ `ParquetRecordBatchReaderBuilder::try_new(file)` re-reads and re-parses the Parquet footer metadata on every invocation. The `ArrowReaderMetadata::load(&file, options)` API paired with `ParquetRecordBatchReaderBuilder::new_with_metadata(file, metadata)` allows sharing cached metadata in memory in $O(1)$ time.
21
+ - **`ParquetMetaDataReader::load_and_finish` Signature Change (`parquet 58.0` - `60.0.0`)**:
22
+ The async method `ParquetMetaDataReader::load_and_finish` now takes a generic parameter `F: MetadataSuffixFetch` (blanket-implemented for `&mut T` where `T: AsyncFileReader`). Calling `.load_and_finish(self, file_size)` directly inside `fn get_metadata(&'a mut self, ...)` fails trait-bound resolution because `self` has type `&'a mut Self`, requiring an explicit reborrow `&mut *self`.
23
+
24
+ ### B. `reqwest` (`0.12.0` - `0.13.5`) & `tokio` (`1.38.0` - `1.53.3`)
25
+ - **Default TLS Backend Change in `reqwest 0.13.x`**:
26
+ Starting in `reqwest 0.13.0`, default features (`default-features = true`) switched from `native-tls` to `rustls` backed by `aws-lc-sys`. On Windows, compiling `aws-lc-sys` requires external CMake and NASM installations, causing build failures when absent.
27
+ - **Modularization of `json`, `stream`, and `gzip` Features**:
28
+ `.json(...)`, `.bytes_stream()`, and automatic response decompression in `reqwest 0.13.5` are gated behind explicit feature flags.
29
+
30
+ ### C. `bytes` (`1.6.0` - `1.12.1`): Mitigation of `RUSTSEC-2026-0007` / `CVE-2026-25541`
31
+ - **What changed**: In `bytes < 1.11.1`, an integer overflow vulnerability existed in capacity calculations inside `BytesMut::reserve` when reservation sizes approached `usize::MAX`, potentially allocating a smaller buffer than requested (*heap buffer overflow*).
32
+ - **Upstream fix (`1.11.1` - `1.12.1`)**: Added checked arithmetic across all `BytesMut` capacity expansion paths and zero-copy `Bytes::slice` operations.
33
+
34
+ ---
35
+
36
+ ## 3.3 Adaptations in LuminaMoon Source Code
37
+
38
+ 1. **`StringViewArray` and `BinaryViewArray` Support (`data/src/source_local.rs` & `data/src/multimodal.rs`)**:
39
+ - `extract_strings_from_batch` and `extract_array_values` handle all three Arrow string/binary type families (`StringArray` / `LargeStringArray` / `StringViewArray` and `BinaryArray` / `LargeBinaryArray` / `BinaryViewArray`).
40
+ 2. **`ArrowReaderMetadata` Caching and `RowSelection` (`data/src/source_local.rs` & `data/src/multimodal.rs`)**:
41
+ - `LocalParquetSource` and `LocalMultimodalParquetSource` load `ArrowReaderMetadata` once during `open()` and combine it with `ProjectionMask::roots(...)` and `RowSelection::from(vec![RowSelector::skip(...), RowSelector::select(...)])`.
42
+ 3. **Explicit `&mut *self` Reborrow on `HttpParquetReader` (`data/src/source_hf.rs`)**:
43
+ - `AsyncFileReader::get_metadata` for `HttpParquetReader` calls `ParquetMetaDataReader::new().with_arrow_reader_options(options).load_and_finish(&mut *self, file_size).await?` and caches the result in `cached_metadata: Option<Arc<ParquetMetaData>>`.
44
+ 4. **`reqwest 0.13.5` and `bytes 1.12.1` Configuration (`Cargo.toml`)**:
45
+ - Configured `reqwest = { version = "0.13.5", default-features = false, features = ["default-tls", "http2", "system-proxy", "json", "stream", "gzip"] }` and `bytes = "1.12.1"` so cross-platform builds remain free of external `aws-lc-sys` CMake/NASM dependencies while mitigating `CVE-2026-25541`.
46
+ 5. **Concurrent Multi-Range Downloads (`fetch_ranges_concurrent`) & Multi-Thread Tokio Pool (`data/src/source_hf.rs` & `data/src/preram.rs`)**:
47
+ - `HttpRangeClient::fetch_ranges_concurrent` uses `futures::stream::iter(...).buffered(concurrency)` to download multiple HTTP 206 byte ranges in parallel across workers while preserving range ordering.
48
+ - `HttpParquetReader::get_byte_ranges` forwards Arrow/Parquet column range lists to `fetch_ranges_concurrent`, while `RemoteLineSource` and `PreRamDataLoader` run a multi-threaded Tokio runtime (`Builder::new_multi_thread().worker_threads(download_workers)`) for parallel multi-chunk prefetching.
data/docs/library_audit/04_xxh3_dedup_crc32_and_hf_hub_security.md CHANGED
@@ -1,49 +1,49 @@
1
- # 4. `xxhash-rust`, `hashbrown`, `crc32fast`, `parking_lot`, and HuggingFace Hub API Specifications
2
-
3
- ## 4.1 Latest Stable Versions
4
- - **`xxhash-rust`**: `0.8.19` (`features = ["xxh3"]`)
5
- - **`hashbrown`**: `0.17.1`
6
- - **`crc32fast`**: `1.5.2`
7
- - **`parking_lot`**: `0.12.5`
8
- - **External HuggingFace Hub Protocol**: `huggingface_hub >= 0.28 / 1.x` (HTTP Commit NDJSON API & Dataset Viewer Parquet Spec)
9
-
10
- ---
11
-
12
- ## 4.2 Version Range Changelog (`2024` - `2026`)
13
-
14
- ### A. `hashbrown` (`0.14.0` - `0.17.1`)
15
- - **What changed**: Starting in `hashbrown 0.15.0` through `0.17.1`, the internal default hasher for `HashMap` and `HashSet` switched from `ahash` to `foldhash`.
16
- - **SwissTable technical implications**: `hashbrown` uses the SwissTable architecture, splitting a 64-bit hash into two parts: the lower 57 bits (`h1`) for bucket slot selection and the upper 7 bits (`h2`, bits 57..63) as the control byte for SIMD group probing. When table keys are already uniformly distributed 64-bit hashes (`u64`), running `foldhash` on top of them performs redundant computation.
17
-
18
- ### B. `xxhash-rust` (`0.8.10` - `0.8.19`)
19
- - **What changed**: Enhanced `const fn` compilation optimizations and auto-vectorization in `xxh3_64(input: &[u8]) -> u64`.
20
- - **Bit distribution properties**: `xxh3_64` provides full avalanche across all 64 bits (including the upper 7 `h2` bits), making it safe to pair directly with SwissTable via an identity hasher without re-hashing.
21
-
22
- ### C. `crc32fast` (`1.4.0` - `1.5.2`)
23
- - **What changed**: The `1.5.x` series enables `vpclmulqdq` hardware instructions (Vector Carry-Less Multiplication on AVX-512) alongside classic `pclmulqdq` to accelerate IEEE CRC32 checksum calculation.
24
-
25
- ### D. `parking_lot` (`0.12.1` - `0.12.5`)
26
- - **What changed**: Maintenance updates for Rust Edition 2024 lint compatibility across `Mutex<T>` and `Condvar` synchronization primitives with zero OS mutex allocation on the uncontended fast path.
27
-
28
- ### E. HuggingFace Hub Credential & External Endpoint Conventions (`huggingface_hub 0.25` - `1.x`)
29
- - **Standard environment variables**: The official `huggingface_hub` library standardized token resolution priority on `HF_TOKEN` (superseding legacy `HUGGING_FACE_HUB_TOKEN` as primary), local token files at `$HF_HOME/token` or `~/.cache/huggingface/token`, and `HF_ENDPOINT` (defaulting to `https://huggingface.co`).
30
- - **`application/x-ndjson` Commit Protocol**: The `POST /api/datasets/{owner}/{repo}/commit/{revision}` endpoint accepts Newline-Delimited JSON streams beginning with `{"key": "header", "value": {"summary": ...}}` followed by `{"key": "file", "value": {"content": "<base64>", "path": ..., "encoding": "base64"}}`.
31
- - **Dataset Viewer Image Schema Convention**: For image columns in Parquet files to render automatically in the HuggingFace Dataset Viewer without manual configuration, image columns must be stored as an Arrow `Struct` with two fields: `bytes: Binary` and `path: Utf8`.
32
-
33
- ---
34
-
35
- ## 4.3 Adaptations in LuminaMoon Source Code
36
-
37
- 1. **Bypassing `foldhash` with `IdentityBuildHasher` + `xxh3_64` (`data/src/dedup.rs`)**:
38
- - Defines `IdentityHasher` (`write_u64` stores the `xxh3_64` hash directly) for `HashSet<u64, IdentityBuildHasher>` and `HashMap<u64, u32, IdentityBuildHasher>`, eliminating `foldhash` double-hashing overhead in `hashbrown 0.17.1`.
39
- 2. **Checkpoint Slot Validation via `crc32fast::Hasher` (`data/src/metadata.rs`)**:
40
- - Uses `crc32fast` to verify the integrity of both 64-byte Ping-Pong checkpoint slots (`Slot A` and `Slot B`).
41
- 3. **Fine-Grained `parking_lot::Mutex` Separation (`data/src/preram.rs`)**:
42
- - Separates `Mutex<QueueState>` and `Condvar` from `Mutex<DualMetadataManager>` so background Pre-RAM refill workers and `commit_gpu_batch()` callers never block in-memory batch pops.
43
- 4. **HuggingFace Hub Protocol Compliance (`data/src/hf_hub.rs` & `data/src/multimodal_serialize.rs`)**:
44
- - `HfCredentials` resolves `HF_TOKEN`, `HUGGING_FACE_HUB_TOKEN`, local token files, `HF_ORG`, `HF_USERNAME`, and `HF_ENDPOINT`, redacts token values as `"[REDACTED]"` in `fmt::Debug`, and blocks token transmission over non-loopback `http://` connections.
45
- - `serialize_columns_to_parquet_bytes_with_options` encodes `MultimodalValue::Bytes` columns into `{bytes: Binary, path: Utf8}` `StructArray` layouts directly compatible with the HuggingFace Dataset Viewer.
46
- 5. **Colab/Kaggle Credential Auto-Resolver, RFC 5988 `Link` Pagination, and Hybrid LFS/Xet Upload (`python/luminamoon/data/hub.py`)**:
47
- - `resolve_hf_secrets` automatically resolves `HF_TOKEN`, `HF_ORG`, and `HF_USERNAME` from environment variables, `google.colab.userdata`, or `kaggle_secrets.UserSecretsClient`.
48
- - `expand_hf_sources` supports automatic shard discovery from repository roots `hf://datasets/org/repo[@revision]` via `huggingface_hub.HfApi.list_repo_files` or the REST `/api/datasets/.../tree/...?recursive=true` endpoint with RFC 5988 `Link: <...>; rel="next"` header pagination and per-page HTTPS validation.
49
- - `push_to_hub` combines GIL-free Rust serialization (`serialize_table_to_bytes`) with `huggingface_hub.HfApi.upload_file` (supporting Git LFS / Xet multipart uploads for large shards) and automatic fallback to the native Rust `HfHubUploader` (`application/x-ndjson`).
 
1
+ # 4. `xxhash-rust`, `hashbrown`, `crc32fast`, `parking_lot`, and HuggingFace Hub API Specifications
2
+
3
+ ## 4.1 Latest Stable Versions
4
+ - **`xxhash-rust`**: `0.8.19` (`features = ["xxh3"]`)
5
+ - **`hashbrown`**: `0.17.1`
6
+ - **`crc32fast`**: `1.5.2`
7
+ - **`parking_lot`**: `0.12.5`
8
+ - **External HuggingFace Hub Protocol**: `huggingface_hub >= 0.28 / 1.x` (HTTP Commit NDJSON API & Dataset Viewer Parquet Spec)
9
+
10
+ ---
11
+
12
+ ## 4.2 Version Range Changelog (`2024` - `2026`)
13
+
14
+ ### A. `hashbrown` (`0.14.0` - `0.17.1`)
15
+ - **What changed**: Starting in `hashbrown 0.15.0` through `0.17.1`, the internal default hasher for `HashMap` and `HashSet` switched from `ahash` to `foldhash`.
16
+ - **SwissTable technical implications**: `hashbrown` uses the SwissTable architecture, splitting a 64-bit hash into two parts: the lower 57 bits (`h1`) for bucket slot selection and the upper 7 bits (`h2`, bits 57..63) as the control byte for SIMD group probing. When table keys are already uniformly distributed 64-bit hashes (`u64`), running `foldhash` on top of them performs redundant computation.
17
+
18
+ ### B. `xxhash-rust` (`0.8.10` - `0.8.19`)
19
+ - **What changed**: Enhanced `const fn` compilation optimizations and auto-vectorization in `xxh3_64(input: &[u8]) -> u64`.
20
+ - **Bit distribution properties**: `xxh3_64` provides full avalanche across all 64 bits (including the upper 7 `h2` bits), making it safe to pair directly with SwissTable via an identity hasher without re-hashing.
21
+
22
+ ### C. `crc32fast` (`1.4.0` - `1.5.2`)
23
+ - **What changed**: The `1.5.x` series enables `vpclmulqdq` hardware instructions (Vector Carry-Less Multiplication on AVX-512) alongside classic `pclmulqdq` to accelerate IEEE CRC32 checksum calculation.
24
+
25
+ ### D. `parking_lot` (`0.12.1` - `0.12.5`)
26
+ - **What changed**: Maintenance updates for Rust Edition 2024 lint compatibility across `Mutex<T>` and `Condvar` synchronization primitives with zero OS mutex allocation on the uncontended fast path.
27
+
28
+ ### E. HuggingFace Hub Credential & External Endpoint Conventions (`huggingface_hub 0.25` - `1.x`)
29
+ - **Standard environment variables**: The official `huggingface_hub` library standardized token resolution priority on `HF_TOKEN` (superseding legacy `HUGGING_FACE_HUB_TOKEN` as primary), local token files at `$HF_HOME/token` or `~/.cache/huggingface/token`, and `HF_ENDPOINT` (defaulting to `https://huggingface.co`).
30
+ - **`application/x-ndjson` Commit Protocol**: The `POST /api/datasets/{owner}/{repo}/commit/{revision}` endpoint accepts Newline-Delimited JSON streams beginning with `{"key": "header", "value": {"summary": ...}}` followed by `{"key": "file", "value": {"content": "<base64>", "path": ..., "encoding": "base64"}}`.
31
+ - **Dataset Viewer Image Schema Convention**: For image columns in Parquet files to render automatically in the HuggingFace Dataset Viewer without manual configuration, image columns must be stored as an Arrow `Struct` with two fields: `bytes: Binary` and `path: Utf8`.
32
+
33
+ ---
34
+
35
+ ## 4.3 Adaptations in LuminaMoon Source Code
36
+
37
+ 1. **Bypassing `foldhash` with `IdentityBuildHasher` + `xxh3_64` (`data/src/dedup.rs`)**:
38
+ - Defines `IdentityHasher` (`write_u64` stores the `xxh3_64` hash directly) for `HashSet<u64, IdentityBuildHasher>` and `HashMap<u64, u32, IdentityBuildHasher>`, eliminating `foldhash` double-hashing overhead in `hashbrown 0.17.1`.
39
+ 2. **Checkpoint Slot Validation via `crc32fast::Hasher` (`data/src/metadata.rs`)**:
40
+ - Uses `crc32fast` to verify the integrity of both 64-byte Ping-Pong checkpoint slots (`Slot A` and `Slot B`).
41
+ 3. **Fine-Grained `parking_lot::Mutex` Separation (`data/src/preram.rs`)**:
42
+ - Separates `Mutex<QueueState>` and `Condvar` from `Mutex<DualMetadataManager>` so background Pre-RAM refill workers and `commit_gpu_batch()` callers never block in-memory batch pops.
43
+ 4. **HuggingFace Hub Protocol Compliance (`data/src/hf_hub.rs` & `data/src/multimodal_serialize.rs`)**:
44
+ - `HfCredentials` resolves `HF_TOKEN`, `HUGGING_FACE_HUB_TOKEN`, local token files, `HF_ORG`, `HF_USERNAME`, and `HF_ENDPOINT`, redacts token values as `"[REDACTED]"` in `fmt::Debug`, and blocks token transmission over non-loopback `http://` connections.
45
+ - `serialize_columns_to_parquet_bytes_with_options` encodes `MultimodalValue::Bytes` columns into `{bytes: Binary, path: Utf8}` `StructArray` layouts directly compatible with the HuggingFace Dataset Viewer.
46
+ 5. **Colab/Kaggle Credential Auto-Resolver, RFC 5988 `Link` Pagination, and Hybrid LFS/Xet Upload (`python/luminamoon/data/hub.py`)**:
47
+ - `resolve_hf_secrets` automatically resolves `HF_TOKEN`, `HF_ORG`, and `HF_USERNAME` from environment variables, `google.colab.userdata`, or `kaggle_secrets.UserSecretsClient`.
48
+ - `expand_hf_sources` supports automatic shard discovery from repository roots `hf://datasets/org/repo[@revision]` via `huggingface_hub.HfApi.list_repo_files` or the REST `/api/datasets/.../tree/...?recursive=true` endpoint with RFC 5988 `Link: <...>; rel="next"` header pagination and per-page HTTPS validation.
49
+ - `push_to_hub` combines GIL-free Rust serialization (`serialize_table_to_bytes`) with `huggingface_hub.HfApi.upload_file` (supporting Git LFS / Xet multipart uploads for large shards) and automatic fallback to the native Rust `HfHubUploader` (`application/x-ndjson`).
data/docs/library_audit/05_universal_formats_compression_and_diagnostics.md CHANGED
@@ -1,87 +1,87 @@
1
- # 5. Universal Formats (`arrow-ipc`, `half`), Stream Decompression (`flate2`, `zstd`, `lz4_flex`), and Typo Diagnostics
2
-
3
- ## 5.1 Latest Stable Versions (October 2026)
4
- - **`arrow-ipc`**: `60.0.0` (synchronized with `arrow-array = "60.0.0"`, `arrow-schema = "60.0.0"`, `parquet = "60.0.0"`)
5
- - **`half`**: `2.7.1` (`f16` / `bf16` IEEE 754-2008 half-precision floating point)
6
- - **`flate2`**: `1.1.10` (`features = ["rust_backend"]` / `miniz_oxide 0.8.9` + `crc32fast 1.5.2`)
7
- - **`zstd`**: `0.14.3` (`zstd-safe 8.2.0` + `zstd-sys 2.0.16+zstd.1.5.7`)
8
- - **`lz4_flex`**: `0.14.0` (`safe-decode` & `frame` enabled by default, `unsafe`-free decompression path)
9
-
10
- ---
11
-
12
- ## 5.2 Detailed Version-by-Version Changelog over a 1-Year Window (`2025` - `October 2026`)
13
-
14
- ### A. `arrow-ipc` (`53.0.0` - `60.0.0`)
15
- | Version | Date / Era | API, ABI, and Internal Behavior Changes |
16
- | :--- | :--- | :--- |
17
- | **`53.0.0` - `54.0.0`** | Q4 2024 - Q1 2025 | Added full IPC serialization and deserialization support (`FileReader`, `StreamReader`, `FileWriter`, `StreamWriter`) for variable-length view types `Utf8View` (`StringViewArray`) and `BinaryView` (`BinaryViewArray`) with dedicated variadic buffers. |
18
- | **`55.0.0` - `56.0.0`** | Q2 2025 | Strict 8-byte / 64-byte buffer alignment validation on IPC `RecordBatch` messages to prevent unaligned pointer reads when memory-mapping `.arrow` / `.feather` files (`mmap` or `Cursor<&[u8]>`). |
19
- | **`57.0.0`** | Q3 2025 | Improved IPC body compression (`LZ4_FRAME` and `ZSTD`) and variadic buffer bounds checking on `BinaryView` to prevent out-of-bounds slice panics on corrupted IPC streams. |
20
- | **`58.0.0`** | Q4 2025 | Deprecated `StreamReader::try_new_unbuffered`; unified internal buffering in `StreamReader::try_new` and raised MSRV to match modern Rust toolchains. |
21
- | **`59.0.0` - `60.0.0`** | 2026 | Removed legacy methods deprecated in `57.x`/`58.x`, aligned `Field` and `Schema` structures with `arrow-schema 60.0.0`, and enabled zero-copy slicing across `ListArray`, `LargeListArray`, `FixedSizeListArray`, and `Float16Array`. |
22
-
23
- ### B. `lz4_flex` (`0.11.0` - `0.14.0`) & Mitigation of `CVE-2026-32829` (`RUSTSEC-2026-0041`)
24
- | Version | Release Date | API Changes, Security Features, and CVE Mitigations |
25
- | :--- | :--- | :--- |
26
- | **`0.11.0` - `0.11.5`** | 2024 - 2025 | Inverted the feature flag from `checked-decode` to `unchecked-decode`. Previously, additive Cargo feature unification meant a transitive dependency disabling `checked-decode` could silently enable `unsafe` pointer decoding. Inverting the flag to `unchecked-decode` (off by default) with `safe-decode` (on by default) maintains `unsafe`-free decompression unless explicitly opted in. |
27
- | **`0.11.6` & `0.12.0` - `0.12.1`** | **2026-05-09** | **Critical Security Mitigation `CVE-2026-32829` / `RUSTSEC-2026-0041` (CVSS 7.5 High)**: In `lz4_flex < 0.11.6` and `0.12.0`, when using APIs that write into uninitialized output buffers (`decompress_into` / `Sink`) on malformed LZ4 inputs containing an invalid match offset beyond already-written bytes, the decompressor could copy uninitialized memory bytes into the output buffer before returning an error (*information leak*). Fixed in `0.11.6` and `0.12.1` by validating match offset bounds against the actual output cursor prior to copying. |
28
- | **`0.13.0`** | **2026-05-09** | Added external dictionary compression/decompression support (`compress_prepend_size_with_dict`, `decompress_size_prepended_with_dict`) for small LZ4 blocks. |
29
- | **`0.13.1`** | **2026-05-09** | Fixed an out-of-bounds panic when an external dictionary smaller than 4 bytes (`dict.len() < 4`) was passed to dictionary compression functions. |
30
- | **`0.14.0`** | **2026-07-14** | Explicitly separated the `alloc` feature flag from `no_std` paths, refined `frame::FrameDecoder` (`std::io::Read`) for framed `.lz4` streams (*LZ4 Frame Format*), and optimized `safe-decode` throughput. |
31
-
32
- ### C. `flate2` (`1.0.30` - `1.1.10`)
33
- | Version | Release Date | Backend, Performance, and Feature Flag Changes |
34
- | :--- | :--- | :--- |
35
- | **`1.0.30` - `1.0.35`** | 2024 - Early 2025 | Upgraded `miniz_oxide` to the `0.8.x` series and introduced optional `zlib-rs` backend support (pure-Rust SIMD-accelerated Zlib). |
36
- | **`1.1.0` - `1.1.4`** | Mid 2025 | Added `.reset()` and internal buffer reuse across decoders (`GzDecoder`, `ZlibDecoder`, `DeflateDecoder`), eliminating repeated heap allocations when decompressing thousands of small `.gz` shards. |
37
- | **`1.1.5` - `1.1.8`** | Late 2025 - Early 2026 | Optimized `MultiGzDecoder` for concatenated `.jsonl.gz` streams and improved handling of trailing bytes after Gzip CRC32 footers. |
38
- | **`1.1.9`** | Q2 2026 | Refactored internal `zlib-rs` and `crc32fast` feature flags, which temporarily disabled runtime CPU SIMD detection (`std`) when consumers customized default features. |
39
- | **`1.1.10`** | **Q3 2026** | **Throughput Regression Fix (`runtime_detection`)**: Added the `runtime_detection` feature flag (`zlib-rs?/std`, `crc32fast?/std`) directly into `default` features, ensuring `crc32fast` (`vpclmulqdq` / `pclmulqdq` / `asimd`) and compression backends automatically detect CPU SIMD capabilities at runtime. |
40
-
41
- ### D. `zstd` (`0.13.0` - `0.14.3`) & `zstd-safe` (`7.2.0` - `8.2.0`)
42
- | Version | Release Date | Memory Safety (`Soundness`) and C `libzstd` Engine Changes |
43
- | :--- | :--- | :--- |
44
- | **`0.13.0` - `0.13.3`** | 2024 - 2025 | Upgraded `zstd-sys` bindings to `libzstd 1.5.6` and `1.5.7` (faster large-frame decompression and rare long-distance matcher bug fixes). |
45
- | **`0.14.0`** | **2026** | **`zstd-safe 8.0.0+` Soundness Fix (`CCtx` / `DCtx`)**: Resolved a memory-safety soundness bug where streaming wrappers allowed direct `&mut CCtx` / `&mut DCtx` borrows (`DerefMut`), permitting callers to `std::mem::swap` contexts while holding raw pointers to short-lived dictionary buffers (*lifetime violation*). In `0.14.x`, direct mutable context access is restricted in favor of safe delegation methods on `Encoder` and `Decoder`. |
46
- | **`0.14.1` - `0.14.3`** | **2026** | Updated dependencies to `zstd-safe ^8.2.0` (`zstd 1.5.7`), refined `zstd::stream::read::Decoder` and `zstd::stream::encode_all` for multi-frame `.zst` streams (including HuggingFace Xet/Parquet skippable metadata frames), and eliminated compiler warnings on Rust `1.99.1`. |
47
-
48
- ### E. `half` (`2.4.0` - `2.7.1`)
49
- | Version | Release Date | `f16` / `bf16` Conversion and Hardware Intrinsic Changes |
50
- | :--- | :--- | :--- |
51
- | **`2.4.0` - `2.5.0`** | 2024 - 2025 | Stabilized batch slice conversions (`f16::to_f32_slice`, `bf16::to_f32_slice`) and full `bytemuck` and `serde` integration. |
52
- | **`2.6.0`** | 2025 | Raised MSRV to Rust `1.81+`, utilizing `f16c` / `avx512fp16` intrinsics on `x86_64` and `fp16` on `aarch64` on stable compilers without nightly feature flags. |
53
- | **`2.7.0` - `2.7.1`** | **2026** | Full alignment with `arrow-array 60.0.0` (`Float16Array` exports `half::f16` elements), branchless `to_f64()` optimization for subnormal and `NaN` values, and full compliance with Rust Edition 2024 `unsafe_op_in_unsafe_fn` rules. |
54
-
55
- ### F. Self-Contained WebDataset `.tar` Parser & Typo Diagnostics (Self-Contained Implementation)
56
- - **Why External `tar` Crates Were Avoided (`CVE-2025-62518` / TARmageddon)**:
57
- In late 2025, a critical vulnerability (**`CVE-2025-62518` / TARmageddon**) was disclosed across async TAR parsers in the Rust ecosystem (`tokio-tar` / `astral-tokio-tar`) caused by desynchronization between PAX extended headers and GNU longname headers, enabling file smuggling and path traversal.
58
- - **LuminaMoon's Self-Contained Solution (`parse_webdataset_tar_bytes` in `data/src/multimodal_formats.rs`)**:
59
- Because WebDataset ingestion only groups paired in-memory payloads (e.g., `sample_001.jpg`, `sample_001.txt`, `sample_001.json`) into tabular rows without ever extracting files to disk, LuminaMoon implements a pure in-memory 512-byte USTAR block parser that strictly rejects directory/symlink entries and deterministically groups sample stems.
60
-
61
- ---
62
-
63
- ## 5.3 Adaptations in LuminaMoon Source Code
64
-
65
- 1. **Universal Format Support (`data/src/multimodal_formats.rs`, `data/src/multimodal_serialize.rs`, `data/src/preram.rs`, `data/src/hf_hub.rs`)**:
66
- - `InMemoryMultimodalTable::from_bytes_auto` automatically detects and parses:
67
- * **Apache Arrow IPC / Feather v2** (`.arrow`, `.feather`, `.ipc`, or `ARROW1` magic bytes) via `arrow_ipc::reader::{FileReader, StreamReader}`.
68
- * **WebDataset TAR** (`.tar`, `.tgz`, `.tar.gz`, `.tar.zst`, `.tar.lz4`), grouping sample files by stem (`00001.png` + `00001.txt` + `00001.json` $\rightarrow$ 1 multimodal row).
69
- * **CSV & TSV** (`.csv`, `.tsv`) with an RFC 4180 state-machine parser (escaped quotes, automatic `Int`/`Float`/`Bool`/`Json`/`Text` type inference, and `min_score` filtering).
70
- * **JSON Array / Object Wrapper & JSONL** (`.json`, `.jsonl`, `.ndjson`).
71
- - `serialize_columns_by_path_format` (`data/src/hf_hub.rs`) serializes tables into `.parquet`, `.arrow`/`.feather`/`.ipc`, `.jsonl`/`.ndjson`, `.json`, `.csv`, and `.tsv`, transparently wrapping output in `.gz` (`flate2::write::GzEncoder`) or `.zst` (`zstd::stream::encode_all`) compression based on `path_in_repo`.
72
-
73
- 2. **Transparent Local & Remote Stream Decompression (`data/src/source_local.rs` & `data/src/source_hf.rs`)**:
74
- - `decompress_bytes_if_compressed` detects compression via both file extensions (`.gz`, `.zst`/`.zstd`, `.lz4`) and header magic bytes:
75
- * **Gzip (`[0x1F, 0x8B]`)**: `flate2::read::MultiGzDecoder` (`1.1.10`).
76
- * **Zstandard (`[0x28, 0xB5, 0x2F, 0xFD]`)**: `zstd::stream::decode_all` (`0.14.3`).
77
- * **LZ4 Frame (`[0x04, 0x22, 0x4D, 0x18]`)**: `lz4_flex::frame::FrameDecoder` (`0.14.0`, protected against `CVE-2026-32829`).
78
-
79
- 3. **Full Arrow Type Extraction & Nested JSON Paths (`data/src/multimodal.rs` & `data/src/postprocess.rs`)**:
80
- - `MultimodalValue::List(Vec<MultimodalValue>)` recursively extracts `ListArray`, `LargeListArray`, and `FixedSizeListArray` (supporting multi-turn chat `messages` and embedding vectors).
81
- - `Float16Array` values are converted losslessly via `half::f16::to_f64()`.
82
- - `extract_json_value_by_path` supports nested dot-paths and array indices (e.g., `json_extract_key="messages.0.content"` or `"meta.quality.score"`).
83
-
84
- 4. **Typo Diagnostic Engine & Developer-Friendly Errors (`data/src/error.rs` & `python/luminamoon/data/diagnostics.py`)**:
85
- - **Damerau-Levenshtein Optimal String Alignment (`damerau_levenshtein`, `find_closest_matches`)**: Computes edit distance including adjacent character transpositions (e.g., `"txet"` $\rightarrow$ `"text"`, `"prmopt"` $\rightarrow$ `"prompt"`).
86
- - **Fake File / Git LFS Pointer Detection (`inspect_magic_bytes_hint`)**: Detects when a `.parquet`/`.arrow` file that fails to open is actually an unfetched Git LFS text pointer (`version https://git-lfs.github.com/spec/v1`), an HTML error page (`<!DOCTYPE html>`), or a misnamed JSON/Gzip/Zstd file.
87
- - **Sibling Filename & URI Suggestions (`open_local_file_with_diagnostics`, `diagnose_uri_typo`)**: Suggests similar filenames in the same directory on `FileNotFound`, corrects URI scheme typos (`hf://dataset/...` $\rightarrow$ `hf://datasets/...`, `httpss://`), and validates unknown Python `**kwargs` with closest-parameter suggestions.
 
1
+ # 5. Universal Formats (`arrow-ipc`, `half`), Stream Decompression (`flate2`, `zstd`, `lz4_flex`), and Typo Diagnostics
2
+
3
+ ## 5.1 Latest Stable Versions (October 2026)
4
+ - **`arrow-ipc`**: `60.0.0` (synchronized with `arrow-array = "60.0.0"`, `arrow-schema = "60.0.0"`, `parquet = "60.0.0"`)
5
+ - **`half`**: `2.7.1` (`f16` / `bf16` IEEE 754-2008 half-precision floating point)
6
+ - **`flate2`**: `1.1.10` (`features = ["rust_backend"]` / `miniz_oxide 0.8.9` + `crc32fast 1.5.2`)
7
+ - **`zstd`**: `0.14.3` (`zstd-safe 8.2.0` + `zstd-sys 2.0.16+zstd.1.5.7`)
8
+ - **`lz4_flex`**: `0.14.0` (`safe-decode` & `frame` enabled by default, `unsafe`-free decompression path)
9
+
10
+ ---
11
+
12
+ ## 5.2 Detailed Version-by-Version Changelog over a 1-Year Window (`2025` - `October 2026`)
13
+
14
+ ### A. `arrow-ipc` (`53.0.0` - `60.0.0`)
15
+ | Version | Date / Era | API, ABI, and Internal Behavior Changes |
16
+ | :--- | :--- | :--- |
17
+ | **`53.0.0` - `54.0.0`** | Q4 2024 - Q1 2025 | Added full IPC serialization and deserialization support (`FileReader`, `StreamReader`, `FileWriter`, `StreamWriter`) for variable-length view types `Utf8View` (`StringViewArray`) and `BinaryView` (`BinaryViewArray`) with dedicated variadic buffers. |
18
+ | **`55.0.0` - `56.0.0`** | Q2 2025 | Strict 8-byte / 64-byte buffer alignment validation on IPC `RecordBatch` messages to prevent unaligned pointer reads when memory-mapping `.arrow` / `.feather` files (`mmap` or `Cursor<&[u8]>`). |
19
+ | **`57.0.0`** | Q3 2025 | Improved IPC body compression (`LZ4_FRAME` and `ZSTD`) and variadic buffer bounds checking on `BinaryView` to prevent out-of-bounds slice panics on corrupted IPC streams. |
20
+ | **`58.0.0`** | Q4 2025 | Deprecated `StreamReader::try_new_unbuffered`; unified internal buffering in `StreamReader::try_new` and raised MSRV to match modern Rust toolchains. |
21
+ | **`59.0.0` - `60.0.0`** | 2026 | Removed legacy methods deprecated in `57.x`/`58.x`, aligned `Field` and `Schema` structures with `arrow-schema 60.0.0`, and enabled zero-copy slicing across `ListArray`, `LargeListArray`, `FixedSizeListArray`, and `Float16Array`. |
22
+
23
+ ### B. `lz4_flex` (`0.11.0` - `0.14.0`) & Mitigation of `CVE-2026-32829` (`RUSTSEC-2026-0041`)
24
+ | Version | Release Date | API Changes, Security Features, and CVE Mitigations |
25
+ | :--- | :--- | :--- |
26
+ | **`0.11.0` - `0.11.5`** | 2024 - 2025 | Inverted the feature flag from `checked-decode` to `unchecked-decode`. Previously, additive Cargo feature unification meant a transitive dependency disabling `checked-decode` could silently enable `unsafe` pointer decoding. Inverting the flag to `unchecked-decode` (off by default) with `safe-decode` (on by default) maintains `unsafe`-free decompression unless explicitly opted in. |
27
+ | **`0.11.6` & `0.12.0` - `0.12.1`** | **2026-05-09** | **Critical Security Mitigation `CVE-2026-32829` / `RUSTSEC-2026-0041` (CVSS 7.5 High)**: In `lz4_flex < 0.11.6` and `0.12.0`, when using APIs that write into uninitialized output buffers (`decompress_into` / `Sink`) on malformed LZ4 inputs containing an invalid match offset beyond already-written bytes, the decompressor could copy uninitialized memory bytes into the output buffer before returning an error (*information leak*). Fixed in `0.11.6` and `0.12.1` by validating match offset bounds against the actual output cursor prior to copying. |
28
+ | **`0.13.0`** | **2026-05-09** | Added external dictionary compression/decompression support (`compress_prepend_size_with_dict`, `decompress_size_prepended_with_dict`) for small LZ4 blocks. |
29
+ | **`0.13.1`** | **2026-05-09** | Fixed an out-of-bounds panic when an external dictionary smaller than 4 bytes (`dict.len() < 4`) was passed to dictionary compression functions. |
30
+ | **`0.14.0`** | **2026-07-14** | Explicitly separated the `alloc` feature flag from `no_std` paths, refined `frame::FrameDecoder` (`std::io::Read`) for framed `.lz4` streams (*LZ4 Frame Format*), and optimized `safe-decode` throughput. |
31
+
32
+ ### C. `flate2` (`1.0.30` - `1.1.10`)
33
+ | Version | Release Date | Backend, Performance, and Feature Flag Changes |
34
+ | :--- | :--- | :--- |
35
+ | **`1.0.30` - `1.0.35`** | 2024 - Early 2025 | Upgraded `miniz_oxide` to the `0.8.x` series and introduced optional `zlib-rs` backend support (pure-Rust SIMD-accelerated Zlib). |
36
+ | **`1.1.0` - `1.1.4`** | Mid 2025 | Added `.reset()` and internal buffer reuse across decoders (`GzDecoder`, `ZlibDecoder`, `DeflateDecoder`), eliminating repeated heap allocations when decompressing thousands of small `.gz` shards. |
37
+ | **`1.1.5` - `1.1.8`** | Late 2025 - Early 2026 | Optimized `MultiGzDecoder` for concatenated `.jsonl.gz` streams and improved handling of trailing bytes after Gzip CRC32 footers. |
38
+ | **`1.1.9`** | Q2 2026 | Refactored internal `zlib-rs` and `crc32fast` feature flags, which temporarily disabled runtime CPU SIMD detection (`std`) when consumers customized default features. |
39
+ | **`1.1.10`** | **Q3 2026** | **Throughput Regression Fix (`runtime_detection`)**: Added the `runtime_detection` feature flag (`zlib-rs?/std`, `crc32fast?/std`) directly into `default` features, ensuring `crc32fast` (`vpclmulqdq` / `pclmulqdq` / `asimd`) and compression backends automatically detect CPU SIMD capabilities at runtime. |
40
+
41
+ ### D. `zstd` (`0.13.0` - `0.14.3`) & `zstd-safe` (`7.2.0` - `8.2.0`)
42
+ | Version | Release Date | Memory Safety (`Soundness`) and C `libzstd` Engine Changes |
43
+ | :--- | :--- | :--- |
44
+ | **`0.13.0` - `0.13.3`** | 2024 - 2025 | Upgraded `zstd-sys` bindings to `libzstd 1.5.6` and `1.5.7` (faster large-frame decompression and rare long-distance matcher bug fixes). |
45
+ | **`0.14.0`** | **2026** | **`zstd-safe 8.0.0+` Soundness Fix (`CCtx` / `DCtx`)**: Resolved a memory-safety soundness bug where streaming wrappers allowed direct `&mut CCtx` / `&mut DCtx` borrows (`DerefMut`), permitting callers to `std::mem::swap` contexts while holding raw pointers to short-lived dictionary buffers (*lifetime violation*). In `0.14.x`, direct mutable context access is restricted in favor of safe delegation methods on `Encoder` and `Decoder`. |
46
+ | **`0.14.1` - `0.14.3`** | **2026** | Updated dependencies to `zstd-safe ^8.2.0` (`zstd 1.5.7`), refined `zstd::stream::read::Decoder` and `zstd::stream::encode_all` for multi-frame `.zst` streams (including HuggingFace Xet/Parquet skippable metadata frames), and eliminated compiler warnings on Rust `1.99.1`. |
47
+
48
+ ### E. `half` (`2.4.0` - `2.7.1`)
49
+ | Version | Release Date | `f16` / `bf16` Conversion and Hardware Intrinsic Changes |
50
+ | :--- | :--- | :--- |
51
+ | **`2.4.0` - `2.5.0`** | 2024 - 2025 | Stabilized batch slice conversions (`f16::to_f32_slice`, `bf16::to_f32_slice`) and full `bytemuck` and `serde` integration. |
52
+ | **`2.6.0`** | 2025 | Raised MSRV to Rust `1.81+`, utilizing `f16c` / `avx512fp16` intrinsics on `x86_64` and `fp16` on `aarch64` on stable compilers without nightly feature flags. |
53
+ | **`2.7.0` - `2.7.1`** | **2026** | Full alignment with `arrow-array 60.0.0` (`Float16Array` exports `half::f16` elements), branchless `to_f64()` optimization for subnormal and `NaN` values, and full compliance with Rust Edition 2024 `unsafe_op_in_unsafe_fn` rules. |
54
+
55
+ ### F. Self-Contained WebDataset `.tar` Parser & Typo Diagnostics (Self-Contained Implementation)
56
+ - **Why External `tar` Crates Were Avoided (`CVE-2025-62518` / TARmageddon)**:
57
+ In late 2025, a critical vulnerability (**`CVE-2025-62518` / TARmageddon**) was disclosed across async TAR parsers in the Rust ecosystem (`tokio-tar` / `astral-tokio-tar`) caused by desynchronization between PAX extended headers and GNU longname headers, enabling file smuggling and path traversal.
58
+ - **LuminaMoon's Self-Contained Solution (`parse_webdataset_tar_bytes` in `data/src/multimodal_formats.rs`)**:
59
+ Because WebDataset ingestion only groups paired in-memory payloads (e.g., `sample_001.jpg`, `sample_001.txt`, `sample_001.json`) into tabular rows without ever extracting files to disk, LuminaMoon implements a pure in-memory 512-byte USTAR block parser that strictly rejects directory/symlink entries and deterministically groups sample stems.
60
+
61
+ ---
62
+
63
+ ## 5.3 Adaptations in LuminaMoon Source Code
64
+
65
+ 1. **Universal Format Support (`data/src/multimodal_formats.rs`, `data/src/multimodal_serialize.rs`, `data/src/preram.rs`, `data/src/hf_hub.rs`)**:
66
+ - `InMemoryMultimodalTable::from_bytes_auto` automatically detects and parses:
67
+ * **Apache Arrow IPC / Feather v2** (`.arrow`, `.feather`, `.ipc`, or `ARROW1` magic bytes) via `arrow_ipc::reader::{FileReader, StreamReader}`.
68
+ * **WebDataset TAR** (`.tar`, `.tgz`, `.tar.gz`, `.tar.zst`, `.tar.lz4`), grouping sample files by stem (`00001.png` + `00001.txt` + `00001.json` $\rightarrow$ 1 multimodal row).
69
+ * **CSV & TSV** (`.csv`, `.tsv`) with an RFC 4180 state-machine parser (escaped quotes, automatic `Int`/`Float`/`Bool`/`Json`/`Text` type inference, and `min_score` filtering).
70
+ * **JSON Array / Object Wrapper & JSONL** (`.json`, `.jsonl`, `.ndjson`).
71
+ - `serialize_columns_by_path_format` (`data/src/hf_hub.rs`) serializes tables into `.parquet`, `.arrow`/`.feather`/`.ipc`, `.jsonl`/`.ndjson`, `.json`, `.csv`, and `.tsv`, transparently wrapping output in `.gz` (`flate2::write::GzEncoder`) or `.zst` (`zstd::stream::encode_all`) compression based on `path_in_repo`.
72
+
73
+ 2. **Transparent Local & Remote Stream Decompression (`data/src/source_local.rs` & `data/src/source_hf.rs`)**:
74
+ - `decompress_bytes_if_compressed` detects compression via both file extensions (`.gz`, `.zst`/`.zstd`, `.lz4`) and header magic bytes:
75
+ * **Gzip (`[0x1F, 0x8B]`)**: `flate2::read::MultiGzDecoder` (`1.1.10`).
76
+ * **Zstandard (`[0x28, 0xB5, 0x2F, 0xFD]`)**: `zstd::stream::decode_all` (`0.14.3`).
77
+ * **LZ4 Frame (`[0x04, 0x22, 0x4D, 0x18]`)**: `lz4_flex::frame::FrameDecoder` (`0.14.0`, protected against `CVE-2026-32829`).
78
+
79
+ 3. **Full Arrow Type Extraction & Nested JSON Paths (`data/src/multimodal.rs` & `data/src/postprocess.rs`)**:
80
+ - `MultimodalValue::List(Vec<MultimodalValue>)` recursively extracts `ListArray`, `LargeListArray`, and `FixedSizeListArray` (supporting multi-turn chat `messages` and embedding vectors).
81
+ - `Float16Array` values are converted losslessly via `half::f16::to_f64()`.
82
+ - `extract_json_value_by_path` supports nested dot-paths and array indices (e.g., `json_extract_key="messages.0.content"` or `"meta.quality.score"`).
83
+
84
+ 4. **Typo Diagnostic Engine & Developer-Friendly Errors (`data/src/error.rs` & `python/luminamoon/data/diagnostics.py`)**:
85
+ - **Damerau-Levenshtein Optimal String Alignment (`damerau_levenshtein`, `find_closest_matches`)**: Computes edit distance including adjacent character transpositions (e.g., `"txet"` $\rightarrow$ `"text"`, `"prmopt"` $\rightarrow$ `"prompt"`).
86
+ - **Fake File / Git LFS Pointer Detection (`inspect_magic_bytes_hint`)**: Detects when a `.parquet`/`.arrow` file that fails to open is actually an unfetched Git LFS text pointer (`version https://git-lfs.github.com/spec/v1`), an HTML error page (`<!DOCTYPE html>`), or a misnamed JSON/Gzip/Zstd file.
87
+ - **Sibling Filename & URI Suggestions (`open_local_file_with_diagnostics`, `diagnose_uri_typo`)**: Suggests similar filenames in the same directory on `FileNotFound`, corrects URI scheme typos (`hf://dataset/...` $\rightarrow$ `hf://datasets/...`, `httpss://`), and validates unknown Python `**kwargs` with closest-parameter suggestions.
data/docs/library_audit/README.md CHANGED
@@ -1,30 +1,30 @@
1
- # Library Version & Changelog Audit Documentation (October 2026)
2
-
3
- This directory documents the version-by-version changelog audit, intermediate API/ABI deprecations, security advisory mitigations (`RUSTSEC` / `CVE`), and source-code adaptations to the latest stable releases across all external libraries used by the **LuminaMoon Data Engine (`luminamoon-dataloader v1.1.0`)** crate.
4
-
5
- ---
6
-
7
- ## Library Summary
8
-
9
- | Component / Library | Audited Version Range | Latest Stable Version | Adaptation Status | Detailed Document |
10
- | :--- | :--- | :--- | :--- | :--- |
11
- | **Rust Toolchain & `std::arch`** | `1.85.0` - `1.99.1` | `1.99.1` (`edition = "2024"`) | Adopted `std::cfg_select!`, `[T]::as_chunks::<3>()`, and `unsafe_op_in_unsafe_fn` compliance across `AVX-512BW` / `AVX2` / `NEON` intrinsics | [01_rust_toolchain_simd_and_mmap.md](01_rust_toolchain_simd_and_mmap.md) |
12
- | **System & Parallelism Crates** | `2024` - `2026` | `memmap2 0.9.11`, `rayon 1.12.0`, `thiserror 2.0.21`, `serde 1.0.229`, `serde_json 1.0.154` | Mitigated `RUSTSEC-2026-0186` (`memmap2`), indexed parallel batch processing via `rayon`, and direct byte-buffer streaming via `serde_json::to_writer` | [01_rust_toolchain_simd_and_mmap.md](01_rust_toolchain_simd_and_mmap.md) |
13
- | **Python, PyO3 & Maturin** | `Py 3.10` - `3.15.0`, `pyo3 0.24` - `0.30.1` | `Python 3.15.0`, `pyo3 0.30.1`, `maturin 1.15.0` | Removed `extension-module`, enabled `abi3t`, migrated to `py.detach`, `Bound::cast::<T>()`, and `#[pyclass(frozen, subclass)]` | [02_python_pyo3_abi3t_and_maturin.md](02_python_pyo3_abi3t_and_maturin.md) |
14
- | **Apache Arrow & Parquet** | `53.0.0` - `60.0.0` | `parquet 60.0.0`, `arrow-array 60.0.0`, `arrow-schema 60.0.0` | Added `StringViewArray`/`BinaryViewArray` support, `ArrowReaderMetadata` caching, and `&mut *self` reborrowing in `ParquetMetaDataReader` | [03_parquet_arrow_and_http206_streaming.md](03_parquet_arrow_and_http206_streaming.md) |
15
- | **Async Networking & HTTP Buffers** | `reqwest 0.12` - `0.13.5`, `tokio 1.38` - `1.53.3`, `bytes 1.6` - `1.12.1` | `reqwest 0.13.5`, `tokio 1.53.3`, `bytes 1.12.1`, `futures 0.3.34` | Mitigated `CVE-2026-25541` (`bytes 1.12.1`), explicit `default-tls` + `stream` + `json` feature selection on `reqwest 0.13.5` | [03_parquet_arrow_and_http206_streaming.md](03_parquet_arrow_and_http206_streaming.md) |
16
- | **Hashing, Tables & Synchronization** | `2024` - `2026` | `xxhash-rust 0.8.19`, `hashbrown 0.17.1`, `crc32fast 1.5.2`, `parking_lot 0.12.5` | Bypassed default `foldhash` in `hashbrown 0.17` via `IdentityBuildHasher`, enabled `vpclmulqdq` hardware acceleration in `crc32fast`, and enforced HuggingFace Hub NDJSON security | [04_xxh3_dedup_crc32_and_hf_hub_security.md](04_xxh3_dedup_crc32_and_hf_hub_security.md) |
17
- | **Universal Formats & Decompression** | `2025` - `2026` | `arrow-ipc 60.0.0`, `half 2.7.1`, `flate2 1.1.10`, `zstd 0.14.3`, `lz4_flex 0.14.0` | Mitigated `CVE-2026-32829` (`lz4_flex 0.14.0`), `DCtx` soundness fixes (`zstd 0.14.3`), `runtime_detection` (`flate2 1.1.10`), Arrow IPC/Feather/WebDataset TAR, and Damerau-Levenshtein typo diagnostics | [05_universal_formats_compression_and_diagnostics.md](05_universal_formats_compression_and_diagnostics.md) |
18
-
19
- ---
20
-
21
- ## File Navigation Map
22
-
23
- | File Path | Role Summary | Status |
24
- | :--- | :--- | :--- |
25
- | `data/docs/library_audit/README.md` | Master index and version summary for all external libraries in `luminamoon-dataloader`. | Active |
26
- | `data/docs/library_audit/01_rust_toolchain_simd_and_mmap.md` | Changelog audit for Rust 1.85-1.99.1 (`std::arch`), `memmap2 0.9.11`, `rayon 1.12.0`, `thiserror 2.0.21`, and `serde`. | Active |
27
- | `data/docs/library_audit/02_python_pyo3_abi3t_and_maturin.md` | Changelog audit for Python 3.10-3.15, PyO3 0.24-0.30.1 (`abi3t`, `py.detach`, `Bound::cast`), and Maturin 1.15. | Active |
28
- | `data/docs/library_audit/03_parquet_arrow_and_http206_streaming.md` | Changelog audit for Apache Arrow/Parquet 53-60.0.0, `reqwest 0.13.5`, `tokio 1.53.3`, and `bytes 1.12.1`. | Active |
29
- | `data/docs/library_audit/04_xxh3_dedup_crc32_and_hf_hub_security.md` | Changelog audit for `xxhash-rust 0.8.19`, `hashbrown 0.17.1`, `crc32fast 1.5.2`, `parking_lot 0.12.5`, and HuggingFace Hub API specs. | Active |
30
- | `data/docs/library_audit/05_universal_formats_compression_and_diagnostics.md` | Version-by-version changelog audit (2025-2026) for `arrow-ipc 60.0.0`, `half 2.7.1`, `flate2 1.1.10`, `zstd 0.14.3`, `lz4_flex 0.14.0`, WebDataset `.tar` parser, and typo diagnostics. | Active |
 
1
+ # Library Version & Changelog Audit Documentation (October 2026)
2
+
3
+ This directory documents the version-by-version changelog audit, intermediate API/ABI deprecations, security advisory mitigations (`RUSTSEC` / `CVE`), and source-code adaptations to the latest stable releases across all external libraries used by the **LuminaMoon Data Engine (`luminamoon-dataloader v1.1.0`)** crate.
4
+
5
+ ---
6
+
7
+ ## Library Summary
8
+
9
+ | Component / Library | Audited Version Range | Latest Stable Version | Adaptation Status | Detailed Document |
10
+ | :--- | :--- | :--- | :--- | :--- |
11
+ | **Rust Toolchain & `std::arch`** | `1.85.0` - `1.99.1` | `1.99.1` (`edition = "2024"`) | Adopted `std::cfg_select!`, `[T]::as_chunks::<3>()`, and `unsafe_op_in_unsafe_fn` compliance across `AVX-512BW` / `AVX2` / `NEON` intrinsics | [01_rust_toolchain_simd_and_mmap.md](01_rust_toolchain_simd_and_mmap.md) |
12
+ | **System & Parallelism Crates** | `2024` - `2026` | `memmap2 0.9.11`, `rayon 1.12.0`, `thiserror 2.0.21`, `serde 1.0.229`, `serde_json 1.0.154` | Mitigated `RUSTSEC-2026-0186` (`memmap2`), indexed parallel batch processing via `rayon`, and direct byte-buffer streaming via `serde_json::to_writer` | [01_rust_toolchain_simd_and_mmap.md](01_rust_toolchain_simd_and_mmap.md) |
13
+ | **Python, PyO3 & Maturin** | `Py 3.10` - `3.15.0`, `pyo3 0.24` - `0.30.1` | `Python 3.15.0`, `pyo3 0.30.1`, `maturin 1.15.0` | Removed `extension-module`, enabled `abi3t`, migrated to `py.detach`, `Bound::cast::<T>()`, and `#[pyclass(frozen, subclass)]` | [02_python_pyo3_abi3t_and_maturin.md](02_python_pyo3_abi3t_and_maturin.md) |
14
+ | **Apache Arrow & Parquet** | `53.0.0` - `60.0.0` | `parquet 60.0.0`, `arrow-array 60.0.0`, `arrow-schema 60.0.0` | Added `StringViewArray`/`BinaryViewArray` support, `ArrowReaderMetadata` caching, and `&mut *self` reborrowing in `ParquetMetaDataReader` | [03_parquet_arrow_and_http206_streaming.md](03_parquet_arrow_and_http206_streaming.md) |
15
+ | **Async Networking & HTTP Buffers** | `reqwest 0.12` - `0.13.5`, `tokio 1.38` - `1.53.3`, `bytes 1.6` - `1.12.1` | `reqwest 0.13.5`, `tokio 1.53.3`, `bytes 1.12.1`, `futures 0.3.34` | Mitigated `CVE-2026-25541` (`bytes 1.12.1`), explicit `default-tls` + `stream` + `json` feature selection on `reqwest 0.13.5` | [03_parquet_arrow_and_http206_streaming.md](03_parquet_arrow_and_http206_streaming.md) |
16
+ | **Hashing, Tables & Synchronization** | `2024` - `2026` | `xxhash-rust 0.8.19`, `hashbrown 0.17.1`, `crc32fast 1.5.2`, `parking_lot 0.12.5` | Bypassed default `foldhash` in `hashbrown 0.17` via `IdentityBuildHasher`, enabled `vpclmulqdq` hardware acceleration in `crc32fast`, and enforced HuggingFace Hub NDJSON security | [04_xxh3_dedup_crc32_and_hf_hub_security.md](04_xxh3_dedup_crc32_and_hf_hub_security.md) |
17
+ | **Universal Formats & Decompression** | `2025` - `2026` | `arrow-ipc 60.0.0`, `half 2.7.1`, `flate2 1.1.10`, `zstd 0.14.3`, `lz4_flex 0.14.0` | Mitigated `CVE-2026-32829` (`lz4_flex 0.14.0`), `DCtx` soundness fixes (`zstd 0.14.3`), `runtime_detection` (`flate2 1.1.10`), Arrow IPC/Feather/WebDataset TAR, and Damerau-Levenshtein typo diagnostics | [05_universal_formats_compression_and_diagnostics.md](05_universal_formats_compression_and_diagnostics.md) |
18
+
19
+ ---
20
+
21
+ ## File Navigation Map
22
+
23
+ | File Path | Role Summary | Status |
24
+ | :--- | :--- | :--- |
25
+ | `data/docs/library_audit/README.md` | Master index and version summary for all external libraries in `luminamoon-dataloader`. | Active |
26
+ | `data/docs/library_audit/01_rust_toolchain_simd_and_mmap.md` | Changelog audit for Rust 1.85-1.99.1 (`std::arch`), `memmap2 0.9.11`, `rayon 1.12.0`, `thiserror 2.0.21`, and `serde`. | Active |
27
+ | `data/docs/library_audit/02_python_pyo3_abi3t_and_maturin.md` | Changelog audit for Python 3.10-3.15, PyO3 0.24-0.30.1 (`abi3t`, `py.detach`, `Bound::cast`), and Maturin 1.15. | Active |
28
+ | `data/docs/library_audit/03_parquet_arrow_and_http206_streaming.md` | Changelog audit for Apache Arrow/Parquet 53-60.0.0, `reqwest 0.13.5`, `tokio 1.53.3`, and `bytes 1.12.1`. | Active |
29
+ | `data/docs/library_audit/04_xxh3_dedup_crc32_and_hf_hub_security.md` | Changelog audit for `xxhash-rust 0.8.19`, `hashbrown 0.17.1`, `crc32fast 1.5.2`, `parking_lot 0.12.5`, and HuggingFace Hub API specs. | Active |
30
+ | `data/docs/library_audit/05_universal_formats_compression_and_diagnostics.md` | Version-by-version changelog audit (2025-2026) for `arrow-ipc 60.0.0`, `half 2.7.1`, `flate2 1.1.10`, `zstd 0.14.3`, `lz4_flex 0.14.0`, WebDataset `.tar` parser, and typo diagnostics. | Active |
data/src/constants.rs CHANGED
@@ -1,21 +1,21 @@
1
- pub const STREAM_COORD_BYTE_LEN: usize = 32;
2
- pub const STREAM_COORDINATE_BYTE_LEN: usize = STREAM_COORD_BYTE_LEN;
3
-
4
- pub const CHECKPOINT_SLOT_MAGIC: [u8; 4] = *b"LMDL";
5
- pub const META_SLOT_MAGIC: [u8; 4] = CHECKPOINT_SLOT_MAGIC;
6
- pub const CHECKPOINT_SLOT_VERSION: u32 = 1;
7
- pub const META_SLOT_VERSION: u32 = CHECKPOINT_SLOT_VERSION;
8
- pub const CHECKPOINT_SLOT_SIZE: usize = 64;
9
- pub const META_SLOT_SIZE: usize = CHECKPOINT_SLOT_SIZE;
10
- pub const CHECKPOINT_TOTAL_META_SIZE: u64 = (CHECKPOINT_SLOT_SIZE * 2) as u64;
11
- pub const TOTAL_META_SIZE: u64 = CHECKPOINT_TOTAL_META_SIZE;
12
- pub const DEDUP_FILE_SUFFIX: &str = ".dedup";
13
-
14
- pub const PARQUET_MAGIC_BYTES: &[u8; 4] = b"PAR1";
15
- pub const MAGIC_PARQUET: &[u8; 4] = PARQUET_MAGIC_BYTES;
16
- pub const ARROW_IPC_MAGIC_BYTES: &[u8; 6] = b"ARROW1";
17
- pub const MAGIC_ARROW_IPC: &[u8; 6] = ARROW_IPC_MAGIC_BYTES;
18
-
19
- pub const DEFAULT_BATCH_SIZE: usize = 64;
20
- pub const MAX_TYPO_SUGGESTIONS: usize = 3;
21
- pub const MAX_COLUMN_PREVIEW_COUNT: usize = 16;
 
1
+ pub const STREAM_COORD_BYTE_LEN: usize = 32;
2
+ pub const STREAM_COORDINATE_BYTE_LEN: usize = STREAM_COORD_BYTE_LEN;
3
+
4
+ pub const CHECKPOINT_SLOT_MAGIC: [u8; 4] = *b"LMDL";
5
+ pub const META_SLOT_MAGIC: [u8; 4] = CHECKPOINT_SLOT_MAGIC;
6
+ pub const CHECKPOINT_SLOT_VERSION: u32 = 1;
7
+ pub const META_SLOT_VERSION: u32 = CHECKPOINT_SLOT_VERSION;
8
+ pub const CHECKPOINT_SLOT_SIZE: usize = 64;
9
+ pub const META_SLOT_SIZE: usize = CHECKPOINT_SLOT_SIZE;
10
+ pub const CHECKPOINT_TOTAL_META_SIZE: u64 = (CHECKPOINT_SLOT_SIZE * 2) as u64;
11
+ pub const TOTAL_META_SIZE: u64 = CHECKPOINT_TOTAL_META_SIZE;
12
+ pub const DEDUP_FILE_SUFFIX: &str = ".dedup";
13
+
14
+ pub const PARQUET_MAGIC_BYTES: &[u8; 4] = b"PAR1";
15
+ pub const MAGIC_PARQUET: &[u8; 4] = PARQUET_MAGIC_BYTES;
16
+ pub const ARROW_IPC_MAGIC_BYTES: &[u8; 6] = b"ARROW1";
17
+ pub const MAGIC_ARROW_IPC: &[u8; 6] = ARROW_IPC_MAGIC_BYTES;
18
+
19
+ pub const DEFAULT_BATCH_SIZE: usize = 64;
20
+ pub const MAX_TYPO_SUGGESTIONS: usize = 3;
21
+ pub const MAX_COLUMN_PREVIEW_COUNT: usize = 16;
data/src/coord.rs CHANGED
@@ -1,39 +1,39 @@
1
- use crate::constants::STREAM_COORDINATE_BYTE_LEN;
2
-
3
- #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
4
- pub struct StreamCoordinate {
5
- pub shard_id: u32,
6
- pub byte_offset: u64,
7
- pub row_group_idx: u32,
8
- pub page_idx: u32,
9
- pub row_offset_in_page: u32,
10
- pub global_row_idx: u64,
11
- }
12
-
13
- impl StreamCoordinate {
14
- pub const BYTE_LEN: usize = STREAM_COORDINATE_BYTE_LEN;
15
-
16
- #[inline]
17
- pub fn to_bytes(&self) -> [u8; Self::BYTE_LEN] {
18
- let mut buf = [0u8; Self::BYTE_LEN];
19
- buf[0..4].copy_from_slice(&self.shard_id.to_le_bytes());
20
- buf[4..12].copy_from_slice(&self.byte_offset.to_le_bytes());
21
- buf[12..16].copy_from_slice(&self.row_group_idx.to_le_bytes());
22
- buf[16..20].copy_from_slice(&self.page_idx.to_le_bytes());
23
- buf[20..24].copy_from_slice(&self.row_offset_in_page.to_le_bytes());
24
- buf[24..32].copy_from_slice(&self.global_row_idx.to_le_bytes());
25
- buf
26
- }
27
-
28
- #[inline]
29
- pub fn from_bytes(buf: &[u8; Self::BYTE_LEN]) -> Self {
30
- Self {
31
- shard_id: u32::from_le_bytes(buf[0..4].try_into().unwrap()),
32
- byte_offset: u64::from_le_bytes(buf[4..12].try_into().unwrap()),
33
- row_group_idx: u32::from_le_bytes(buf[12..16].try_into().unwrap()),
34
- page_idx: u32::from_le_bytes(buf[16..20].try_into().unwrap()),
35
- row_offset_in_page: u32::from_le_bytes(buf[20..24].try_into().unwrap()),
36
- global_row_idx: u64::from_le_bytes(buf[24..32].try_into().unwrap()),
37
- }
38
- }
39
- }
 
1
+ use crate::constants::STREAM_COORDINATE_BYTE_LEN;
2
+
3
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
4
+ pub struct StreamCoordinate {
5
+ pub shard_id: u32,
6
+ pub byte_offset: u64,
7
+ pub row_group_idx: u32,
8
+ pub page_idx: u32,
9
+ pub row_offset_in_page: u32,
10
+ pub global_row_idx: u64,
11
+ }
12
+
13
+ impl StreamCoordinate {
14
+ pub const BYTE_LEN: usize = STREAM_COORDINATE_BYTE_LEN;
15
+
16
+ #[inline]
17
+ pub fn to_bytes(&self) -> [u8; Self::BYTE_LEN] {
18
+ let mut buf = [0u8; Self::BYTE_LEN];
19
+ buf[0..4].copy_from_slice(&self.shard_id.to_le_bytes());
20
+ buf[4..12].copy_from_slice(&self.byte_offset.to_le_bytes());
21
+ buf[12..16].copy_from_slice(&self.row_group_idx.to_le_bytes());
22
+ buf[16..20].copy_from_slice(&self.page_idx.to_le_bytes());
23
+ buf[20..24].copy_from_slice(&self.row_offset_in_page.to_le_bytes());
24
+ buf[24..32].copy_from_slice(&self.global_row_idx.to_le_bytes());
25
+ buf
26
+ }
27
+
28
+ #[inline]
29
+ pub fn from_bytes(buf: &[u8; Self::BYTE_LEN]) -> Self {
30
+ Self {
31
+ shard_id: u32::from_le_bytes(buf[0..4].try_into().unwrap()),
32
+ byte_offset: u64::from_le_bytes(buf[4..12].try_into().unwrap()),
33
+ row_group_idx: u32::from_le_bytes(buf[12..16].try_into().unwrap()),
34
+ page_idx: u32::from_le_bytes(buf[16..20].try_into().unwrap()),
35
+ row_offset_in_page: u32::from_le_bytes(buf[20..24].try_into().unwrap()),
36
+ global_row_idx: u64::from_le_bytes(buf[24..32].try_into().unwrap()),
37
+ }
38
+ }
39
+ }
data/src/dedup.rs CHANGED
@@ -1,112 +1,112 @@
1
- use std::hash::{BuildHasherDefault, Hasher};
2
-
3
- use hashbrown::{HashMap, HashSet};
4
- use xxhash_rust::xxh3::xxh3_64;
5
-
6
- #[derive(Default)]
7
- pub struct IdentityHasher(u64);
8
-
9
- impl Hasher for IdentityHasher {
10
- #[inline]
11
- fn finish(&self) -> u64 {
12
- self.0
13
- }
14
-
15
- #[inline]
16
- fn write(&mut self, bytes: &[u8]) {
17
- self.0 = xxh3_64(bytes);
18
- }
19
-
20
- #[inline]
21
- fn write_u64(&mut self, i: u64) {
22
- self.0 = i;
23
- }
24
- }
25
-
26
- pub type IdentityBuildHasher = BuildHasherDefault<IdentityHasher>;
27
-
28
- #[derive(Debug, Default)]
29
- pub struct TwoStageDeduplicator {
30
- committed_hashes: HashSet<u64, IdentityBuildHasher>,
31
- staged_lookup: HashSet<u64, IdentityBuildHasher>,
32
- staged_batches: HashMap<u64, Vec<u64>>,
33
- }
34
-
35
- impl TwoStageDeduplicator {
36
- pub fn new() -> Self {
37
- Self::default()
38
- }
39
-
40
- pub fn with_committed(hashes: impl IntoIterator<Item = u64>) -> Self {
41
- let committed_hashes: HashSet<u64, IdentityBuildHasher> = hashes.into_iter().collect();
42
- Self {
43
- committed_hashes,
44
- staged_lookup: HashSet::default(),
45
- staged_batches: HashMap::new(),
46
- }
47
- }
48
-
49
- #[inline]
50
- pub fn compute_hash(payload: &[u8]) -> u64 {
51
- xxh3_64(payload)
52
- }
53
-
54
- #[inline]
55
- pub fn is_seen(&self, payload: &[u8]) -> bool {
56
- let hash = Self::compute_hash(payload);
57
- self.committed_hashes.contains(&hash) || self.staged_lookup.contains(&hash)
58
- }
59
-
60
- #[inline]
61
- pub fn try_stage(&mut self, batch_id: u64, payload: &[u8]) -> Option<u64> {
62
- let hash = Self::compute_hash(payload);
63
- if self.committed_hashes.contains(&hash) || self.staged_lookup.contains(&hash) {
64
- return None;
65
- }
66
- self.staged_lookup.insert(hash);
67
- self.staged_batches.entry(batch_id).or_default().push(hash);
68
- Some(hash)
69
- }
70
-
71
- pub fn commit_batch(&mut self, batch_id: u64) -> Vec<u64> {
72
- let Some(hashes) = self.staged_batches.remove(&batch_id) else {
73
- return Vec::new();
74
- };
75
- for &h in &hashes {
76
- self.staged_lookup.remove(&h);
77
- self.committed_hashes.insert(h);
78
- }
79
- hashes
80
- }
81
-
82
- pub fn rollback_batch(&mut self, batch_id: u64) -> usize {
83
- let Some(hashes) = self.staged_batches.remove(&batch_id) else {
84
- return 0;
85
- };
86
- for &h in &hashes {
87
- self.staged_lookup.remove(&h);
88
- }
89
- hashes.len()
90
- }
91
-
92
- pub fn clear_staged(&mut self) {
93
- self.staged_lookup.clear();
94
- self.staged_batches.clear();
95
- }
96
-
97
- pub fn committed_len(&self) -> usize {
98
- self.committed_hashes.len()
99
- }
100
-
101
- pub fn staged_len(&self) -> usize {
102
- self.staged_lookup.len()
103
- }
104
-
105
- pub fn export_committed_hashes(&self) -> Vec<u64> {
106
- self.committed_hashes.iter().copied().collect()
107
- }
108
-
109
- pub fn import_committed_hashes(&mut self, hashes: impl IntoIterator<Item = u64>) {
110
- self.committed_hashes.extend(hashes);
111
- }
112
- }
 
1
+ use std::hash::{BuildHasherDefault, Hasher};
2
+
3
+ use hashbrown::{HashMap, HashSet};
4
+ use xxhash_rust::xxh3::xxh3_64;
5
+
6
+ #[derive(Default)]
7
+ pub struct IdentityHasher(u64);
8
+
9
+ impl Hasher for IdentityHasher {
10
+ #[inline]
11
+ fn finish(&self) -> u64 {
12
+ self.0
13
+ }
14
+
15
+ #[inline]
16
+ fn write(&mut self, bytes: &[u8]) {
17
+ self.0 = xxh3_64(bytes);
18
+ }
19
+
20
+ #[inline]
21
+ fn write_u64(&mut self, i: u64) {
22
+ self.0 = i;
23
+ }
24
+ }
25
+
26
+ pub type IdentityBuildHasher = BuildHasherDefault<IdentityHasher>;
27
+
28
+ #[derive(Debug, Default)]
29
+ pub struct TwoStageDeduplicator {
30
+ committed_hashes: HashSet<u64, IdentityBuildHasher>,
31
+ staged_lookup: HashSet<u64, IdentityBuildHasher>,
32
+ staged_batches: HashMap<u64, Vec<u64>>,
33
+ }
34
+
35
+ impl TwoStageDeduplicator {
36
+ pub fn new() -> Self {
37
+ Self::default()
38
+ }
39
+
40
+ pub fn with_committed(hashes: impl IntoIterator<Item = u64>) -> Self {
41
+ let committed_hashes: HashSet<u64, IdentityBuildHasher> = hashes.into_iter().collect();
42
+ Self {
43
+ committed_hashes,
44
+ staged_lookup: HashSet::default(),
45
+ staged_batches: HashMap::new(),
46
+ }
47
+ }
48
+
49
+ #[inline]
50
+ pub fn compute_hash(payload: &[u8]) -> u64 {
51
+ xxh3_64(payload)
52
+ }
53
+
54
+ #[inline]
55
+ pub fn is_seen(&self, payload: &[u8]) -> bool {
56
+ let hash = Self::compute_hash(payload);
57
+ self.committed_hashes.contains(&hash) || self.staged_lookup.contains(&hash)
58
+ }
59
+
60
+ #[inline]
61
+ pub fn try_stage(&mut self, batch_id: u64, payload: &[u8]) -> Option<u64> {
62
+ let hash = Self::compute_hash(payload);
63
+ if self.committed_hashes.contains(&hash) || self.staged_lookup.contains(&hash) {
64
+ return None;
65
+ }
66
+ self.staged_lookup.insert(hash);
67
+ self.staged_batches.entry(batch_id).or_default().push(hash);
68
+ Some(hash)
69
+ }
70
+
71
+ pub fn commit_batch(&mut self, batch_id: u64) -> Vec<u64> {
72
+ let Some(hashes) = self.staged_batches.remove(&batch_id) else {
73
+ return Vec::new();
74
+ };
75
+ for &h in &hashes {
76
+ self.staged_lookup.remove(&h);
77
+ self.committed_hashes.insert(h);
78
+ }
79
+ hashes
80
+ }
81
+
82
+ pub fn rollback_batch(&mut self, batch_id: u64) -> usize {
83
+ let Some(hashes) = self.staged_batches.remove(&batch_id) else {
84
+ return 0;
85
+ };
86
+ for &h in &hashes {
87
+ self.staged_lookup.remove(&h);
88
+ }
89
+ hashes.len()
90
+ }
91
+
92
+ pub fn clear_staged(&mut self) {
93
+ self.staged_lookup.clear();
94
+ self.staged_batches.clear();
95
+ }
96
+
97
+ pub fn committed_len(&self) -> usize {
98
+ self.committed_hashes.len()
99
+ }
100
+
101
+ pub fn staged_len(&self) -> usize {
102
+ self.staged_lookup.len()
103
+ }
104
+
105
+ pub fn export_committed_hashes(&self) -> Vec<u64> {
106
+ self.committed_hashes.iter().copied().collect()
107
+ }
108
+
109
+ pub fn import_committed_hashes(&mut self, hashes: impl IntoIterator<Item = u64>) {
110
+ self.committed_hashes.extend(hashes);
111
+ }
112
+ }
data/src/error.rs CHANGED
@@ -1,166 +1,166 @@
1
- use thiserror::Error;
2
-
3
- use crate::constants::{MAX_COLUMN_PREVIEW_COUNT, MAX_TYPO_SUGGESTIONS};
4
-
5
- #[derive(Debug, Error)]
6
- pub enum DataLoaderError {
7
- #[error("I/O failure: {0}")]
8
- Io(#[from] std::io::Error),
9
-
10
- #[error("Parquet failure: {0}")]
11
- Parquet(#[from] parquet::errors::ParquetError),
12
-
13
- #[error("Arrow failure: {0}")]
14
- Arrow(#[from] arrow_schema::ArrowError),
15
-
16
- #[error("Corrupted metadata checkpoint in both slots")]
17
- CorruptedMetadata,
18
-
19
- #[error("Unknown staged batch id: {0}")]
20
- UnknownBatchId(u64),
21
-
22
- #[error("Invalid coordinate buffer length")]
23
- InvalidCoordinate,
24
-
25
- #[error("Target column not found: {0}")]
26
- ColumnNotFound(String),
27
-
28
- #[error("Invalid configuration: {0}")]
29
- InvalidConfig(String),
30
- }
31
-
32
- pub type Result<T> = std::result::Result<T, DataLoaderError>;
33
-
34
- pub fn damerau_levenshtein(a: &str, b: &str) -> usize {
35
- let a_chars: Vec<char> = a.chars().collect();
36
- let b_chars: Vec<char> = b.chars().collect();
37
- let len_a = a_chars.len();
38
- let len_b = b_chars.len();
39
-
40
- if len_a == 0 {
41
- return len_b;
42
- }
43
- if len_b == 0 {
44
- return len_a;
45
- }
46
-
47
- let width = len_b + 1;
48
- let mut matrix = vec![0usize; (len_a + 1) * width];
49
-
50
- for i in 0..=len_a {
51
- matrix[i * width] = i;
52
- }
53
- for (j, cell) in matrix.iter_mut().enumerate().take(len_b + 1) {
54
- *cell = j;
55
- }
56
-
57
- for i in 1..=len_a {
58
- for j in 1..=len_b {
59
- let cost = usize::from(a_chars[i - 1] != b_chars[j - 1]);
60
- let deletion = matrix[(i - 1) * width + j] + 1;
61
- let insertion = matrix[i * width + (j - 1)] + 1;
62
- let substitution = matrix[(i - 1) * width + (j - 1)] + cost;
63
- let mut best = deletion.min(insertion).min(substitution);
64
-
65
- if i > 1
66
- && j > 1
67
- && a_chars[i - 1] == b_chars[j - 2]
68
- && a_chars[i - 2] == b_chars[j - 1]
69
- {
70
- let transposition = matrix[(i - 2) * width + (j - 2)] + 1;
71
- best = best.min(transposition);
72
- }
73
- matrix[i * width + j] = best;
74
- }
75
- }
76
-
77
- matrix[len_a * width + len_b]
78
- }
79
-
80
- pub fn find_closest_matches<S: AsRef<str>>(
81
- target: &str,
82
- candidates: &[S],
83
- max_suggestions: usize,
84
- ) -> Vec<String> {
85
- let target_trim = target.trim();
86
- if target_trim.is_empty() || candidates.is_empty() || max_suggestions == 0 {
87
- return Vec::new();
88
- }
89
- let target_lower = target_trim.to_ascii_lowercase();
90
-
91
- let mut scored: Vec<(usize, usize, String)> = Vec::new();
92
- for cand in candidates {
93
- let c_str = cand.as_ref();
94
- if c_str.is_empty() {
95
- continue;
96
- }
97
- let c_lower = c_str.to_ascii_lowercase();
98
- if c_lower == target_lower {
99
- scored.push((0, 0, c_str.to_owned()));
100
- continue;
101
- }
102
- let dist = damerau_levenshtein(&target_lower, &c_lower);
103
- let max_len = target_lower.chars().count().max(c_lower.chars().count());
104
- let threshold = match max_len {
105
- 0..=3 => 1,
106
- 4..=6 => 2,
107
- 7..=11 => 3,
108
- _ => (max_len * 2) / 5,
109
- };
110
- let is_sub = c_lower.contains(&target_lower) || target_lower.contains(&c_lower);
111
- if dist <= threshold || (target_lower.len() >= 3 && is_sub) {
112
- let rank_bonus = if is_sub { 0 } else { 1 };
113
- scored.push((dist, rank_bonus, c_str.to_owned()));
114
- }
115
- }
116
-
117
- scored.sort_by(|a, b| a.0.cmp(&b.0).then(a.1.cmp(&b.1)).then(a.2.cmp(&b.2)));
118
- scored.dedup_by(|a, b| a.2 == b.2);
119
- scored
120
- .into_iter()
121
- .take(max_suggestions)
122
- .map(|(_, _, s)| s)
123
- .collect()
124
- }
125
-
126
- pub fn format_did_you_mean<S: AsRef<str>>(target: &str, candidates: &[S]) -> String {
127
- let matches = find_closest_matches(target, candidates, MAX_TYPO_SUGGESTIONS);
128
- if matches.is_empty() {
129
- return String::new();
130
- }
131
- if matches.len() == 1 {
132
- format!(" Did you mean '{}'?", matches[0])
133
- } else {
134
- let quoted: Vec<String> = matches.iter().map(|m| format!("'{m}'")).collect();
135
- format!(" Did you mean one of [{}]?", quoted.join(", "))
136
- }
137
- }
138
-
139
- pub fn column_not_found_with_suggestions<S: AsRef<str>>(
140
- requested: &str,
141
- available: &[S],
142
- ) -> DataLoaderError {
143
- let suggestion = format_did_you_mean(requested, available);
144
- let avail_preview: Vec<String> = available
145
- .iter()
146
- .take(MAX_COLUMN_PREVIEW_COUNT)
147
- .map(|s| format!("'{}'", s.as_ref()))
148
- .collect();
149
- let avail_suffix = if available.len() > MAX_COLUMN_PREVIEW_COUNT {
150
- format!(
151
- " (and {} more)",
152
- available.len().saturating_sub(avail_preview.len())
153
- )
154
- } else {
155
- String::new()
156
- };
157
-
158
- if avail_preview.is_empty() {
159
- DataLoaderError::ColumnNotFound(requested.to_owned())
160
- } else {
161
- DataLoaderError::ColumnNotFound(format!(
162
- "'{requested}'.{suggestion} Available columns: [{}]{avail_suffix}",
163
- avail_preview.join(", ")
164
- ))
165
- }
166
- }
 
1
+ use thiserror::Error;
2
+
3
+ use crate::constants::{MAX_COLUMN_PREVIEW_COUNT, MAX_TYPO_SUGGESTIONS};
4
+
5
+ #[derive(Debug, Error)]
6
+ pub enum DataLoaderError {
7
+ #[error("I/O failure: {0}")]
8
+ Io(#[from] std::io::Error),
9
+
10
+ #[error("Parquet failure: {0}")]
11
+ Parquet(#[from] parquet::errors::ParquetError),
12
+
13
+ #[error("Arrow failure: {0}")]
14
+ Arrow(#[from] arrow_schema::ArrowError),
15
+
16
+ #[error("Corrupted metadata checkpoint in both slots")]
17
+ CorruptedMetadata,
18
+
19
+ #[error("Unknown staged batch id: {0}")]
20
+ UnknownBatchId(u64),
21
+
22
+ #[error("Invalid coordinate buffer length")]
23
+ InvalidCoordinate,
24
+
25
+ #[error("Target column not found: {0}")]
26
+ ColumnNotFound(String),
27
+
28
+ #[error("Invalid configuration: {0}")]
29
+ InvalidConfig(String),
30
+ }
31
+
32
+ pub type Result<T> = std::result::Result<T, DataLoaderError>;
33
+
34
+ pub fn damerau_levenshtein(a: &str, b: &str) -> usize {
35
+ let a_chars: Vec<char> = a.chars().collect();
36
+ let b_chars: Vec<char> = b.chars().collect();
37
+ let len_a = a_chars.len();
38
+ let len_b = b_chars.len();
39
+
40
+ if len_a == 0 {
41
+ return len_b;
42
+ }
43
+ if len_b == 0 {
44
+ return len_a;
45
+ }
46
+
47
+ let width = len_b + 1;
48
+ let mut matrix = vec![0usize; (len_a + 1) * width];
49
+
50
+ for i in 0..=len_a {
51
+ matrix[i * width] = i;
52
+ }
53
+ for (j, cell) in matrix.iter_mut().enumerate().take(len_b + 1) {
54
+ *cell = j;
55
+ }
56
+
57
+ for i in 1..=len_a {
58
+ for j in 1..=len_b {
59
+ let cost = usize::from(a_chars[i - 1] != b_chars[j - 1]);
60
+ let deletion = matrix[(i - 1) * width + j] + 1;
61
+ let insertion = matrix[i * width + (j - 1)] + 1;
62
+ let substitution = matrix[(i - 1) * width + (j - 1)] + cost;
63
+ let mut best = deletion.min(insertion).min(substitution);
64
+
65
+ if i > 1
66
+ && j > 1
67
+ && a_chars[i - 1] == b_chars[j - 2]
68
+ && a_chars[i - 2] == b_chars[j - 1]
69
+ {
70
+ let transposition = matrix[(i - 2) * width + (j - 2)] + 1;
71
+ best = best.min(transposition);
72
+ }
73
+ matrix[i * width + j] = best;
74
+ }
75
+ }
76
+
77
+ matrix[len_a * width + len_b]
78
+ }
79
+
80
+ pub fn find_closest_matches<S: AsRef<str>>(
81
+ target: &str,
82
+ candidates: &[S],
83
+ max_suggestions: usize,
84
+ ) -> Vec<String> {
85
+ let target_trim = target.trim();
86
+ if target_trim.is_empty() || candidates.is_empty() || max_suggestions == 0 {
87
+ return Vec::new();
88
+ }
89
+ let target_lower = target_trim.to_ascii_lowercase();
90
+
91
+ let mut scored: Vec<(usize, usize, String)> = Vec::new();
92
+ for cand in candidates {
93
+ let c_str = cand.as_ref();
94
+ if c_str.is_empty() {
95
+ continue;
96
+ }
97
+ let c_lower = c_str.to_ascii_lowercase();
98
+ if c_lower == target_lower {
99
+ scored.push((0, 0, c_str.to_owned()));
100
+ continue;
101
+ }
102
+ let dist = damerau_levenshtein(&target_lower, &c_lower);
103
+ let max_len = target_lower.chars().count().max(c_lower.chars().count());
104
+ let threshold = match max_len {
105
+ 0..=3 => 1,
106
+ 4..=6 => 2,
107
+ 7..=11 => 3,
108
+ _ => (max_len * 2) / 5,
109
+ };
110
+ let is_sub = c_lower.contains(&target_lower) || target_lower.contains(&c_lower);
111
+ if dist <= threshold || (target_lower.len() >= 3 && is_sub) {
112
+ let rank_bonus = if is_sub { 0 } else { 1 };
113
+ scored.push((dist, rank_bonus, c_str.to_owned()));
114
+ }
115
+ }
116
+
117
+ scored.sort_by(|a, b| a.0.cmp(&b.0).then(a.1.cmp(&b.1)).then(a.2.cmp(&b.2)));
118
+ scored.dedup_by(|a, b| a.2 == b.2);
119
+ scored
120
+ .into_iter()
121
+ .take(max_suggestions)
122
+ .map(|(_, _, s)| s)
123
+ .collect()
124
+ }
125
+
126
+ pub fn format_did_you_mean<S: AsRef<str>>(target: &str, candidates: &[S]) -> String {
127
+ let matches = find_closest_matches(target, candidates, MAX_TYPO_SUGGESTIONS);
128
+ if matches.is_empty() {
129
+ return String::new();
130
+ }
131
+ if matches.len() == 1 {
132
+ format!(" Did you mean '{}'?", matches[0])
133
+ } else {
134
+ let quoted: Vec<String> = matches.iter().map(|m| format!("'{m}'")).collect();
135
+ format!(" Did you mean one of [{}]?", quoted.join(", "))
136
+ }
137
+ }
138
+
139
+ pub fn column_not_found_with_suggestions<S: AsRef<str>>(
140
+ requested: &str,
141
+ available: &[S],
142
+ ) -> DataLoaderError {
143
+ let suggestion = format_did_you_mean(requested, available);
144
+ let avail_preview: Vec<String> = available
145
+ .iter()
146
+ .take(MAX_COLUMN_PREVIEW_COUNT)
147
+ .map(|s| format!("'{}'", s.as_ref()))
148
+ .collect();
149
+ let avail_suffix = if available.len() > MAX_COLUMN_PREVIEW_COUNT {
150
+ format!(
151
+ " (and {} more)",
152
+ available.len().saturating_sub(avail_preview.len())
153
+ )
154
+ } else {
155
+ String::new()
156
+ };
157
+
158
+ if avail_preview.is_empty() {
159
+ DataLoaderError::ColumnNotFound(requested.to_owned())
160
+ } else {
161
+ DataLoaderError::ColumnNotFound(format!(
162
+ "'{requested}'.{suggestion} Available columns: [{}]{avail_suffix}",
163
+ avail_preview.join(", ")
164
+ ))
165
+ }
166
+ }
data/src/lib.rs CHANGED
@@ -1,15 +1,15 @@
1
- pub mod constants;
2
- pub mod coord;
3
- pub mod dedup;
4
- pub mod error;
5
- pub mod metadata;
6
- pub mod minhash;
7
- pub mod postprocess;
8
- pub mod py_bridge;
9
- pub mod quality;
10
- pub mod serialize;
11
- pub mod simd_cpu;
12
- pub mod string_tensor;
13
-
14
- #[cfg(test)]
15
- mod tests;
 
1
+ pub mod constants;
2
+ pub mod coord;
3
+ pub mod dedup;
4
+ pub mod error;
5
+ pub mod metadata;
6
+ pub mod minhash;
7
+ pub mod postprocess;
8
+ pub mod py_bridge;
9
+ pub mod quality;
10
+ pub mod serialize;
11
+ pub mod simd_cpu;
12
+ pub mod string_tensor;
13
+
14
+ #[cfg(test)]
15
+ mod tests;
data/src/metadata.rs CHANGED
@@ -1,264 +1,264 @@
1
- use std::fs::{File, OpenOptions};
2
- use std::io::{Read, Seek, SeekFrom, Write};
3
- use std::path::{Path, PathBuf};
4
-
5
- use crc32fast::Hasher;
6
- use hashbrown::HashMap;
7
-
8
- use crate::constants::{
9
- DEDUP_FILE_SUFFIX, META_SLOT_MAGIC, META_SLOT_SIZE, META_SLOT_VERSION, TOTAL_META_SIZE,
10
- };
11
- use crate::coord::StreamCoordinate;
12
- use crate::dedup::TwoStageDeduplicator;
13
- use crate::error::{DataLoaderError, Result};
14
-
15
- const SLOT_MAGIC: [u8; 4] = META_SLOT_MAGIC;
16
- const SLOT_VERSION: u32 = META_SLOT_VERSION;
17
- const SLOT_SIZE: usize = META_SLOT_SIZE;
18
-
19
- #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
20
- struct DiskSlot {
21
- epoch: u64,
22
- committed_batch_id: u64,
23
- coord: StreamCoordinate,
24
- dedup_count: u64,
25
- }
26
-
27
- impl DiskSlot {
28
- fn encode(&self) -> [u8; SLOT_SIZE] {
29
- let mut buf = [0u8; SLOT_SIZE];
30
- buf[0..4].copy_from_slice(&SLOT_MAGIC);
31
- buf[4..8].copy_from_slice(&SLOT_VERSION.to_le_bytes());
32
- buf[8..16].copy_from_slice(&self.epoch.to_le_bytes());
33
- buf[16..24].copy_from_slice(&self.committed_batch_id.to_le_bytes());
34
- buf[24..56].copy_from_slice(&self.coord.to_bytes());
35
- buf[56..60].copy_from_slice(&(self.dedup_count as u32).to_le_bytes());
36
-
37
- let mut hasher = Hasher::new();
38
- hasher.update(&buf[0..60]);
39
- let crc = hasher.finalize();
40
- buf[60..64].copy_from_slice(&crc.to_le_bytes());
41
- buf
42
- }
43
-
44
- fn decode(buf: &[u8; SLOT_SIZE]) -> Option<Self> {
45
- if buf[0..4] != SLOT_MAGIC {
46
- return None;
47
- }
48
- let version = u32::from_le_bytes(buf[4..8].try_into().unwrap());
49
- if version != SLOT_VERSION {
50
- return None;
51
- }
52
- let expected_crc = u32::from_le_bytes(buf[60..64].try_into().unwrap());
53
- let mut hasher = Hasher::new();
54
- hasher.update(&buf[0..60]);
55
- if hasher.finalize() != expected_crc {
56
- return None;
57
- }
58
- let coord_bytes: &[u8; StreamCoordinate::BYTE_LEN] = buf[24..56].try_into().unwrap();
59
- Some(Self {
60
- epoch: u64::from_le_bytes(buf[8..16].try_into().unwrap()),
61
- committed_batch_id: u64::from_le_bytes(buf[16..24].try_into().unwrap()),
62
- coord: StreamCoordinate::from_bytes(coord_bytes),
63
- dedup_count: u32::from_le_bytes(buf[56..60].try_into().unwrap()) as u64,
64
- })
65
- }
66
- }
67
-
68
- pub struct DualMetadataManager {
69
- meta_file: File,
70
- dedup_file: File,
71
- epoch: u64,
72
- committed_batch_id: u64,
73
- committed_coord: StreamCoordinate,
74
- in_flight_coord: StreamCoordinate,
75
- staged_coords: HashMap<u64, StreamCoordinate>,
76
- pub deduplicator: TwoStageDeduplicator,
77
- }
78
-
79
- impl DualMetadataManager {
80
- pub fn open(meta_path: impl AsRef<Path>) -> Result<Self> {
81
- let meta_path = meta_path.as_ref();
82
- if let Some(parent) = meta_path.parent()
83
- && !parent.as_os_str().is_empty()
84
- {
85
- std::fs::create_dir_all(parent)?;
86
- }
87
-
88
- let dedup_path = Self::dedup_path_for(meta_path);
89
-
90
- let mut meta_file = OpenOptions::new()
91
- .read(true)
92
- .write(true)
93
- .create(true)
94
- .truncate(false)
95
- .open(meta_path)?;
96
-
97
- let mut dedup_file = OpenOptions::new()
98
- .read(true)
99
- .write(true)
100
- .create(true)
101
- .truncate(false)
102
- .open(&dedup_path)?;
103
-
104
- let file_len = meta_file.metadata()?.len();
105
- let active_slot = if file_len < TOTAL_META_SIZE {
106
- meta_file.set_len(TOTAL_META_SIZE)?;
107
- let initial = DiskSlot::default();
108
- let encoded = initial.encode();
109
- meta_file.seek(SeekFrom::Start(0))?;
110
- meta_file.write_all(&encoded)?;
111
- meta_file.write_all(&encoded)?;
112
- meta_file.sync_data()?;
113
- initial
114
- } else {
115
- let mut raw = [0u8; SLOT_SIZE * 2];
116
- meta_file.seek(SeekFrom::Start(0))?;
117
- meta_file.read_exact(&mut raw)?;
118
-
119
- let slot_a = DiskSlot::decode(raw[0..SLOT_SIZE].try_into().unwrap());
120
- let slot_b = DiskSlot::decode(raw[SLOT_SIZE..SLOT_SIZE * 2].try_into().unwrap());
121
-
122
- match (slot_a, slot_b) {
123
- (Some(a), Some(b)) => {
124
- if a.epoch >= b.epoch {
125
- a
126
- } else {
127
- b
128
- }
129
- }
130
- (Some(a), None) => a,
131
- (None, Some(b)) => b,
132
- (None, None) => return Err(DataLoaderError::CorruptedMetadata),
133
- }
134
- };
135
-
136
- let valid_dedup_bytes = active_slot.dedup_count * 8;
137
- if dedup_file.metadata()?.len() < valid_dedup_bytes {
138
- return Err(DataLoaderError::CorruptedMetadata);
139
- }
140
- dedup_file.set_len(valid_dedup_bytes)?;
141
- dedup_file.seek(SeekFrom::Start(0))?;
142
-
143
- let mut raw_hashes = vec![0u8; valid_dedup_bytes as usize];
144
- if valid_dedup_bytes > 0 {
145
- dedup_file.read_exact(&mut raw_hashes)?;
146
- }
147
-
148
- let restored_hashes = raw_hashes
149
- .as_chunks::<8>()
150
- .0
151
- .iter()
152
- .map(|&c| u64::from_le_bytes(c));
153
-
154
- let deduplicator = TwoStageDeduplicator::with_committed(restored_hashes);
155
-
156
- Ok(Self {
157
- meta_file,
158
- dedup_file,
159
- epoch: active_slot.epoch,
160
- committed_batch_id: active_slot.committed_batch_id,
161
- committed_coord: active_slot.coord,
162
- in_flight_coord: active_slot.coord,
163
- staged_coords: HashMap::new(),
164
- deduplicator,
165
- })
166
- }
167
-
168
- fn dedup_path_for(meta_path: &Path) -> PathBuf {
169
- let mut p = meta_path.as_os_str().to_owned();
170
- p.push(DEDUP_FILE_SUFFIX);
171
- PathBuf::from(p)
172
- }
173
-
174
- #[inline]
175
- pub fn committed_coordinate(&self) -> StreamCoordinate {
176
- self.committed_coord
177
- }
178
-
179
- #[inline]
180
- pub fn in_flight_coordinate(&self) -> StreamCoordinate {
181
- self.in_flight_coord
182
- }
183
-
184
- #[inline]
185
- pub fn committed_batch_id(&self) -> u64 {
186
- self.committed_batch_id
187
- }
188
-
189
- #[inline]
190
- pub fn set_in_flight_coordinate(&mut self, next_coord: StreamCoordinate) {
191
- self.in_flight_coord = next_coord;
192
- }
193
-
194
- pub fn stage_batch_in_ram(&mut self, batch_id: u64, next_coord: StreamCoordinate) {
195
- self.in_flight_coord = next_coord;
196
- self.staged_coords.insert(batch_id, next_coord);
197
- }
198
-
199
- pub fn commit_gpu_batch(&mut self, batch_id: u64) -> Result<StreamCoordinate> {
200
- let Some(next_coord) = self.staged_coords.remove(&batch_id) else {
201
- return Err(DataLoaderError::UnknownBatchId(batch_id));
202
- };
203
-
204
- let prev_dedup_count = self.deduplicator.committed_len() as u64;
205
- let new_hashes = self.deduplicator.commit_batch(batch_id);
206
- if !new_hashes.is_empty() {
207
- let mut hash_buf = Vec::with_capacity(new_hashes.len() * 8);
208
- for h in new_hashes {
209
- hash_buf.extend_from_slice(&h.to_le_bytes());
210
- }
211
- let write_offset = prev_dedup_count * 8;
212
- self.dedup_file.seek(SeekFrom::Start(write_offset))?;
213
- self.dedup_file.write_all(&hash_buf)?;
214
- self.dedup_file
215
- .set_len(write_offset + hash_buf.len() as u64)?;
216
- self.dedup_file.sync_data()?;
217
- }
218
-
219
- self.epoch += 1;
220
- self.committed_batch_id = batch_id;
221
- self.committed_coord = next_coord;
222
-
223
- let slot = DiskSlot {
224
- epoch: self.epoch,
225
- committed_batch_id: self.committed_batch_id,
226
- coord: self.committed_coord,
227
- dedup_count: self.deduplicator.committed_len() as u64,
228
- };
229
-
230
- let slot_offset = if self.epoch % 2 == 1 {
231
- 0
232
- } else {
233
- SLOT_SIZE as u64
234
- };
235
-
236
- let encoded = slot.encode();
237
- self.meta_file.seek(SeekFrom::Start(slot_offset))?;
238
- self.meta_file.write_all(&encoded)?;
239
- self.meta_file.sync_data()?;
240
-
241
- Ok(self.committed_coord)
242
- }
243
-
244
- pub fn rollback_staged_batch(&mut self, batch_id: u64) -> Result<StreamCoordinate> {
245
- if self.staged_coords.remove(&batch_id).is_none() {
246
- return Err(DataLoaderError::UnknownBatchId(batch_id));
247
- }
248
- self.deduplicator.rollback_batch(batch_id);
249
- self.in_flight_coord = self
250
- .staged_coords
251
- .iter()
252
- .max_by_key(|&(&id, _)| id)
253
- .map(|(_, &coord)| coord)
254
- .unwrap_or(self.committed_coord);
255
- Ok(self.in_flight_coord)
256
- }
257
-
258
- pub fn rollback_ram_to_committed(&mut self) -> StreamCoordinate {
259
- self.staged_coords.clear();
260
- self.deduplicator.clear_staged();
261
- self.in_flight_coord = self.committed_coord;
262
- self.committed_coord
263
- }
264
- }
 
1
+ use std::fs::{File, OpenOptions};
2
+ use std::io::{Read, Seek, SeekFrom, Write};
3
+ use std::path::{Path, PathBuf};
4
+
5
+ use crc32fast::Hasher;
6
+ use hashbrown::HashMap;
7
+
8
+ use crate::constants::{
9
+ DEDUP_FILE_SUFFIX, META_SLOT_MAGIC, META_SLOT_SIZE, META_SLOT_VERSION, TOTAL_META_SIZE,
10
+ };
11
+ use crate::coord::StreamCoordinate;
12
+ use crate::dedup::TwoStageDeduplicator;
13
+ use crate::error::{DataLoaderError, Result};
14
+
15
+ const SLOT_MAGIC: [u8; 4] = META_SLOT_MAGIC;
16
+ const SLOT_VERSION: u32 = META_SLOT_VERSION;
17
+ const SLOT_SIZE: usize = META_SLOT_SIZE;
18
+
19
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
20
+ struct DiskSlot {
21
+ epoch: u64,
22
+ committed_batch_id: u64,
23
+ coord: StreamCoordinate,
24
+ dedup_count: u64,
25
+ }
26
+
27
+ impl DiskSlot {
28
+ fn encode(&self) -> [u8; SLOT_SIZE] {
29
+ let mut buf = [0u8; SLOT_SIZE];
30
+ buf[0..4].copy_from_slice(&SLOT_MAGIC);
31
+ buf[4..8].copy_from_slice(&SLOT_VERSION.to_le_bytes());
32
+ buf[8..16].copy_from_slice(&self.epoch.to_le_bytes());
33
+ buf[16..24].copy_from_slice(&self.committed_batch_id.to_le_bytes());
34
+ buf[24..56].copy_from_slice(&self.coord.to_bytes());
35
+ buf[56..60].copy_from_slice(&(self.dedup_count as u32).to_le_bytes());
36
+
37
+ let mut hasher = Hasher::new();
38
+ hasher.update(&buf[0..60]);
39
+ let crc = hasher.finalize();
40
+ buf[60..64].copy_from_slice(&crc.to_le_bytes());
41
+ buf
42
+ }
43
+
44
+ fn decode(buf: &[u8; SLOT_SIZE]) -> Option<Self> {
45
+ if buf[0..4] != SLOT_MAGIC {
46
+ return None;
47
+ }
48
+ let version = u32::from_le_bytes(buf[4..8].try_into().unwrap());
49
+ if version != SLOT_VERSION {
50
+ return None;
51
+ }
52
+ let expected_crc = u32::from_le_bytes(buf[60..64].try_into().unwrap());
53
+ let mut hasher = Hasher::new();
54
+ hasher.update(&buf[0..60]);
55
+ if hasher.finalize() != expected_crc {
56
+ return None;
57
+ }
58
+ let coord_bytes: &[u8; StreamCoordinate::BYTE_LEN] = buf[24..56].try_into().unwrap();
59
+ Some(Self {
60
+ epoch: u64::from_le_bytes(buf[8..16].try_into().unwrap()),
61
+ committed_batch_id: u64::from_le_bytes(buf[16..24].try_into().unwrap()),
62
+ coord: StreamCoordinate::from_bytes(coord_bytes),
63
+ dedup_count: u32::from_le_bytes(buf[56..60].try_into().unwrap()) as u64,
64
+ })
65
+ }
66
+ }
67
+
68
+ pub struct DualMetadataManager {
69
+ meta_file: File,
70
+ dedup_file: File,
71
+ epoch: u64,
72
+ committed_batch_id: u64,
73
+ committed_coord: StreamCoordinate,
74
+ in_flight_coord: StreamCoordinate,
75
+ staged_coords: HashMap<u64, StreamCoordinate>,
76
+ pub deduplicator: TwoStageDeduplicator,
77
+ }
78
+
79
+ impl DualMetadataManager {
80
+ pub fn open(meta_path: impl AsRef<Path>) -> Result<Self> {
81
+ let meta_path = meta_path.as_ref();
82
+ if let Some(parent) = meta_path.parent()
83
+ && !parent.as_os_str().is_empty()
84
+ {
85
+ std::fs::create_dir_all(parent)?;
86
+ }
87
+
88
+ let dedup_path = Self::dedup_path_for(meta_path);
89
+
90
+ let mut meta_file = OpenOptions::new()
91
+ .read(true)
92
+ .write(true)
93
+ .create(true)
94
+ .truncate(false)
95
+ .open(meta_path)?;
96
+
97
+ let mut dedup_file = OpenOptions::new()
98
+ .read(true)
99
+ .write(true)
100
+ .create(true)
101
+ .truncate(false)
102
+ .open(&dedup_path)?;
103
+
104
+ let file_len = meta_file.metadata()?.len();
105
+ let active_slot = if file_len < TOTAL_META_SIZE {
106
+ meta_file.set_len(TOTAL_META_SIZE)?;
107
+ let initial = DiskSlot::default();
108
+ let encoded = initial.encode();
109
+ meta_file.seek(SeekFrom::Start(0))?;
110
+ meta_file.write_all(&encoded)?;
111
+ meta_file.write_all(&encoded)?;
112
+ meta_file.sync_data()?;
113
+ initial
114
+ } else {
115
+ let mut raw = [0u8; SLOT_SIZE * 2];
116
+ meta_file.seek(SeekFrom::Start(0))?;
117
+ meta_file.read_exact(&mut raw)?;
118
+
119
+ let slot_a = DiskSlot::decode(raw[0..SLOT_SIZE].try_into().unwrap());
120
+ let slot_b = DiskSlot::decode(raw[SLOT_SIZE..SLOT_SIZE * 2].try_into().unwrap());
121
+
122
+ match (slot_a, slot_b) {
123
+ (Some(a), Some(b)) => {
124
+ if a.epoch >= b.epoch {
125
+ a
126
+ } else {
127
+ b
128
+ }
129
+ }
130
+ (Some(a), None) => a,
131
+ (None, Some(b)) => b,
132
+ (None, None) => return Err(DataLoaderError::CorruptedMetadata),
133
+ }
134
+ };
135
+
136
+ let valid_dedup_bytes = active_slot.dedup_count * 8;
137
+ if dedup_file.metadata()?.len() < valid_dedup_bytes {
138
+ return Err(DataLoaderError::CorruptedMetadata);
139
+ }
140
+ dedup_file.set_len(valid_dedup_bytes)?;
141
+ dedup_file.seek(SeekFrom::Start(0))?;
142
+
143
+ let mut raw_hashes = vec![0u8; valid_dedup_bytes as usize];
144
+ if valid_dedup_bytes > 0 {
145
+ dedup_file.read_exact(&mut raw_hashes)?;
146
+ }
147
+
148
+ let restored_hashes = raw_hashes
149
+ .as_chunks::<8>()
150
+ .0
151
+ .iter()
152
+ .map(|&c| u64::from_le_bytes(c));
153
+
154
+ let deduplicator = TwoStageDeduplicator::with_committed(restored_hashes);
155
+
156
+ Ok(Self {
157
+ meta_file,
158
+ dedup_file,
159
+ epoch: active_slot.epoch,
160
+ committed_batch_id: active_slot.committed_batch_id,
161
+ committed_coord: active_slot.coord,
162
+ in_flight_coord: active_slot.coord,
163
+ staged_coords: HashMap::new(),
164
+ deduplicator,
165
+ })
166
+ }
167
+
168
+ fn dedup_path_for(meta_path: &Path) -> PathBuf {
169
+ let mut p = meta_path.as_os_str().to_owned();
170
+ p.push(DEDUP_FILE_SUFFIX);
171
+ PathBuf::from(p)
172
+ }
173
+
174
+ #[inline]
175
+ pub fn committed_coordinate(&self) -> StreamCoordinate {
176
+ self.committed_coord
177
+ }
178
+
179
+ #[inline]
180
+ pub fn in_flight_coordinate(&self) -> StreamCoordinate {
181
+ self.in_flight_coord
182
+ }
183
+
184
+ #[inline]
185
+ pub fn committed_batch_id(&self) -> u64 {
186
+ self.committed_batch_id
187
+ }
188
+
189
+ #[inline]
190
+ pub fn set_in_flight_coordinate(&mut self, next_coord: StreamCoordinate) {
191
+ self.in_flight_coord = next_coord;
192
+ }
193
+
194
+ pub fn stage_batch_in_ram(&mut self, batch_id: u64, next_coord: StreamCoordinate) {
195
+ self.in_flight_coord = next_coord;
196
+ self.staged_coords.insert(batch_id, next_coord);
197
+ }
198
+
199
+ pub fn commit_gpu_batch(&mut self, batch_id: u64) -> Result<StreamCoordinate> {
200
+ let Some(next_coord) = self.staged_coords.remove(&batch_id) else {
201
+ return Err(DataLoaderError::UnknownBatchId(batch_id));
202
+ };
203
+
204
+ let prev_dedup_count = self.deduplicator.committed_len() as u64;
205
+ let new_hashes = self.deduplicator.commit_batch(batch_id);
206
+ if !new_hashes.is_empty() {
207
+ let mut hash_buf = Vec::with_capacity(new_hashes.len() * 8);
208
+ for h in new_hashes {
209
+ hash_buf.extend_from_slice(&h.to_le_bytes());
210
+ }
211
+ let write_offset = prev_dedup_count * 8;
212
+ self.dedup_file.seek(SeekFrom::Start(write_offset))?;
213
+ self.dedup_file.write_all(&hash_buf)?;
214
+ self.dedup_file
215
+ .set_len(write_offset + hash_buf.len() as u64)?;
216
+ self.dedup_file.sync_data()?;
217
+ }
218
+
219
+ self.epoch += 1;
220
+ self.committed_batch_id = batch_id;
221
+ self.committed_coord = next_coord;
222
+
223
+ let slot = DiskSlot {
224
+ epoch: self.epoch,
225
+ committed_batch_id: self.committed_batch_id,
226
+ coord: self.committed_coord,
227
+ dedup_count: self.deduplicator.committed_len() as u64,
228
+ };
229
+
230
+ let slot_offset = if self.epoch % 2 == 1 {
231
+ 0
232
+ } else {
233
+ SLOT_SIZE as u64
234
+ };
235
+
236
+ let encoded = slot.encode();
237
+ self.meta_file.seek(SeekFrom::Start(slot_offset))?;
238
+ self.meta_file.write_all(&encoded)?;
239
+ self.meta_file.sync_data()?;
240
+
241
+ Ok(self.committed_coord)
242
+ }
243
+
244
+ pub fn rollback_staged_batch(&mut self, batch_id: u64) -> Result<StreamCoordinate> {
245
+ if self.staged_coords.remove(&batch_id).is_none() {
246
+ return Err(DataLoaderError::UnknownBatchId(batch_id));
247
+ }
248
+ self.deduplicator.rollback_batch(batch_id);
249
+ self.in_flight_coord = self
250
+ .staged_coords
251
+ .iter()
252
+ .max_by_key(|&(&id, _)| id)
253
+ .map(|(_, &coord)| coord)
254
+ .unwrap_or(self.committed_coord);
255
+ Ok(self.in_flight_coord)
256
+ }
257
+
258
+ pub fn rollback_ram_to_committed(&mut self) -> StreamCoordinate {
259
+ self.staged_coords.clear();
260
+ self.deduplicator.clear_staged();
261
+ self.in_flight_coord = self.committed_coord;
262
+ self.committed_coord
263
+ }
264
+ }
data/src/minhash.rs CHANGED
@@ -1,186 +1,186 @@
1
- use hashbrown::{HashMap, HashSet};
2
- use xxhash_rust::xxh3::{xxh3_64, xxh3_64_with_seed};
3
-
4
- pub const DEFAULT_MINHASH_PERMUTATIONS: usize = 64;
5
-
6
- #[derive(Debug, Clone)]
7
- pub struct MinHashSignature {
8
- pub values: Vec<u64>,
9
- }
10
-
11
- impl MinHashSignature {
12
- pub fn compute_char_shingles(text: &str, k: usize, num_perm: usize) -> Self {
13
- let mut mins = vec![u64::MAX; num_perm];
14
- if text.is_empty() {
15
- return Self { values: mins };
16
- }
17
-
18
- if text.is_ascii() {
19
- let raw = text.as_bytes();
20
- let effective_k = k.min(raw.len()).max(1);
21
- for bytes in raw.windows(effective_k) {
22
- for (i, min_val) in mins.iter_mut().enumerate() {
23
- let seed = (i as u64).wrapping_mul(0x517cc1b727220a95);
24
- let h = xxh3_64_with_seed(bytes, seed);
25
- if h < *min_val {
26
- *min_val = h;
27
- }
28
- }
29
- }
30
- return Self { values: mins };
31
- }
32
-
33
- let char_offsets: Vec<usize> = text.char_indices().map(|(idx, _)| idx).collect();
34
- let num_chars = char_offsets.len();
35
- let effective_k = k.min(num_chars).max(1);
36
-
37
- for start_idx in 0..=(num_chars - effective_k) {
38
- let byte_start = char_offsets[start_idx];
39
- let byte_end = if start_idx + effective_k < num_chars {
40
- char_offsets[start_idx + effective_k]
41
- } else {
42
- text.len()
43
- };
44
- let bytes = &text.as_bytes()[byte_start..byte_end];
45
-
46
- for (i, min_val) in mins.iter_mut().enumerate() {
47
- let seed = (i as u64).wrapping_mul(0x517cc1b727220a95);
48
- let h = xxh3_64_with_seed(bytes, seed);
49
- if h < *min_val {
50
- *min_val = h;
51
- }
52
- }
53
- }
54
-
55
- Self { values: mins }
56
- }
57
-
58
- pub fn jaccard_similarity(&self, other: &Self) -> f64 {
59
- if self.values.is_empty() || self.values.len() != other.values.len() {
60
- return 0.0;
61
- }
62
-
63
- let mut matches = 0usize;
64
- for (a, b) in self.values.iter().zip(other.values.iter()) {
65
- if a == b {
66
- matches += 1;
67
- }
68
- }
69
-
70
- (matches as f64) / (self.values.len() as f64)
71
- }
72
- }
73
-
74
- pub struct MinHashDeduplicator {
75
- threshold: f64,
76
- k_shingle: usize,
77
- num_perm: usize,
78
- num_bands: usize,
79
- rows_per_band: usize,
80
- signatures: Vec<MinHashSignature>,
81
- lsh_buckets: HashMap<(usize, u64), Vec<usize>>,
82
- }
83
-
84
- impl MinHashDeduplicator {
85
- pub fn new(threshold: f64, k_shingle: usize, num_perm: usize) -> Self {
86
- let n_perm = num_perm.max(8);
87
- let num_bands = if n_perm >= 64 { 8 } else { 4.min(n_perm) };
88
- let rows_per_band = n_perm / num_bands;
89
-
90
- Self {
91
- threshold: threshold.clamp(0.0, 1.0),
92
- k_shingle: k_shingle.max(1),
93
- num_perm: n_perm,
94
- num_bands,
95
- rows_per_band,
96
- signatures: Vec::new(),
97
- lsh_buckets: HashMap::new(),
98
- }
99
- }
100
-
101
- pub fn compute_signature(&self, text: &str) -> MinHashSignature {
102
- MinHashSignature::compute_char_shingles(text, self.k_shingle, self.num_perm)
103
- }
104
-
105
- fn band_hash(&self, sig: &MinHashSignature, band_idx: usize) -> u64 {
106
- let start = band_idx * self.rows_per_band;
107
- let end = (start + self.rows_per_band).min(sig.values.len());
108
- let slice = &sig.values[start..end];
109
- let mut byte_buf = Vec::with_capacity(slice.len() * 8);
110
- for &v in slice {
111
- byte_buf.extend_from_slice(&v.to_le_bytes());
112
- }
113
- xxh3_64(&byte_buf)
114
- }
115
-
116
- pub fn max_similarity(&self, sig: &MinHashSignature) -> f64 {
117
- if self.signatures.is_empty() {
118
- return 0.0;
119
- }
120
-
121
- // For datasets under 128 items, perform exact scan for 100% precision
122
- if self.signatures.len() <= 128 {
123
- let mut max_sim = 0.0f64;
124
- for existing in &self.signatures {
125
- let sim = sig.jaccard_similarity(existing);
126
- if sim > max_sim {
127
- max_sim = sim;
128
- if max_sim >= self.threshold {
129
- return max_sim;
130
- }
131
- }
132
- }
133
- return max_sim;
134
- }
135
-
136
- // For larger datasets, query LSH candidate buckets in O(1) amortized time
137
- let mut candidates = HashSet::new();
138
- for b in 0..self.num_bands {
139
- let bh = self.band_hash(sig, b);
140
- if let Some(doc_ids) = self.lsh_buckets.get(&(b, bh)) {
141
- for &id in doc_ids {
142
- candidates.insert(id);
143
- }
144
- }
145
- }
146
-
147
- let mut max_sim = 0.0f64;
148
- for &cand_id in &candidates {
149
- if let Some(existing) = self.signatures.get(cand_id) {
150
- let sim = sig.jaccard_similarity(existing);
151
- if sim > max_sim {
152
- max_sim = sim;
153
- if max_sim >= self.threshold {
154
- return max_sim;
155
- }
156
- }
157
- }
158
- }
159
- max_sim
160
- }
161
-
162
- pub fn is_unique(&mut self, text: &str) -> bool {
163
- let sig = self.compute_signature(text);
164
- let max_sim = self.max_similarity(&sig);
165
- if max_sim >= self.threshold {
166
- return false;
167
- }
168
-
169
- let doc_id = self.signatures.len();
170
- for b in 0..self.num_bands {
171
- let bh = self.band_hash(&sig, b);
172
- self.lsh_buckets.entry((b, bh)).or_default().push(doc_id);
173
- }
174
- self.signatures.push(sig);
175
- true
176
- }
177
-
178
- pub fn count(&self) -> usize {
179
- self.signatures.len()
180
- }
181
-
182
- pub fn clear(&mut self) {
183
- self.signatures.clear();
184
- self.lsh_buckets.clear();
185
- }
186
- }
 
1
+ use hashbrown::{HashMap, HashSet};
2
+ use xxhash_rust::xxh3::{xxh3_64, xxh3_64_with_seed};
3
+
4
+ pub const DEFAULT_MINHASH_PERMUTATIONS: usize = 64;
5
+
6
+ #[derive(Debug, Clone)]
7
+ pub struct MinHashSignature {
8
+ pub values: Vec<u64>,
9
+ }
10
+
11
+ impl MinHashSignature {
12
+ pub fn compute_char_shingles(text: &str, k: usize, num_perm: usize) -> Self {
13
+ let mut mins = vec![u64::MAX; num_perm];
14
+ if text.is_empty() {
15
+ return Self { values: mins };
16
+ }
17
+
18
+ if text.is_ascii() {
19
+ let raw = text.as_bytes();
20
+ let effective_k = k.min(raw.len()).max(1);
21
+ for bytes in raw.windows(effective_k) {
22
+ for (i, min_val) in mins.iter_mut().enumerate() {
23
+ let seed = (i as u64).wrapping_mul(0x517cc1b727220a95);
24
+ let h = xxh3_64_with_seed(bytes, seed);
25
+ if h < *min_val {
26
+ *min_val = h;
27
+ }
28
+ }
29
+ }
30
+ return Self { values: mins };
31
+ }
32
+
33
+ let char_offsets: Vec<usize> = text.char_indices().map(|(idx, _)| idx).collect();
34
+ let num_chars = char_offsets.len();
35
+ let effective_k = k.min(num_chars).max(1);
36
+
37
+ for start_idx in 0..=(num_chars - effective_k) {
38
+ let byte_start = char_offsets[start_idx];
39
+ let byte_end = if start_idx + effective_k < num_chars {
40
+ char_offsets[start_idx + effective_k]
41
+ } else {
42
+ text.len()
43
+ };
44
+ let bytes = &text.as_bytes()[byte_start..byte_end];
45
+
46
+ for (i, min_val) in mins.iter_mut().enumerate() {
47
+ let seed = (i as u64).wrapping_mul(0x517cc1b727220a95);
48
+ let h = xxh3_64_with_seed(bytes, seed);
49
+ if h < *min_val {
50
+ *min_val = h;
51
+ }
52
+ }
53
+ }
54
+
55
+ Self { values: mins }
56
+ }
57
+
58
+ pub fn jaccard_similarity(&self, other: &Self) -> f64 {
59
+ if self.values.is_empty() || self.values.len() != other.values.len() {
60
+ return 0.0;
61
+ }
62
+
63
+ let mut matches = 0usize;
64
+ for (a, b) in self.values.iter().zip(other.values.iter()) {
65
+ if a == b {
66
+ matches += 1;
67
+ }
68
+ }
69
+
70
+ (matches as f64) / (self.values.len() as f64)
71
+ }
72
+ }
73
+
74
+ pub struct MinHashDeduplicator {
75
+ threshold: f64,
76
+ k_shingle: usize,
77
+ num_perm: usize,
78
+ num_bands: usize,
79
+ rows_per_band: usize,
80
+ signatures: Vec<MinHashSignature>,
81
+ lsh_buckets: HashMap<(usize, u64), Vec<usize>>,
82
+ }
83
+
84
+ impl MinHashDeduplicator {
85
+ pub fn new(threshold: f64, k_shingle: usize, num_perm: usize) -> Self {
86
+ let n_perm = num_perm.max(8);
87
+ let num_bands = if n_perm >= 64 { 8 } else { 4.min(n_perm) };
88
+ let rows_per_band = n_perm / num_bands;
89
+
90
+ Self {
91
+ threshold: threshold.clamp(0.0, 1.0),
92
+ k_shingle: k_shingle.max(1),
93
+ num_perm: n_perm,
94
+ num_bands,
95
+ rows_per_band,
96
+ signatures: Vec::new(),
97
+ lsh_buckets: HashMap::new(),
98
+ }
99
+ }
100
+
101
+ pub fn compute_signature(&self, text: &str) -> MinHashSignature {
102
+ MinHashSignature::compute_char_shingles(text, self.k_shingle, self.num_perm)
103
+ }
104
+
105
+ fn band_hash(&self, sig: &MinHashSignature, band_idx: usize) -> u64 {
106
+ let start = band_idx * self.rows_per_band;
107
+ let end = (start + self.rows_per_band).min(sig.values.len());
108
+ let slice = &sig.values[start..end];
109
+ let mut byte_buf = Vec::with_capacity(slice.len() * 8);
110
+ for &v in slice {
111
+ byte_buf.extend_from_slice(&v.to_le_bytes());
112
+ }
113
+ xxh3_64(&byte_buf)
114
+ }
115
+
116
+ pub fn max_similarity(&self, sig: &MinHashSignature) -> f64 {
117
+ if self.signatures.is_empty() {
118
+ return 0.0;
119
+ }
120
+
121
+ // For datasets under 128 items, perform exact scan for 100% precision
122
+ if self.signatures.len() <= 128 {
123
+ let mut max_sim = 0.0f64;
124
+ for existing in &self.signatures {
125
+ let sim = sig.jaccard_similarity(existing);
126
+ if sim > max_sim {
127
+ max_sim = sim;
128
+ if max_sim >= self.threshold {
129
+ return max_sim;
130
+ }
131
+ }
132
+ }
133
+ return max_sim;
134
+ }
135
+
136
+ // For larger datasets, query LSH candidate buckets in O(1) amortized time
137
+ let mut candidates = HashSet::new();
138
+ for b in 0..self.num_bands {
139
+ let bh = self.band_hash(sig, b);
140
+ if let Some(doc_ids) = self.lsh_buckets.get(&(b, bh)) {
141
+ for &id in doc_ids {
142
+ candidates.insert(id);
143
+ }
144
+ }
145
+ }
146
+
147
+ let mut max_sim = 0.0f64;
148
+ for &cand_id in &candidates {
149
+ if let Some(existing) = self.signatures.get(cand_id) {
150
+ let sim = sig.jaccard_similarity(existing);
151
+ if sim > max_sim {
152
+ max_sim = sim;
153
+ if max_sim >= self.threshold {
154
+ return max_sim;
155
+ }
156
+ }
157
+ }
158
+ }
159
+ max_sim
160
+ }
161
+
162
+ pub fn is_unique(&mut self, text: &str) -> bool {
163
+ let sig = self.compute_signature(text);
164
+ let max_sim = self.max_similarity(&sig);
165
+ if max_sim >= self.threshold {
166
+ return false;
167
+ }
168
+
169
+ let doc_id = self.signatures.len();
170
+ for b in 0..self.num_bands {
171
+ let bh = self.band_hash(&sig, b);
172
+ self.lsh_buckets.entry((b, bh)).or_default().push(doc_id);
173
+ }
174
+ self.signatures.push(sig);
175
+ true
176
+ }
177
+
178
+ pub fn count(&self) -> usize {
179
+ self.signatures.len()
180
+ }
181
+
182
+ pub fn clear(&mut self) {
183
+ self.signatures.clear();
184
+ self.lsh_buckets.clear();
185
+ }
186
+ }
data/src/postprocess.rs CHANGED
@@ -1,135 +1,135 @@
1
- use rayon::prelude::*;
2
- use serde_json::Value;
3
-
4
- #[derive(Debug, Clone)]
5
- pub struct PostProcessConfig {
6
- pub trim_whitespace: bool,
7
- pub normalize_newlines: bool,
8
- pub min_byte_len: usize,
9
- pub max_byte_len: usize,
10
- pub truncate_over_max: bool,
11
- pub json_extract_key: Option<String>,
12
- }
13
-
14
- impl Default for PostProcessConfig {
15
- fn default() -> Self {
16
- Self {
17
- trim_whitespace: true,
18
- normalize_newlines: true,
19
- min_byte_len: 0,
20
- max_byte_len: 0,
21
- truncate_over_max: false,
22
- json_extract_key: None,
23
- }
24
- }
25
- }
26
-
27
- pub fn extract_json_value_by_path<'a>(root: &'a Value, key_path: &str) -> Option<&'a Value> {
28
- if let Some(direct) = root.get(key_path) {
29
- return Some(direct);
30
- }
31
- if !key_path.contains('.') {
32
- return None;
33
- }
34
- let mut current = root;
35
- for segment in key_path.split('.') {
36
- if segment.is_empty() {
37
- continue;
38
- }
39
- match current {
40
- Value::Object(map) => {
41
- current = map.get(segment)?;
42
- }
43
- Value::Array(arr) => {
44
- let idx: isize = segment.parse().ok()?;
45
- let resolved = if idx < 0 {
46
- let abs = idx.unsigned_abs();
47
- if abs > arr.len() {
48
- return None;
49
- }
50
- arr.len() - abs
51
- } else {
52
- idx as usize
53
- };
54
- current = arr.get(resolved)?;
55
- }
56
- _ => return None,
57
- }
58
- }
59
- Some(current)
60
- }
61
-
62
- fn json_value_to_extracted_string(val: &Value) -> Option<String> {
63
- match val {
64
- Value::Null => None,
65
- Value::String(s) => Some(s.clone()),
66
- Value::Bool(b) => Some(b.to_string()),
67
- Value::Number(n) => Some(n.to_string()),
68
- Value::Array(_) | Value::Object(_) => Some(val.to_string()),
69
- }
70
- }
71
-
72
- pub fn collect_json_object_keys(raw_line: &str) -> Option<Vec<String>> {
73
- let parsed: Value = serde_json::from_str(raw_line).ok()?;
74
- let obj = parsed.as_object()?;
75
- Some(obj.keys().cloned().collect())
76
- }
77
-
78
- pub fn process_single_record(raw: &str, cfg: &PostProcessConfig) -> Option<String> {
79
- let mut text = if let Some(key) = &cfg.json_extract_key {
80
- let parsed: Value = serde_json::from_str(raw).ok()?;
81
- let val = extract_json_value_by_path(&parsed, key)?;
82
- json_value_to_extracted_string(val)?
83
- } else {
84
- raw.to_owned()
85
- };
86
-
87
- if cfg.normalize_newlines && crate::simd_cpu::contains_byte_fast(text.as_bytes(), b'\r') {
88
- text = text.replace("\r\n", "\n").replace('\r', "\n");
89
- }
90
-
91
- if cfg.trim_whitespace {
92
- let trimmed = text.trim();
93
- if trimmed.len() != text.len() {
94
- text = trimmed.to_owned();
95
- }
96
- }
97
-
98
- if cfg.min_byte_len > 0 && text.len() < cfg.min_byte_len {
99
- return None;
100
- }
101
-
102
- if cfg.max_byte_len > 0 && text.len() > cfg.max_byte_len {
103
- if cfg.truncate_over_max {
104
- let mut cut = cfg.max_byte_len;
105
- while cut > 0 && !text.is_char_boundary(cut) {
106
- cut -= 1;
107
- }
108
- text.truncate(cut);
109
- if cfg.min_byte_len > 0 && text.len() < cfg.min_byte_len {
110
- return None;
111
- }
112
- } else {
113
- return None;
114
- }
115
- }
116
-
117
- Some(text)
118
- }
119
-
120
- pub fn process_batch_parallel(raw_records: &[String], cfg: &PostProcessConfig) -> Vec<String> {
121
- raw_records
122
- .par_iter()
123
- .filter_map(|item| process_single_record(item, cfg))
124
- .collect()
125
- }
126
-
127
- pub fn process_batch_parallel_indexed(
128
- raw_records: &[String],
129
- cfg: &PostProcessConfig,
130
- ) -> Vec<Option<String>> {
131
- raw_records
132
- .par_iter()
133
- .map(|item| process_single_record(item, cfg))
134
- .collect()
135
- }
 
1
+ use rayon::prelude::*;
2
+ use serde_json::Value;
3
+
4
+ #[derive(Debug, Clone)]
5
+ pub struct PostProcessConfig {
6
+ pub trim_whitespace: bool,
7
+ pub normalize_newlines: bool,
8
+ pub min_byte_len: usize,
9
+ pub max_byte_len: usize,
10
+ pub truncate_over_max: bool,
11
+ pub json_extract_key: Option<String>,
12
+ }
13
+
14
+ impl Default for PostProcessConfig {
15
+ fn default() -> Self {
16
+ Self {
17
+ trim_whitespace: true,
18
+ normalize_newlines: true,
19
+ min_byte_len: 0,
20
+ max_byte_len: 0,
21
+ truncate_over_max: false,
22
+ json_extract_key: None,
23
+ }
24
+ }
25
+ }
26
+
27
+ pub fn extract_json_value_by_path<'a>(root: &'a Value, key_path: &str) -> Option<&'a Value> {
28
+ if let Some(direct) = root.get(key_path) {
29
+ return Some(direct);
30
+ }
31
+ if !key_path.contains('.') {
32
+ return None;
33
+ }
34
+ let mut current = root;
35
+ for segment in key_path.split('.') {
36
+ if segment.is_empty() {
37
+ continue;
38
+ }
39
+ match current {
40
+ Value::Object(map) => {
41
+ current = map.get(segment)?;
42
+ }
43
+ Value::Array(arr) => {
44
+ let idx: isize = segment.parse().ok()?;
45
+ let resolved = if idx < 0 {
46
+ let abs = idx.unsigned_abs();
47
+ if abs > arr.len() {
48
+ return None;
49
+ }
50
+ arr.len() - abs
51
+ } else {
52
+ idx as usize
53
+ };
54
+ current = arr.get(resolved)?;
55
+ }
56
+ _ => return None,
57
+ }
58
+ }
59
+ Some(current)
60
+ }
61
+
62
+ fn json_value_to_extracted_string(val: &Value) -> Option<String> {
63
+ match val {
64
+ Value::Null => None,
65
+ Value::String(s) => Some(s.clone()),
66
+ Value::Bool(b) => Some(b.to_string()),
67
+ Value::Number(n) => Some(n.to_string()),
68
+ Value::Array(_) | Value::Object(_) => Some(val.to_string()),
69
+ }
70
+ }
71
+
72
+ pub fn collect_json_object_keys(raw_line: &str) -> Option<Vec<String>> {
73
+ let parsed: Value = serde_json::from_str(raw_line).ok()?;
74
+ let obj = parsed.as_object()?;
75
+ Some(obj.keys().cloned().collect())
76
+ }
77
+
78
+ pub fn process_single_record(raw: &str, cfg: &PostProcessConfig) -> Option<String> {
79
+ let mut text = if let Some(key) = &cfg.json_extract_key {
80
+ let parsed: Value = serde_json::from_str(raw).ok()?;
81
+ let val = extract_json_value_by_path(&parsed, key)?;
82
+ json_value_to_extracted_string(val)?
83
+ } else {
84
+ raw.to_owned()
85
+ };
86
+
87
+ if cfg.normalize_newlines && crate::simd_cpu::contains_byte_fast(text.as_bytes(), b'\r') {
88
+ text = text.replace("\r\n", "\n").replace('\r', "\n");
89
+ }
90
+
91
+ if cfg.trim_whitespace {
92
+ let trimmed = text.trim();
93
+ if trimmed.len() != text.len() {
94
+ text = trimmed.to_owned();
95
+ }
96
+ }
97
+
98
+ if cfg.min_byte_len > 0 && text.len() < cfg.min_byte_len {
99
+ return None;
100
+ }
101
+
102
+ if cfg.max_byte_len > 0 && text.len() > cfg.max_byte_len {
103
+ if cfg.truncate_over_max {
104
+ let mut cut = cfg.max_byte_len;
105
+ while cut > 0 && !text.is_char_boundary(cut) {
106
+ cut -= 1;
107
+ }
108
+ text.truncate(cut);
109
+ if cfg.min_byte_len > 0 && text.len() < cfg.min_byte_len {
110
+ return None;
111
+ }
112
+ } else {
113
+ return None;
114
+ }
115
+ }
116
+
117
+ Some(text)
118
+ }
119
+
120
+ pub fn process_batch_parallel(raw_records: &[String], cfg: &PostProcessConfig) -> Vec<String> {
121
+ raw_records
122
+ .par_iter()
123
+ .filter_map(|item| process_single_record(item, cfg))
124
+ .collect()
125
+ }
126
+
127
+ pub fn process_batch_parallel_indexed(
128
+ raw_records: &[String],
129
+ cfg: &PostProcessConfig,
130
+ ) -> Vec<Option<String>> {
131
+ raw_records
132
+ .par_iter()
133
+ .map(|item| process_single_record(item, cfg))
134
+ .collect()
135
+ }
data/src/py_bridge.rs CHANGED
@@ -1,839 +1,839 @@
1
- use std::fs::File;
2
- use std::io::{Read, Write};
3
-
4
- use parking_lot::Mutex;
5
- use pyo3::exceptions::{PyRuntimeError, PyValueError};
6
- use pyo3::prelude::*;
7
- use pyo3::types::{PyBool, PyByteArray, PyBytes, PyDict, PyFloat, PyInt, PySequence, PyString};
8
- use rayon::prelude::*;
9
-
10
- use crate::coord::StreamCoordinate;
11
- use crate::dedup::TwoStageDeduplicator;
12
- use crate::error::{DataLoaderError, find_closest_matches};
13
- use crate::metadata::DualMetadataManager;
14
- use crate::postprocess::PostProcessConfig;
15
- use crate::quality::QualityConfig;
16
- use crate::serialize::{DataValue, classify_string_or_json, serialize_columns_by_format};
17
-
18
- fn to_py_err(err: DataLoaderError) -> PyErr {
19
- match err {
20
- DataLoaderError::InvalidConfig(msg) | DataLoaderError::ColumnNotFound(msg) => {
21
- PyValueError::new_err(msg)
22
- }
23
- DataLoaderError::InvalidCoordinate => PyValueError::new_err(err.to_string()),
24
- other => PyRuntimeError::new_err(other.to_string()),
25
- }
26
- }
27
-
28
- #[pyclass(frozen, name = "StreamCoordinate")]
29
- #[derive(Clone, Copy)]
30
- pub struct PyStreamCoordinate {
31
- #[pyo3(get)]
32
- pub shard_id: u32,
33
- #[pyo3(get)]
34
- pub byte_offset: u64,
35
- #[pyo3(get)]
36
- pub row_group_idx: u32,
37
- #[pyo3(get)]
38
- pub page_idx: u32,
39
- #[pyo3(get)]
40
- pub row_offset_in_page: u32,
41
- #[pyo3(get)]
42
- pub global_row_idx: u64,
43
- }
44
-
45
- #[pymethods]
46
- impl PyStreamCoordinate {
47
- #[new]
48
- #[pyo3(signature = (
49
- shard_id = 0,
50
- byte_offset = 0,
51
- row_group_idx = 0,
52
- page_idx = 0,
53
- row_offset_in_page = 0,
54
- global_row_idx = 0,
55
- ))]
56
- pub fn new(
57
- shard_id: u32,
58
- byte_offset: u64,
59
- row_group_idx: u32,
60
- page_idx: u32,
61
- row_offset_in_page: u32,
62
- global_row_idx: u64,
63
- ) -> Self {
64
- Self {
65
- shard_id,
66
- byte_offset,
67
- row_group_idx,
68
- page_idx,
69
- row_offset_in_page,
70
- global_row_idx,
71
- }
72
- }
73
-
74
- pub fn to_bytes<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
75
- let coord = StreamCoordinate {
76
- shard_id: self.shard_id,
77
- byte_offset: self.byte_offset,
78
- row_group_idx: self.row_group_idx,
79
- page_idx: self.page_idx,
80
- row_offset_in_page: self.row_offset_in_page,
81
- global_row_idx: self.global_row_idx,
82
- };
83
- PyBytes::new(py, &coord.to_bytes())
84
- }
85
-
86
- #[staticmethod]
87
- pub fn from_bytes(raw: &[u8]) -> PyResult<Self> {
88
- if raw.len() != StreamCoordinate::BYTE_LEN {
89
- return Err(PyValueError::new_err(format!(
90
- "Invalid coordinate length: expected {} bytes, got {}",
91
- StreamCoordinate::BYTE_LEN,
92
- raw.len()
93
- )));
94
- }
95
- let arr: &[u8; StreamCoordinate::BYTE_LEN] = raw.try_into().unwrap();
96
- let c = StreamCoordinate::from_bytes(arr);
97
- Ok(Self {
98
- shard_id: c.shard_id,
99
- byte_offset: c.byte_offset,
100
- row_group_idx: c.row_group_idx,
101
- page_idx: c.page_idx,
102
- row_offset_in_page: c.row_offset_in_page,
103
- global_row_idx: c.global_row_idx,
104
- })
105
- }
106
-
107
- pub fn __repr__(&self) -> String {
108
- format!(
109
- "StreamCoordinate(shard_id={}, byte_offset={}, row_group={}, page={}, row_offset={}, global_row={})",
110
- self.shard_id,
111
- self.byte_offset,
112
- self.row_group_idx,
113
- self.page_idx,
114
- self.row_offset_in_page,
115
- self.global_row_idx
116
- )
117
- }
118
- }
119
-
120
- #[pyclass(name = "StreamDeduplicator")]
121
- pub struct PyStreamDeduplicator {
122
- inner: Mutex<TwoStageDeduplicator>,
123
- }
124
-
125
- #[pymethods]
126
- impl PyStreamDeduplicator {
127
- #[new]
128
- #[pyo3(signature = (initial_hashes = None))]
129
- pub fn new(initial_hashes: Option<Vec<u64>>) -> Self {
130
- let dedup = match initial_hashes {
131
- Some(hashes) => TwoStageDeduplicator::with_committed(hashes),
132
- None => TwoStageDeduplicator::new(),
133
- };
134
- Self {
135
- inner: Mutex::new(dedup),
136
- }
137
- }
138
-
139
- #[staticmethod]
140
- pub fn compute_hash(payload: &[u8]) -> u64 {
141
- TwoStageDeduplicator::compute_hash(payload)
142
- }
143
-
144
- pub fn is_seen(&self, payload: &[u8]) -> bool {
145
- self.inner.lock().is_seen(payload)
146
- }
147
-
148
- pub fn try_add(&self, payload: &[u8]) -> bool {
149
- let mut dedup = self.inner.lock();
150
- dedup.try_stage(1, payload).is_some()
151
- }
152
-
153
- pub fn filter_batch(&self, records: Vec<String>) -> Vec<String> {
154
- let mut dedup = self.inner.lock();
155
- let mut out = Vec::with_capacity(records.len());
156
- for rec in records {
157
- if dedup.try_stage(1, rec.as_bytes()).is_some() {
158
- out.push(rec);
159
- }
160
- }
161
- out
162
- }
163
-
164
- pub fn commit(&self) -> usize {
165
- let mut dedup = self.inner.lock();
166
- dedup.commit_batch(1).len()
167
- }
168
-
169
- pub fn rollback(&self) -> usize {
170
- let mut dedup = self.inner.lock();
171
- dedup.rollback_batch(1)
172
- }
173
-
174
- pub fn clear(&self) {
175
- let mut dedup = self.inner.lock();
176
- dedup.clear_staged();
177
- }
178
-
179
- pub fn count(&self) -> usize {
180
- self.inner.lock().committed_len()
181
- }
182
-
183
- pub fn save_wal(&self, path: &str) -> PyResult<usize> {
184
- let dedup = self.inner.lock();
185
- let hashes = dedup.export_committed_hashes();
186
- let mut buf = Vec::with_capacity(hashes.len() * 8);
187
- for h in &hashes {
188
- buf.extend_from_slice(&h.to_le_bytes());
189
- }
190
- let mut file = File::create(path).map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
191
- file.write_all(&buf)
192
- .map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
193
- file.sync_data()
194
- .map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
195
- Ok(hashes.len())
196
- }
197
-
198
- pub fn load_wal(&self, path: &str) -> PyResult<usize> {
199
- let mut file = match File::open(path) {
200
- Ok(f) => f,
201
- Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(0),
202
- Err(e) => return Err(PyRuntimeError::new_err(e.to_string())),
203
- };
204
- let mut buf = Vec::new();
205
- file.read_to_end(&mut buf)
206
- .map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
207
- if buf.len() % 8 != 0 {
208
- return Err(PyValueError::new_err(format!(
209
- "Corrupted WAL file '{}': byte length {} is not a multiple of 8",
210
- path,
211
- buf.len()
212
- )));
213
- }
214
- let count = buf.len() / 8;
215
- let mut dedup = self.inner.lock();
216
- let (chunks, _) = buf.as_chunks::<8>();
217
- for chunk in chunks {
218
- let h = u64::from_le_bytes(*chunk);
219
- dedup.import_committed_hashes(std::iter::once(h));
220
- }
221
- Ok(count)
222
- }
223
-
224
- pub fn export_hashes(&self) -> Vec<u64> {
225
- self.inner.lock().export_committed_hashes()
226
- }
227
- }
228
-
229
- #[pyclass(name = "StateTracker")]
230
- pub struct PyStateTracker {
231
- inner: Mutex<DualMetadataManager>,
232
- }
233
-
234
- #[pymethods]
235
- impl PyStateTracker {
236
- #[new]
237
- pub fn new(meta_path: &str) -> PyResult<Self> {
238
- let mgr = DualMetadataManager::open(meta_path).map_err(to_py_err)?;
239
- Ok(Self {
240
- inner: Mutex::new(mgr),
241
- })
242
- }
243
-
244
- pub fn read(&self) -> PyStreamCoordinate {
245
- let mgr = self.inner.lock();
246
- let c = mgr.committed_coordinate();
247
- PyStreamCoordinate {
248
- shard_id: c.shard_id,
249
- byte_offset: c.byte_offset,
250
- row_group_idx: c.row_group_idx,
251
- page_idx: c.page_idx,
252
- row_offset_in_page: c.row_offset_in_page,
253
- global_row_idx: c.global_row_idx,
254
- }
255
- }
256
-
257
- #[pyo3(signature = (
258
- shard_id = 0,
259
- byte_offset = 0,
260
- row_group_idx = 0,
261
- page_idx = 0,
262
- row_offset_in_page = 0,
263
- global_row_idx = 0,
264
- batch_id = 1
265
- ))]
266
- #[allow(clippy::too_many_arguments)]
267
- pub fn update(
268
- &self,
269
- shard_id: u32,
270
- byte_offset: u64,
271
- row_group_idx: u32,
272
- page_idx: u32,
273
- row_offset_in_page: u32,
274
- global_row_idx: u64,
275
- batch_id: u64,
276
- ) {
277
- let coord = StreamCoordinate {
278
- shard_id,
279
- byte_offset,
280
- row_group_idx,
281
- page_idx,
282
- row_offset_in_page,
283
- global_row_idx,
284
- };
285
- let mut mgr = self.inner.lock();
286
- mgr.stage_batch_in_ram(batch_id, coord);
287
- }
288
-
289
- #[pyo3(signature = (batch_id = 1))]
290
- pub fn commit(&self, batch_id: u64) -> PyResult<PyStreamCoordinate> {
291
- let mut mgr = self.inner.lock();
292
- let c = mgr.commit_gpu_batch(batch_id).map_err(to_py_err)?;
293
- Ok(PyStreamCoordinate {
294
- shard_id: c.shard_id,
295
- byte_offset: c.byte_offset,
296
- row_group_idx: c.row_group_idx,
297
- page_idx: c.page_idx,
298
- row_offset_in_page: c.row_offset_in_page,
299
- global_row_idx: c.global_row_idx,
300
- })
301
- }
302
-
303
- pub fn committed_batch_id(&self) -> u64 {
304
- self.inner.lock().committed_batch_id()
305
- }
306
- }
307
-
308
- #[pyclass(name = "QualityFilter")]
309
- pub struct PyQualityFilter {
310
- cfg: QualityConfig,
311
- }
312
-
313
- #[pymethods]
314
- impl PyQualityFilter {
315
- #[new]
316
- #[pyo3(signature = (
317
- min_chars = 0,
318
- max_chars = 0,
319
- min_words = 0,
320
- min_entropy = 0.0,
321
- max_repetition_ratio = 1.0,
322
- max_symbol_ratio = 1.0
323
- ))]
324
- pub fn new(
325
- min_chars: usize,
326
- max_chars: usize,
327
- min_words: usize,
328
- min_entropy: f64,
329
- max_repetition_ratio: f64,
330
- max_symbol_ratio: f64,
331
- ) -> Self {
332
- Self {
333
- cfg: QualityConfig {
334
- min_chars,
335
- max_chars,
336
- min_words,
337
- min_entropy,
338
- max_repetition_ratio,
339
- max_symbol_ratio,
340
- },
341
- }
342
- }
343
-
344
- pub fn is_high_quality(&self, text: &str) -> bool {
345
- crate::quality::evaluate_quality(text, &self.cfg).is_passed
346
- }
347
-
348
- pub fn metrics(&self, text: &str) -> (usize, usize, f64, f64, f64, bool) {
349
- let m = crate::quality::evaluate_quality(text, &self.cfg);
350
- (
351
- m.char_count,
352
- m.word_count,
353
- m.entropy,
354
- m.repetition_ratio,
355
- m.symbol_ratio,
356
- m.is_passed,
357
- )
358
- }
359
-
360
- pub fn filter_flags(&self, py: Python<'_>, texts: Vec<String>) -> Vec<bool> {
361
- let cfg = self.cfg.clone();
362
- py.detach(|| crate::quality::filter_quality_batch_parallel(&texts, &cfg))
363
- }
364
-
365
- pub fn filter_batch(&self, py: Python<'_>, texts: Vec<String>) -> Vec<String> {
366
- let cfg = self.cfg.clone();
367
- py.detach(|| {
368
- texts
369
- .into_iter()
370
- .filter(|t| crate::quality::evaluate_quality(t, &cfg).is_passed)
371
- .collect()
372
- })
373
- }
374
- }
375
-
376
- #[pyclass(name = "MinHashDeduplicator")]
377
- pub struct PyMinHashDeduplicator {
378
- inner: Mutex<crate::minhash::MinHashDeduplicator>,
379
- }
380
-
381
- #[pymethods]
382
- impl PyMinHashDeduplicator {
383
- #[new]
384
- #[pyo3(signature = (threshold = 0.85, k_shingle = 5, num_perm = 64))]
385
- pub fn new(threshold: f64, k_shingle: usize, num_perm: usize) -> Self {
386
- Self {
387
- inner: Mutex::new(crate::minhash::MinHashDeduplicator::new(
388
- threshold, k_shingle, num_perm,
389
- )),
390
- }
391
- }
392
-
393
- pub fn is_unique(&self, text: &str) -> bool {
394
- self.inner.lock().is_unique(text)
395
- }
396
-
397
- pub fn similarity(&self, text_a: &str, text_b: &str) -> f64 {
398
- let dedup = self.inner.lock();
399
- let sig_a = dedup.compute_signature(text_a);
400
- let sig_b = dedup.compute_signature(text_b);
401
- sig_a.jaccard_similarity(&sig_b)
402
- }
403
-
404
- pub fn count(&self) -> usize {
405
- self.inner.lock().count()
406
- }
407
-
408
- pub fn clear(&self) {
409
- self.inner.lock().clear();
410
- }
411
- }
412
-
413
- #[pyclass(name = "StringTensor")]
414
- #[derive(Clone)]
415
- pub struct PyStringTensor {
416
- inner: crate::string_tensor::StringTensor,
417
- }
418
-
419
- #[pymethods]
420
- impl PyStringTensor {
421
- #[staticmethod]
422
- pub fn from_list(strings: Vec<String>) -> Self {
423
- Self {
424
- inner: crate::string_tensor::StringTensor::from_strings(strings),
425
- }
426
- }
427
-
428
- #[staticmethod]
429
- pub fn from_gpu_buffers(offsets: Vec<u32>, payload: &[u8]) -> Self {
430
- Self {
431
- inner: crate::string_tensor::StringTensor::from_gpu_buffers(&offsets, payload),
432
- }
433
- }
434
-
435
- pub fn to_gpu_buffers<'py>(
436
- &self,
437
- py: Python<'py>,
438
- ) -> (Bound<'py, PyBytes>, Bound<'py, PyBytes>) {
439
- let (offsets, payload) = self.inner.to_gpu_buffers();
440
- let mut offset_bytes = Vec::with_capacity(offsets.len() * 4);
441
- for &off in &offsets {
442
- offset_bytes.extend_from_slice(&off.to_le_bytes());
443
- }
444
- (PyBytes::new(py, &offset_bytes), PyBytes::new(py, &payload))
445
- }
446
-
447
- pub fn to_offsets(&self) -> Vec<u32> {
448
- self.inner.to_gpu_buffers().0
449
- }
450
-
451
- pub fn to_payload<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
452
- let (_, payload) = self.inner.to_gpu_buffers();
453
- PyBytes::new(py, &payload)
454
- }
455
-
456
- pub fn __len__(&self) -> usize {
457
- self.inner.len()
458
- }
459
-
460
- pub fn __getitem__(&self, index: usize) -> PyResult<String> {
461
- self.inner
462
- .get(index)
463
- .map(|s| s.to_owned())
464
- .ok_or_else(|| PyValueError::new_err("StringTensor index out of range"))
465
- }
466
-
467
- pub fn get(&self, index: usize) -> Option<String> {
468
- self.inner.get(index).map(|s| s.to_owned())
469
- }
470
-
471
- pub fn to_list(&self) -> Vec<String> {
472
- self.inner.to_vec()
473
- }
474
-
475
- pub fn lengths(&self) -> Vec<u32> {
476
- self.inner.lengths()
477
- }
478
-
479
- pub fn is_inlined_mask(&self) -> Vec<bool> {
480
- self.inner.is_inlined_mask()
481
- }
482
-
483
- pub fn total_bytes(&self) -> usize {
484
- self.inner.views.iter().map(|v| v.len as usize).sum()
485
- }
486
-
487
- pub fn filter_by_length(&self, min_len: u32, max_len: u32) -> Self {
488
- Self {
489
- inner: self.inner.filter_by_length(min_len, max_len),
490
- }
491
- }
492
-
493
- pub fn filter_by_prefix(&self, prefix: &str) -> Self {
494
- Self {
495
- inner: self.inner.filter_by_prefix(prefix),
496
- }
497
- }
498
-
499
- pub fn hash_all(&self, py: Python<'_>) -> Vec<u64> {
500
- py.detach(|| self.inner.hash_all_parallel())
501
- }
502
-
503
- pub fn __repr__(&self) -> String {
504
- format!(
505
- "StringTensor(count={}, inlined={})",
506
- self.inner.len(),
507
- self.inner.views.iter().filter(|v| v.is_inlined()).count()
508
- )
509
- }
510
- }
511
-
512
- pub fn extract_py_columns_table(
513
- py: Python<'_>,
514
- columns: &Bound<'_, PyAny>,
515
- ) -> PyResult<Vec<(String, Vec<DataValue>)>> {
516
- let mut rust_cols = Vec::new();
517
-
518
- if let Ok(dict) = columns.cast::<PyDict>() {
519
- for (k, v) in dict.iter() {
520
- let col_name = k.extract::<String>()?;
521
- let vals = extract_py_seq_values(py, &v)?;
522
- rust_cols.push((col_name, vals));
523
- }
524
- } else if let Ok(seq) = columns.cast::<PySequence>() {
525
- let len = seq.len()?;
526
- for i in 0..len {
527
- let item = seq.get_item(i)?;
528
- if let Ok(pair) = item.cast::<PySequence>()
529
- && pair.len()? == 2
530
- {
531
- let col_name = pair.get_item(0)?.extract::<String>()?;
532
- let col_vals_seq = pair.get_item(1)?;
533
- let vals = extract_py_seq_values(py, &col_vals_seq)?;
534
- rust_cols.push((col_name, vals));
535
- continue;
536
- }
537
- return Err(PyValueError::new_err(
538
- "columns sequence must contain (name, values) pairs or be a dict",
539
- ));
540
- }
541
- } else {
542
- return Err(PyValueError::new_err(
543
- "columns must be a dict {name: [values]} or sequence of (name, [values]) pairs",
544
- ));
545
- }
546
-
547
- Ok(rust_cols)
548
- }
549
-
550
- fn extract_py_seq_values(_py: Python<'_>, seq_obj: &Bound<'_, PyAny>) -> PyResult<Vec<DataValue>> {
551
- let seq = seq_obj
552
- .cast::<PySequence>()
553
- .map_err(|_| PyValueError::new_err("column values must be a sequence (list/tuple)"))?;
554
- let len = seq.len()?;
555
- let mut out = Vec::with_capacity(len);
556
-
557
- for i in 0..len {
558
- let item = seq.get_item(i)?;
559
- if item.is_none() {
560
- out.push(DataValue::Null);
561
- } else if let Ok(b) = item.cast::<PyBool>() {
562
- out.push(DataValue::Bool(b.is_true()));
563
- } else if let Ok(i) = item.cast::<PyInt>() {
564
- out.push(DataValue::Int(i.extract::<i64>()?));
565
- } else if let Ok(f) = item.cast::<PyFloat>() {
566
- out.push(DataValue::Float(f.extract::<f64>()?));
567
- } else if let Ok(s) = item.cast::<PyString>() {
568
- let str_val = s.to_string_lossy();
569
- out.push(classify_string_or_json(&str_val));
570
- } else if let Ok(bytes) = item.cast::<PyBytes>() {
571
- out.push(DataValue::Bytes(bytes.as_bytes().to_vec()));
572
- } else if let Ok(bytes) = item.cast::<PyByteArray>() {
573
- out.push(DataValue::Bytes(bytes.to_vec()));
574
- } else {
575
- let py_str = item.str()?;
576
- let str_repr = py_str.to_string_lossy();
577
- out.push(classify_string_or_json(&str_repr));
578
- }
579
- }
580
-
581
- Ok(out)
582
- }
583
-
584
- #[pyfunction]
585
- pub fn dataloader_cpu_isa() -> &'static str {
586
- crate::simd_cpu::detect_cpu_isa().as_str()
587
- }
588
-
589
- #[pyfunction]
590
- #[pyo3(signature = (target, candidates, max_suggestions=3))]
591
- pub fn suggest_closest_matches(
592
- target: &str,
593
- candidates: Vec<String>,
594
- max_suggestions: usize,
595
- ) -> Vec<String> {
596
- find_closest_matches(target, &candidates, max_suggestions)
597
- }
598
-
599
- #[pyfunction]
600
- #[pyo3(signature = (columns, format="parquet", hf_image_struct=true))]
601
- pub fn serialize_table_to_bytes(
602
- py: Python<'_>,
603
- columns: &Bound<'_, PyAny>,
604
- format: &str,
605
- hf_image_struct: bool,
606
- ) -> PyResult<Vec<u8>> {
607
- let rust_cols = extract_py_columns_table(py, columns)?;
608
- py.detach(|| serialize_columns_by_format(&rust_cols, format, hf_image_struct))
609
- .map_err(to_py_err)
610
- }
611
-
612
- #[pyfunction]
613
- #[pyo3(signature = (
614
- raw,
615
- trim_whitespace=true,
616
- normalize_newlines=true,
617
- min_byte_len=0,
618
- max_byte_len=0,
619
- truncate_over_max=false,
620
- json_extract_key=None
621
- ))]
622
- pub fn clean_single_record(
623
- raw: &str,
624
- trim_whitespace: bool,
625
- normalize_newlines: bool,
626
- min_byte_len: usize,
627
- max_byte_len: usize,
628
- truncate_over_max: bool,
629
- json_extract_key: Option<String>,
630
- ) -> Option<String> {
631
- let cfg = PostProcessConfig {
632
- trim_whitespace,
633
- normalize_newlines,
634
- min_byte_len,
635
- max_byte_len,
636
- truncate_over_max,
637
- json_extract_key,
638
- };
639
- crate::postprocess::process_single_record(raw, &cfg)
640
- }
641
-
642
- #[pyfunction]
643
- #[pyo3(signature = (
644
- records,
645
- trim_whitespace=true,
646
- normalize_newlines=true,
647
- min_byte_len=0,
648
- max_byte_len=0,
649
- truncate_over_max=false,
650
- json_extract_key=None
651
- ))]
652
- #[allow(clippy::too_many_arguments)]
653
- pub fn clean_batch_records(
654
- py: Python<'_>,
655
- records: Vec<String>,
656
- trim_whitespace: bool,
657
- normalize_newlines: bool,
658
- min_byte_len: usize,
659
- max_byte_len: usize,
660
- truncate_over_max: bool,
661
- json_extract_key: Option<String>,
662
- ) -> Vec<String> {
663
- let cfg = PostProcessConfig {
664
- trim_whitespace,
665
- normalize_newlines,
666
- min_byte_len,
667
- max_byte_len,
668
- truncate_over_max,
669
- json_extract_key,
670
- };
671
- py.detach(|| crate::postprocess::process_batch_parallel(&records, &cfg))
672
- }
673
-
674
- #[pyfunction]
675
- #[pyo3(signature = (
676
- records,
677
- trim_whitespace=true,
678
- normalize_newlines=true,
679
- min_byte_len=1,
680
- max_byte_len=0,
681
- truncate_over_max=false,
682
- json_extract_key=None,
683
- seen_hashes=None,
684
- deduplicate=true
685
- ))]
686
- #[allow(clippy::too_many_arguments)]
687
- pub fn postprocess_and_dedup_records(
688
- py: Python<'_>,
689
- records: Vec<String>,
690
- trim_whitespace: bool,
691
- normalize_newlines: bool,
692
- min_byte_len: usize,
693
- max_byte_len: usize,
694
- truncate_over_max: bool,
695
- json_extract_key: Option<String>,
696
- seen_hashes: Option<Vec<u64>>,
697
- deduplicate: bool,
698
- ) -> PyResult<(Vec<String>, Vec<u64>)> {
699
- let cfg = PostProcessConfig {
700
- trim_whitespace,
701
- normalize_newlines,
702
- min_byte_len,
703
- max_byte_len,
704
- truncate_over_max,
705
- json_extract_key,
706
- };
707
- Ok(py.detach(|| {
708
- let processed = crate::postprocess::process_batch_parallel_indexed(&records, &cfg);
709
- if !deduplicate {
710
- let cleaned_all: Vec<String> = processed.into_iter().flatten().collect();
711
- return (cleaned_all, Vec::new());
712
- }
713
- let mut dedup = TwoStageDeduplicator::with_committed(seen_hashes.unwrap_or_default());
714
- let mut cleaned_unique = Vec::with_capacity(processed.len());
715
- for cleaned in processed.into_iter().flatten() {
716
- if dedup.try_stage(1, cleaned.as_bytes()).is_some() {
717
- cleaned_unique.push(cleaned);
718
- }
719
- }
720
- let newly_committed = dedup.commit_batch(1);
721
- (cleaned_unique, newly_committed)
722
- }))
723
- }
724
-
725
- #[pyfunction]
726
- #[pyo3(signature = (
727
- records,
728
- trim_whitespace = true,
729
- normalize_newlines = true,
730
- min_byte_len = 0,
731
- max_byte_len = 0,
732
- truncate_over_max = false,
733
- json_extract_key = None,
734
- filter_quality = false,
735
- min_chars = 0,
736
- max_chars = 0,
737
- min_words = 0,
738
- min_entropy = 0.0,
739
- max_repetition_ratio = 1.0,
740
- max_symbol_ratio = 1.0,
741
- deduplicator = None,
742
- commit_dedup = false,
743
- ))]
744
- #[allow(clippy::too_many_arguments)]
745
- pub fn fused_pipeline_process_batch(
746
- py: Python<'_>,
747
- records: Vec<String>,
748
- trim_whitespace: bool,
749
- normalize_newlines: bool,
750
- min_byte_len: usize,
751
- max_byte_len: usize,
752
- truncate_over_max: bool,
753
- json_extract_key: Option<String>,
754
- filter_quality: bool,
755
- min_chars: usize,
756
- max_chars: usize,
757
- min_words: usize,
758
- min_entropy: f64,
759
- max_repetition_ratio: f64,
760
- max_symbol_ratio: f64,
761
- deduplicator: Option<&PyStreamDeduplicator>,
762
- commit_dedup: bool,
763
- ) -> PyResult<PyStringTensor> {
764
- let post_cfg = PostProcessConfig {
765
- trim_whitespace,
766
- normalize_newlines,
767
- min_byte_len,
768
- max_byte_len,
769
- truncate_over_max,
770
- json_extract_key,
771
- };
772
- let qual_cfg = crate::quality::QualityConfig {
773
- min_chars,
774
- max_chars,
775
- min_words,
776
- min_entropy,
777
- max_repetition_ratio,
778
- max_symbol_ratio,
779
- };
780
-
781
- let result = py.detach(|| {
782
- let cleaned_candidates: Vec<String> = records
783
- .par_iter()
784
- .filter_map(|raw| {
785
- let cleaned = crate::postprocess::process_single_record(raw, &post_cfg)?;
786
- if filter_quality {
787
- let eval = crate::quality::evaluate_quality(&cleaned, &qual_cfg);
788
- if !eval.is_passed {
789
- return None;
790
- }
791
- }
792
- Some(cleaned)
793
- })
794
- .collect();
795
-
796
- let final_strings = if let Some(dedup_wrapper) = deduplicator {
797
- let mut dedup = dedup_wrapper.inner.lock();
798
- let mut unique = Vec::with_capacity(cleaned_candidates.len());
799
- for s in cleaned_candidates {
800
- if dedup.try_stage(1, s.as_bytes()).is_some() {
801
- unique.push(s);
802
- }
803
- }
804
- if commit_dedup {
805
- dedup.commit_batch(1);
806
- }
807
- unique
808
- } else {
809
- cleaned_candidates
810
- };
811
-
812
- crate::string_tensor::StringTensor::from_strings(final_strings)
813
- });
814
-
815
- Ok(PyStringTensor { inner: result })
816
- }
817
-
818
- pub fn register_dataloader_module(parent_module: &Bound<'_, PyModule>) -> PyResult<()> {
819
- parent_module.add_class::<PyStreamCoordinate>()?;
820
- parent_module.add_class::<PyStreamDeduplicator>()?;
821
- parent_module.add_class::<PyStateTracker>()?;
822
- parent_module.add_class::<PyQualityFilter>()?;
823
- parent_module.add_class::<PyMinHashDeduplicator>()?;
824
- parent_module.add_class::<PyStringTensor>()?;
825
- parent_module.add_function(wrap_pyfunction!(dataloader_cpu_isa, parent_module)?)?;
826
- parent_module.add_function(wrap_pyfunction!(suggest_closest_matches, parent_module)?)?;
827
- parent_module.add_function(wrap_pyfunction!(serialize_table_to_bytes, parent_module)?)?;
828
- parent_module.add_function(wrap_pyfunction!(clean_single_record, parent_module)?)?;
829
- parent_module.add_function(wrap_pyfunction!(clean_batch_records, parent_module)?)?;
830
- parent_module.add_function(wrap_pyfunction!(
831
- postprocess_and_dedup_records,
832
- parent_module
833
- )?)?;
834
- parent_module.add_function(wrap_pyfunction!(
835
- fused_pipeline_process_batch,
836
- parent_module
837
- )?)?;
838
- Ok(())
839
- }
 
1
+ use std::fs::File;
2
+ use std::io::{Read, Write};
3
+
4
+ use parking_lot::Mutex;
5
+ use pyo3::exceptions::{PyRuntimeError, PyValueError};
6
+ use pyo3::prelude::*;
7
+ use pyo3::types::{PyBool, PyByteArray, PyBytes, PyDict, PyFloat, PyInt, PySequence, PyString};
8
+ use rayon::prelude::*;
9
+
10
+ use crate::coord::StreamCoordinate;
11
+ use crate::dedup::TwoStageDeduplicator;
12
+ use crate::error::{DataLoaderError, find_closest_matches};
13
+ use crate::metadata::DualMetadataManager;
14
+ use crate::postprocess::PostProcessConfig;
15
+ use crate::quality::QualityConfig;
16
+ use crate::serialize::{DataValue, classify_string_or_json, serialize_columns_by_format};
17
+
18
+ fn to_py_err(err: DataLoaderError) -> PyErr {
19
+ match err {
20
+ DataLoaderError::InvalidConfig(msg) | DataLoaderError::ColumnNotFound(msg) => {
21
+ PyValueError::new_err(msg)
22
+ }
23
+ DataLoaderError::InvalidCoordinate => PyValueError::new_err(err.to_string()),
24
+ other => PyRuntimeError::new_err(other.to_string()),
25
+ }
26
+ }
27
+
28
+ #[pyclass(frozen, name = "StreamCoordinate")]
29
+ #[derive(Clone, Copy)]
30
+ pub struct PyStreamCoordinate {
31
+ #[pyo3(get)]
32
+ pub shard_id: u32,
33
+ #[pyo3(get)]
34
+ pub byte_offset: u64,
35
+ #[pyo3(get)]
36
+ pub row_group_idx: u32,
37
+ #[pyo3(get)]
38
+ pub page_idx: u32,
39
+ #[pyo3(get)]
40
+ pub row_offset_in_page: u32,
41
+ #[pyo3(get)]
42
+ pub global_row_idx: u64,
43
+ }
44
+
45
+ #[pymethods]
46
+ impl PyStreamCoordinate {
47
+ #[new]
48
+ #[pyo3(signature = (
49
+ shard_id = 0,
50
+ byte_offset = 0,
51
+ row_group_idx = 0,
52
+ page_idx = 0,
53
+ row_offset_in_page = 0,
54
+ global_row_idx = 0,
55
+ ))]
56
+ pub fn new(
57
+ shard_id: u32,
58
+ byte_offset: u64,
59
+ row_group_idx: u32,
60
+ page_idx: u32,
61
+ row_offset_in_page: u32,
62
+ global_row_idx: u64,
63
+ ) -> Self {
64
+ Self {
65
+ shard_id,
66
+ byte_offset,
67
+ row_group_idx,
68
+ page_idx,
69
+ row_offset_in_page,
70
+ global_row_idx,
71
+ }
72
+ }
73
+
74
+ pub fn to_bytes<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
75
+ let coord = StreamCoordinate {
76
+ shard_id: self.shard_id,
77
+ byte_offset: self.byte_offset,
78
+ row_group_idx: self.row_group_idx,
79
+ page_idx: self.page_idx,
80
+ row_offset_in_page: self.row_offset_in_page,
81
+ global_row_idx: self.global_row_idx,
82
+ };
83
+ PyBytes::new(py, &coord.to_bytes())
84
+ }
85
+
86
+ #[staticmethod]
87
+ pub fn from_bytes(raw: &[u8]) -> PyResult<Self> {
88
+ if raw.len() != StreamCoordinate::BYTE_LEN {
89
+ return Err(PyValueError::new_err(format!(
90
+ "Invalid coordinate length: expected {} bytes, got {}",
91
+ StreamCoordinate::BYTE_LEN,
92
+ raw.len()
93
+ )));
94
+ }
95
+ let arr: &[u8; StreamCoordinate::BYTE_LEN] = raw.try_into().unwrap();
96
+ let c = StreamCoordinate::from_bytes(arr);
97
+ Ok(Self {
98
+ shard_id: c.shard_id,
99
+ byte_offset: c.byte_offset,
100
+ row_group_idx: c.row_group_idx,
101
+ page_idx: c.page_idx,
102
+ row_offset_in_page: c.row_offset_in_page,
103
+ global_row_idx: c.global_row_idx,
104
+ })
105
+ }
106
+
107
+ pub fn __repr__(&self) -> String {
108
+ format!(
109
+ "StreamCoordinate(shard_id={}, byte_offset={}, row_group={}, page={}, row_offset={}, global_row={})",
110
+ self.shard_id,
111
+ self.byte_offset,
112
+ self.row_group_idx,
113
+ self.page_idx,
114
+ self.row_offset_in_page,
115
+ self.global_row_idx
116
+ )
117
+ }
118
+ }
119
+
120
+ #[pyclass(name = "StreamDeduplicator")]
121
+ pub struct PyStreamDeduplicator {
122
+ inner: Mutex<TwoStageDeduplicator>,
123
+ }
124
+
125
+ #[pymethods]
126
+ impl PyStreamDeduplicator {
127
+ #[new]
128
+ #[pyo3(signature = (initial_hashes = None))]
129
+ pub fn new(initial_hashes: Option<Vec<u64>>) -> Self {
130
+ let dedup = match initial_hashes {
131
+ Some(hashes) => TwoStageDeduplicator::with_committed(hashes),
132
+ None => TwoStageDeduplicator::new(),
133
+ };
134
+ Self {
135
+ inner: Mutex::new(dedup),
136
+ }
137
+ }
138
+
139
+ #[staticmethod]
140
+ pub fn compute_hash(payload: &[u8]) -> u64 {
141
+ TwoStageDeduplicator::compute_hash(payload)
142
+ }
143
+
144
+ pub fn is_seen(&self, payload: &[u8]) -> bool {
145
+ self.inner.lock().is_seen(payload)
146
+ }
147
+
148
+ pub fn try_add(&self, payload: &[u8]) -> bool {
149
+ let mut dedup = self.inner.lock();
150
+ dedup.try_stage(1, payload).is_some()
151
+ }
152
+
153
+ pub fn filter_batch(&self, records: Vec<String>) -> Vec<String> {
154
+ let mut dedup = self.inner.lock();
155
+ let mut out = Vec::with_capacity(records.len());
156
+ for rec in records {
157
+ if dedup.try_stage(1, rec.as_bytes()).is_some() {
158
+ out.push(rec);
159
+ }
160
+ }
161
+ out
162
+ }
163
+
164
+ pub fn commit(&self) -> usize {
165
+ let mut dedup = self.inner.lock();
166
+ dedup.commit_batch(1).len()
167
+ }
168
+
169
+ pub fn rollback(&self) -> usize {
170
+ let mut dedup = self.inner.lock();
171
+ dedup.rollback_batch(1)
172
+ }
173
+
174
+ pub fn clear(&self) {
175
+ let mut dedup = self.inner.lock();
176
+ dedup.clear_staged();
177
+ }
178
+
179
+ pub fn count(&self) -> usize {
180
+ self.inner.lock().committed_len()
181
+ }
182
+
183
+ pub fn save_wal(&self, path: &str) -> PyResult<usize> {
184
+ let dedup = self.inner.lock();
185
+ let hashes = dedup.export_committed_hashes();
186
+ let mut buf = Vec::with_capacity(hashes.len() * 8);
187
+ for h in &hashes {
188
+ buf.extend_from_slice(&h.to_le_bytes());
189
+ }
190
+ let mut file = File::create(path).map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
191
+ file.write_all(&buf)
192
+ .map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
193
+ file.sync_data()
194
+ .map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
195
+ Ok(hashes.len())
196
+ }
197
+
198
+ pub fn load_wal(&self, path: &str) -> PyResult<usize> {
199
+ let mut file = match File::open(path) {
200
+ Ok(f) => f,
201
+ Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(0),
202
+ Err(e) => return Err(PyRuntimeError::new_err(e.to_string())),
203
+ };
204
+ let mut buf = Vec::new();
205
+ file.read_to_end(&mut buf)
206
+ .map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
207
+ if buf.len() % 8 != 0 {
208
+ return Err(PyValueError::new_err(format!(
209
+ "Corrupted WAL file '{}': byte length {} is not a multiple of 8",
210
+ path,
211
+ buf.len()
212
+ )));
213
+ }
214
+ let count = buf.len() / 8;
215
+ let mut dedup = self.inner.lock();
216
+ let (chunks, _) = buf.as_chunks::<8>();
217
+ for chunk in chunks {
218
+ let h = u64::from_le_bytes(*chunk);
219
+ dedup.import_committed_hashes(std::iter::once(h));
220
+ }
221
+ Ok(count)
222
+ }
223
+
224
+ pub fn export_hashes(&self) -> Vec<u64> {
225
+ self.inner.lock().export_committed_hashes()
226
+ }
227
+ }
228
+
229
+ #[pyclass(name = "StateTracker")]
230
+ pub struct PyStateTracker {
231
+ inner: Mutex<DualMetadataManager>,
232
+ }
233
+
234
+ #[pymethods]
235
+ impl PyStateTracker {
236
+ #[new]
237
+ pub fn new(meta_path: &str) -> PyResult<Self> {
238
+ let mgr = DualMetadataManager::open(meta_path).map_err(to_py_err)?;
239
+ Ok(Self {
240
+ inner: Mutex::new(mgr),
241
+ })
242
+ }
243
+
244
+ pub fn read(&self) -> PyStreamCoordinate {
245
+ let mgr = self.inner.lock();
246
+ let c = mgr.committed_coordinate();
247
+ PyStreamCoordinate {
248
+ shard_id: c.shard_id,
249
+ byte_offset: c.byte_offset,
250
+ row_group_idx: c.row_group_idx,
251
+ page_idx: c.page_idx,
252
+ row_offset_in_page: c.row_offset_in_page,
253
+ global_row_idx: c.global_row_idx,
254
+ }
255
+ }
256
+
257
+ #[pyo3(signature = (
258
+ shard_id = 0,
259
+ byte_offset = 0,
260
+ row_group_idx = 0,
261
+ page_idx = 0,
262
+ row_offset_in_page = 0,
263
+ global_row_idx = 0,
264
+ batch_id = 1
265
+ ))]
266
+ #[allow(clippy::too_many_arguments)]
267
+ pub fn update(
268
+ &self,
269
+ shard_id: u32,
270
+ byte_offset: u64,
271
+ row_group_idx: u32,
272
+ page_idx: u32,
273
+ row_offset_in_page: u32,
274
+ global_row_idx: u64,
275
+ batch_id: u64,
276
+ ) {
277
+ let coord = StreamCoordinate {
278
+ shard_id,
279
+ byte_offset,
280
+ row_group_idx,
281
+ page_idx,
282
+ row_offset_in_page,
283
+ global_row_idx,
284
+ };
285
+ let mut mgr = self.inner.lock();
286
+ mgr.stage_batch_in_ram(batch_id, coord);
287
+ }
288
+
289
+ #[pyo3(signature = (batch_id = 1))]
290
+ pub fn commit(&self, batch_id: u64) -> PyResult<PyStreamCoordinate> {
291
+ let mut mgr = self.inner.lock();
292
+ let c = mgr.commit_gpu_batch(batch_id).map_err(to_py_err)?;
293
+ Ok(PyStreamCoordinate {
294
+ shard_id: c.shard_id,
295
+ byte_offset: c.byte_offset,
296
+ row_group_idx: c.row_group_idx,
297
+ page_idx: c.page_idx,
298
+ row_offset_in_page: c.row_offset_in_page,
299
+ global_row_idx: c.global_row_idx,
300
+ })
301
+ }
302
+
303
+ pub fn committed_batch_id(&self) -> u64 {
304
+ self.inner.lock().committed_batch_id()
305
+ }
306
+ }
307
+
308
+ #[pyclass(name = "QualityFilter")]
309
+ pub struct PyQualityFilter {
310
+ cfg: QualityConfig,
311
+ }
312
+
313
+ #[pymethods]
314
+ impl PyQualityFilter {
315
+ #[new]
316
+ #[pyo3(signature = (
317
+ min_chars = 0,
318
+ max_chars = 0,
319
+ min_words = 0,
320
+ min_entropy = 0.0,
321
+ max_repetition_ratio = 1.0,
322
+ max_symbol_ratio = 1.0
323
+ ))]
324
+ pub fn new(
325
+ min_chars: usize,
326
+ max_chars: usize,
327
+ min_words: usize,
328
+ min_entropy: f64,
329
+ max_repetition_ratio: f64,
330
+ max_symbol_ratio: f64,
331
+ ) -> Self {
332
+ Self {
333
+ cfg: QualityConfig {
334
+ min_chars,
335
+ max_chars,
336
+ min_words,
337
+ min_entropy,
338
+ max_repetition_ratio,
339
+ max_symbol_ratio,
340
+ },
341
+ }
342
+ }
343
+
344
+ pub fn is_high_quality(&self, text: &str) -> bool {
345
+ crate::quality::evaluate_quality(text, &self.cfg).is_passed
346
+ }
347
+
348
+ pub fn metrics(&self, text: &str) -> (usize, usize, f64, f64, f64, bool) {
349
+ let m = crate::quality::evaluate_quality(text, &self.cfg);
350
+ (
351
+ m.char_count,
352
+ m.word_count,
353
+ m.entropy,
354
+ m.repetition_ratio,
355
+ m.symbol_ratio,
356
+ m.is_passed,
357
+ )
358
+ }
359
+
360
+ pub fn filter_flags(&self, py: Python<'_>, texts: Vec<String>) -> Vec<bool> {
361
+ let cfg = self.cfg.clone();
362
+ py.detach(|| crate::quality::filter_quality_batch_parallel(&texts, &cfg))
363
+ }
364
+
365
+ pub fn filter_batch(&self, py: Python<'_>, texts: Vec<String>) -> Vec<String> {
366
+ let cfg = self.cfg.clone();
367
+ py.detach(|| {
368
+ texts
369
+ .into_iter()
370
+ .filter(|t| crate::quality::evaluate_quality(t, &cfg).is_passed)
371
+ .collect()
372
+ })
373
+ }
374
+ }
375
+
376
+ #[pyclass(name = "MinHashDeduplicator")]
377
+ pub struct PyMinHashDeduplicator {
378
+ inner: Mutex<crate::minhash::MinHashDeduplicator>,
379
+ }
380
+
381
+ #[pymethods]
382
+ impl PyMinHashDeduplicator {
383
+ #[new]
384
+ #[pyo3(signature = (threshold = 0.85, k_shingle = 5, num_perm = 64))]
385
+ pub fn new(threshold: f64, k_shingle: usize, num_perm: usize) -> Self {
386
+ Self {
387
+ inner: Mutex::new(crate::minhash::MinHashDeduplicator::new(
388
+ threshold, k_shingle, num_perm,
389
+ )),
390
+ }
391
+ }
392
+
393
+ pub fn is_unique(&self, text: &str) -> bool {
394
+ self.inner.lock().is_unique(text)
395
+ }
396
+
397
+ pub fn similarity(&self, text_a: &str, text_b: &str) -> f64 {
398
+ let dedup = self.inner.lock();
399
+ let sig_a = dedup.compute_signature(text_a);
400
+ let sig_b = dedup.compute_signature(text_b);
401
+ sig_a.jaccard_similarity(&sig_b)
402
+ }
403
+
404
+ pub fn count(&self) -> usize {
405
+ self.inner.lock().count()
406
+ }
407
+
408
+ pub fn clear(&self) {
409
+ self.inner.lock().clear();
410
+ }
411
+ }
412
+
413
+ #[pyclass(name = "StringTensor")]
414
+ #[derive(Clone)]
415
+ pub struct PyStringTensor {
416
+ inner: crate::string_tensor::StringTensor,
417
+ }
418
+
419
+ #[pymethods]
420
+ impl PyStringTensor {
421
+ #[staticmethod]
422
+ pub fn from_list(strings: Vec<String>) -> Self {
423
+ Self {
424
+ inner: crate::string_tensor::StringTensor::from_strings(strings),
425
+ }
426
+ }
427
+
428
+ #[staticmethod]
429
+ pub fn from_gpu_buffers(offsets: Vec<u32>, payload: &[u8]) -> Self {
430
+ Self {
431
+ inner: crate::string_tensor::StringTensor::from_gpu_buffers(&offsets, payload),
432
+ }
433
+ }
434
+
435
+ pub fn to_gpu_buffers<'py>(
436
+ &self,
437
+ py: Python<'py>,
438
+ ) -> (Bound<'py, PyBytes>, Bound<'py, PyBytes>) {
439
+ let (offsets, payload) = self.inner.to_gpu_buffers();
440
+ let mut offset_bytes = Vec::with_capacity(offsets.len() * 4);
441
+ for &off in &offsets {
442
+ offset_bytes.extend_from_slice(&off.to_le_bytes());
443
+ }
444
+ (PyBytes::new(py, &offset_bytes), PyBytes::new(py, &payload))
445
+ }
446
+
447
+ pub fn to_offsets(&self) -> Vec<u32> {
448
+ self.inner.to_gpu_buffers().0
449
+ }
450
+
451
+ pub fn to_payload<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
452
+ let (_, payload) = self.inner.to_gpu_buffers();
453
+ PyBytes::new(py, &payload)
454
+ }
455
+
456
+ pub fn __len__(&self) -> usize {
457
+ self.inner.len()
458
+ }
459
+
460
+ pub fn __getitem__(&self, index: usize) -> PyResult<String> {
461
+ self.inner
462
+ .get(index)
463
+ .map(|s| s.to_owned())
464
+ .ok_or_else(|| PyValueError::new_err("StringTensor index out of range"))
465
+ }
466
+
467
+ pub fn get(&self, index: usize) -> Option<String> {
468
+ self.inner.get(index).map(|s| s.to_owned())
469
+ }
470
+
471
+ pub fn to_list(&self) -> Vec<String> {
472
+ self.inner.to_vec()
473
+ }
474
+
475
+ pub fn lengths(&self) -> Vec<u32> {
476
+ self.inner.lengths()
477
+ }
478
+
479
+ pub fn is_inlined_mask(&self) -> Vec<bool> {
480
+ self.inner.is_inlined_mask()
481
+ }
482
+
483
+ pub fn total_bytes(&self) -> usize {
484
+ self.inner.views.iter().map(|v| v.len as usize).sum()
485
+ }
486
+
487
+ pub fn filter_by_length(&self, min_len: u32, max_len: u32) -> Self {
488
+ Self {
489
+ inner: self.inner.filter_by_length(min_len, max_len),
490
+ }
491
+ }
492
+
493
+ pub fn filter_by_prefix(&self, prefix: &str) -> Self {
494
+ Self {
495
+ inner: self.inner.filter_by_prefix(prefix),
496
+ }
497
+ }
498
+
499
+ pub fn hash_all(&self, py: Python<'_>) -> Vec<u64> {
500
+ py.detach(|| self.inner.hash_all_parallel())
501
+ }
502
+
503
+ pub fn __repr__(&self) -> String {
504
+ format!(
505
+ "StringTensor(count={}, inlined={})",
506
+ self.inner.len(),
507
+ self.inner.views.iter().filter(|v| v.is_inlined()).count()
508
+ )
509
+ }
510
+ }
511
+
512
+ pub fn extract_py_columns_table(
513
+ py: Python<'_>,
514
+ columns: &Bound<'_, PyAny>,
515
+ ) -> PyResult<Vec<(String, Vec<DataValue>)>> {
516
+ let mut rust_cols = Vec::new();
517
+
518
+ if let Ok(dict) = columns.cast::<PyDict>() {
519
+ for (k, v) in dict.iter() {
520
+ let col_name = k.extract::<String>()?;
521
+ let vals = extract_py_seq_values(py, &v)?;
522
+ rust_cols.push((col_name, vals));
523
+ }
524
+ } else if let Ok(seq) = columns.cast::<PySequence>() {
525
+ let len = seq.len()?;
526
+ for i in 0..len {
527
+ let item = seq.get_item(i)?;
528
+ if let Ok(pair) = item.cast::<PySequence>()
529
+ && pair.len()? == 2
530
+ {
531
+ let col_name = pair.get_item(0)?.extract::<String>()?;
532
+ let col_vals_seq = pair.get_item(1)?;
533
+ let vals = extract_py_seq_values(py, &col_vals_seq)?;
534
+ rust_cols.push((col_name, vals));
535
+ continue;
536
+ }
537
+ return Err(PyValueError::new_err(
538
+ "columns sequence must contain (name, values) pairs or be a dict",
539
+ ));
540
+ }
541
+ } else {
542
+ return Err(PyValueError::new_err(
543
+ "columns must be a dict {name: [values]} or sequence of (name, [values]) pairs",
544
+ ));
545
+ }
546
+
547
+ Ok(rust_cols)
548
+ }
549
+
550
+ fn extract_py_seq_values(_py: Python<'_>, seq_obj: &Bound<'_, PyAny>) -> PyResult<Vec<DataValue>> {
551
+ let seq = seq_obj
552
+ .cast::<PySequence>()
553
+ .map_err(|_| PyValueError::new_err("column values must be a sequence (list/tuple)"))?;
554
+ let len = seq.len()?;
555
+ let mut out = Vec::with_capacity(len);
556
+
557
+ for i in 0..len {
558
+ let item = seq.get_item(i)?;
559
+ if item.is_none() {
560
+ out.push(DataValue::Null);
561
+ } else if let Ok(b) = item.cast::<PyBool>() {
562
+ out.push(DataValue::Bool(b.is_true()));
563
+ } else if let Ok(i) = item.cast::<PyInt>() {
564
+ out.push(DataValue::Int(i.extract::<i64>()?));
565
+ } else if let Ok(f) = item.cast::<PyFloat>() {
566
+ out.push(DataValue::Float(f.extract::<f64>()?));
567
+ } else if let Ok(s) = item.cast::<PyString>() {
568
+ let str_val = s.to_string_lossy();
569
+ out.push(classify_string_or_json(&str_val));
570
+ } else if let Ok(bytes) = item.cast::<PyBytes>() {
571
+ out.push(DataValue::Bytes(bytes.as_bytes().to_vec()));
572
+ } else if let Ok(bytes) = item.cast::<PyByteArray>() {
573
+ out.push(DataValue::Bytes(bytes.to_vec()));
574
+ } else {
575
+ let py_str = item.str()?;
576
+ let str_repr = py_str.to_string_lossy();
577
+ out.push(classify_string_or_json(&str_repr));
578
+ }
579
+ }
580
+
581
+ Ok(out)
582
+ }
583
+
584
+ #[pyfunction]
585
+ pub fn dataloader_cpu_isa() -> &'static str {
586
+ crate::simd_cpu::detect_cpu_isa().as_str()
587
+ }
588
+
589
+ #[pyfunction]
590
+ #[pyo3(signature = (target, candidates, max_suggestions=3))]
591
+ pub fn suggest_closest_matches(
592
+ target: &str,
593
+ candidates: Vec<String>,
594
+ max_suggestions: usize,
595
+ ) -> Vec<String> {
596
+ find_closest_matches(target, &candidates, max_suggestions)
597
+ }
598
+
599
+ #[pyfunction]
600
+ #[pyo3(signature = (columns, format="parquet", hf_image_struct=true))]
601
+ pub fn serialize_table_to_bytes(
602
+ py: Python<'_>,
603
+ columns: &Bound<'_, PyAny>,
604
+ format: &str,
605
+ hf_image_struct: bool,
606
+ ) -> PyResult<Vec<u8>> {
607
+ let rust_cols = extract_py_columns_table(py, columns)?;
608
+ py.detach(|| serialize_columns_by_format(&rust_cols, format, hf_image_struct))
609
+ .map_err(to_py_err)
610
+ }
611
+
612
+ #[pyfunction]
613
+ #[pyo3(signature = (
614
+ raw,
615
+ trim_whitespace=true,
616
+ normalize_newlines=true,
617
+ min_byte_len=0,
618
+ max_byte_len=0,
619
+ truncate_over_max=false,
620
+ json_extract_key=None
621
+ ))]
622
+ pub fn clean_single_record(
623
+ raw: &str,
624
+ trim_whitespace: bool,
625
+ normalize_newlines: bool,
626
+ min_byte_len: usize,
627
+ max_byte_len: usize,
628
+ truncate_over_max: bool,
629
+ json_extract_key: Option<String>,
630
+ ) -> Option<String> {
631
+ let cfg = PostProcessConfig {
632
+ trim_whitespace,
633
+ normalize_newlines,
634
+ min_byte_len,
635
+ max_byte_len,
636
+ truncate_over_max,
637
+ json_extract_key,
638
+ };
639
+ crate::postprocess::process_single_record(raw, &cfg)
640
+ }
641
+
642
+ #[pyfunction]
643
+ #[pyo3(signature = (
644
+ records,
645
+ trim_whitespace=true,
646
+ normalize_newlines=true,
647
+ min_byte_len=0,
648
+ max_byte_len=0,
649
+ truncate_over_max=false,
650
+ json_extract_key=None
651
+ ))]
652
+ #[allow(clippy::too_many_arguments)]
653
+ pub fn clean_batch_records(
654
+ py: Python<'_>,
655
+ records: Vec<String>,
656
+ trim_whitespace: bool,
657
+ normalize_newlines: bool,
658
+ min_byte_len: usize,
659
+ max_byte_len: usize,
660
+ truncate_over_max: bool,
661
+ json_extract_key: Option<String>,
662
+ ) -> Vec<String> {
663
+ let cfg = PostProcessConfig {
664
+ trim_whitespace,
665
+ normalize_newlines,
666
+ min_byte_len,
667
+ max_byte_len,
668
+ truncate_over_max,
669
+ json_extract_key,
670
+ };
671
+ py.detach(|| crate::postprocess::process_batch_parallel(&records, &cfg))
672
+ }
673
+
674
+ #[pyfunction]
675
+ #[pyo3(signature = (
676
+ records,
677
+ trim_whitespace=true,
678
+ normalize_newlines=true,
679
+ min_byte_len=1,
680
+ max_byte_len=0,
681
+ truncate_over_max=false,
682
+ json_extract_key=None,
683
+ seen_hashes=None,
684
+ deduplicate=true
685
+ ))]
686
+ #[allow(clippy::too_many_arguments)]
687
+ pub fn postprocess_and_dedup_records(
688
+ py: Python<'_>,
689
+ records: Vec<String>,
690
+ trim_whitespace: bool,
691
+ normalize_newlines: bool,
692
+ min_byte_len: usize,
693
+ max_byte_len: usize,
694
+ truncate_over_max: bool,
695
+ json_extract_key: Option<String>,
696
+ seen_hashes: Option<Vec<u64>>,
697
+ deduplicate: bool,
698
+ ) -> PyResult<(Vec<String>, Vec<u64>)> {
699
+ let cfg = PostProcessConfig {
700
+ trim_whitespace,
701
+ normalize_newlines,
702
+ min_byte_len,
703
+ max_byte_len,
704
+ truncate_over_max,
705
+ json_extract_key,
706
+ };
707
+ Ok(py.detach(|| {
708
+ let processed = crate::postprocess::process_batch_parallel_indexed(&records, &cfg);
709
+ if !deduplicate {
710
+ let cleaned_all: Vec<String> = processed.into_iter().flatten().collect();
711
+ return (cleaned_all, Vec::new());
712
+ }
713
+ let mut dedup = TwoStageDeduplicator::with_committed(seen_hashes.unwrap_or_default());
714
+ let mut cleaned_unique = Vec::with_capacity(processed.len());
715
+ for cleaned in processed.into_iter().flatten() {
716
+ if dedup.try_stage(1, cleaned.as_bytes()).is_some() {
717
+ cleaned_unique.push(cleaned);
718
+ }
719
+ }
720
+ let newly_committed = dedup.commit_batch(1);
721
+ (cleaned_unique, newly_committed)
722
+ }))
723
+ }
724
+
725
+ #[pyfunction]
726
+ #[pyo3(signature = (
727
+ records,
728
+ trim_whitespace = true,
729
+ normalize_newlines = true,
730
+ min_byte_len = 0,
731
+ max_byte_len = 0,
732
+ truncate_over_max = false,
733
+ json_extract_key = None,
734
+ filter_quality = false,
735
+ min_chars = 0,
736
+ max_chars = 0,
737
+ min_words = 0,
738
+ min_entropy = 0.0,
739
+ max_repetition_ratio = 1.0,
740
+ max_symbol_ratio = 1.0,
741
+ deduplicator = None,
742
+ commit_dedup = false,
743
+ ))]
744
+ #[allow(clippy::too_many_arguments)]
745
+ pub fn fused_pipeline_process_batch(
746
+ py: Python<'_>,
747
+ records: Vec<String>,
748
+ trim_whitespace: bool,
749
+ normalize_newlines: bool,
750
+ min_byte_len: usize,
751
+ max_byte_len: usize,
752
+ truncate_over_max: bool,
753
+ json_extract_key: Option<String>,
754
+ filter_quality: bool,
755
+ min_chars: usize,
756
+ max_chars: usize,
757
+ min_words: usize,
758
+ min_entropy: f64,
759
+ max_repetition_ratio: f64,
760
+ max_symbol_ratio: f64,
761
+ deduplicator: Option<&PyStreamDeduplicator>,
762
+ commit_dedup: bool,
763
+ ) -> PyResult<PyStringTensor> {
764
+ let post_cfg = PostProcessConfig {
765
+ trim_whitespace,
766
+ normalize_newlines,
767
+ min_byte_len,
768
+ max_byte_len,
769
+ truncate_over_max,
770
+ json_extract_key,
771
+ };
772
+ let qual_cfg = crate::quality::QualityConfig {
773
+ min_chars,
774
+ max_chars,
775
+ min_words,
776
+ min_entropy,
777
+ max_repetition_ratio,
778
+ max_symbol_ratio,
779
+ };
780
+
781
+ let result = py.detach(|| {
782
+ let cleaned_candidates: Vec<String> = records
783
+ .par_iter()
784
+ .filter_map(|raw| {
785
+ let cleaned = crate::postprocess::process_single_record(raw, &post_cfg)?;
786
+ if filter_quality {
787
+ let eval = crate::quality::evaluate_quality(&cleaned, &qual_cfg);
788
+ if !eval.is_passed {
789
+ return None;
790
+ }
791
+ }
792
+ Some(cleaned)
793
+ })
794
+ .collect();
795
+
796
+ let final_strings = if let Some(dedup_wrapper) = deduplicator {
797
+ let mut dedup = dedup_wrapper.inner.lock();
798
+ let mut unique = Vec::with_capacity(cleaned_candidates.len());
799
+ for s in cleaned_candidates {
800
+ if dedup.try_stage(1, s.as_bytes()).is_some() {
801
+ unique.push(s);
802
+ }
803
+ }
804
+ if commit_dedup {
805
+ dedup.commit_batch(1);
806
+ }
807
+ unique
808
+ } else {
809
+ cleaned_candidates
810
+ };
811
+
812
+ crate::string_tensor::StringTensor::from_strings(final_strings)
813
+ });
814
+
815
+ Ok(PyStringTensor { inner: result })
816
+ }
817
+
818
+ pub fn register_dataloader_module(parent_module: &Bound<'_, PyModule>) -> PyResult<()> {
819
+ parent_module.add_class::<PyStreamCoordinate>()?;
820
+ parent_module.add_class::<PyStreamDeduplicator>()?;
821
+ parent_module.add_class::<PyStateTracker>()?;
822
+ parent_module.add_class::<PyQualityFilter>()?;
823
+ parent_module.add_class::<PyMinHashDeduplicator>()?;
824
+ parent_module.add_class::<PyStringTensor>()?;
825
+ parent_module.add_function(wrap_pyfunction!(dataloader_cpu_isa, parent_module)?)?;
826
+ parent_module.add_function(wrap_pyfunction!(suggest_closest_matches, parent_module)?)?;
827
+ parent_module.add_function(wrap_pyfunction!(serialize_table_to_bytes, parent_module)?)?;
828
+ parent_module.add_function(wrap_pyfunction!(clean_single_record, parent_module)?)?;
829
+ parent_module.add_function(wrap_pyfunction!(clean_batch_records, parent_module)?)?;
830
+ parent_module.add_function(wrap_pyfunction!(
831
+ postprocess_and_dedup_records,
832
+ parent_module
833
+ )?)?;
834
+ parent_module.add_function(wrap_pyfunction!(
835
+ fused_pipeline_process_batch,
836
+ parent_module
837
+ )?)?;
838
+ Ok(())
839
+ }
data/src/quality.rs CHANGED
@@ -1,169 +1,169 @@
1
- use hashbrown::{HashMap, HashSet};
2
- use rayon::prelude::*;
3
-
4
- #[derive(Debug, Clone)]
5
- pub struct QualityConfig {
6
- pub min_chars: usize,
7
- pub max_chars: usize,
8
- pub min_words: usize,
9
- pub min_entropy: f64,
10
- pub max_repetition_ratio: f64,
11
- pub max_symbol_ratio: f64,
12
- }
13
-
14
- impl Default for QualityConfig {
15
- fn default() -> Self {
16
- Self {
17
- min_chars: 0,
18
- max_chars: 0,
19
- min_words: 0,
20
- min_entropy: 0.0,
21
- max_repetition_ratio: 1.0,
22
- max_symbol_ratio: 1.0,
23
- }
24
- }
25
- }
26
-
27
- #[derive(Debug, Clone, Copy, PartialEq)]
28
- pub struct QualityMetrics {
29
- pub char_count: usize,
30
- pub word_count: usize,
31
- pub entropy: f64,
32
- pub repetition_ratio: f64,
33
- pub symbol_ratio: f64,
34
- pub is_passed: bool,
35
- }
36
-
37
- pub fn calculate_shannon_entropy(text: &str) -> f64 {
38
- if text.is_empty() {
39
- return 0.0;
40
- }
41
-
42
- // Zero-allocation stack array fast-path for pure ASCII text
43
- if text.is_ascii() {
44
- let mut freq = [0u32; 256];
45
- for &b in text.as_bytes() {
46
- freq[b as usize] += 1;
47
- }
48
-
49
- let n = text.len() as f64;
50
- let mut entropy = 0.0;
51
- for &count in &freq {
52
- if count > 0 {
53
- let p = (count as f64) / n;
54
- entropy -= p * p.log2();
55
- }
56
- }
57
- return entropy;
58
- }
59
-
60
- let char_count = text.chars().count();
61
- let n = char_count as f64;
62
- let mut freq = HashMap::new();
63
- for c in text.chars() {
64
- *freq.entry(c).or_insert(0usize) += 1;
65
- }
66
- let mut entropy = 0.0;
67
- for &count in freq.values() {
68
- let p = (count as f64) / n;
69
- entropy -= p * p.log2();
70
- }
71
- entropy
72
- }
73
-
74
- pub fn calculate_repetition_ratio(words: &[&str]) -> f64 {
75
- if words.len() < 4 {
76
- return 0.0;
77
- }
78
-
79
- let total_trigrams = words.len() - 2;
80
- let mut unique_trigrams = HashSet::with_capacity(total_trigrams);
81
-
82
- for w in words.windows(3) {
83
- unique_trigrams.insert((w[0], w[1], w[2]));
84
- }
85
-
86
- 1.0 - ((unique_trigrams.len() as f64) / (total_trigrams as f64))
87
- }
88
-
89
- pub fn calculate_symbol_ratio(text: &str) -> (usize, f64) {
90
- let mut symbols = 0usize;
91
- let mut visible = 0usize;
92
-
93
- for c in text.chars() {
94
- if !c.is_whitespace() {
95
- visible += 1;
96
- if !c.is_alphanumeric() {
97
- symbols += 1;
98
- }
99
- }
100
- }
101
-
102
- if visible == 0 {
103
- return (0, 0.0);
104
- }
105
-
106
- (visible, (symbols as f64) / (visible as f64))
107
- }
108
-
109
- pub fn evaluate_quality(text: &str, cfg: &QualityConfig) -> QualityMetrics {
110
- let char_count = text.chars().count();
111
- if cfg.min_chars > 0 && char_count < cfg.min_chars {
112
- return QualityMetrics {
113
- char_count,
114
- word_count: 0,
115
- entropy: 0.0,
116
- repetition_ratio: 0.0,
117
- symbol_ratio: 0.0,
118
- is_passed: false,
119
- };
120
- }
121
-
122
- if cfg.max_chars > 0 && char_count > cfg.max_chars {
123
- return QualityMetrics {
124
- char_count,
125
- word_count: 0,
126
- entropy: 0.0,
127
- repetition_ratio: 0.0,
128
- symbol_ratio: 0.0,
129
- is_passed: false,
130
- };
131
- }
132
-
133
- let words: Vec<&str> = text.split_whitespace().collect();
134
- let word_count = words.len();
135
- if cfg.min_words > 0 && word_count < cfg.min_words {
136
- return QualityMetrics {
137
- char_count,
138
- word_count,
139
- entropy: 0.0,
140
- repetition_ratio: 0.0,
141
- symbol_ratio: 0.0,
142
- is_passed: false,
143
- };
144
- }
145
-
146
- let entropy = calculate_shannon_entropy(text);
147
- let repetition_ratio = calculate_repetition_ratio(&words);
148
- let (_, symbol_ratio) = calculate_symbol_ratio(text);
149
-
150
- let is_passed = (cfg.min_entropy <= 0.0 || entropy >= cfg.min_entropy)
151
- && (cfg.max_repetition_ratio >= 1.0 || repetition_ratio <= cfg.max_repetition_ratio)
152
- && (cfg.max_symbol_ratio >= 1.0 || symbol_ratio <= cfg.max_symbol_ratio);
153
-
154
- QualityMetrics {
155
- char_count,
156
- word_count,
157
- entropy,
158
- repetition_ratio,
159
- symbol_ratio,
160
- is_passed,
161
- }
162
- }
163
-
164
- pub fn filter_quality_batch_parallel(texts: &[String], cfg: &QualityConfig) -> Vec<bool> {
165
- texts
166
- .par_iter()
167
- .map(|t| evaluate_quality(t, cfg).is_passed)
168
- .collect()
169
- }
 
1
+ use hashbrown::{HashMap, HashSet};
2
+ use rayon::prelude::*;
3
+
4
+ #[derive(Debug, Clone)]
5
+ pub struct QualityConfig {
6
+ pub min_chars: usize,
7
+ pub max_chars: usize,
8
+ pub min_words: usize,
9
+ pub min_entropy: f64,
10
+ pub max_repetition_ratio: f64,
11
+ pub max_symbol_ratio: f64,
12
+ }
13
+
14
+ impl Default for QualityConfig {
15
+ fn default() -> Self {
16
+ Self {
17
+ min_chars: 0,
18
+ max_chars: 0,
19
+ min_words: 0,
20
+ min_entropy: 0.0,
21
+ max_repetition_ratio: 1.0,
22
+ max_symbol_ratio: 1.0,
23
+ }
24
+ }
25
+ }
26
+
27
+ #[derive(Debug, Clone, Copy, PartialEq)]
28
+ pub struct QualityMetrics {
29
+ pub char_count: usize,
30
+ pub word_count: usize,
31
+ pub entropy: f64,
32
+ pub repetition_ratio: f64,
33
+ pub symbol_ratio: f64,
34
+ pub is_passed: bool,
35
+ }
36
+
37
+ pub fn calculate_shannon_entropy(text: &str) -> f64 {
38
+ if text.is_empty() {
39
+ return 0.0;
40
+ }
41
+
42
+ // Zero-allocation stack array fast-path for pure ASCII text
43
+ if text.is_ascii() {
44
+ let mut freq = [0u32; 256];
45
+ for &b in text.as_bytes() {
46
+ freq[b as usize] += 1;
47
+ }
48
+
49
+ let n = text.len() as f64;
50
+ let mut entropy = 0.0;
51
+ for &count in &freq {
52
+ if count > 0 {
53
+ let p = (count as f64) / n;
54
+ entropy -= p * p.log2();
55
+ }
56
+ }
57
+ return entropy;
58
+ }
59
+
60
+ let char_count = text.chars().count();
61
+ let n = char_count as f64;
62
+ let mut freq = HashMap::new();
63
+ for c in text.chars() {
64
+ *freq.entry(c).or_insert(0usize) += 1;
65
+ }
66
+ let mut entropy = 0.0;
67
+ for &count in freq.values() {
68
+ let p = (count as f64) / n;
69
+ entropy -= p * p.log2();
70
+ }
71
+ entropy
72
+ }
73
+
74
+ pub fn calculate_repetition_ratio(words: &[&str]) -> f64 {
75
+ if words.len() < 4 {
76
+ return 0.0;
77
+ }
78
+
79
+ let total_trigrams = words.len() - 2;
80
+ let mut unique_trigrams = HashSet::with_capacity(total_trigrams);
81
+
82
+ for w in words.windows(3) {
83
+ unique_trigrams.insert((w[0], w[1], w[2]));
84
+ }
85
+
86
+ 1.0 - ((unique_trigrams.len() as f64) / (total_trigrams as f64))
87
+ }
88
+
89
+ pub fn calculate_symbol_ratio(text: &str) -> (usize, f64) {
90
+ let mut symbols = 0usize;
91
+ let mut visible = 0usize;
92
+
93
+ for c in text.chars() {
94
+ if !c.is_whitespace() {
95
+ visible += 1;
96
+ if !c.is_alphanumeric() {
97
+ symbols += 1;
98
+ }
99
+ }
100
+ }
101
+
102
+ if visible == 0 {
103
+ return (0, 0.0);
104
+ }
105
+
106
+ (visible, (symbols as f64) / (visible as f64))
107
+ }
108
+
109
+ pub fn evaluate_quality(text: &str, cfg: &QualityConfig) -> QualityMetrics {
110
+ let char_count = text.chars().count();
111
+ if cfg.min_chars > 0 && char_count < cfg.min_chars {
112
+ return QualityMetrics {
113
+ char_count,
114
+ word_count: 0,
115
+ entropy: 0.0,
116
+ repetition_ratio: 0.0,
117
+ symbol_ratio: 0.0,
118
+ is_passed: false,
119
+ };
120
+ }
121
+
122
+ if cfg.max_chars > 0 && char_count > cfg.max_chars {
123
+ return QualityMetrics {
124
+ char_count,
125
+ word_count: 0,
126
+ entropy: 0.0,
127
+ repetition_ratio: 0.0,
128
+ symbol_ratio: 0.0,
129
+ is_passed: false,
130
+ };
131
+ }
132
+
133
+ let words: Vec<&str> = text.split_whitespace().collect();
134
+ let word_count = words.len();
135
+ if cfg.min_words > 0 && word_count < cfg.min_words {
136
+ return QualityMetrics {
137
+ char_count,
138
+ word_count,
139
+ entropy: 0.0,
140
+ repetition_ratio: 0.0,
141
+ symbol_ratio: 0.0,
142
+ is_passed: false,
143
+ };
144
+ }
145
+
146
+ let entropy = calculate_shannon_entropy(text);
147
+ let repetition_ratio = calculate_repetition_ratio(&words);
148
+ let (_, symbol_ratio) = calculate_symbol_ratio(text);
149
+
150
+ let is_passed = (cfg.min_entropy <= 0.0 || entropy >= cfg.min_entropy)
151
+ && (cfg.max_repetition_ratio >= 1.0 || repetition_ratio <= cfg.max_repetition_ratio)
152
+ && (cfg.max_symbol_ratio >= 1.0 || symbol_ratio <= cfg.max_symbol_ratio);
153
+
154
+ QualityMetrics {
155
+ char_count,
156
+ word_count,
157
+ entropy,
158
+ repetition_ratio,
159
+ symbol_ratio,
160
+ is_passed,
161
+ }
162
+ }
163
+
164
+ pub fn filter_quality_batch_parallel(texts: &[String], cfg: &QualityConfig) -> Vec<bool> {
165
+ texts
166
+ .par_iter()
167
+ .map(|t| evaluate_quality(t, cfg).is_passed)
168
+ .collect()
169
+ }
data/src/serialize.rs CHANGED
@@ -1,268 +1,268 @@
1
- use std::sync::Arc;
2
-
3
- use arrow_array::builder::{
4
- BinaryBuilder, BooleanBuilder, Float64Builder, Int64Builder, StringBuilder,
5
- };
6
- use arrow_array::{Array, RecordBatch, StructArray};
7
- use arrow_ipc::writer::FileWriter as ArrowIpcFileWriter;
8
- use arrow_schema::{DataType, Field, Fields, Schema};
9
- use parquet::arrow::ArrowWriter;
10
- use parquet::basic::Compression;
11
- use parquet::file::properties::WriterProperties;
12
-
13
- use crate::error::{DataLoaderError, Result};
14
-
15
- #[derive(Debug, Clone, PartialEq)]
16
- pub enum DataValue {
17
- Null,
18
- Text(String),
19
- Bytes(Vec<u8>),
20
- Json(String),
21
- Int(i64),
22
- Float(f64),
23
- Bool(bool),
24
- List(Vec<DataValue>),
25
- }
26
-
27
- pub fn classify_string_or_json(s: &str) -> DataValue {
28
- let trimmed = s.trim();
29
- if ((trimmed.starts_with('{') && trimmed.ends_with('}'))
30
- || (trimmed.starts_with('[') && trimmed.ends_with(']')))
31
- && serde_json::from_str::<serde_json::Value>(trimmed).is_ok()
32
- {
33
- return DataValue::Json(trimmed.to_owned());
34
- }
35
- DataValue::Text(s.to_owned())
36
- }
37
-
38
- pub fn build_arrow_record_batch(
39
- columns: &[(String, Vec<DataValue>)],
40
- hf_image_struct: bool,
41
- ) -> Result<(Arc<Schema>, RecordBatch)> {
42
- if columns.is_empty() {
43
- return Err(DataLoaderError::InvalidConfig(
44
- "Cannot serialize empty column list".to_owned(),
45
- ));
46
- }
47
-
48
- let num_rows = columns[0].1.len();
49
- let mut fields = Vec::with_capacity(columns.len());
50
- let mut arrays: Vec<Arc<dyn Array>> = Vec::with_capacity(columns.len());
51
-
52
- for (name, vals) in columns {
53
- if vals.len() != num_rows {
54
- return Err(DataLoaderError::InvalidConfig(format!(
55
- "Column '{name}' length mismatch: expected {num_rows}, got {}",
56
- vals.len()
57
- )));
58
- }
59
-
60
- let has_non_null = vals.iter().any(|v| !matches!(v, DataValue::Null));
61
- let is_binary = vals.iter().any(|v| matches!(v, DataValue::Bytes(_)));
62
- let all_int = has_non_null
63
- && vals
64
- .iter()
65
- .all(|v| matches!(v, DataValue::Null | DataValue::Int(_)));
66
- let has_float = vals.iter().any(|v| matches!(v, DataValue::Float(_)));
67
- let all_numeric = has_non_null
68
- && has_float
69
- && vals
70
- .iter()
71
- .all(|v| matches!(v, DataValue::Null | DataValue::Int(_) | DataValue::Float(_)));
72
- let all_bool = has_non_null
73
- && vals
74
- .iter()
75
- .all(|v| matches!(v, DataValue::Null | DataValue::Bool(_)));
76
-
77
- if is_binary {
78
- if hf_image_struct {
79
- let mut bytes_builder = BinaryBuilder::with_capacity(num_rows, num_rows * 1024);
80
- let mut path_builder = StringBuilder::with_capacity(num_rows, num_rows * 16);
81
-
82
- for val in vals {
83
- match val {
84
- DataValue::Bytes(b) => {
85
- bytes_builder.append_value(b);
86
- path_builder.append_null();
87
- }
88
- DataValue::Text(t) => {
89
- bytes_builder.append_null();
90
- path_builder.append_value(t);
91
- }
92
- _ => {
93
- bytes_builder.append_null();
94
- path_builder.append_null();
95
- }
96
- }
97
- }
98
-
99
- let bytes_arr = Arc::new(bytes_builder.finish()) as Arc<dyn Array>;
100
- let path_arr = Arc::new(path_builder.finish()) as Arc<dyn Array>;
101
-
102
- let struct_fields = Fields::from(vec![
103
- Field::new("bytes", DataType::Binary, true),
104
- Field::new("path", DataType::Utf8, true),
105
- ]);
106
-
107
- let struct_arr =
108
- StructArray::new(struct_fields.clone(), vec![bytes_arr, path_arr], None);
109
-
110
- fields.push(Field::new(name, DataType::Struct(struct_fields), true));
111
- arrays.push(Arc::new(struct_arr));
112
- } else {
113
- let mut b_builder = BinaryBuilder::with_capacity(num_rows, num_rows * 1024);
114
- for val in vals {
115
- match val {
116
- DataValue::Bytes(b) => b_builder.append_value(b),
117
- _ => b_builder.append_null(),
118
- }
119
- }
120
- fields.push(Field::new(name, DataType::Binary, true));
121
- arrays.push(Arc::new(b_builder.finish()));
122
- }
123
- } else if all_int {
124
- let mut i_builder = Int64Builder::with_capacity(num_rows);
125
- for val in vals {
126
- match val {
127
- DataValue::Int(i) => i_builder.append_value(*i),
128
- _ => i_builder.append_null(),
129
- }
130
- }
131
- fields.push(Field::new(name, DataType::Int64, true));
132
- arrays.push(Arc::new(i_builder.finish()));
133
- } else if all_numeric {
134
- let mut f_builder = Float64Builder::with_capacity(num_rows);
135
- for val in vals {
136
- match val {
137
- DataValue::Float(f) => f_builder.append_value(*f),
138
- DataValue::Int(i) => f_builder.append_value(*i as f64),
139
- _ => f_builder.append_null(),
140
- }
141
- }
142
- fields.push(Field::new(name, DataType::Float64, true));
143
- arrays.push(Arc::new(f_builder.finish()));
144
- } else if all_bool {
145
- let mut b_builder = BooleanBuilder::with_capacity(num_rows);
146
- for val in vals {
147
- match val {
148
- DataValue::Bool(b) => b_builder.append_value(*b),
149
- _ => b_builder.append_null(),
150
- }
151
- }
152
- fields.push(Field::new(name, DataType::Boolean, true));
153
- arrays.push(Arc::new(b_builder.finish()));
154
- } else {
155
- let mut s_builder = StringBuilder::with_capacity(num_rows, num_rows * 64);
156
- for val in vals {
157
- match val {
158
- DataValue::Null => s_builder.append_null(),
159
- DataValue::Text(s) | DataValue::Json(s) => s_builder.append_value(s),
160
- DataValue::Int(i) => s_builder.append_value(i.to_string()),
161
- DataValue::Float(f) => s_builder.append_value(f.to_string()),
162
- DataValue::Bool(b) => s_builder.append_value(b.to_string()),
163
- DataValue::Bytes(b) => s_builder.append_value(format!("<{} bytes>", b.len())),
164
- DataValue::List(l) => {
165
- let json = serde_json::to_string(l).unwrap_or_default();
166
- s_builder.append_value(json);
167
- }
168
- }
169
- }
170
- fields.push(Field::new(name, DataType::Utf8, true));
171
- arrays.push(Arc::new(s_builder.finish()));
172
- }
173
- }
174
-
175
- let schema = Arc::new(Schema::new(fields));
176
- let batch = RecordBatch::try_new(schema.clone(), arrays)?;
177
- Ok((schema, batch))
178
- }
179
-
180
- impl serde::Serialize for DataValue {
181
- fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
182
- where
183
- S: serde::Serializer,
184
- {
185
- match self {
186
- DataValue::Null => serializer.serialize_none(),
187
- DataValue::Text(s) => serializer.serialize_str(s),
188
- DataValue::Bytes(b) => serializer.serialize_bytes(b),
189
- DataValue::Json(j) => {
190
- if let Ok(v) = serde_json::from_str::<serde_json::Value>(j) {
191
- v.serialize(serializer)
192
- } else {
193
- serializer.serialize_str(j)
194
- }
195
- }
196
- DataValue::Int(i) => serializer.serialize_i64(*i),
197
- DataValue::Float(f) => serializer.serialize_f64(*f),
198
- DataValue::Bool(b) => serializer.serialize_bool(*b),
199
- DataValue::List(l) => l.serialize(serializer),
200
- }
201
- }
202
- }
203
-
204
- pub fn serialize_columns_to_parquet_bytes(
205
- columns: &[(String, Vec<DataValue>)],
206
- hf_image_struct: bool,
207
- ) -> Result<Vec<u8>> {
208
- let (schema, batch) = build_arrow_record_batch(columns, hf_image_struct)?;
209
- let mut out = Vec::new();
210
- let props = WriterProperties::builder()
211
- .set_compression(Compression::ZSTD(Default::default()))
212
- .build();
213
- let mut writer = ArrowWriter::try_new(&mut out, schema, Some(props))?;
214
- writer.write(&batch)?;
215
- writer.close()?;
216
- Ok(out)
217
- }
218
-
219
- pub fn serialize_columns_to_arrow_ipc_bytes(
220
- columns: &[(String, Vec<DataValue>)],
221
- hf_image_struct: bool,
222
- ) -> Result<Vec<u8>> {
223
- let (schema, batch) = build_arrow_record_batch(columns, hf_image_struct)?;
224
- let mut out = Vec::new();
225
- let mut writer = ArrowIpcFileWriter::try_new(&mut out, &schema)?;
226
- writer.write(&batch)?;
227
- writer.finish()?;
228
- Ok(out)
229
- }
230
-
231
- pub fn serialize_columns_to_jsonl_bytes(columns: &[(String, Vec<DataValue>)]) -> Result<Vec<u8>> {
232
- if columns.is_empty() {
233
- return Ok(Vec::new());
234
- }
235
- let num_rows = columns[0].1.len();
236
- let mut out = Vec::new();
237
- for row_idx in 0..num_rows {
238
- let mut map = serde_json::Map::with_capacity(columns.len());
239
- for (col_name, vals) in columns {
240
- let val = vals.get(row_idx).unwrap_or(&DataValue::Null);
241
- let json_val = serde_json::to_value(val).unwrap_or(serde_json::Value::Null);
242
- map.insert(col_name.clone(), json_val);
243
- }
244
- let line = serde_json::to_string(&map)
245
- .map_err(|e| DataLoaderError::InvalidConfig(e.to_string()))?;
246
- out.extend_from_slice(line.as_bytes());
247
- out.push(b'\n');
248
- }
249
- Ok(out)
250
- }
251
-
252
- pub fn serialize_columns_by_format(
253
- columns: &[(String, Vec<DataValue>)],
254
- format: &str,
255
- hf_image_struct: bool,
256
- ) -> Result<Vec<u8>> {
257
- let lower = format.trim().to_ascii_lowercase();
258
- if lower == "parquet" || lower.ends_with(".parquet") || lower.ends_with(".pq") {
259
- serialize_columns_to_parquet_bytes(columns, hf_image_struct)
260
- } else if lower == "arrow" || lower == "feather" || lower == "ipc" || lower.ends_with(".arrow")
261
- {
262
- serialize_columns_to_arrow_ipc_bytes(columns, hf_image_struct)
263
- } else if lower == "jsonl" || lower == "ndjson" || lower.ends_with(".jsonl") {
264
- serialize_columns_to_jsonl_bytes(columns)
265
- } else {
266
- serialize_columns_to_parquet_bytes(columns, hf_image_struct)
267
- }
268
- }
 
1
+ use std::sync::Arc;
2
+
3
+ use arrow_array::builder::{
4
+ BinaryBuilder, BooleanBuilder, Float64Builder, Int64Builder, StringBuilder,
5
+ };
6
+ use arrow_array::{Array, RecordBatch, StructArray};
7
+ use arrow_ipc::writer::FileWriter as ArrowIpcFileWriter;
8
+ use arrow_schema::{DataType, Field, Fields, Schema};
9
+ use parquet::arrow::ArrowWriter;
10
+ use parquet::basic::Compression;
11
+ use parquet::file::properties::WriterProperties;
12
+
13
+ use crate::error::{DataLoaderError, Result};
14
+
15
+ #[derive(Debug, Clone, PartialEq)]
16
+ pub enum DataValue {
17
+ Null,
18
+ Text(String),
19
+ Bytes(Vec<u8>),
20
+ Json(String),
21
+ Int(i64),
22
+ Float(f64),
23
+ Bool(bool),
24
+ List(Vec<DataValue>),
25
+ }
26
+
27
+ pub fn classify_string_or_json(s: &str) -> DataValue {
28
+ let trimmed = s.trim();
29
+ if ((trimmed.starts_with('{') && trimmed.ends_with('}'))
30
+ || (trimmed.starts_with('[') && trimmed.ends_with(']')))
31
+ && serde_json::from_str::<serde_json::Value>(trimmed).is_ok()
32
+ {
33
+ return DataValue::Json(trimmed.to_owned());
34
+ }
35
+ DataValue::Text(s.to_owned())
36
+ }
37
+
38
+ pub fn build_arrow_record_batch(
39
+ columns: &[(String, Vec<DataValue>)],
40
+ hf_image_struct: bool,
41
+ ) -> Result<(Arc<Schema>, RecordBatch)> {
42
+ if columns.is_empty() {
43
+ return Err(DataLoaderError::InvalidConfig(
44
+ "Cannot serialize empty column list".to_owned(),
45
+ ));
46
+ }
47
+
48
+ let num_rows = columns[0].1.len();
49
+ let mut fields = Vec::with_capacity(columns.len());
50
+ let mut arrays: Vec<Arc<dyn Array>> = Vec::with_capacity(columns.len());
51
+
52
+ for (name, vals) in columns {
53
+ if vals.len() != num_rows {
54
+ return Err(DataLoaderError::InvalidConfig(format!(
55
+ "Column '{name}' length mismatch: expected {num_rows}, got {}",
56
+ vals.len()
57
+ )));
58
+ }
59
+
60
+ let has_non_null = vals.iter().any(|v| !matches!(v, DataValue::Null));
61
+ let is_binary = vals.iter().any(|v| matches!(v, DataValue::Bytes(_)));
62
+ let all_int = has_non_null
63
+ && vals
64
+ .iter()
65
+ .all(|v| matches!(v, DataValue::Null | DataValue::Int(_)));
66
+ let has_float = vals.iter().any(|v| matches!(v, DataValue::Float(_)));
67
+ let all_numeric = has_non_null
68
+ && has_float
69
+ && vals
70
+ .iter()
71
+ .all(|v| matches!(v, DataValue::Null | DataValue::Int(_) | DataValue::Float(_)));
72
+ let all_bool = has_non_null
73
+ && vals
74
+ .iter()
75
+ .all(|v| matches!(v, DataValue::Null | DataValue::Bool(_)));
76
+
77
+ if is_binary {
78
+ if hf_image_struct {
79
+ let mut bytes_builder = BinaryBuilder::with_capacity(num_rows, num_rows * 1024);
80
+ let mut path_builder = StringBuilder::with_capacity(num_rows, num_rows * 16);
81
+
82
+ for val in vals {
83
+ match val {
84
+ DataValue::Bytes(b) => {
85
+ bytes_builder.append_value(b);
86
+ path_builder.append_null();
87
+ }
88
+ DataValue::Text(t) => {
89
+ bytes_builder.append_null();
90
+ path_builder.append_value(t);
91
+ }
92
+ _ => {
93
+ bytes_builder.append_null();
94
+ path_builder.append_null();
95
+ }
96
+ }
97
+ }
98
+
99
+ let bytes_arr = Arc::new(bytes_builder.finish()) as Arc<dyn Array>;
100
+ let path_arr = Arc::new(path_builder.finish()) as Arc<dyn Array>;
101
+
102
+ let struct_fields = Fields::from(vec![
103
+ Field::new("bytes", DataType::Binary, true),
104
+ Field::new("path", DataType::Utf8, true),
105
+ ]);
106
+
107
+ let struct_arr =
108
+ StructArray::new(struct_fields.clone(), vec![bytes_arr, path_arr], None);
109
+
110
+ fields.push(Field::new(name, DataType::Struct(struct_fields), true));
111
+ arrays.push(Arc::new(struct_arr));
112
+ } else {
113
+ let mut b_builder = BinaryBuilder::with_capacity(num_rows, num_rows * 1024);
114
+ for val in vals {
115
+ match val {
116
+ DataValue::Bytes(b) => b_builder.append_value(b),
117
+ _ => b_builder.append_null(),
118
+ }
119
+ }
120
+ fields.push(Field::new(name, DataType::Binary, true));
121
+ arrays.push(Arc::new(b_builder.finish()));
122
+ }
123
+ } else if all_int {
124
+ let mut i_builder = Int64Builder::with_capacity(num_rows);
125
+ for val in vals {
126
+ match val {
127
+ DataValue::Int(i) => i_builder.append_value(*i),
128
+ _ => i_builder.append_null(),
129
+ }
130
+ }
131
+ fields.push(Field::new(name, DataType::Int64, true));
132
+ arrays.push(Arc::new(i_builder.finish()));
133
+ } else if all_numeric {
134
+ let mut f_builder = Float64Builder::with_capacity(num_rows);
135
+ for val in vals {
136
+ match val {
137
+ DataValue::Float(f) => f_builder.append_value(*f),
138
+ DataValue::Int(i) => f_builder.append_value(*i as f64),
139
+ _ => f_builder.append_null(),
140
+ }
141
+ }
142
+ fields.push(Field::new(name, DataType::Float64, true));
143
+ arrays.push(Arc::new(f_builder.finish()));
144
+ } else if all_bool {
145
+ let mut b_builder = BooleanBuilder::with_capacity(num_rows);
146
+ for val in vals {
147
+ match val {
148
+ DataValue::Bool(b) => b_builder.append_value(*b),
149
+ _ => b_builder.append_null(),
150
+ }
151
+ }
152
+ fields.push(Field::new(name, DataType::Boolean, true));
153
+ arrays.push(Arc::new(b_builder.finish()));
154
+ } else {
155
+ let mut s_builder = StringBuilder::with_capacity(num_rows, num_rows * 64);
156
+ for val in vals {
157
+ match val {
158
+ DataValue::Null => s_builder.append_null(),
159
+ DataValue::Text(s) | DataValue::Json(s) => s_builder.append_value(s),
160
+ DataValue::Int(i) => s_builder.append_value(i.to_string()),
161
+ DataValue::Float(f) => s_builder.append_value(f.to_string()),
162
+ DataValue::Bool(b) => s_builder.append_value(b.to_string()),
163
+ DataValue::Bytes(b) => s_builder.append_value(format!("<{} bytes>", b.len())),
164
+ DataValue::List(l) => {
165
+ let json = serde_json::to_string(l).unwrap_or_default();
166
+ s_builder.append_value(json);
167
+ }
168
+ }
169
+ }
170
+ fields.push(Field::new(name, DataType::Utf8, true));
171
+ arrays.push(Arc::new(s_builder.finish()));
172
+ }
173
+ }
174
+
175
+ let schema = Arc::new(Schema::new(fields));
176
+ let batch = RecordBatch::try_new(schema.clone(), arrays)?;
177
+ Ok((schema, batch))
178
+ }
179
+
180
+ impl serde::Serialize for DataValue {
181
+ fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
182
+ where
183
+ S: serde::Serializer,
184
+ {
185
+ match self {
186
+ DataValue::Null => serializer.serialize_none(),
187
+ DataValue::Text(s) => serializer.serialize_str(s),
188
+ DataValue::Bytes(b) => serializer.serialize_bytes(b),
189
+ DataValue::Json(j) => {
190
+ if let Ok(v) = serde_json::from_str::<serde_json::Value>(j) {
191
+ v.serialize(serializer)
192
+ } else {
193
+ serializer.serialize_str(j)
194
+ }
195
+ }
196
+ DataValue::Int(i) => serializer.serialize_i64(*i),
197
+ DataValue::Float(f) => serializer.serialize_f64(*f),
198
+ DataValue::Bool(b) => serializer.serialize_bool(*b),
199
+ DataValue::List(l) => l.serialize(serializer),
200
+ }
201
+ }
202
+ }
203
+
204
+ pub fn serialize_columns_to_parquet_bytes(
205
+ columns: &[(String, Vec<DataValue>)],
206
+ hf_image_struct: bool,
207
+ ) -> Result<Vec<u8>> {
208
+ let (schema, batch) = build_arrow_record_batch(columns, hf_image_struct)?;
209
+ let mut out = Vec::new();
210
+ let props = WriterProperties::builder()
211
+ .set_compression(Compression::ZSTD(Default::default()))
212
+ .build();
213
+ let mut writer = ArrowWriter::try_new(&mut out, schema, Some(props))?;
214
+ writer.write(&batch)?;
215
+ writer.close()?;
216
+ Ok(out)
217
+ }
218
+
219
+ pub fn serialize_columns_to_arrow_ipc_bytes(
220
+ columns: &[(String, Vec<DataValue>)],
221
+ hf_image_struct: bool,
222
+ ) -> Result<Vec<u8>> {
223
+ let (schema, batch) = build_arrow_record_batch(columns, hf_image_struct)?;
224
+ let mut out = Vec::new();
225
+ let mut writer = ArrowIpcFileWriter::try_new(&mut out, &schema)?;
226
+ writer.write(&batch)?;
227
+ writer.finish()?;
228
+ Ok(out)
229
+ }
230
+
231
+ pub fn serialize_columns_to_jsonl_bytes(columns: &[(String, Vec<DataValue>)]) -> Result<Vec<u8>> {
232
+ if columns.is_empty() {
233
+ return Ok(Vec::new());
234
+ }
235
+ let num_rows = columns[0].1.len();
236
+ let mut out = Vec::new();
237
+ for row_idx in 0..num_rows {
238
+ let mut map = serde_json::Map::with_capacity(columns.len());
239
+ for (col_name, vals) in columns {
240
+ let val = vals.get(row_idx).unwrap_or(&DataValue::Null);
241
+ let json_val = serde_json::to_value(val).unwrap_or(serde_json::Value::Null);
242
+ map.insert(col_name.clone(), json_val);
243
+ }
244
+ let line = serde_json::to_string(&map)
245
+ .map_err(|e| DataLoaderError::InvalidConfig(e.to_string()))?;
246
+ out.extend_from_slice(line.as_bytes());
247
+ out.push(b'\n');
248
+ }
249
+ Ok(out)
250
+ }
251
+
252
+ pub fn serialize_columns_by_format(
253
+ columns: &[(String, Vec<DataValue>)],
254
+ format: &str,
255
+ hf_image_struct: bool,
256
+ ) -> Result<Vec<u8>> {
257
+ let lower = format.trim().to_ascii_lowercase();
258
+ if lower == "parquet" || lower.ends_with(".parquet") || lower.ends_with(".pq") {
259
+ serialize_columns_to_parquet_bytes(columns, hf_image_struct)
260
+ } else if lower == "arrow" || lower == "feather" || lower == "ipc" || lower.ends_with(".arrow")
261
+ {
262
+ serialize_columns_to_arrow_ipc_bytes(columns, hf_image_struct)
263
+ } else if lower == "jsonl" || lower == "ndjson" || lower.ends_with(".jsonl") {
264
+ serialize_columns_to_jsonl_bytes(columns)
265
+ } else {
266
+ serialize_columns_to_parquet_bytes(columns, hf_image_struct)
267
+ }
268
+ }
data/src/simd_cpu.rs CHANGED
@@ -1,313 +1,313 @@
1
- use std::sync::OnceLock;
2
-
3
- #[derive(Debug, Clone, Copy, PartialEq, Eq)]
4
- pub enum CpuIsa {
5
- Avx512,
6
- Avx2,
7
- Neon,
8
- Scalar,
9
- }
10
-
11
- impl CpuIsa {
12
- pub fn as_str(self) -> &'static str {
13
- match self {
14
- Self::Avx512 => "AVX-512",
15
- Self::Avx2 => "AVX2",
16
- Self::Neon => "NEON",
17
- Self::Scalar => "SCALAR",
18
- }
19
- }
20
- }
21
-
22
- #[repr(align(64))]
23
- #[derive(Debug, Clone)]
24
- pub struct CacheAlignedBuffer {
25
- pub data: Vec<u8>,
26
- }
27
-
28
- impl CacheAlignedBuffer {
29
- pub fn with_capacity(cap: usize) -> Self {
30
- let aligned_cap = (cap + 63) & !63;
31
- Self {
32
- data: Vec::with_capacity(aligned_cap),
33
- }
34
- }
35
- }
36
-
37
- #[inline]
38
- pub fn detect_cpu_isa() -> CpuIsa {
39
- static CACHED_ISA: OnceLock<CpuIsa> = OnceLock::new();
40
- *CACHED_ISA.get_or_init(|| {
41
- std::cfg_select! {
42
- any(target_arch = "x86", target_arch = "x86_64") => {
43
- if std::arch::is_x86_feature_detected!("avx512f")
44
- && std::arch::is_x86_feature_detected!("avx512bw")
45
- {
46
- CpuIsa::Avx512
47
- } else if std::arch::is_x86_feature_detected!("avx2") {
48
- CpuIsa::Avx2
49
- } else {
50
- CpuIsa::Scalar
51
- }
52
- }
53
- target_arch = "aarch64" => {
54
- if std::arch::is_aarch64_feature_detected!("neon") {
55
- CpuIsa::Neon
56
- } else {
57
- CpuIsa::Scalar
58
- }
59
- }
60
- _ => {
61
- CpuIsa::Scalar
62
- }
63
- }
64
- })
65
- }
66
-
67
- #[inline]
68
- pub fn find_byte_fast(haystack: &[u8], needle: u8) -> Option<usize> {
69
- #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
70
- {
71
- match detect_cpu_isa() {
72
- CpuIsa::Avx512 => return unsafe { find_byte_avx512(haystack, needle) },
73
- CpuIsa::Avx2 => return unsafe { find_byte_avx2(haystack, needle) },
74
- _ => {}
75
- }
76
- }
77
- #[cfg(target_arch = "aarch64")]
78
- {
79
- if detect_cpu_isa() == CpuIsa::Neon {
80
- return unsafe { find_byte_neon(haystack, needle) };
81
- }
82
- }
83
- haystack.iter().position(|&b| b == needle)
84
- }
85
-
86
- #[inline]
87
- pub fn rfind_byte_fast(haystack: &[u8], needle: u8) -> Option<usize> {
88
- #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
89
- {
90
- match detect_cpu_isa() {
91
- CpuIsa::Avx512 => return unsafe { rfind_byte_avx512(haystack, needle) },
92
- CpuIsa::Avx2 => return unsafe { rfind_byte_avx2(haystack, needle) },
93
- _ => {}
94
- }
95
- }
96
- #[cfg(target_arch = "aarch64")]
97
- {
98
- if detect_cpu_isa() == CpuIsa::Neon {
99
- return unsafe { rfind_byte_neon(haystack, needle) };
100
- }
101
- }
102
- haystack.iter().rposition(|&b| b == needle)
103
- }
104
-
105
- #[inline]
106
- pub fn contains_byte_fast(haystack: &[u8], needle: u8) -> bool {
107
- find_byte_fast(haystack, needle).is_some()
108
- }
109
-
110
- #[inline]
111
- pub fn find_newline_fast(haystack: &[u8]) -> Option<usize> {
112
- find_byte_fast(haystack, b'\n')
113
- }
114
-
115
- #[inline]
116
- pub fn rfind_newline_fast(haystack: &[u8]) -> Option<usize> {
117
- rfind_byte_fast(haystack, b'\n')
118
- }
119
-
120
- #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
121
- #[target_feature(enable = "avx512f,avx512bw")]
122
- unsafe fn find_byte_avx512(haystack: &[u8], needle: u8) -> Option<usize> {
123
- #[cfg(target_arch = "x86")]
124
- use std::arch::x86::*;
125
- #[cfg(target_arch = "x86_64")]
126
- use std::arch::x86_64::*;
127
-
128
- let len = haystack.len();
129
- let ptr = haystack.as_ptr();
130
- let v_needle = _mm512_set1_epi8(needle as i8);
131
- let mut offset = 0usize;
132
-
133
- while offset + 64 <= len {
134
- let chunk = unsafe { _mm512_loadu_si512(ptr.add(offset).cast()) };
135
- let mask = _mm512_cmpeq_epi8_mask(chunk, v_needle);
136
- if mask != 0 {
137
- return Some(offset + mask.trailing_zeros() as usize);
138
- }
139
- offset += 64;
140
- }
141
-
142
- while offset < len {
143
- if unsafe { *ptr.add(offset) } == needle {
144
- return Some(offset);
145
- }
146
- offset += 1;
147
- }
148
- None
149
- }
150
-
151
- #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
152
- #[target_feature(enable = "avx512f,avx512bw")]
153
- unsafe fn rfind_byte_avx512(haystack: &[u8], needle: u8) -> Option<usize> {
154
- #[cfg(target_arch = "x86")]
155
- use std::arch::x86::*;
156
- #[cfg(target_arch = "x86_64")]
157
- use std::arch::x86_64::*;
158
-
159
- let len = haystack.len();
160
- let ptr = haystack.as_ptr();
161
- let v_needle = _mm512_set1_epi8(needle as i8);
162
- let mut end = len;
163
-
164
- while end >= 64 {
165
- let start = end - 64;
166
- let chunk = unsafe { _mm512_loadu_si512(ptr.add(start).cast()) };
167
- let mask = _mm512_cmpeq_epi8_mask(chunk, v_needle);
168
- if mask != 0 {
169
- let highest_bit = 63 - mask.leading_zeros() as usize;
170
- return Some(start + highest_bit);
171
- }
172
- end = start;
173
- }
174
-
175
- while end > 0 {
176
- end -= 1;
177
- if unsafe { *ptr.add(end) } == needle {
178
- return Some(end);
179
- }
180
- }
181
- None
182
- }
183
-
184
- #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
185
- #[target_feature(enable = "avx2")]
186
- unsafe fn find_byte_avx2(haystack: &[u8], needle: u8) -> Option<usize> {
187
- #[cfg(target_arch = "x86")]
188
- use std::arch::x86::*;
189
- #[cfg(target_arch = "x86_64")]
190
- use std::arch::x86_64::*;
191
-
192
- let len = haystack.len();
193
- let ptr = haystack.as_ptr();
194
- let v_needle = _mm256_set1_epi8(needle as i8);
195
- let mut offset = 0usize;
196
-
197
- while offset + 32 <= len {
198
- let chunk = unsafe { _mm256_loadu_si256(ptr.add(offset).cast()) };
199
- let cmp = _mm256_cmpeq_epi8(chunk, v_needle);
200
- let mask = _mm256_movemask_epi8(cmp) as u32;
201
- if mask != 0 {
202
- return Some(offset + mask.trailing_zeros() as usize);
203
- }
204
- offset += 32;
205
- }
206
-
207
- while offset < len {
208
- if unsafe { *ptr.add(offset) } == needle {
209
- return Some(offset);
210
- }
211
- offset += 1;
212
- }
213
- None
214
- }
215
-
216
- #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
217
- #[target_feature(enable = "avx2")]
218
- unsafe fn rfind_byte_avx2(haystack: &[u8], needle: u8) -> Option<usize> {
219
- #[cfg(target_arch = "x86")]
220
- use std::arch::x86::*;
221
- #[cfg(target_arch = "x86_64")]
222
- use std::arch::x86_64::*;
223
-
224
- let len = haystack.len();
225
- let ptr = haystack.as_ptr();
226
- let v_needle = _mm256_set1_epi8(needle as i8);
227
- let mut end = len;
228
-
229
- while end >= 32 {
230
- let start = end - 32;
231
- let chunk = unsafe { _mm256_loadu_si256(ptr.add(start).cast()) };
232
- let cmp = _mm256_cmpeq_epi8(chunk, v_needle);
233
- let mask = _mm256_movemask_epi8(cmp) as u32;
234
- if mask != 0 {
235
- let highest_bit = 31 - mask.leading_zeros() as usize;
236
- return Some(start + highest_bit);
237
- }
238
- end = start;
239
- }
240
-
241
- while end > 0 {
242
- end -= 1;
243
- if unsafe { *ptr.add(end) } == needle {
244
- return Some(end);
245
- }
246
- }
247
- None
248
- }
249
-
250
- #[cfg(target_arch = "aarch64")]
251
- #[target_feature(enable = "neon")]
252
- unsafe fn find_byte_neon(haystack: &[u8], needle: u8) -> Option<usize> {
253
- use std::arch::aarch64::*;
254
-
255
- let len = haystack.len();
256
- let ptr = haystack.as_ptr();
257
- let v_needle = vdupq_n_u8(needle);
258
- let mut offset = 0usize;
259
-
260
- while offset + 16 <= len {
261
- let chunk = unsafe { vld1q_u8(ptr.add(offset)) };
262
- let cmp = vceqq_u8(chunk, v_needle);
263
- if vmaxvq_u8(cmp) != 0 {
264
- for lane in 0..16 {
265
- if unsafe { *ptr.add(offset + lane) } == needle {
266
- return Some(offset + lane);
267
- }
268
- }
269
- }
270
- offset += 16;
271
- }
272
-
273
- while offset < len {
274
- if unsafe { *ptr.add(offset) } == needle {
275
- return Some(offset);
276
- }
277
- offset += 1;
278
- }
279
- None
280
- }
281
-
282
- #[cfg(target_arch = "aarch64")]
283
- #[target_feature(enable = "neon")]
284
- unsafe fn rfind_byte_neon(haystack: &[u8], needle: u8) -> Option<usize> {
285
- use std::arch::aarch64::*;
286
-
287
- let len = haystack.len();
288
- let ptr = haystack.as_ptr();
289
- let v_needle = vdupq_n_u8(needle);
290
- let mut end = len;
291
-
292
- while end >= 16 {
293
- let start = end - 16;
294
- let chunk = unsafe { vld1q_u8(ptr.add(start)) };
295
- let cmp = vceqq_u8(chunk, v_needle);
296
- if vmaxvq_u8(cmp) != 0 {
297
- for lane in (0..16).rev() {
298
- if unsafe { *ptr.add(start + lane) } == needle {
299
- return Some(start + lane);
300
- }
301
- }
302
- }
303
- end = start;
304
- }
305
-
306
- while end > 0 {
307
- end -= 1;
308
- if unsafe { *ptr.add(end) } == needle {
309
- return Some(end);
310
- }
311
- }
312
- None
313
- }
 
1
+ use std::sync::OnceLock;
2
+
3
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
4
+ pub enum CpuIsa {
5
+ Avx512,
6
+ Avx2,
7
+ Neon,
8
+ Scalar,
9
+ }
10
+
11
+ impl CpuIsa {
12
+ pub fn as_str(self) -> &'static str {
13
+ match self {
14
+ Self::Avx512 => "AVX-512",
15
+ Self::Avx2 => "AVX2",
16
+ Self::Neon => "NEON",
17
+ Self::Scalar => "SCALAR",
18
+ }
19
+ }
20
+ }
21
+
22
+ #[repr(align(64))]
23
+ #[derive(Debug, Clone)]
24
+ pub struct CacheAlignedBuffer {
25
+ pub data: Vec<u8>,
26
+ }
27
+
28
+ impl CacheAlignedBuffer {
29
+ pub fn with_capacity(cap: usize) -> Self {
30
+ let aligned_cap = (cap + 63) & !63;
31
+ Self {
32
+ data: Vec::with_capacity(aligned_cap),
33
+ }
34
+ }
35
+ }
36
+
37
+ #[inline]
38
+ pub fn detect_cpu_isa() -> CpuIsa {
39
+ static CACHED_ISA: OnceLock<CpuIsa> = OnceLock::new();
40
+ *CACHED_ISA.get_or_init(|| {
41
+ std::cfg_select! {
42
+ any(target_arch = "x86", target_arch = "x86_64") => {
43
+ if std::arch::is_x86_feature_detected!("avx512f")
44
+ && std::arch::is_x86_feature_detected!("avx512bw")
45
+ {
46
+ CpuIsa::Avx512
47
+ } else if std::arch::is_x86_feature_detected!("avx2") {
48
+ CpuIsa::Avx2
49
+ } else {
50
+ CpuIsa::Scalar
51
+ }
52
+ }
53
+ target_arch = "aarch64" => {
54
+ if std::arch::is_aarch64_feature_detected!("neon") {
55
+ CpuIsa::Neon
56
+ } else {
57
+ CpuIsa::Scalar
58
+ }
59
+ }
60
+ _ => {
61
+ CpuIsa::Scalar
62
+ }
63
+ }
64
+ })
65
+ }
66
+
67
+ #[inline]
68
+ pub fn find_byte_fast(haystack: &[u8], needle: u8) -> Option<usize> {
69
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
70
+ {
71
+ match detect_cpu_isa() {
72
+ CpuIsa::Avx512 => return unsafe { find_byte_avx512(haystack, needle) },
73
+ CpuIsa::Avx2 => return unsafe { find_byte_avx2(haystack, needle) },
74
+ _ => {}
75
+ }
76
+ }
77
+ #[cfg(target_arch = "aarch64")]
78
+ {
79
+ if detect_cpu_isa() == CpuIsa::Neon {
80
+ return unsafe { find_byte_neon(haystack, needle) };
81
+ }
82
+ }
83
+ haystack.iter().position(|&b| b == needle)
84
+ }
85
+
86
+ #[inline]
87
+ pub fn rfind_byte_fast(haystack: &[u8], needle: u8) -> Option<usize> {
88
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
89
+ {
90
+ match detect_cpu_isa() {
91
+ CpuIsa::Avx512 => return unsafe { rfind_byte_avx512(haystack, needle) },
92
+ CpuIsa::Avx2 => return unsafe { rfind_byte_avx2(haystack, needle) },
93
+ _ => {}
94
+ }
95
+ }
96
+ #[cfg(target_arch = "aarch64")]
97
+ {
98
+ if detect_cpu_isa() == CpuIsa::Neon {
99
+ return unsafe { rfind_byte_neon(haystack, needle) };
100
+ }
101
+ }
102
+ haystack.iter().rposition(|&b| b == needle)
103
+ }
104
+
105
+ #[inline]
106
+ pub fn contains_byte_fast(haystack: &[u8], needle: u8) -> bool {
107
+ find_byte_fast(haystack, needle).is_some()
108
+ }
109
+
110
+ #[inline]
111
+ pub fn find_newline_fast(haystack: &[u8]) -> Option<usize> {
112
+ find_byte_fast(haystack, b'\n')
113
+ }
114
+
115
+ #[inline]
116
+ pub fn rfind_newline_fast(haystack: &[u8]) -> Option<usize> {
117
+ rfind_byte_fast(haystack, b'\n')
118
+ }
119
+
120
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
121
+ #[target_feature(enable = "avx512f,avx512bw")]
122
+ unsafe fn find_byte_avx512(haystack: &[u8], needle: u8) -> Option<usize> {
123
+ #[cfg(target_arch = "x86")]
124
+ use std::arch::x86::*;
125
+ #[cfg(target_arch = "x86_64")]
126
+ use std::arch::x86_64::*;
127
+
128
+ let len = haystack.len();
129
+ let ptr = haystack.as_ptr();
130
+ let v_needle = _mm512_set1_epi8(needle as i8);
131
+ let mut offset = 0usize;
132
+
133
+ while offset + 64 <= len {
134
+ let chunk = unsafe { _mm512_loadu_si512(ptr.add(offset).cast()) };
135
+ let mask = _mm512_cmpeq_epi8_mask(chunk, v_needle);
136
+ if mask != 0 {
137
+ return Some(offset + mask.trailing_zeros() as usize);
138
+ }
139
+ offset += 64;
140
+ }
141
+
142
+ while offset < len {
143
+ if unsafe { *ptr.add(offset) } == needle {
144
+ return Some(offset);
145
+ }
146
+ offset += 1;
147
+ }
148
+ None
149
+ }
150
+
151
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
152
+ #[target_feature(enable = "avx512f,avx512bw")]
153
+ unsafe fn rfind_byte_avx512(haystack: &[u8], needle: u8) -> Option<usize> {
154
+ #[cfg(target_arch = "x86")]
155
+ use std::arch::x86::*;
156
+ #[cfg(target_arch = "x86_64")]
157
+ use std::arch::x86_64::*;
158
+
159
+ let len = haystack.len();
160
+ let ptr = haystack.as_ptr();
161
+ let v_needle = _mm512_set1_epi8(needle as i8);
162
+ let mut end = len;
163
+
164
+ while end >= 64 {
165
+ let start = end - 64;
166
+ let chunk = unsafe { _mm512_loadu_si512(ptr.add(start).cast()) };
167
+ let mask = _mm512_cmpeq_epi8_mask(chunk, v_needle);
168
+ if mask != 0 {
169
+ let highest_bit = 63 - mask.leading_zeros() as usize;
170
+ return Some(start + highest_bit);
171
+ }
172
+ end = start;
173
+ }
174
+
175
+ while end > 0 {
176
+ end -= 1;
177
+ if unsafe { *ptr.add(end) } == needle {
178
+ return Some(end);
179
+ }
180
+ }
181
+ None
182
+ }
183
+
184
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
185
+ #[target_feature(enable = "avx2")]
186
+ unsafe fn find_byte_avx2(haystack: &[u8], needle: u8) -> Option<usize> {
187
+ #[cfg(target_arch = "x86")]
188
+ use std::arch::x86::*;
189
+ #[cfg(target_arch = "x86_64")]
190
+ use std::arch::x86_64::*;
191
+
192
+ let len = haystack.len();
193
+ let ptr = haystack.as_ptr();
194
+ let v_needle = _mm256_set1_epi8(needle as i8);
195
+ let mut offset = 0usize;
196
+
197
+ while offset + 32 <= len {
198
+ let chunk = unsafe { _mm256_loadu_si256(ptr.add(offset).cast()) };
199
+ let cmp = _mm256_cmpeq_epi8(chunk, v_needle);
200
+ let mask = _mm256_movemask_epi8(cmp) as u32;
201
+ if mask != 0 {
202
+ return Some(offset + mask.trailing_zeros() as usize);
203
+ }
204
+ offset += 32;
205
+ }
206
+
207
+ while offset < len {
208
+ if unsafe { *ptr.add(offset) } == needle {
209
+ return Some(offset);
210
+ }
211
+ offset += 1;
212
+ }
213
+ None
214
+ }
215
+
216
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
217
+ #[target_feature(enable = "avx2")]
218
+ unsafe fn rfind_byte_avx2(haystack: &[u8], needle: u8) -> Option<usize> {
219
+ #[cfg(target_arch = "x86")]
220
+ use std::arch::x86::*;
221
+ #[cfg(target_arch = "x86_64")]
222
+ use std::arch::x86_64::*;
223
+
224
+ let len = haystack.len();
225
+ let ptr = haystack.as_ptr();
226
+ let v_needle = _mm256_set1_epi8(needle as i8);
227
+ let mut end = len;
228
+
229
+ while end >= 32 {
230
+ let start = end - 32;
231
+ let chunk = unsafe { _mm256_loadu_si256(ptr.add(start).cast()) };
232
+ let cmp = _mm256_cmpeq_epi8(chunk, v_needle);
233
+ let mask = _mm256_movemask_epi8(cmp) as u32;
234
+ if mask != 0 {
235
+ let highest_bit = 31 - mask.leading_zeros() as usize;
236
+ return Some(start + highest_bit);
237
+ }
238
+ end = start;
239
+ }
240
+
241
+ while end > 0 {
242
+ end -= 1;
243
+ if unsafe { *ptr.add(end) } == needle {
244
+ return Some(end);
245
+ }
246
+ }
247
+ None
248
+ }
249
+
250
+ #[cfg(target_arch = "aarch64")]
251
+ #[target_feature(enable = "neon")]
252
+ unsafe fn find_byte_neon(haystack: &[u8], needle: u8) -> Option<usize> {
253
+ use std::arch::aarch64::*;
254
+
255
+ let len = haystack.len();
256
+ let ptr = haystack.as_ptr();
257
+ let v_needle = vdupq_n_u8(needle);
258
+ let mut offset = 0usize;
259
+
260
+ while offset + 16 <= len {
261
+ let chunk = unsafe { vld1q_u8(ptr.add(offset)) };
262
+ let cmp = vceqq_u8(chunk, v_needle);
263
+ if vmaxvq_u8(cmp) != 0 {
264
+ for lane in 0..16 {
265
+ if unsafe { *ptr.add(offset + lane) } == needle {
266
+ return Some(offset + lane);
267
+ }
268
+ }
269
+ }
270
+ offset += 16;
271
+ }
272
+
273
+ while offset < len {
274
+ if unsafe { *ptr.add(offset) } == needle {
275
+ return Some(offset);
276
+ }
277
+ offset += 1;
278
+ }
279
+ None
280
+ }
281
+
282
+ #[cfg(target_arch = "aarch64")]
283
+ #[target_feature(enable = "neon")]
284
+ unsafe fn rfind_byte_neon(haystack: &[u8], needle: u8) -> Option<usize> {
285
+ use std::arch::aarch64::*;
286
+
287
+ let len = haystack.len();
288
+ let ptr = haystack.as_ptr();
289
+ let v_needle = vdupq_n_u8(needle);
290
+ let mut end = len;
291
+
292
+ while end >= 16 {
293
+ let start = end - 16;
294
+ let chunk = unsafe { vld1q_u8(ptr.add(start)) };
295
+ let cmp = vceqq_u8(chunk, v_needle);
296
+ if vmaxvq_u8(cmp) != 0 {
297
+ for lane in (0..16).rev() {
298
+ if unsafe { *ptr.add(start + lane) } == needle {
299
+ return Some(start + lane);
300
+ }
301
+ }
302
+ }
303
+ end = start;
304
+ }
305
+
306
+ while end > 0 {
307
+ end -= 1;
308
+ if unsafe { *ptr.add(end) } == needle {
309
+ return Some(end);
310
+ }
311
+ }
312
+ None
313
+ }
data/src/string_tensor.rs CHANGED
@@ -1,269 +1,269 @@
1
- use std::sync::Arc;
2
-
3
- use rayon::prelude::*;
4
- use xxhash_rust::xxh3::xxh3_64;
5
-
6
- #[repr(C)]
7
- #[derive(Clone, Copy, Debug, PartialEq, Eq)]
8
- pub struct StringView16 {
9
- pub(crate) len: u32,
10
- pub(crate) prefix_or_inline: [u8; 4],
11
- pub(crate) suffix_or_offset: [u8; 8],
12
- }
13
-
14
- impl StringView16 {
15
- pub const INLINE_CAPACITY: usize = 12;
16
-
17
- #[inline]
18
- pub fn new(s: &str, buffer_id: u32, offset: u32) -> Self {
19
- let len = s.len() as u32;
20
- let bytes = s.as_bytes();
21
- let mut prefix_or_inline = [0u8; 4];
22
- let mut suffix_or_offset = [0u8; 8];
23
-
24
- if (len as usize) <= Self::INLINE_CAPACITY {
25
- let n = bytes.len();
26
- let first_part = n.min(4);
27
- prefix_or_inline[..first_part].copy_from_slice(&bytes[..first_part]);
28
- if n > 4 {
29
- suffix_or_offset[..n - 4].copy_from_slice(&bytes[4..n]);
30
- }
31
- } else {
32
- prefix_or_inline.copy_from_slice(&bytes[..4]);
33
- suffix_or_offset[0..4].copy_from_slice(&offset.to_le_bytes());
34
- suffix_or_offset[4..8].copy_from_slice(&buffer_id.to_le_bytes());
35
- }
36
-
37
- Self {
38
- len,
39
- prefix_or_inline,
40
- suffix_or_offset,
41
- }
42
- }
43
-
44
- #[inline]
45
- pub fn is_inlined(&self) -> bool {
46
- (self.len as usize) <= Self::INLINE_CAPACITY
47
- }
48
-
49
- #[inline]
50
- pub fn as_str<'a>(&'a self, pool: &'a [u8]) -> &'a str {
51
- if self.is_inlined() {
52
- let len = (self.len as usize).min(Self::INLINE_CAPACITY);
53
- // The 12 inline bytes immediately follow the 4-byte len in memory
54
- unsafe {
55
- let ptr = (self as *const Self as *const u8).add(4);
56
- let slice = std::slice::from_raw_parts(ptr, len);
57
- std::str::from_utf8_unchecked(slice)
58
- }
59
- } else {
60
- let offset =
61
- u32::from_le_bytes(self.suffix_or_offset[0..4].try_into().unwrap()) as usize;
62
- let len = self.len as usize;
63
- if let Some(slice) = pool.get(offset..offset + len) {
64
- unsafe { std::str::from_utf8_unchecked(slice) }
65
- } else {
66
- ""
67
- }
68
- }
69
- }
70
-
71
- #[inline]
72
- pub fn fast_eq<'a>(&'a self, other: &'a Self, pool: &'a [u8]) -> bool {
73
- if self.len != other.len {
74
- return false;
75
- }
76
- if self.prefix_or_inline != other.prefix_or_inline {
77
- return false;
78
- }
79
- if self.is_inlined() {
80
- return self.suffix_or_offset == other.suffix_or_offset;
81
- }
82
- self.as_str(pool) == other.as_str(pool)
83
- }
84
-
85
- #[inline]
86
- pub fn compute_hash<'a>(&'a self, pool: &'a [u8]) -> u64 {
87
- let s = self.as_str(pool);
88
- xxh3_64(s.as_bytes())
89
- }
90
- }
91
-
92
- #[derive(Clone, Debug, Default)]
93
- pub struct StringTensor {
94
- pub views: Vec<StringView16>,
95
- pub pool: Arc<Vec<u8>>,
96
- }
97
-
98
- impl StringTensor {
99
- pub fn new() -> Self {
100
- Self {
101
- views: Vec::new(),
102
- pool: Arc::new(Vec::new()),
103
- }
104
- }
105
-
106
- pub fn from_strings<I, S>(strings: I) -> Self
107
- where
108
- I: IntoIterator<Item = S>,
109
- S: AsRef<str>,
110
- {
111
- let items: Vec<S> = strings.into_iter().collect();
112
- let mut total_ref_bytes = 0usize;
113
- for s in &items {
114
- let str_ref = s.as_ref();
115
- if str_ref.len() > StringView16::INLINE_CAPACITY {
116
- total_ref_bytes += str_ref.len();
117
- }
118
- }
119
-
120
- let mut pool = Vec::with_capacity(total_ref_bytes);
121
- let mut views = Vec::with_capacity(items.len());
122
-
123
- for s in items {
124
- let str_ref = s.as_ref();
125
- if str_ref.len() <= StringView16::INLINE_CAPACITY {
126
- views.push(StringView16::new(str_ref, 0, 0));
127
- } else {
128
- let offset = u32::try_from(pool.len()).unwrap_or(u32::MAX);
129
- pool.extend_from_slice(str_ref.as_bytes());
130
- views.push(StringView16::new(str_ref, 0, offset));
131
- }
132
- }
133
-
134
- Self {
135
- views,
136
- pool: Arc::new(pool),
137
- }
138
- }
139
-
140
- pub fn from_gpu_buffers(offsets: &[u32], payload: &[u8]) -> Self {
141
- if offsets.is_empty() {
142
- return Self::new();
143
- }
144
-
145
- let num_strings = offsets.len().saturating_sub(1);
146
- let mut views = Vec::with_capacity(num_strings);
147
- let pool = Arc::new(payload.to_vec());
148
-
149
- for i in 0..num_strings {
150
- let start = offsets[i] as usize;
151
- let end = offsets[i + 1] as usize;
152
- if start <= end && end <= payload.len() {
153
- if let Ok(s) = std::str::from_utf8(&payload[start..end]) {
154
- views.push(StringView16::new(s, 0, start as u32));
155
- } else {
156
- views.push(StringView16::new("", 0, 0));
157
- }
158
- } else {
159
- views.push(StringView16::new("", 0, 0));
160
- }
161
- }
162
-
163
- Self { views, pool }
164
- }
165
-
166
- pub fn to_gpu_buffers(&self) -> (Vec<u32>, Vec<u8>) {
167
- let num_strings = self.views.len();
168
- let mut offsets = Vec::with_capacity(num_strings + 1);
169
- let mut total_chars = 0usize;
170
-
171
- for v in &self.views {
172
- total_chars += v.len as usize;
173
- }
174
-
175
- let mut payload = Vec::with_capacity(total_chars);
176
- let mut current_offset = 0u32;
177
- offsets.push(current_offset);
178
-
179
- for v in &self.views {
180
- let s = v.as_str(&self.pool);
181
- payload.extend_from_slice(s.as_bytes());
182
- current_offset = current_offset.saturating_add(v.len);
183
- offsets.push(current_offset);
184
- }
185
-
186
- (offsets, payload)
187
- }
188
-
189
- #[inline]
190
- pub fn len(&self) -> usize {
191
- self.views.len()
192
- }
193
-
194
- #[inline]
195
- pub fn is_empty(&self) -> bool {
196
- self.views.is_empty()
197
- }
198
-
199
- #[inline]
200
- pub fn get(&self, index: usize) -> Option<&str> {
201
- self.views.get(index).map(|v| v.as_str(&self.pool))
202
- }
203
-
204
- pub fn to_vec(&self) -> Vec<String> {
205
- self.views
206
- .iter()
207
- .map(|v| v.as_str(&self.pool).to_owned())
208
- .collect()
209
- }
210
-
211
- pub fn lengths(&self) -> Vec<u32> {
212
- self.views.iter().map(|v| v.len).collect()
213
- }
214
-
215
- pub fn is_inlined_mask(&self) -> Vec<bool> {
216
- self.views.iter().map(|v| v.is_inlined()).collect()
217
- }
218
-
219
- pub fn hash_all_parallel(&self) -> Vec<u64> {
220
- let pool = &self.pool;
221
- self.views
222
- .par_iter()
223
- .map(|v| v.compute_hash(pool))
224
- .collect()
225
- }
226
-
227
- pub fn filter_by_length(&self, min_len: u32, max_len: u32) -> Self {
228
- let mut new_views = Vec::new();
229
- for v in &self.views {
230
- let l = v.len;
231
- if l >= min_len && (max_len == 0 || l <= max_len) {
232
- new_views.push(*v);
233
- }
234
- }
235
- Self {
236
- views: new_views,
237
- pool: self.pool.clone(),
238
- }
239
- }
240
-
241
- pub fn filter_by_prefix(&self, prefix: &str) -> Self {
242
- let prefix_bytes = prefix.as_bytes();
243
- let pool = &self.pool;
244
- let mut new_views = Vec::new();
245
-
246
- for v in &self.views {
247
- let l = v.len as usize;
248
- if l < prefix_bytes.len() {
249
- continue;
250
- }
251
- // Fast prefix check
252
- if prefix_bytes.len() <= 4 {
253
- if v.prefix_or_inline[..prefix_bytes.len()] == prefix_bytes[..] {
254
- new_views.push(*v);
255
- }
256
- } else if v.prefix_or_inline[..4] == prefix_bytes[..4] {
257
- let s = v.as_str(pool);
258
- if s.as_bytes().starts_with(prefix_bytes) {
259
- new_views.push(*v);
260
- }
261
- }
262
- }
263
-
264
- Self {
265
- views: new_views,
266
- pool: self.pool.clone(),
267
- }
268
- }
269
- }
 
1
+ use std::sync::Arc;
2
+
3
+ use rayon::prelude::*;
4
+ use xxhash_rust::xxh3::xxh3_64;
5
+
6
+ #[repr(C)]
7
+ #[derive(Clone, Copy, Debug, PartialEq, Eq)]
8
+ pub struct StringView16 {
9
+ pub(crate) len: u32,
10
+ pub(crate) prefix_or_inline: [u8; 4],
11
+ pub(crate) suffix_or_offset: [u8; 8],
12
+ }
13
+
14
+ impl StringView16 {
15
+ pub const INLINE_CAPACITY: usize = 12;
16
+
17
+ #[inline]
18
+ pub fn new(s: &str, buffer_id: u32, offset: u32) -> Self {
19
+ let len = s.len() as u32;
20
+ let bytes = s.as_bytes();
21
+ let mut prefix_or_inline = [0u8; 4];
22
+ let mut suffix_or_offset = [0u8; 8];
23
+
24
+ if (len as usize) <= Self::INLINE_CAPACITY {
25
+ let n = bytes.len();
26
+ let first_part = n.min(4);
27
+ prefix_or_inline[..first_part].copy_from_slice(&bytes[..first_part]);
28
+ if n > 4 {
29
+ suffix_or_offset[..n - 4].copy_from_slice(&bytes[4..n]);
30
+ }
31
+ } else {
32
+ prefix_or_inline.copy_from_slice(&bytes[..4]);
33
+ suffix_or_offset[0..4].copy_from_slice(&offset.to_le_bytes());
34
+ suffix_or_offset[4..8].copy_from_slice(&buffer_id.to_le_bytes());
35
+ }
36
+
37
+ Self {
38
+ len,
39
+ prefix_or_inline,
40
+ suffix_or_offset,
41
+ }
42
+ }
43
+
44
+ #[inline]
45
+ pub fn is_inlined(&self) -> bool {
46
+ (self.len as usize) <= Self::INLINE_CAPACITY
47
+ }
48
+
49
+ #[inline]
50
+ pub fn as_str<'a>(&'a self, pool: &'a [u8]) -> &'a str {
51
+ if self.is_inlined() {
52
+ let len = (self.len as usize).min(Self::INLINE_CAPACITY);
53
+ // The 12 inline bytes immediately follow the 4-byte len in memory
54
+ unsafe {
55
+ let ptr = (self as *const Self as *const u8).add(4);
56
+ let slice = std::slice::from_raw_parts(ptr, len);
57
+ std::str::from_utf8_unchecked(slice)
58
+ }
59
+ } else {
60
+ let offset =
61
+ u32::from_le_bytes(self.suffix_or_offset[0..4].try_into().unwrap()) as usize;
62
+ let len = self.len as usize;
63
+ if let Some(slice) = pool.get(offset..offset + len) {
64
+ unsafe { std::str::from_utf8_unchecked(slice) }
65
+ } else {
66
+ ""
67
+ }
68
+ }
69
+ }
70
+
71
+ #[inline]
72
+ pub fn fast_eq<'a>(&'a self, other: &'a Self, pool: &'a [u8]) -> bool {
73
+ if self.len != other.len {
74
+ return false;
75
+ }
76
+ if self.prefix_or_inline != other.prefix_or_inline {
77
+ return false;
78
+ }
79
+ if self.is_inlined() {
80
+ return self.suffix_or_offset == other.suffix_or_offset;
81
+ }
82
+ self.as_str(pool) == other.as_str(pool)
83
+ }
84
+
85
+ #[inline]
86
+ pub fn compute_hash<'a>(&'a self, pool: &'a [u8]) -> u64 {
87
+ let s = self.as_str(pool);
88
+ xxh3_64(s.as_bytes())
89
+ }
90
+ }
91
+
92
+ #[derive(Clone, Debug, Default)]
93
+ pub struct StringTensor {
94
+ pub views: Vec<StringView16>,
95
+ pub pool: Arc<Vec<u8>>,
96
+ }
97
+
98
+ impl StringTensor {
99
+ pub fn new() -> Self {
100
+ Self {
101
+ views: Vec::new(),
102
+ pool: Arc::new(Vec::new()),
103
+ }
104
+ }
105
+
106
+ pub fn from_strings<I, S>(strings: I) -> Self
107
+ where
108
+ I: IntoIterator<Item = S>,
109
+ S: AsRef<str>,
110
+ {
111
+ let items: Vec<S> = strings.into_iter().collect();
112
+ let mut total_ref_bytes = 0usize;
113
+ for s in &items {
114
+ let str_ref = s.as_ref();
115
+ if str_ref.len() > StringView16::INLINE_CAPACITY {
116
+ total_ref_bytes += str_ref.len();
117
+ }
118
+ }
119
+
120
+ let mut pool = Vec::with_capacity(total_ref_bytes);
121
+ let mut views = Vec::with_capacity(items.len());
122
+
123
+ for s in items {
124
+ let str_ref = s.as_ref();
125
+ if str_ref.len() <= StringView16::INLINE_CAPACITY {
126
+ views.push(StringView16::new(str_ref, 0, 0));
127
+ } else {
128
+ let offset = u32::try_from(pool.len()).unwrap_or(u32::MAX);
129
+ pool.extend_from_slice(str_ref.as_bytes());
130
+ views.push(StringView16::new(str_ref, 0, offset));
131
+ }
132
+ }
133
+
134
+ Self {
135
+ views,
136
+ pool: Arc::new(pool),
137
+ }
138
+ }
139
+
140
+ pub fn from_gpu_buffers(offsets: &[u32], payload: &[u8]) -> Self {
141
+ if offsets.is_empty() {
142
+ return Self::new();
143
+ }
144
+
145
+ let num_strings = offsets.len().saturating_sub(1);
146
+ let mut views = Vec::with_capacity(num_strings);
147
+ let pool = Arc::new(payload.to_vec());
148
+
149
+ for i in 0..num_strings {
150
+ let start = offsets[i] as usize;
151
+ let end = offsets[i + 1] as usize;
152
+ if start <= end && end <= payload.len() {
153
+ if let Ok(s) = std::str::from_utf8(&payload[start..end]) {
154
+ views.push(StringView16::new(s, 0, start as u32));
155
+ } else {
156
+ views.push(StringView16::new("", 0, 0));
157
+ }
158
+ } else {
159
+ views.push(StringView16::new("", 0, 0));
160
+ }
161
+ }
162
+
163
+ Self { views, pool }
164
+ }
165
+
166
+ pub fn to_gpu_buffers(&self) -> (Vec<u32>, Vec<u8>) {
167
+ let num_strings = self.views.len();
168
+ let mut offsets = Vec::with_capacity(num_strings + 1);
169
+ let mut total_chars = 0usize;
170
+
171
+ for v in &self.views {
172
+ total_chars += v.len as usize;
173
+ }
174
+
175
+ let mut payload = Vec::with_capacity(total_chars);
176
+ let mut current_offset = 0u32;
177
+ offsets.push(current_offset);
178
+
179
+ for v in &self.views {
180
+ let s = v.as_str(&self.pool);
181
+ payload.extend_from_slice(s.as_bytes());
182
+ current_offset = current_offset.saturating_add(v.len);
183
+ offsets.push(current_offset);
184
+ }
185
+
186
+ (offsets, payload)
187
+ }
188
+
189
+ #[inline]
190
+ pub fn len(&self) -> usize {
191
+ self.views.len()
192
+ }
193
+
194
+ #[inline]
195
+ pub fn is_empty(&self) -> bool {
196
+ self.views.is_empty()
197
+ }
198
+
199
+ #[inline]
200
+ pub fn get(&self, index: usize) -> Option<&str> {
201
+ self.views.get(index).map(|v| v.as_str(&self.pool))
202
+ }
203
+
204
+ pub fn to_vec(&self) -> Vec<String> {
205
+ self.views
206
+ .iter()
207
+ .map(|v| v.as_str(&self.pool).to_owned())
208
+ .collect()
209
+ }
210
+
211
+ pub fn lengths(&self) -> Vec<u32> {
212
+ self.views.iter().map(|v| v.len).collect()
213
+ }
214
+
215
+ pub fn is_inlined_mask(&self) -> Vec<bool> {
216
+ self.views.iter().map(|v| v.is_inlined()).collect()
217
+ }
218
+
219
+ pub fn hash_all_parallel(&self) -> Vec<u64> {
220
+ let pool = &self.pool;
221
+ self.views
222
+ .par_iter()
223
+ .map(|v| v.compute_hash(pool))
224
+ .collect()
225
+ }
226
+
227
+ pub fn filter_by_length(&self, min_len: u32, max_len: u32) -> Self {
228
+ let mut new_views = Vec::new();
229
+ for v in &self.views {
230
+ let l = v.len;
231
+ if l >= min_len && (max_len == 0 || l <= max_len) {
232
+ new_views.push(*v);
233
+ }
234
+ }
235
+ Self {
236
+ views: new_views,
237
+ pool: self.pool.clone(),
238
+ }
239
+ }
240
+
241
+ pub fn filter_by_prefix(&self, prefix: &str) -> Self {
242
+ let prefix_bytes = prefix.as_bytes();
243
+ let pool = &self.pool;
244
+ let mut new_views = Vec::new();
245
+
246
+ for v in &self.views {
247
+ let l = v.len as usize;
248
+ if l < prefix_bytes.len() {
249
+ continue;
250
+ }
251
+ // Fast prefix check
252
+ if prefix_bytes.len() <= 4 {
253
+ if v.prefix_or_inline[..prefix_bytes.len()] == prefix_bytes[..] {
254
+ new_views.push(*v);
255
+ }
256
+ } else if v.prefix_or_inline[..4] == prefix_bytes[..4] {
257
+ let s = v.as_str(pool);
258
+ if s.as_bytes().starts_with(prefix_bytes) {
259
+ new_views.push(*v);
260
+ }
261
+ }
262
+ }
263
+
264
+ Self {
265
+ views: new_views,
266
+ pool: self.pool.clone(),
267
+ }
268
+ }
269
+ }
data/src/tests.rs CHANGED
@@ -1,384 +1,384 @@
1
- use std::fs;
2
- use std::path::PathBuf;
3
-
4
- use crate::coord::StreamCoordinate;
5
- use crate::dedup::TwoStageDeduplicator;
6
- use crate::metadata::DualMetadataManager;
7
- use crate::postprocess::{PostProcessConfig, process_batch_parallel, process_single_record};
8
- use crate::serialize::{
9
- DataValue, build_arrow_record_batch, classify_string_or_json,
10
- serialize_columns_to_arrow_ipc_bytes, serialize_columns_to_jsonl_bytes,
11
- serialize_columns_to_parquet_bytes,
12
- };
13
- use crate::simd_cpu::{contains_byte_fast, detect_cpu_isa, find_newline_fast};
14
-
15
- fn temp_test_path(name: &str, ext: &str) -> PathBuf {
16
- let mut p = std::env::temp_dir();
17
- p.push(format!(
18
- "luminamoon_test_{}_{}.{}",
19
- name,
20
- std::process::id(),
21
- ext
22
- ));
23
- p
24
- }
25
-
26
- #[test]
27
- fn test_stream_coordinate_roundtrip() {
28
- let c = StreamCoordinate {
29
- shard_id: 42,
30
- byte_offset: 1024 * 1024,
31
- row_group_idx: 3,
32
- page_idx: 7,
33
- row_offset_in_page: 128,
34
- global_row_idx: 99999,
35
- };
36
- let bytes = c.to_bytes();
37
- assert_eq!(bytes.len(), StreamCoordinate::BYTE_LEN);
38
- let decoded = StreamCoordinate::from_bytes(&bytes);
39
- assert_eq!(c, decoded);
40
- }
41
-
42
- #[test]
43
- fn test_dual_metadata_ping_pong_and_dedup_wal() {
44
- let meta_path = temp_test_path("ping_pong_meta", "meta");
45
- let _ = fs::remove_file(&meta_path);
46
- let _ = fs::remove_file(format!("{}.dedup", meta_path.display()));
47
-
48
- {
49
- let mut mgr = DualMetadataManager::open(&meta_path).unwrap();
50
- assert_eq!(mgr.committed_batch_id(), 0);
51
-
52
- assert!(mgr.deduplicator.try_stage(1, b"line_alpha").is_some());
53
- assert!(mgr.deduplicator.try_stage(1, b"line_alpha").is_none());
54
-
55
- let c1 = StreamCoordinate {
56
- shard_id: 1,
57
- byte_offset: 512,
58
- row_group_idx: 0,
59
- page_idx: 0,
60
- row_offset_in_page: 0,
61
- global_row_idx: 10,
62
- };
63
- mgr.stage_batch_in_ram(1, c1);
64
- let committed = mgr.commit_gpu_batch(1).unwrap();
65
- assert_eq!(committed, c1);
66
- assert_eq!(mgr.committed_batch_id(), 1);
67
- }
68
-
69
- // Re-open and verify recovery
70
- {
71
- let mut mgr = DualMetadataManager::open(&meta_path).unwrap();
72
- assert_eq!(mgr.committed_batch_id(), 1);
73
- assert_eq!(mgr.committed_coordinate().shard_id, 1);
74
- assert_eq!(mgr.committed_coordinate().byte_offset, 512);
75
-
76
- // "line_alpha" must still be marked as committed
77
- assert!(mgr.deduplicator.try_stage(2, b"line_alpha").is_none());
78
- assert!(mgr.deduplicator.try_stage(2, b"line_beta").is_some());
79
-
80
- let c2 = StreamCoordinate {
81
- shard_id: 1,
82
- byte_offset: 1024,
83
- row_group_idx: 0,
84
- page_idx: 1,
85
- row_offset_in_page: 0,
86
- global_row_idx: 20,
87
- };
88
- mgr.stage_batch_in_ram(2, c2);
89
- mgr.commit_gpu_batch(2).unwrap();
90
- }
91
-
92
- let _ = fs::remove_file(&meta_path);
93
- let _ = fs::remove_file(format!("{}.dedup", meta_path.display()));
94
- }
95
-
96
- #[test]
97
- fn test_two_stage_deduplicator() {
98
- let mut dedup = TwoStageDeduplicator::new();
99
- assert_eq!(dedup.committed_len(), 0);
100
-
101
- assert!(dedup.try_stage(1, b"apple").is_some());
102
- assert!(dedup.try_stage(1, b"apple").is_none());
103
- assert!(dedup.try_stage(1, b"banana").is_some());
104
- assert_eq!(dedup.staged_len(), 2);
105
-
106
- let committed = dedup.commit_batch(1);
107
- assert_eq!(committed.len(), 2);
108
- assert_eq!(dedup.committed_len(), 2);
109
- assert_eq!(dedup.staged_len(), 0);
110
-
111
- // Staging the same in batch 2 should be rejected
112
- assert!(dedup.try_stage(2, b"apple").is_none());
113
- assert!(dedup.try_stage(2, b"orange").is_some());
114
-
115
- // Rollback batch 2
116
- let rolled = dedup.rollback_batch(2);
117
- assert_eq!(rolled, 1);
118
- assert!(dedup.try_stage(3, b"orange").is_some());
119
- }
120
-
121
- #[test]
122
- fn test_simd_cpu_detection_and_scans() {
123
- let isa = detect_cpu_isa();
124
- assert!(!isa.as_str().is_empty());
125
-
126
- let text = b"hello\r\nworld\nfrom\rsimd";
127
- assert!(contains_byte_fast(text, b'\r'));
128
- assert!(contains_byte_fast(text, b'\n'));
129
- assert!(!contains_byte_fast(text, b'z'));
130
-
131
- let pos = find_newline_fast(text);
132
- assert!(pos.is_some());
133
- }
134
-
135
- #[test]
136
- fn test_postprocess_cleaning_and_json_extraction() {
137
- let cfg = PostProcessConfig {
138
- trim_whitespace: true,
139
- normalize_newlines: true,
140
- min_byte_len: 3,
141
- max_byte_len: 20,
142
- truncate_over_max: true,
143
- json_extract_key: Some("data.text".to_owned()),
144
- };
145
-
146
- let raw_json = r#"{"data": {"text": " Hello\r\nWorld! "}}"#;
147
- let cleaned = process_single_record(raw_json, &cfg);
148
- assert_eq!(cleaned, Some("Hello\nWorld!".to_owned()));
149
-
150
- // Truncation test
151
- let long_json = r#"{"data": {"text": "A very long sentence that will exceed twenty bytes"}}"#;
152
- let truncated = process_single_record(long_json, &cfg).unwrap();
153
- assert!(truncated.len() <= 20);
154
-
155
- // Parallel batch test
156
- let batch = vec![
157
- r#"{"data": {"text": "row 1\r\n"}}"#.to_owned(),
158
- r#"{"data": {"text": "row 2"}}"#.to_owned(),
159
- r#"{"data": {"text": ""}}"#.to_owned(), // filtered due to min_byte_len
160
- ];
161
- let batch_cleaned = process_batch_parallel(&batch, &cfg);
162
- assert_eq!(batch_cleaned.len(), 2);
163
- assert_eq!(batch_cleaned[0], "row 1");
164
- assert_eq!(batch_cleaned[1], "row 2");
165
- }
166
-
167
- #[test]
168
- fn test_classify_string_or_json() {
169
- let s1 = "just plain text";
170
- assert_eq!(classify_string_or_json(s1), DataValue::Text(s1.to_owned()));
171
-
172
- let s2 = r#"{"key": "value", "id": 123}"#;
173
- assert_eq!(classify_string_or_json(s2), DataValue::Json(s2.to_owned()));
174
- }
175
-
176
- #[test]
177
- fn test_serialization_parquet_and_jsonl() {
178
- let columns = vec![
179
- (
180
- "text".to_owned(),
181
- vec![
182
- DataValue::Text("first".to_owned()),
183
- DataValue::Text("second".to_owned()),
184
- ],
185
- ),
186
- (
187
- "score".to_owned(),
188
- vec![DataValue::Float(0.95), DataValue::Float(0.42)],
189
- ),
190
- (
191
- "label".to_owned(),
192
- vec![DataValue::Int(1), DataValue::Int(0)],
193
- ),
194
- ];
195
-
196
- let (schema, batch) = build_arrow_record_batch(&columns, false).unwrap();
197
- assert_eq!(batch.num_rows(), 2);
198
- assert_eq!(schema.fields().len(), 3);
199
-
200
- // Parquet serialization
201
- let pq_bytes = serialize_columns_to_parquet_bytes(&columns, false).unwrap();
202
- assert!(!pq_bytes.is_empty());
203
- assert_eq!(&pq_bytes[0..4], b"PAR1");
204
-
205
- // Arrow IPC serialization
206
- let ipc_bytes = serialize_columns_to_arrow_ipc_bytes(&columns, false).unwrap();
207
- assert!(!ipc_bytes.is_empty());
208
- assert_eq!(&ipc_bytes[0..6], b"ARROW1");
209
-
210
- // JSONL serialization
211
- let jsonl_bytes = serialize_columns_to_jsonl_bytes(&columns).unwrap();
212
- assert!(!jsonl_bytes.is_empty());
213
- let jsonl_str = String::from_utf8(jsonl_bytes).unwrap();
214
- assert!(jsonl_str.contains("\"text\":\"first\""));
215
- assert!(jsonl_str.contains("\"score\":0.95"));
216
- }
217
-
218
- #[test]
219
- fn test_quality_filter() {
220
- use crate::quality::{QualityConfig, evaluate_quality};
221
-
222
- let cfg = QualityConfig {
223
- min_chars: 20,
224
- max_chars: 0,
225
- min_words: 3,
226
- min_entropy: 2.5,
227
- max_repetition_ratio: 0.35,
228
- max_symbol_ratio: 0.30,
229
- };
230
-
231
- // 1. High quality normal text
232
- let good_text =
233
- "The quick brown fox jumps over the lazy dog and runs across the wide green meadow.";
234
- let res = evaluate_quality(good_text, &cfg);
235
- assert!(res.is_passed);
236
- assert!(res.entropy > 2.5);
237
- assert!(res.repetition_ratio < 0.2);
238
- assert!(res.symbol_ratio < 0.1);
239
-
240
- // 2. Degenerate repetition
241
- let repeated =
242
- "spam spam spam spam spam spam spam spam spam spam spam spam spam spam spam spam";
243
- let res_rep = evaluate_quality(repeated, &cfg);
244
- assert!(!res_rep.is_passed);
245
- assert!(res_rep.repetition_ratio > 0.35);
246
-
247
- // 3. Code / symbol dump
248
- let symbol_dump = "{{{[(((@@@###$$$%%%^^^&&&***)))}}}];;;===+++///<<<>>>!!!???";
249
- let res_sym = evaluate_quality(symbol_dump, &cfg);
250
- assert!(!res_sym.is_passed);
251
- }
252
-
253
- #[test]
254
- fn test_minhash_fuzzy_deduplicator() {
255
- use crate::minhash::MinHashDeduplicator;
256
-
257
- let mut dedup = MinHashDeduplicator::new(0.80, 4, 64);
258
-
259
- let doc_a =
260
- "Artificial intelligence and deep learning models require efficient data pipelines.";
261
- // Near duplicate (just 1 typo/word difference)
262
- let doc_a_near =
263
- "Artificial intelligence and deep learning models require efficient data pipelining.";
264
- // Completely different document
265
- let doc_b = "Quantum mechanical systems exhibit wave particle duality in atomic physics.";
266
-
267
- assert!(dedup.is_unique(doc_a));
268
- // doc_a_near should be caught as near-duplicate (> 80% similarity)
269
- assert!(!dedup.is_unique(doc_a_near));
270
- // doc_b is different, should pass
271
- assert!(dedup.is_unique(doc_b));
272
- assert_eq!(dedup.count(), 2);
273
- }
274
-
275
- #[test]
276
- fn test_string_tensor_cpu_and_gpu() {
277
- use crate::string_tensor::StringTensor;
278
-
279
- let raw = vec![
280
- "apple", // 5B <= 12B (inlined)
281
- "cat", // 3B <= 12B (inlined)
282
- "twelve_bytes", // 12B <= 12B (inlined boundary)
283
- "thirteen_bytes!", // 15B > 12B (referenced into pool)
284
- "The quick brown fox jumps over the lazy dog.", // 44B > 12B
285
- ];
286
-
287
- let st = StringTensor::from_strings(&raw);
288
- assert_eq!(st.len(), 5);
289
-
290
- // Verify inlining
291
- assert!(st.views[0].is_inlined());
292
- assert!(st.views[1].is_inlined());
293
- assert!(st.views[2].is_inlined());
294
- assert!(!st.views[3].is_inlined());
295
- assert!(!st.views[4].is_inlined());
296
-
297
- // Verify string content recovery
298
- assert_eq!(st.get(0).unwrap(), "apple");
299
- assert_eq!(st.get(1).unwrap(), "cat");
300
- assert_eq!(st.get(2).unwrap(), "twelve_bytes");
301
- assert_eq!(st.get(3).unwrap(), "thirteen_bytes!");
302
- assert_eq!(
303
- st.get(4).unwrap(),
304
- "The quick brown fox jumps over the lazy dog."
305
- );
306
-
307
- // Fast equality check
308
- let v0 = st.views[0];
309
- let v1 = st.views[1];
310
- assert!(v0.fast_eq(&v0, &st.pool));
311
- assert!(!v0.fast_eq(&v1, &st.pool));
312
-
313
- // Convert to GPU buffers: (offsets, payload)
314
- let (offsets, payload) = st.to_gpu_buffers();
315
- assert_eq!(offsets.len(), 6);
316
- assert_eq!(offsets[0], 0);
317
- assert_eq!(offsets[5] as usize, payload.len());
318
-
319
- // Reconstruct from GPU buffers
320
- let st_gpu = StringTensor::from_gpu_buffers(&offsets, &payload);
321
- assert_eq!(st_gpu.len(), 5);
322
- assert_eq!(st_gpu.to_vec(), raw);
323
-
324
- // Filtering
325
- let long_only = st.filter_by_length(13, 0);
326
- assert_eq!(long_only.len(), 2);
327
- assert_eq!(long_only.get(0).unwrap(), "thirteen_bytes!");
328
-
329
- let prefix_filtered = st.filter_by_prefix("thirt");
330
- assert_eq!(prefix_filtered.len(), 1);
331
- assert_eq!(prefix_filtered.get(0).unwrap(), "thirteen_bytes!");
332
-
333
- // Parallel hashing
334
- let hashes = st.hash_all_parallel();
335
- assert_eq!(hashes.len(), 5);
336
- assert!(hashes[0] != hashes[1]);
337
- }
338
-
339
- #[test]
340
- fn test_fused_pipeline_core() {
341
- let raw = vec![
342
- " Valid prose sentence for LLM training alpha\r\n".to_string(),
343
- "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".to_string(), // degenerate repetition -> reject
344
- "Valid prose sentence for LLM training beta".to_string(),
345
- "Valid prose sentence for LLM training alpha\r\n".to_string(), // duplicate -> reject
346
- "".to_string(), // empty -> reject
347
- ];
348
-
349
- let post_cfg = PostProcessConfig {
350
- min_byte_len: 1,
351
- ..Default::default()
352
- };
353
- let qual_cfg = crate::quality::QualityConfig {
354
- min_entropy: 1.5,
355
- max_repetition_ratio: 0.4,
356
- ..Default::default()
357
- };
358
- let mut dedup = TwoStageDeduplicator::new();
359
-
360
- let mut survivors = Vec::new();
361
- for item in raw {
362
- if let Some(cleaned) = process_single_record(&item, &post_cfg) {
363
- let eval = crate::quality::evaluate_quality(&cleaned, &qual_cfg);
364
- if eval.is_passed && dedup.try_stage(1, cleaned.as_bytes()).is_some() {
365
- survivors.push(cleaned);
366
- }
367
- }
368
- }
369
-
370
- assert_eq!(survivors.len(), 2);
371
- assert_eq!(survivors[0], "Valid prose sentence for LLM training alpha");
372
- assert_eq!(survivors[1], "Valid prose sentence for LLM training beta");
373
-
374
- let st = crate::string_tensor::StringTensor::from_strings(survivors);
375
- assert_eq!(st.len(), 2);
376
- assert_eq!(
377
- st.get(0).unwrap(),
378
- "Valid prose sentence for LLM training alpha"
379
- );
380
- assert_eq!(
381
- st.get(1).unwrap(),
382
- "Valid prose sentence for LLM training beta"
383
- );
384
- }
 
1
+ use std::fs;
2
+ use std::path::PathBuf;
3
+
4
+ use crate::coord::StreamCoordinate;
5
+ use crate::dedup::TwoStageDeduplicator;
6
+ use crate::metadata::DualMetadataManager;
7
+ use crate::postprocess::{PostProcessConfig, process_batch_parallel, process_single_record};
8
+ use crate::serialize::{
9
+ DataValue, build_arrow_record_batch, classify_string_or_json,
10
+ serialize_columns_to_arrow_ipc_bytes, serialize_columns_to_jsonl_bytes,
11
+ serialize_columns_to_parquet_bytes,
12
+ };
13
+ use crate::simd_cpu::{contains_byte_fast, detect_cpu_isa, find_newline_fast};
14
+
15
+ fn temp_test_path(name: &str, ext: &str) -> PathBuf {
16
+ let mut p = std::env::temp_dir();
17
+ p.push(format!(
18
+ "luminamoon_test_{}_{}.{}",
19
+ name,
20
+ std::process::id(),
21
+ ext
22
+ ));
23
+ p
24
+ }
25
+
26
+ #[test]
27
+ fn test_stream_coordinate_roundtrip() {
28
+ let c = StreamCoordinate {
29
+ shard_id: 42,
30
+ byte_offset: 1024 * 1024,
31
+ row_group_idx: 3,
32
+ page_idx: 7,
33
+ row_offset_in_page: 128,
34
+ global_row_idx: 99999,
35
+ };
36
+ let bytes = c.to_bytes();
37
+ assert_eq!(bytes.len(), StreamCoordinate::BYTE_LEN);
38
+ let decoded = StreamCoordinate::from_bytes(&bytes);
39
+ assert_eq!(c, decoded);
40
+ }
41
+
42
+ #[test]
43
+ fn test_dual_metadata_ping_pong_and_dedup_wal() {
44
+ let meta_path = temp_test_path("ping_pong_meta", "meta");
45
+ let _ = fs::remove_file(&meta_path);
46
+ let _ = fs::remove_file(format!("{}.dedup", meta_path.display()));
47
+
48
+ {
49
+ let mut mgr = DualMetadataManager::open(&meta_path).unwrap();
50
+ assert_eq!(mgr.committed_batch_id(), 0);
51
+
52
+ assert!(mgr.deduplicator.try_stage(1, b"line_alpha").is_some());
53
+ assert!(mgr.deduplicator.try_stage(1, b"line_alpha").is_none());
54
+
55
+ let c1 = StreamCoordinate {
56
+ shard_id: 1,
57
+ byte_offset: 512,
58
+ row_group_idx: 0,
59
+ page_idx: 0,
60
+ row_offset_in_page: 0,
61
+ global_row_idx: 10,
62
+ };
63
+ mgr.stage_batch_in_ram(1, c1);
64
+ let committed = mgr.commit_gpu_batch(1).unwrap();
65
+ assert_eq!(committed, c1);
66
+ assert_eq!(mgr.committed_batch_id(), 1);
67
+ }
68
+
69
+ // Re-open and verify recovery
70
+ {
71
+ let mut mgr = DualMetadataManager::open(&meta_path).unwrap();
72
+ assert_eq!(mgr.committed_batch_id(), 1);
73
+ assert_eq!(mgr.committed_coordinate().shard_id, 1);
74
+ assert_eq!(mgr.committed_coordinate().byte_offset, 512);
75
+
76
+ // "line_alpha" must still be marked as committed
77
+ assert!(mgr.deduplicator.try_stage(2, b"line_alpha").is_none());
78
+ assert!(mgr.deduplicator.try_stage(2, b"line_beta").is_some());
79
+
80
+ let c2 = StreamCoordinate {
81
+ shard_id: 1,
82
+ byte_offset: 1024,
83
+ row_group_idx: 0,
84
+ page_idx: 1,
85
+ row_offset_in_page: 0,
86
+ global_row_idx: 20,
87
+ };
88
+ mgr.stage_batch_in_ram(2, c2);
89
+ mgr.commit_gpu_batch(2).unwrap();
90
+ }
91
+
92
+ let _ = fs::remove_file(&meta_path);
93
+ let _ = fs::remove_file(format!("{}.dedup", meta_path.display()));
94
+ }
95
+
96
+ #[test]
97
+ fn test_two_stage_deduplicator() {
98
+ let mut dedup = TwoStageDeduplicator::new();
99
+ assert_eq!(dedup.committed_len(), 0);
100
+
101
+ assert!(dedup.try_stage(1, b"apple").is_some());
102
+ assert!(dedup.try_stage(1, b"apple").is_none());
103
+ assert!(dedup.try_stage(1, b"banana").is_some());
104
+ assert_eq!(dedup.staged_len(), 2);
105
+
106
+ let committed = dedup.commit_batch(1);
107
+ assert_eq!(committed.len(), 2);
108
+ assert_eq!(dedup.committed_len(), 2);
109
+ assert_eq!(dedup.staged_len(), 0);
110
+
111
+ // Staging the same in batch 2 should be rejected
112
+ assert!(dedup.try_stage(2, b"apple").is_none());
113
+ assert!(dedup.try_stage(2, b"orange").is_some());
114
+
115
+ // Rollback batch 2
116
+ let rolled = dedup.rollback_batch(2);
117
+ assert_eq!(rolled, 1);
118
+ assert!(dedup.try_stage(3, b"orange").is_some());
119
+ }
120
+
121
+ #[test]
122
+ fn test_simd_cpu_detection_and_scans() {
123
+ let isa = detect_cpu_isa();
124
+ assert!(!isa.as_str().is_empty());
125
+
126
+ let text = b"hello\r\nworld\nfrom\rsimd";
127
+ assert!(contains_byte_fast(text, b'\r'));
128
+ assert!(contains_byte_fast(text, b'\n'));
129
+ assert!(!contains_byte_fast(text, b'z'));
130
+
131
+ let pos = find_newline_fast(text);
132
+ assert!(pos.is_some());
133
+ }
134
+
135
+ #[test]
136
+ fn test_postprocess_cleaning_and_json_extraction() {
137
+ let cfg = PostProcessConfig {
138
+ trim_whitespace: true,
139
+ normalize_newlines: true,
140
+ min_byte_len: 3,
141
+ max_byte_len: 20,
142
+ truncate_over_max: true,
143
+ json_extract_key: Some("data.text".to_owned()),
144
+ };
145
+
146
+ let raw_json = r#"{"data": {"text": " Hello\r\nWorld! "}}"#;
147
+ let cleaned = process_single_record(raw_json, &cfg);
148
+ assert_eq!(cleaned, Some("Hello\nWorld!".to_owned()));
149
+
150
+ // Truncation test
151
+ let long_json = r#"{"data": {"text": "A very long sentence that will exceed twenty bytes"}}"#;
152
+ let truncated = process_single_record(long_json, &cfg).unwrap();
153
+ assert!(truncated.len() <= 20);
154
+
155
+ // Parallel batch test
156
+ let batch = vec![
157
+ r#"{"data": {"text": "row 1\r\n"}}"#.to_owned(),
158
+ r#"{"data": {"text": "row 2"}}"#.to_owned(),
159
+ r#"{"data": {"text": ""}}"#.to_owned(), // filtered due to min_byte_len
160
+ ];
161
+ let batch_cleaned = process_batch_parallel(&batch, &cfg);
162
+ assert_eq!(batch_cleaned.len(), 2);
163
+ assert_eq!(batch_cleaned[0], "row 1");
164
+ assert_eq!(batch_cleaned[1], "row 2");
165
+ }
166
+
167
+ #[test]
168
+ fn test_classify_string_or_json() {
169
+ let s1 = "just plain text";
170
+ assert_eq!(classify_string_or_json(s1), DataValue::Text(s1.to_owned()));
171
+
172
+ let s2 = r#"{"key": "value", "id": 123}"#;
173
+ assert_eq!(classify_string_or_json(s2), DataValue::Json(s2.to_owned()));
174
+ }
175
+
176
+ #[test]
177
+ fn test_serialization_parquet_and_jsonl() {
178
+ let columns = vec![
179
+ (
180
+ "text".to_owned(),
181
+ vec![
182
+ DataValue::Text("first".to_owned()),
183
+ DataValue::Text("second".to_owned()),
184
+ ],
185
+ ),
186
+ (
187
+ "score".to_owned(),
188
+ vec![DataValue::Float(0.95), DataValue::Float(0.42)],
189
+ ),
190
+ (
191
+ "label".to_owned(),
192
+ vec![DataValue::Int(1), DataValue::Int(0)],
193
+ ),
194
+ ];
195
+
196
+ let (schema, batch) = build_arrow_record_batch(&columns, false).unwrap();
197
+ assert_eq!(batch.num_rows(), 2);
198
+ assert_eq!(schema.fields().len(), 3);
199
+
200
+ // Parquet serialization
201
+ let pq_bytes = serialize_columns_to_parquet_bytes(&columns, false).unwrap();
202
+ assert!(!pq_bytes.is_empty());
203
+ assert_eq!(&pq_bytes[0..4], b"PAR1");
204
+
205
+ // Arrow IPC serialization
206
+ let ipc_bytes = serialize_columns_to_arrow_ipc_bytes(&columns, false).unwrap();
207
+ assert!(!ipc_bytes.is_empty());
208
+ assert_eq!(&ipc_bytes[0..6], b"ARROW1");
209
+
210
+ // JSONL serialization
211
+ let jsonl_bytes = serialize_columns_to_jsonl_bytes(&columns).unwrap();
212
+ assert!(!jsonl_bytes.is_empty());
213
+ let jsonl_str = String::from_utf8(jsonl_bytes).unwrap();
214
+ assert!(jsonl_str.contains("\"text\":\"first\""));
215
+ assert!(jsonl_str.contains("\"score\":0.95"));
216
+ }
217
+
218
+ #[test]
219
+ fn test_quality_filter() {
220
+ use crate::quality::{QualityConfig, evaluate_quality};
221
+
222
+ let cfg = QualityConfig {
223
+ min_chars: 20,
224
+ max_chars: 0,
225
+ min_words: 3,
226
+ min_entropy: 2.5,
227
+ max_repetition_ratio: 0.35,
228
+ max_symbol_ratio: 0.30,
229
+ };
230
+
231
+ // 1. High quality normal text
232
+ let good_text =
233
+ "The quick brown fox jumps over the lazy dog and runs across the wide green meadow.";
234
+ let res = evaluate_quality(good_text, &cfg);
235
+ assert!(res.is_passed);
236
+ assert!(res.entropy > 2.5);
237
+ assert!(res.repetition_ratio < 0.2);
238
+ assert!(res.symbol_ratio < 0.1);
239
+
240
+ // 2. Degenerate repetition
241
+ let repeated =
242
+ "spam spam spam spam spam spam spam spam spam spam spam spam spam spam spam spam";
243
+ let res_rep = evaluate_quality(repeated, &cfg);
244
+ assert!(!res_rep.is_passed);
245
+ assert!(res_rep.repetition_ratio > 0.35);
246
+
247
+ // 3. Code / symbol dump
248
+ let symbol_dump = "{{{[(((@@@###$$$%%%^^^&&&***)))}}}];;;===+++///<<<>>>!!!???";
249
+ let res_sym = evaluate_quality(symbol_dump, &cfg);
250
+ assert!(!res_sym.is_passed);
251
+ }
252
+
253
+ #[test]
254
+ fn test_minhash_fuzzy_deduplicator() {
255
+ use crate::minhash::MinHashDeduplicator;
256
+
257
+ let mut dedup = MinHashDeduplicator::new(0.80, 4, 64);
258
+
259
+ let doc_a =
260
+ "Artificial intelligence and deep learning models require efficient data pipelines.";
261
+ // Near duplicate (just 1 typo/word difference)
262
+ let doc_a_near =
263
+ "Artificial intelligence and deep learning models require efficient data pipelining.";
264
+ // Completely different document
265
+ let doc_b = "Quantum mechanical systems exhibit wave particle duality in atomic physics.";
266
+
267
+ assert!(dedup.is_unique(doc_a));
268
+ // doc_a_near should be caught as near-duplicate (> 80% similarity)
269
+ assert!(!dedup.is_unique(doc_a_near));
270
+ // doc_b is different, should pass
271
+ assert!(dedup.is_unique(doc_b));
272
+ assert_eq!(dedup.count(), 2);
273
+ }
274
+
275
+ #[test]
276
+ fn test_string_tensor_cpu_and_gpu() {
277
+ use crate::string_tensor::StringTensor;
278
+
279
+ let raw = vec![
280
+ "apple", // 5B <= 12B (inlined)
281
+ "cat", // 3B <= 12B (inlined)
282
+ "twelve_bytes", // 12B <= 12B (inlined boundary)
283
+ "thirteen_bytes!", // 15B > 12B (referenced into pool)
284
+ "The quick brown fox jumps over the lazy dog.", // 44B > 12B
285
+ ];
286
+
287
+ let st = StringTensor::from_strings(&raw);
288
+ assert_eq!(st.len(), 5);
289
+
290
+ // Verify inlining
291
+ assert!(st.views[0].is_inlined());
292
+ assert!(st.views[1].is_inlined());
293
+ assert!(st.views[2].is_inlined());
294
+ assert!(!st.views[3].is_inlined());
295
+ assert!(!st.views[4].is_inlined());
296
+
297
+ // Verify string content recovery
298
+ assert_eq!(st.get(0).unwrap(), "apple");
299
+ assert_eq!(st.get(1).unwrap(), "cat");
300
+ assert_eq!(st.get(2).unwrap(), "twelve_bytes");
301
+ assert_eq!(st.get(3).unwrap(), "thirteen_bytes!");
302
+ assert_eq!(
303
+ st.get(4).unwrap(),
304
+ "The quick brown fox jumps over the lazy dog."
305
+ );
306
+
307
+ // Fast equality check
308
+ let v0 = st.views[0];
309
+ let v1 = st.views[1];
310
+ assert!(v0.fast_eq(&v0, &st.pool));
311
+ assert!(!v0.fast_eq(&v1, &st.pool));
312
+
313
+ // Convert to GPU buffers: (offsets, payload)
314
+ let (offsets, payload) = st.to_gpu_buffers();
315
+ assert_eq!(offsets.len(), 6);
316
+ assert_eq!(offsets[0], 0);
317
+ assert_eq!(offsets[5] as usize, payload.len());
318
+
319
+ // Reconstruct from GPU buffers
320
+ let st_gpu = StringTensor::from_gpu_buffers(&offsets, &payload);
321
+ assert_eq!(st_gpu.len(), 5);
322
+ assert_eq!(st_gpu.to_vec(), raw);
323
+
324
+ // Filtering
325
+ let long_only = st.filter_by_length(13, 0);
326
+ assert_eq!(long_only.len(), 2);
327
+ assert_eq!(long_only.get(0).unwrap(), "thirteen_bytes!");
328
+
329
+ let prefix_filtered = st.filter_by_prefix("thirt");
330
+ assert_eq!(prefix_filtered.len(), 1);
331
+ assert_eq!(prefix_filtered.get(0).unwrap(), "thirteen_bytes!");
332
+
333
+ // Parallel hashing
334
+ let hashes = st.hash_all_parallel();
335
+ assert_eq!(hashes.len(), 5);
336
+ assert!(hashes[0] != hashes[1]);
337
+ }
338
+
339
+ #[test]
340
+ fn test_fused_pipeline_core() {
341
+ let raw = vec![
342
+ " Valid prose sentence for LLM training alpha\r\n".to_string(),
343
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".to_string(), // degenerate repetition -> reject
344
+ "Valid prose sentence for LLM training beta".to_string(),
345
+ "Valid prose sentence for LLM training alpha\r\n".to_string(), // duplicate -> reject
346
+ "".to_string(), // empty -> reject
347
+ ];
348
+
349
+ let post_cfg = PostProcessConfig {
350
+ min_byte_len: 1,
351
+ ..Default::default()
352
+ };
353
+ let qual_cfg = crate::quality::QualityConfig {
354
+ min_entropy: 1.5,
355
+ max_repetition_ratio: 0.4,
356
+ ..Default::default()
357
+ };
358
+ let mut dedup = TwoStageDeduplicator::new();
359
+
360
+ let mut survivors = Vec::new();
361
+ for item in raw {
362
+ if let Some(cleaned) = process_single_record(&item, &post_cfg) {
363
+ let eval = crate::quality::evaluate_quality(&cleaned, &qual_cfg);
364
+ if eval.is_passed && dedup.try_stage(1, cleaned.as_bytes()).is_some() {
365
+ survivors.push(cleaned);
366
+ }
367
+ }
368
+ }
369
+
370
+ assert_eq!(survivors.len(), 2);
371
+ assert_eq!(survivors[0], "Valid prose sentence for LLM training alpha");
372
+ assert_eq!(survivors[1], "Valid prose sentence for LLM training beta");
373
+
374
+ let st = crate::string_tensor::StringTensor::from_strings(survivors);
375
+ assert_eq!(st.len(), 2);
376
+ assert_eq!(
377
+ st.get(0).unwrap(),
378
+ "Valid prose sentence for LLM training alpha"
379
+ );
380
+ assert_eq!(
381
+ st.get(1).unwrap(),
382
+ "Valid prose sentence for LLM training beta"
383
+ );
384
+ }
docs/README.md CHANGED
@@ -1,101 +1,101 @@
1
- # Loomy (luminamoon) Documentation
2
-
3
- <div class="portal-badges">
4
- <span class="pill-badge">LuminaV Optimizer v1.6.0</span>
5
- <span class="pill-badge pill-badge-green">Data Engine v1.1.0</span>
6
- <span class="pill-badge pill-badge-cyan">LSO-Eval Verified</span>
7
- <span class="pill-badge pill-badge-cyan">Rust 1.99.1 + Python ABI3T</span>
8
- </div>
9
-
10
- **Loomy (`luminamoon`)** provides low-precision numerical optimization, rational neural network layers, and dataset text preprocessing utilities developed by **Silver Moon ([@cloverxion](https://github.com/cloverxion))** and **Lumina Moon ([@cloverx-id](https://github.com/cloverx-id)) contributors**.
11
-
12
- <div class="friendly-grid">
13
- <a class="friendly-card" href="en/01_getting_started/">
14
- <h3>Getting Started</h3>
15
- <p>System requirements, pip install, Rust compilation, and quickstart examples for NumPy, PyTorch, and Data Engine.</p>
16
- </a>
17
- <a class="friendly-card" href="en/02_user_guide/">
18
- <h3>LuminaV Optimizer</h3>
19
- <p>Dual-Buffer CLM vs Single-Buffer TD-CLM, mixed-precision, stochastic rounding, and FSDP distributed training.</p>
20
- </a>
21
- <a class="friendly-card" href="dataloader/standalone_guide/">
22
- <h3>Data Engine</h3>
23
- <p>Fused SIMD text preprocessing, StringTensor CPU/GPU buffers, deduplication, and 128B Ping-Pong checkpoint recovery.</p>
24
- </a>
25
- <a class="friendly-card" href="lso_eval/">
26
- <h3>LSO-Eval</h3>
27
- <p>Z3 SMT safety checks, 2D trajectory benchmarks, 1-Pass EMA parity, and Qwen3.5-4B & Vision Two-Class evaluation.</p>
28
- </a>
29
- <a class="friendly-card" href="en/03_configuration_api/">
30
- <h3>API Reference</h3>
31
- <p>Complete signatures, parameter reference tables, validation constraints, and return types for all modules.</p>
32
- </a>
33
- <a class="friendly-card" href="security/">
34
- <h3>Security & Notes</h3>
35
- <p>Direct vulnerability disclosure policy to Silver Moon, CodeQL scanning, and third-party library compatibility notes.</p>
36
- </a>
37
- <a class="friendly-card" href="changelogs/">
38
- <h3>Changelogs</h3>
39
- <p>Version-by-version release history for Software package distributions and LuminaV algorithm specifications.</p>
40
- </a>
41
- </div>
42
-
43
- ---
44
-
45
- ## 1. User Documentation
46
-
47
- Source directory: `docs/en/`
48
-
49
- - **[01. Getting Started](en/01_getting_started.md)**: System requirements, installation, source compilation via Maturin/Rust, hardware & SIMD ISA detection (`LuminaV.detect_hardware()`, `lm.data_cpu_isa()`), and quickstarts for NumPy, PyTorch, and Data Engine (`FusedPipeline`, `StringTensor`, `StateTracker`, `ResumablePipeline`).
50
- - **[02. User Guide](en/02_user_guide.md)**: Optimizer buffer modes (2-Buffer CLM, 1-Buffer TD-CLM, and Triangle Family T1/T2), mixed-precision & stochastic rounding, step bounding, FSDP distributed training, plus Data Engine SIMD text cleaning, quality filtering, two-stage XXH3-64 & MinHash deduplication, PII masking, StringTensor buffers, and Ping-Pong checkpoint recovery.
51
- - **[03. Configuration & API Reference](en/03_configuration_api.md)**: Complete parameter reference tables, public methods, and validation rules for `LuminaV`, `TextCleaner`, `QualityFilter`, `StreamDeduplicator`, `MinHashDeduplicator`, `PIIMasker`, `StateTracker`, `StreamCoordinate`, `StringTensor`, `FusedPipeline`, and `ResumablePipeline`.
52
- - **[04. Architecture & Hardware Acceleration](en/04_architecture_and_hardware.md)**: C4 dispatch hierarchy, Optimizer Rust crates (`core`, `simd`, `vulkan`, `dispatch`, `triton-aot`), Data Engine Rust modules (`constants`, `coord`, `dedup`, `error`, `metadata`, `minhash`, `postprocess`, `py_bridge`, `quality`, `serialize`, `simd_cpu`, `string_tensor`), buffer memory protocols, and free-threaded Python (`abi3t` / PEP 703).
53
-
54
- ---
55
-
56
- ## 2. Developer Modular Documentation
57
-
58
- Technical documentation for contributors working on the Rust crates, GPU kernels, and Python bindings (`docs/optimize/` & `data/docs/`):
59
-
60
- ### A. LuminaV Optimizer
61
- Source directories: `docs/optimize/` & `optimizer/`
62
-
63
- - **[LuminaV Optimizer Overview & User Guide (`optimizer/README.md`)](optimize/standalone_guide.md)**
64
- - **[Developer Optimizer Index (`docs/optimize/README.md`)](optimize/README.md)**
65
- - **[01. Core & Math](optimize/core/README.md)**: CLM, TD-CLM Triangle Defect, PRNG stochastic rounding, and bias correction.
66
- - **[02. SIMD Vectorization](optimize/simd/README.md)**: AVX-512 and AVX2/FMA x86_64 intrinsics and Rayon parallel threadpool.
67
- - **[03. Vulkan Compute](optimize/vulkan/README.md)**: Ash Vulkan 1.3/1.4 SPIR-V compute shaders and UMA host buffers.
68
- - **[04. Triton AOT & GPU Drivers](optimize/triton_aot/README.md)**: AOT compilation, CUDA/HIP driver dynamic loading, and standalone C-API.
69
- - **[05. Dispatch & Routing](optimize/dispatch/README.md)**: Hardware capability detector and 16,384-element dispatch threshold heuristic.
70
- - **[06. Python FFI & PyO3 Bridge](optimize/python_ffi/README.md)**: PyO3 GIL release (`py.detach()`), pointer aliasing checks, and NumPy buffer protocols.
71
- - **[07. Python Runtime & GPU Mixin](optimize/python_runtime/README.md)**: `LuminaV` class, `constants.py`, Triton JIT kernels, FSDP synchronization, and state checkpointing.
72
-
73
- ### B. LuminaMoon Data Engine
74
- Source directories: `data/` & `data/docs/`
75
-
76
- - **[LuminaMoon Data Engine Overview & User Guide (`data/README.md`)](dataloader/standalone_guide.md)**
77
- - **[Developer Data Engine Index (`data/docs/README.md`)](dataloader/README.md)**
78
- - **[01. TextCleaner & QualityFilter](dataloader/simd_and_postprocess/README.md)**: AVX-512/AVX2/NEON SIMD carriage-return stripping, JSON extraction, Shannon entropy, repetition loop, and symbol ratio filters.
79
- - **[02. StreamDeduplicator & MinHashDeduplicator](dataloader/dedup/README.md)**: Exact streaming deduplication with `xxh3_64` (`IdentityBuildHasher` SwissTable), WAL persistence, and 64-permutation MinHash LSH fuzzy deduplication.
80
- - **[03. PIIMasker & Data Sanitization](dataloader/simd_and_postprocess/README.md)**: Pattern-based and regex masking for emails, IPv4 addresses, phone numbers, and secret tokens.
81
- - **[04. StateTracker & StreamCoordinate](dataloader/coord_and_metadata/README.md)**: 32-byte `StreamCoordinate` navigation, dual-slot 128-byte Ping-Pong checkpoint persistence, and multi-epoch state tracking.
82
- - **[05. StringTensor & Memory Layouts](dataloader/coord_and_metadata/README.md)**: CPU StringView (inlined <= 12 bytes) and GPU contiguous offset/payload buffers compatible with PyTorch, NumPy, Arrow, and Polars.
83
- - **[06. FusedPipeline & ResumablePipeline](dataloader/python_ffi_and_runtime/README.md)**: Multi-threaded Rayon single-pass preprocessing pipeline and resumable batch pipeline with background prefetcher.
84
- - **[07. Multimodal Serialization](dataloader/multimodal_and_hf_hub/README.md)**: Serialization to JSONL, Parquet with Zstd compression and embedded image payloads, Arrow IPC, and CSV.
85
-
86
- ### C. Ra-Family Neural Architecture & Theoretical References
87
- Source directories: `docs/nn/` & `references/`
88
-
89
- - **[Ra-Family Overview (`docs/nn/README.md`)](nn/README.md)**: Square-root-free `RaNorm` & `RaBatchNorm2d`, rational `RaTanh` & `RaGELU`, bounded `PosRaGLU` / `RaGLU` / `TriGLU`, and compact-support `ra_softmax` & `RaAttention`.
90
- - **[Ra-Family Standalone Guide (`docs/nn/standalone_guide.md`)](nn/standalone_guide.md)**: Drop-in Transformer block replacement, `RaBatchNorm2d` `Conv2d` parameter folding, and backend introspection.
91
- - **[Theoretical References (`docs/references.md`)](references.md)**: Component mapping, literature comparisons across all 29 referenced papers, and BibTeX database (`references/bibliography.bib`).
92
-
93
- ---
94
-
95
- ## 3. LSO-Eval Verification & Library Compatibility Notes
96
-
97
- Source directories: `eval/`, `docs/library_audit/`, & `data/docs/library_audit/`
98
-
99
- - **[LSO-Eval Optimizer Evaluation & Verification (`eval/README.md`)](lso_eval.md)**: Algorithm-agnostic Z3 SMT solver invariant checks, 2D trajectory benchmarks, numerical parity tests, and Two-Class 4B LLM & Vision evaluation (`LuminaV v1.5.3–v1.6.0`).
100
- - **[Optimizer Library Notes (`docs/library_audit/`)](library_audit/README.md)**: Version and ABI compatibility notes for Rust 1.99.1 toolchain, PyO3/Maturin, PyTorch 2.14.1, Triton JIT 3.8.0, CUDA/ROCm, and Vulkan Ash compute pipeline.
101
- - **[Data Engine Library Notes (`data/docs/library_audit/`)](library_audit/README.md)**: Version and compatibility notes for Rust 1.99.1 / Edition 2024, `pyo3 0.30.1` (`abi3t`), Apache Arrow/Parquet/IPC `60.0.0`, `rayon 1.12.0`, `xxhash-rust 0.8.19` (`xxh3`), `crc32fast 1.5.2`, `hashbrown 0.17.1`, `parking_lot 0.12.5`, `serde 1.0.229`, and HuggingFace Hub API protocols.
 
1
+ # Loomy (luminamoon) Documentation
2
+
3
+ <div class="portal-badges">
4
+ <span class="pill-badge">LuminaV Optimizer v1.6.0</span>
5
+ <span class="pill-badge pill-badge-green">Data Engine v1.1.0</span>
6
+ <span class="pill-badge pill-badge-cyan">LSO-Eval Verified</span>
7
+ <span class="pill-badge pill-badge-cyan">Rust 1.99.1 + Python ABI3T</span>
8
+ </div>
9
+
10
+ **Loomy (`luminamoon`)** provides low-precision numerical optimization, rational neural network layers, and dataset text preprocessing utilities developed by **Silver Moon ([@cloverxion](https://github.com/cloverxion))** and **Lumina Moon ([@cloverx-id](https://github.com/cloverx-id)) contributors**.
11
+
12
+ <div class="friendly-grid">
13
+ <a class="friendly-card" href="en/01_getting_started/">
14
+ <h3>Getting Started</h3>
15
+ <p>System requirements, pip install, Rust compilation, and quickstart examples for NumPy, PyTorch, and Data Engine.</p>
16
+ </a>
17
+ <a class="friendly-card" href="en/02_user_guide/">
18
+ <h3>LuminaV Optimizer</h3>
19
+ <p>Dual-Buffer CLM vs Single-Buffer TD-CLM, mixed-precision, stochastic rounding, and FSDP distributed training.</p>
20
+ </a>
21
+ <a class="friendly-card" href="dataloader/standalone_guide/">
22
+ <h3>Data Engine</h3>
23
+ <p>Fused SIMD text preprocessing, StringTensor CPU/GPU buffers, deduplication, and 128B Ping-Pong checkpoint recovery.</p>
24
+ </a>
25
+ <a class="friendly-card" href="lso_eval/">
26
+ <h3>LSO-Eval</h3>
27
+ <p>Z3 SMT safety checks, 2D trajectory benchmarks, 1-Pass EMA parity, and Qwen3.5-4B & Vision Two-Class evaluation.</p>
28
+ </a>
29
+ <a class="friendly-card" href="en/03_configuration_api/">
30
+ <h3>API Reference</h3>
31
+ <p>Complete signatures, parameter reference tables, validation constraints, and return types for all modules.</p>
32
+ </a>
33
+ <a class="friendly-card" href="security/">
34
+ <h3>Security & Notes</h3>
35
+ <p>Direct vulnerability disclosure policy to Silver Moon, CodeQL scanning, and third-party library compatibility notes.</p>
36
+ </a>
37
+ <a class="friendly-card" href="changelogs/">
38
+ <h3>Changelogs</h3>
39
+ <p>Version-by-version release history for Software package distributions and LuminaV algorithm specifications.</p>
40
+ </a>
41
+ </div>
42
+
43
+ ---
44
+
45
+ ## 1. User Documentation
46
+
47
+ Source directory: `docs/en/`
48
+
49
+ - **[01. Getting Started](en/01_getting_started.md)**: System requirements, installation, source compilation via Maturin/Rust, hardware & SIMD ISA detection (`LuminaV.detect_hardware()`, `lm.data_cpu_isa()`), and quickstarts for NumPy, PyTorch, and Data Engine (`FusedPipeline`, `StringTensor`, `StateTracker`, `ResumablePipeline`).
50
+ - **[02. User Guide](en/02_user_guide.md)**: Optimizer buffer modes (2-Buffer CLM, 1-Buffer TD-CLM, and Triangle Family T1/T2), mixed-precision & stochastic rounding, step bounding, FSDP distributed training, plus Data Engine SIMD text cleaning, quality filtering, two-stage XXH3-64 & MinHash deduplication, PII masking, StringTensor buffers, and Ping-Pong checkpoint recovery.
51
+ - **[03. Configuration & API Reference](en/03_configuration_api.md)**: Complete parameter reference tables, public methods, and validation rules for `LuminaV`, `TextCleaner`, `QualityFilter`, `StreamDeduplicator`, `MinHashDeduplicator`, `PIIMasker`, `StateTracker`, `StreamCoordinate`, `StringTensor`, `FusedPipeline`, and `ResumablePipeline`.
52
+ - **[04. Architecture & Hardware Acceleration](en/04_architecture_and_hardware.md)**: C4 dispatch hierarchy, Optimizer Rust crates (`core`, `simd`, `vulkan`, `dispatch`, `triton-aot`), Data Engine Rust modules (`constants`, `coord`, `dedup`, `error`, `metadata`, `minhash`, `postprocess`, `py_bridge`, `quality`, `serialize`, `simd_cpu`, `string_tensor`), buffer memory protocols, and free-threaded Python (`abi3t` / PEP 703).
53
+
54
+ ---
55
+
56
+ ## 2. Developer Modular Documentation
57
+
58
+ Technical documentation for contributors working on the Rust crates, GPU kernels, and Python bindings (`docs/optimize/` & `data/docs/`):
59
+
60
+ ### A. LuminaV Optimizer
61
+ Source directories: `docs/optimize/` & `optimizer/`
62
+
63
+ - **[LuminaV Optimizer Overview & User Guide (`optimizer/README.md`)](optimize/standalone_guide.md)**
64
+ - **[Developer Optimizer Index (`docs/optimize/README.md`)](optimize/README.md)**
65
+ - **[01. Core & Math](optimize/core/README.md)**: CLM, TD-CLM Triangle Defect, PRNG stochastic rounding, and bias correction.
66
+ - **[02. SIMD Vectorization](optimize/simd/README.md)**: AVX-512 and AVX2/FMA x86_64 intrinsics and Rayon parallel threadpool.
67
+ - **[03. Vulkan Compute](optimize/vulkan/README.md)**: Ash Vulkan 1.3/1.4 SPIR-V compute shaders and UMA host buffers.
68
+ - **[04. Triton AOT & GPU Drivers](optimize/triton_aot/README.md)**: AOT compilation, CUDA/HIP driver dynamic loading, and standalone C-API.
69
+ - **[05. Dispatch & Routing](optimize/dispatch/README.md)**: Hardware capability detector and 16,384-element dispatch threshold heuristic.
70
+ - **[06. Python FFI & PyO3 Bridge](optimize/python_ffi/README.md)**: PyO3 GIL release (`py.detach()`), pointer aliasing checks, and NumPy buffer protocols.
71
+ - **[07. Python Runtime & GPU Mixin](optimize/python_runtime/README.md)**: `LuminaV` class, `constants.py`, Triton JIT kernels, FSDP synchronization, and state checkpointing.
72
+
73
+ ### B. LuminaMoon Data Engine
74
+ Source directories: `data/` & `data/docs/`
75
+
76
+ - **[LuminaMoon Data Engine Overview & User Guide (`data/README.md`)](dataloader/standalone_guide.md)**
77
+ - **[Developer Data Engine Index (`data/docs/README.md`)](dataloader/README.md)**
78
+ - **[01. TextCleaner & QualityFilter](dataloader/simd_and_postprocess/README.md)**: AVX-512/AVX2/NEON SIMD carriage-return stripping, JSON extraction, Shannon entropy, repetition loop, and symbol ratio filters.
79
+ - **[02. StreamDeduplicator & MinHashDeduplicator](dataloader/dedup/README.md)**: Exact streaming deduplication with `xxh3_64` (`IdentityBuildHasher` SwissTable), WAL persistence, and 64-permutation MinHash LSH fuzzy deduplication.
80
+ - **[03. PIIMasker & Data Sanitization](dataloader/simd_and_postprocess/README.md)**: Pattern-based and regex masking for emails, IPv4 addresses, phone numbers, and secret tokens.
81
+ - **[04. StateTracker & StreamCoordinate](dataloader/coord_and_metadata/README.md)**: 32-byte `StreamCoordinate` navigation, dual-slot 128-byte Ping-Pong checkpoint persistence, and multi-epoch state tracking.
82
+ - **[05. StringTensor & Memory Layouts](dataloader/coord_and_metadata/README.md)**: CPU StringView (inlined <= 12 bytes) and GPU contiguous offset/payload buffers compatible with PyTorch, NumPy, Arrow, and Polars.
83
+ - **[06. FusedPipeline & ResumablePipeline](dataloader/python_ffi_and_runtime/README.md)**: Multi-threaded Rayon single-pass preprocessing pipeline and resumable batch pipeline with background prefetcher.
84
+ - **[07. Multimodal Serialization](dataloader/multimodal_and_hf_hub/README.md)**: Serialization to JSONL, Parquet with Zstd compression and embedded image payloads, Arrow IPC, and CSV.
85
+
86
+ ### C. Ra-Family Neural Architecture & Theoretical References
87
+ Source directories: `docs/nn/` & `references/`
88
+
89
+ - **[Ra-Family Overview (`docs/nn/README.md`)](nn/README.md)**: Square-root-free `RaNorm` & `RaBatchNorm2d`, rational `RaTanh` & `RaGELU`, bounded `PosRaGLU` / `RaGLU` / `TriGLU`, and compact-support `ra_softmax` & `RaAttention`.
90
+ - **[Ra-Family Standalone Guide (`docs/nn/standalone_guide.md`)](nn/standalone_guide.md)**: Drop-in Transformer block replacement, `RaBatchNorm2d` `Conv2d` parameter folding, and backend introspection.
91
+ - **[Theoretical References (`docs/references.md`)](references.md)**: Component mapping, literature comparisons across all 29 referenced papers, and BibTeX database (`references/bibliography.bib`).
92
+
93
+ ---
94
+
95
+ ## 3. LSO-Eval Verification & Library Compatibility Notes
96
+
97
+ Source directories: `eval/`, `docs/library_audit/`, & `data/docs/library_audit/`
98
+
99
+ - **[LSO-Eval Optimizer Evaluation & Verification (`eval/README.md`)](lso_eval.md)**: Algorithm-agnostic Z3 SMT solver invariant checks, 2D trajectory benchmarks, numerical parity tests, and Two-Class 4B LLM & Vision evaluation (`LuminaV v1.5.3–v1.6.0`).
100
+ - **[Optimizer Library Notes (`docs/library_audit/`)](library_audit/README.md)**: Version and ABI compatibility notes for Rust 1.99.1 toolchain, PyO3/Maturin, PyTorch 2.14.1, Triton JIT 3.8.0, CUDA/ROCm, and Vulkan Ash compute pipeline.
101
+ - **[Data Engine Library Notes (`data/docs/library_audit/`)](library_audit/README.md)**: Version and compatibility notes for Rust 1.99.1 / Edition 2024, `pyo3 0.30.1` (`abi3t`), Apache Arrow/Parquet/IPC `60.0.0`, `rayon 1.12.0`, `xxhash-rust 0.8.19` (`xxh3`), `crc32fast 1.5.2`, `hashbrown 0.17.1`, `parking_lot 0.12.5`, `serde 1.0.229`, and HuggingFace Hub API protocols.
docs/dataloader/README.md CHANGED
@@ -1,69 +1,69 @@
1
- # LuminaMoon Data Engine Architecture
2
-
3
- Technical architecture and module reference for the **LuminaMoon Data Engine (`luminamoon.data` / `luminamoon-dataloader`)**.
4
-
5
- ---
6
-
7
- ## 1. Subsystem Architecture & Modularity
8
-
9
- The Data Engine consists of a modular Python interface (`python/luminamoon/data/` with pure-Python `fallbacks/`), a `PyO3 0.30.1` (`abi3t`) FFI bridge (`data/src/py_bridge.rs`), and a Rust SIMD, deduplication, and columnar processing crate (`data/src/`):
10
-
11
- ```mermaid
12
- flowchart LR
13
- Input["Raw Text / Iterable / HF Stream"] --> Cleaner["1. SIMD TextCleaner & PostProcess (AVX-512BW / AVX2 / NEON)"]
14
- Cleaner --> Quality["2. QualityFilter (Shannon Entropy, Repetition & Symbol Ratio)"]
15
- Quality --> Dedup["3. TwoStageDeduplicator (XXH3-64 SwissTable) & MinHash LSH"]
16
- Dedup --> Masker["4. PIIMasker (Email, IP, API Key, Card)"]
17
- Masker --> Tensor["5. StringTensor (16B CPU StringView16 & Contiguous GPU Buffers)"]
18
- Tensor --> Export["Arrow / Polars / PyTorch GPU Tensors / Parquet / IPC"]
19
- Tensor --> Meta["6. StateTracker (128B Ping-Pong Slot A/B CRC32 & .dedup WAL)"]
20
- ```
21
-
22
- ---
23
-
24
- ## 2. Directory Navigation & Technical Specs
25
-
26
- | Module | Source Path | Focus & Core Invariants | Spec Document |
27
- | :--- | :--- | :--- | :--- |
28
- | **Coordinates, Constants & Dual Metadata** | `data/src/constants.rs`, `error.rs`, `coord.rs`, `metadata.rs` | 32-byte `StreamCoordinate`, 128-byte Ping-Pong (`Slot A`/`Slot B`) CRC32 checkpoint, `.dedup` WAL, Damerau-Levenshtein suggestions | [01. Coord & Metadata](coord_and_metadata/README.md) |
29
- | **Two-Stage XXH3-64 Deduplication** | `data/src/dedup.rs` | `TwoStageDeduplicator`, `xxh3_64`, `IdentityBuildHasher` SwissTable, staged vs committed sets | [02. Deduplication](dedup/README.md) |
30
- | **SIMD & Parallel Post-Process** | `data/src/simd_cpu.rs`, `postprocess.rs` | `AVX-512BW` / `AVX2` / `NEON` byte kernels, `CacheAlignedBuffer`, Rayon UTF-8/CRLF/JSON dotted-path extraction | [03. SIMD & Post-Process](simd_and_postprocess/README.md) |
31
- | **Heuristic Quality & MinHash LSH** | `data/src/quality.rs`, `minhash.rs` | Shannon character entropy, n-gram repetition ratio, symbol ratio, and 64-permutation MinHash Jaccard similarity | [04. Quality & MinHash](sources_and_streaming/README.md) |
32
- | **Dual-Layout `StringTensor` & Adapters** | `data/src/string_tensor.rs`, `python/luminamoon/data/adapters.py` | 16-byte `StringView16` (<=12B inline), GPU contiguous `uint32` offsets + `uint8` payload, `ResumablePipeline` async prefetch | [05. StringTensor & Adapters](preram/README.md) |
33
- | **Columnar Serialization Engine** | `data/src/serialize.rs`, `python/luminamoon/data/serialization.py` | `DataValue` enum, ZSTD Parquet (`hf_image_struct`), Apache Arrow IPC (`ARROW1`), and NDJSON | [06. Serialization](multimodal_and_hf_hub/README.md) |
34
- | **Python FFI & Modular Runtime** | `data/src/py_bridge.rs`, `python/luminamoon/data/` | `abi3t` bindings, `FusedPipeline`, `TextCleaner`, `PIIMasker`, `StateTracker`, and `fallbacks/` | [07. Python FFI & Runtime](python_ffi_and_runtime/README.md) |
35
-
36
- ---
37
-
38
- ## 3. Developer Invariants & Coding Standards
39
-
40
- 1. **Dual-Layout String Representation (`StringTensor`)**:
41
- - CPU operations use a 16-byte `StringView16` struct (storing strings $\le 12$ bytes inline and comparing 4-byte prefixes in a register), while GPU/Arrow exports emit contiguous `uint32` offset and `uint8` UTF-8 payload buffers.
42
- 2. **Dual-Slot Metadata & Two-Stage Deduplication**:
43
- - In-flight batches staged via `StateTracker.update()` and `StreamDeduplicator.filter_batch()` update in-memory state first. On-disk state (`Slot A` / `Slot B` protected by IEEE CRC32 and the `.dedup` WAL) is written when `.commit()` is called.
44
- 3. **Self-Contained SIMD Module**:
45
- - `luminamoon-dataloader` maintains its own `AVX-512BW`, `AVX2`, `NEON`, and `Scalar` kernels in `simd_cpu.rs` (cached via `OnceLock<CpuIsa>`) independent of the `optimizer` crates.
46
- 4. **GIL Release (`py.detach()`)**:
47
- - Multi-threaded Rayon batch cleaning, quality filtering, `xxh3_64` hashing, `FusedPipeline` execution, and Parquet/Arrow IPC/JSONL serialization execute inside `py.detach()` on CPython 3.10–3.15+ (`abi3` and `abi3t`).
48
-
49
- ---
50
-
51
- ## 4. Local Build & Test Verification
52
-
53
- ```bash
54
- # Run static analysis across the Rust workspace
55
- cargo clippy --workspace --all-targets -- -D warnings
56
-
57
- # Build release extension locally
58
- maturin develop --release
59
-
60
- # Run Rust DataLoader unit and integration tests
61
- cargo test -p luminamoon-dataloader
62
-
63
- # Run Python static type check
64
- npx pyright python/luminamoon
65
-
66
- # Run Python Data Engine & StringTensor test suites
67
- python -B tests/test_data_modular.py
68
- python -B tests/test_gpu_vs_cpu_strings.py
69
- ```
 
1
+ # LuminaMoon Data Engine Architecture
2
+
3
+ Technical architecture and module reference for the **LuminaMoon Data Engine (`luminamoon.data` / `luminamoon-dataloader`)**.
4
+
5
+ ---
6
+
7
+ ## 1. Subsystem Architecture & Modularity
8
+
9
+ The Data Engine consists of a modular Python interface (`python/luminamoon/data/` with pure-Python `fallbacks/`), a `PyO3 0.30.1` (`abi3t`) FFI bridge (`data/src/py_bridge.rs`), and a Rust SIMD, deduplication, and columnar processing crate (`data/src/`):
10
+
11
+ ```mermaid
12
+ flowchart LR
13
+ Input["Raw Text / Iterable / HF Stream"] --> Cleaner["1. SIMD TextCleaner & PostProcess (AVX-512BW / AVX2 / NEON)"]
14
+ Cleaner --> Quality["2. QualityFilter (Shannon Entropy, Repetition & Symbol Ratio)"]
15
+ Quality --> Dedup["3. TwoStageDeduplicator (XXH3-64 SwissTable) & MinHash LSH"]
16
+ Dedup --> Masker["4. PIIMasker (Email, IP, API Key, Card)"]
17
+ Masker --> Tensor["5. StringTensor (16B CPU StringView16 & Contiguous GPU Buffers)"]
18
+ Tensor --> Export["Arrow / Polars / PyTorch GPU Tensors / Parquet / IPC"]
19
+ Tensor --> Meta["6. StateTracker (128B Ping-Pong Slot A/B CRC32 & .dedup WAL)"]
20
+ ```
21
+
22
+ ---
23
+
24
+ ## 2. Directory Navigation & Technical Specs
25
+
26
+ | Module | Source Path | Focus & Core Invariants | Spec Document |
27
+ | :--- | :--- | :--- | :--- |
28
+ | **Coordinates, Constants & Dual Metadata** | `data/src/constants.rs`, `error.rs`, `coord.rs`, `metadata.rs` | 32-byte `StreamCoordinate`, 128-byte Ping-Pong (`Slot A`/`Slot B`) CRC32 checkpoint, `.dedup` WAL, Damerau-Levenshtein suggestions | [01. Coord & Metadata](coord_and_metadata/README.md) |
29
+ | **Two-Stage XXH3-64 Deduplication** | `data/src/dedup.rs` | `TwoStageDeduplicator`, `xxh3_64`, `IdentityBuildHasher` SwissTable, staged vs committed sets | [02. Deduplication](dedup/README.md) |
30
+ | **SIMD & Parallel Post-Process** | `data/src/simd_cpu.rs`, `postprocess.rs` | `AVX-512BW` / `AVX2` / `NEON` byte kernels, `CacheAlignedBuffer`, Rayon UTF-8/CRLF/JSON dotted-path extraction | [03. SIMD & Post-Process](simd_and_postprocess/README.md) |
31
+ | **Heuristic Quality & MinHash LSH** | `data/src/quality.rs`, `minhash.rs` | Shannon character entropy, n-gram repetition ratio, symbol ratio, and 64-permutation MinHash Jaccard similarity | [04. Quality & MinHash](sources_and_streaming/README.md) |
32
+ | **Dual-Layout `StringTensor` & Adapters** | `data/src/string_tensor.rs`, `python/luminamoon/data/adapters.py` | 16-byte `StringView16` (<=12B inline), GPU contiguous `uint32` offsets + `uint8` payload, `ResumablePipeline` async prefetch | [05. StringTensor & Adapters](preram/README.md) |
33
+ | **Columnar Serialization Engine** | `data/src/serialize.rs`, `python/luminamoon/data/serialization.py` | `DataValue` enum, ZSTD Parquet (`hf_image_struct`), Apache Arrow IPC (`ARROW1`), and NDJSON | [06. Serialization](multimodal_and_hf_hub/README.md) |
34
+ | **Python FFI & Modular Runtime** | `data/src/py_bridge.rs`, `python/luminamoon/data/` | `abi3t` bindings, `FusedPipeline`, `TextCleaner`, `PIIMasker`, `StateTracker`, and `fallbacks/` | [07. Python FFI & Runtime](python_ffi_and_runtime/README.md) |
35
+
36
+ ---
37
+
38
+ ## 3. Developer Invariants & Coding Standards
39
+
40
+ 1. **Dual-Layout String Representation (`StringTensor`)**:
41
+ - CPU operations use a 16-byte `StringView16` struct (storing strings $\le 12$ bytes inline and comparing 4-byte prefixes in a register), while GPU/Arrow exports emit contiguous `uint32` offset and `uint8` UTF-8 payload buffers.
42
+ 2. **Dual-Slot Metadata & Two-Stage Deduplication**:
43
+ - In-flight batches staged via `StateTracker.update()` and `StreamDeduplicator.filter_batch()` update in-memory state first. On-disk state (`Slot A` / `Slot B` protected by IEEE CRC32 and the `.dedup` WAL) is written when `.commit()` is called.
44
+ 3. **Self-Contained SIMD Module**:
45
+ - `luminamoon-dataloader` maintains its own `AVX-512BW`, `AVX2`, `NEON`, and `Scalar` kernels in `simd_cpu.rs` (cached via `OnceLock<CpuIsa>`) independent of the `optimizer` crates.
46
+ 4. **GIL Release (`py.detach()`)**:
47
+ - Multi-threaded Rayon batch cleaning, quality filtering, `xxh3_64` hashing, `FusedPipeline` execution, and Parquet/Arrow IPC/JSONL serialization execute inside `py.detach()` on CPython 3.10–3.15+ (`abi3` and `abi3t`).
48
+
49
+ ---
50
+
51
+ ## 4. Local Build & Test Verification
52
+
53
+ ```bash
54
+ # Run static analysis across the Rust workspace
55
+ cargo clippy --workspace --all-targets -- -D warnings
56
+
57
+ # Build release extension locally
58
+ maturin develop --release
59
+
60
+ # Run Rust DataLoader unit and integration tests
61
+ cargo test -p luminamoon-dataloader
62
+
63
+ # Run Python static type check
64
+ npx pyright python/luminamoon
65
+
66
+ # Run Python Data Engine & StringTensor test suites
67
+ python -B tests/test_data_modular.py
68
+ python -B tests/test_gpu_vs_cpu_strings.py
69
+ ```
docs/dataloader/coord_and_metadata/README.md CHANGED
@@ -1,70 +1,70 @@
1
- # Stream Coordinates & Ping-Pong Checkpoint Recovery
2
-
3
- [`data/src/coord.rs`](https://github.com/cloverx-id/luminamoon/blob/main/data/src/coord.rs) and [`data/src/metadata.rs`](https://github.com/cloverx-id/luminamoon/blob/main/data/src/metadata.rs) define stream coordinate tracking and atomic dual-slot checkpointing, enabling dataset resume without re-reading or re-hashing earlier shards.
4
-
5
- ---
6
-
7
- ## 1. 32-Byte `StreamCoordinate` Binary Layout (`src/coord.rs`)
8
-
9
- Positions across multi-shard streams are addressed by a fixed 32-byte little-endian struct:
10
-
11
- | Byte Offset | Field | Rust Type | Width | Description |
12
- | :--- | :--- | :--- | :--- | :--- |
13
- | `0..4` | `shard_id` | `u32` | 4 B | Active index in the expanded shard URI list. |
14
- | `4..12` | `byte_offset` | `u64` | 8 B | Exact byte offset for `.jsonl` / `.txt` `mmap` or HTTP 206 Range streams. |
15
- | `12..16` | `row_group_idx` | `u32` | 4 B | Active Parquet Row Group index. |
16
- | `16..20` | `page_idx` | `u32` | 4 B | Reserved page index within the Row Group. |
17
- | `20..24` | `row_offset_in_page` | `u32` | 4 B | Exact row offset within the active Parquet Row Group (`RowSelector::skip`). |
18
- | `24..32` | `global_row_idx` | `u64` | 8 B | Cumulative number of raw records scanned across all shards. |
19
-
20
- ```rust
21
- #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
22
- pub struct StreamCoordinate {
23
- pub shard_id: u32,
24
- pub byte_offset: u64,
25
- pub row_group_idx: u32,
26
- pub page_idx: u32,
27
- pub row_offset_in_page: u32,
28
- pub global_row_idx: u64,
29
- }
30
- ```
31
-
32
- ---
33
-
34
- ## 2. 128-Byte Ping-Pong Checkpoint File (`src/metadata.rs`)
35
-
36
- To prevent checkpoint corruption from sudden power loss or process interruption, `DualMetadataManager` maintains a fixed 128-byte `.meta` file divided into two 64-byte slots (`Slot A` at `0..64` and `Slot B` at `64..128`):
37
-
38
- ```mermaid
39
- flowchart LR
40
- Commit["commit_gpu_batch(batch_id)"] --> WAL["Flush Hashes to .dedup WAL"]
41
- WAL --> Epoch["Increment Epoch Counter"]
42
- Epoch --> SlotA["Odd Epoch: Slot A (Bytes 0..64 + CRC32)"]
43
- Epoch --> SlotB["Even Epoch: Slot B (Bytes 64..128 + CRC32)"]
44
- SlotA --> Recover["Verify CRC32 & Select max(epoch)"]
45
- SlotB --> Recover
46
- ```
47
-
48
- ### 64-Byte `DiskSlot` Wire Format
49
-
50
- | Byte Range | Field | Type | Description |
51
- | :--- | :--- | :--- | :--- |
52
- | `0..4` | `SLOT_MAGIC` | `[u8; 4]` | Constant magic header `*b"LMDL"`. |
53
- | `4..8` | `SLOT_VERSION` | `u32` | Format version (`1`). |
54
- | `8..16` | `epoch` | `u64` | Monotonically increasing commit sequence number. |
55
- | `16..24` | `committed_batch_id` | `u64` | Last batch ID confirmed consumed by the GPU training step. |
56
- | `24..56` | `coord` | `[u8; 32]` | Encoded `StreamCoordinate` immediately following the committed batch. |
57
- | `56..60` | `dedup_count` | `u32` | Total number of committed 8-byte `xxh3_64` hashes valid in `.meta.dedup`. |
58
- | `60..64` | `crc32` | `u32` | Hardware-accelerated IEEE CRC32 (`crc32fast`) over bytes `0..60`. |
59
-
60
- ---
61
-
62
- ## 3. Recovery & Truncation Invariants
63
-
64
- When initializing `DualMetadataManager`:
65
-
66
- 1. Both `Slot A` and `Slot B` are decoded and verified against `SLOT_MAGIC`, `SLOT_VERSION`, and their 32-bit IEEE CRC32 checksum.
67
- 2. If both slots are valid, the slot with the higher `epoch` is selected:
68
- $$\text{Slot}_{\text{active}} = \arg\max_{S \in \{A, B\}} S.\text{epoch}$$
69
- 3. If one slot was torn during a system crash, its CRC32 check fails and the manager falls back to the intact partner slot.
70
- 4. The companion `.meta.dedup` write-ahead log is truncated to exactly $\text{dedup}_{\text{count}} \times 8$ bytes (`dedup_file.set_len(valid_dedup_bytes)`), discarding any uncommitted partial hash appends before reconstructing `TwoStageDeduplicator::with_committed`.
 
1
+ # Stream Coordinates & Ping-Pong Checkpoint Recovery
2
+
3
+ [`data/src/coord.rs`](https://github.com/cloverx-id/luminamoon/blob/main/data/src/coord.rs) and [`data/src/metadata.rs`](https://github.com/cloverx-id/luminamoon/blob/main/data/src/metadata.rs) define stream coordinate tracking and atomic dual-slot checkpointing, enabling dataset resume without re-reading or re-hashing earlier shards.
4
+
5
+ ---
6
+
7
+ ## 1. 32-Byte `StreamCoordinate` Binary Layout (`src/coord.rs`)
8
+
9
+ Positions across multi-shard streams are addressed by a fixed 32-byte little-endian struct:
10
+
11
+ | Byte Offset | Field | Rust Type | Width | Description |
12
+ | :--- | :--- | :--- | :--- | :--- |
13
+ | `0..4` | `shard_id` | `u32` | 4 B | Active index in the expanded shard URI list. |
14
+ | `4..12` | `byte_offset` | `u64` | 8 B | Exact byte offset for `.jsonl` / `.txt` `mmap` or HTTP 206 Range streams. |
15
+ | `12..16` | `row_group_idx` | `u32` | 4 B | Active Parquet Row Group index. |
16
+ | `16..20` | `page_idx` | `u32` | 4 B | Reserved page index within the Row Group. |
17
+ | `20..24` | `row_offset_in_page` | `u32` | 4 B | Exact row offset within the active Parquet Row Group (`RowSelector::skip`). |
18
+ | `24..32` | `global_row_idx` | `u64` | 8 B | Cumulative number of raw records scanned across all shards. |
19
+
20
+ ```rust
21
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
22
+ pub struct StreamCoordinate {
23
+ pub shard_id: u32,
24
+ pub byte_offset: u64,
25
+ pub row_group_idx: u32,
26
+ pub page_idx: u32,
27
+ pub row_offset_in_page: u32,
28
+ pub global_row_idx: u64,
29
+ }
30
+ ```
31
+
32
+ ---
33
+
34
+ ## 2. 128-Byte Ping-Pong Checkpoint File (`src/metadata.rs`)
35
+
36
+ To prevent checkpoint corruption from sudden power loss or process interruption, `DualMetadataManager` maintains a fixed 128-byte `.meta` file divided into two 64-byte slots (`Slot A` at `0..64` and `Slot B` at `64..128`):
37
+
38
+ ```mermaid
39
+ flowchart LR
40
+ Commit["commit_gpu_batch(batch_id)"] --> WAL["Flush Hashes to .dedup WAL"]
41
+ WAL --> Epoch["Increment Epoch Counter"]
42
+ Epoch --> SlotA["Odd Epoch: Slot A (Bytes 0..64 + CRC32)"]
43
+ Epoch --> SlotB["Even Epoch: Slot B (Bytes 64..128 + CRC32)"]
44
+ SlotA --> Recover["Verify CRC32 & Select max(epoch)"]
45
+ SlotB --> Recover
46
+ ```
47
+
48
+ ### 64-Byte `DiskSlot` Wire Format
49
+
50
+ | Byte Range | Field | Type | Description |
51
+ | :--- | :--- | :--- | :--- |
52
+ | `0..4` | `SLOT_MAGIC` | `[u8; 4]` | Constant magic header `*b"LMDL"`. |
53
+ | `4..8` | `SLOT_VERSION` | `u32` | Format version (`1`). |
54
+ | `8..16` | `epoch` | `u64` | Monotonically increasing commit sequence number. |
55
+ | `16..24` | `committed_batch_id` | `u64` | Last batch ID confirmed consumed by the GPU training step. |
56
+ | `24..56` | `coord` | `[u8; 32]` | Encoded `StreamCoordinate` immediately following the committed batch. |
57
+ | `56..60` | `dedup_count` | `u32` | Total number of committed 8-byte `xxh3_64` hashes valid in `.meta.dedup`. |
58
+ | `60..64` | `crc32` | `u32` | Hardware-accelerated IEEE CRC32 (`crc32fast`) over bytes `0..60`. |
59
+
60
+ ---
61
+
62
+ ## 3. Recovery & Truncation Invariants
63
+
64
+ When initializing `DualMetadataManager`:
65
+
66
+ 1. Both `Slot A` and `Slot B` are decoded and verified against `SLOT_MAGIC`, `SLOT_VERSION`, and their 32-bit IEEE CRC32 checksum.
67
+ 2. If both slots are valid, the slot with the higher `epoch` is selected:
68
+ $$\text{Slot}_{\text{active}} = \arg\max_{S \in \{A, B\}} S.\text{epoch}$$
69
+ 3. If one slot was torn during a system crash, its CRC32 check fails and the manager falls back to the intact partner slot.
70
+ 4. The companion `.meta.dedup` write-ahead log is truncated to exactly $\text{dedup}_{\text{count}} \times 8$ bytes (`dedup_file.set_len(valid_dedup_bytes)`), discarding any uncommitted partial hash appends before reconstructing `TwoStageDeduplicator::with_committed`.
docs/dataloader/dedup/README.md CHANGED
@@ -1,66 +1,66 @@
1
- # Two-Stage XXH3-64 Deduplication & Identity SwissTable
2
-
3
- [`data/src/dedup.rs`](https://github.com/cloverx-id/luminamoon/blob/main/data/src/dedup.rs) provides a two-stage streaming deduplicator that separates prefetched records from records committed by training steps.
4
-
5
- ---
6
-
7
- ## 1. Two-Stage State Machine (`TwoStageDeduplicator`)
8
-
9
- In asynchronous pipelines, multiple batches can be staged in memory ahead of the active GPU step. If a single set marked hashes as seen upon prefetch and the process stopped before training consumed them, resuming would either skip unread samples or lose deduplication consistency.
10
-
11
- `TwoStageDeduplicator` avoids this by partitioning tracked hashes into two disjoint sets:
12
-
13
- $$\mathcal{H}_{\text{committed}} \cap \mathcal{H}_{\text{staged}} = \emptyset$$
14
-
15
- ```mermaid
16
- flowchart LR
17
- Record["Candidate Record"] --> Hash["Compute XXH3-64 (IdentityHasher)"]
18
- Hash --> Drop["Duplicate Digest: Drop Record"]
19
- Hash --> Stage["Unique Digest: Insert into staged_lookup"]
20
- Stage --> Commit["commit_batch: Promote to committed_hashes & WAL"]
21
- Stage --> Evict["rollback_batch: Evict from staged_lookup"]
22
- ```
23
-
24
- ### Core Operations
25
-
26
- 1. **`try_stage(batch_id, payload) -> Option<u64>`**:
27
- - Computes $h = \text{XXH3}_{64}(\text{payload})$.
28
- - Rejects the record (`None`) if $h \in \text{committed}_{\text{hashes}} \cup \text{staged}_{\text{lookup}}$.
29
- - Otherwise inserts $h$ into `staged_lookup` and appends $h$ to `staged_batches[batch_id]`.
30
- 2. **`commit_batch(batch_id) -> Vec<u64>`**:
31
- - Atomically promotes all hashes in `staged_batches[batch_id]` from `staged_lookup` into `committed_hashes` and returns the vector of promoted hashes to be flushed to the `.meta.dedup` write-ahead log.
32
- 3. **`rollback_batch(batch_id)` & `clear_staged()`**:
33
- - Evicts uncommitted batch hashes from `staged_lookup` without touching `committed_hashes`, restoring exact state parity with the last committed GPU step.
34
-
35
- ---
36
-
37
- ## 2. Zero-Rehash SwissTable (`IdentityBuildHasher`)
38
-
39
- Because keys stored in `committed_hashes` and `staged_lookup` are already uniformly distributed 64-bit `xxh3_64` digests, hashing them again with `hashbrown`'s default `foldhash` would waste CPU cycles.
40
-
41
- `dedup.rs` defines an `IdentityHasher` that passes the digest directly to the table probe:
42
-
43
- ```rust
44
- #[derive(Default)]
45
- pub struct IdentityHasher(u64);
46
-
47
- impl Hasher for IdentityHasher {
48
- #[inline]
49
- fn finish(&self) -> u64 {
50
- self.0
51
- }
52
-
53
- #[inline]
54
- fn write_u64(&mut self, i: u64) {
55
- self.0 = i;
56
- }
57
- }
58
-
59
- pub type IdentityBuildHasher = BuildHasherDefault<IdentityHasher>;
60
- ```
61
-
62
- ---
63
-
64
- ## 3. MinHash LSH Near-Duplicate Shingling (`src/minhash.rs`)
65
-
66
- For fuzzy near-duplicate rejection alongside exact 64-bit `xxh3_64` deduplication, `MinHashDeduplicator` computes $K$-character UTF-8 shingles hashed across $P$ deterministic permuted `xxh3_64_with_seed` lanes and estimates pairwise Jaccard similarity against indexed document signatures.
 
1
+ # Two-Stage XXH3-64 Deduplication & Identity SwissTable
2
+
3
+ [`data/src/dedup.rs`](https://github.com/cloverx-id/luminamoon/blob/main/data/src/dedup.rs) provides a two-stage streaming deduplicator that separates prefetched records from records committed by training steps.
4
+
5
+ ---
6
+
7
+ ## 1. Two-Stage State Machine (`TwoStageDeduplicator`)
8
+
9
+ In asynchronous pipelines, multiple batches can be staged in memory ahead of the active GPU step. If a single set marked hashes as seen upon prefetch and the process stopped before training consumed them, resuming would either skip unread samples or lose deduplication consistency.
10
+
11
+ `TwoStageDeduplicator` avoids this by partitioning tracked hashes into two disjoint sets:
12
+
13
+ $$\mathcal{H}_{\text{committed}} \cap \mathcal{H}_{\text{staged}} = \emptyset$$
14
+
15
+ ```mermaid
16
+ flowchart LR
17
+ Record["Candidate Record"] --> Hash["Compute XXH3-64 (IdentityHasher)"]
18
+ Hash --> Drop["Duplicate Digest: Drop Record"]
19
+ Hash --> Stage["Unique Digest: Insert into staged_lookup"]
20
+ Stage --> Commit["commit_batch: Promote to committed_hashes & WAL"]
21
+ Stage --> Evict["rollback_batch: Evict from staged_lookup"]
22
+ ```
23
+
24
+ ### Core Operations
25
+
26
+ 1. **`try_stage(batch_id, payload) -> Option<u64>`**:
27
+ - Computes $h = \text{XXH3}_{64}(\text{payload})$.
28
+ - Rejects the record (`None`) if $h \in \text{committed}_{\text{hashes}} \cup \text{staged}_{\text{lookup}}$.
29
+ - Otherwise inserts $h$ into `staged_lookup` and appends $h$ to `staged_batches[batch_id]`.
30
+ 2. **`commit_batch(batch_id) -> Vec<u64>`**:
31
+ - Atomically promotes all hashes in `staged_batches[batch_id]` from `staged_lookup` into `committed_hashes` and returns the vector of promoted hashes to be flushed to the `.meta.dedup` write-ahead log.
32
+ 3. **`rollback_batch(batch_id)` & `clear_staged()`**:
33
+ - Evicts uncommitted batch hashes from `staged_lookup` without touching `committed_hashes`, restoring exact state parity with the last committed GPU step.
34
+
35
+ ---
36
+
37
+ ## 2. Zero-Rehash SwissTable (`IdentityBuildHasher`)
38
+
39
+ Because keys stored in `committed_hashes` and `staged_lookup` are already uniformly distributed 64-bit `xxh3_64` digests, hashing them again with `hashbrown`'s default `foldhash` would waste CPU cycles.
40
+
41
+ `dedup.rs` defines an `IdentityHasher` that passes the digest directly to the table probe:
42
+
43
+ ```rust
44
+ #[derive(Default)]
45
+ pub struct IdentityHasher(u64);
46
+
47
+ impl Hasher for IdentityHasher {
48
+ #[inline]
49
+ fn finish(&self) -> u64 {
50
+ self.0
51
+ }
52
+
53
+ #[inline]
54
+ fn write_u64(&mut self, i: u64) {
55
+ self.0 = i;
56
+ }
57
+ }
58
+
59
+ pub type IdentityBuildHasher = BuildHasherDefault<IdentityHasher>;
60
+ ```
61
+
62
+ ---
63
+
64
+ ## 3. MinHash LSH Near-Duplicate Shingling (`src/minhash.rs`)
65
+
66
+ For fuzzy near-duplicate rejection alongside exact 64-bit `xxh3_64` deduplication, `MinHashDeduplicator` computes $K$-character UTF-8 shingles hashed across $P$ deterministic permuted `xxh3_64_with_seed` lanes and estimates pairwise Jaccard similarity against indexed document signatures.
docs/dataloader/multimodal_and_hf_hub/README.md CHANGED
@@ -1,38 +1,38 @@
1
- # Columnar Serialization (Parquet, Arrow IPC & JSONL)
2
-
3
- [`data/src/serialize.rs`](https://github.com/cloverx-id/luminamoon/blob/main/data/src/serialize.rs) and [`python/luminamoon/data/serialization.py`](https://github.com/cloverx-id/luminamoon/blob/main/python/luminamoon/data/serialization.py) serialize Python dictionaries and columnar tables into Apache Parquet (ZSTD), Apache Arrow IPC (`ARROW1`), and line-delimited JSONL (`NDJSON`) with the Python GIL released.
4
-
5
- ---
6
-
7
- ## 1. Unified Columnar Type System (`DataValue`)
8
-
9
- `data/src/serialize.rs` maps Python sequences and dictionaries into an 8-variant `DataValue` enum:
10
-
11
- | Variant | Python Input Types | Arrow / Parquet Target Type (`hf_image_struct = true`) |
12
- | :--- | :--- | :--- |
13
- | `DataValue::Null` | `None` | Nullable slot in target column array |
14
- | `DataValue::Text(String)` | `str` (non-JSON) | `DataType::Utf8` (`StringArray`) |
15
- | `DataValue::Bytes(Vec<u8>)` | `bytes`, `bytearray` | `DataType::Struct(bytes: Binary, path: Utf8)` (or `DataType::Binary` when `hf_image_struct = false`) |
16
- | `DataValue::Json(String)` | Valid JSON object/array `str` | `DataType::Utf8` in Parquet/Arrow; parsed JSON value in `.jsonl` |
17
- | `DataValue::Int(i64)` | `int` | `DataType::Int64` (`Int64Array`) |
18
- | `DataValue::Float(f64)` | `float` | `DataType::Float64` (`Float64Array`) |
19
- | `DataValue::Bool(bool)` | `bool` | `DataType::Boolean` (`BooleanArray`) |
20
- | `DataValue::List(Vec<DataValue>)` | Nested sequences | JSON-encoded array representation |
21
-
22
- ---
23
-
24
- ## 2. HuggingFace Dataset Viewer Image Struct Compatibility
25
-
26
- When `hf_image_struct = True` (default in `serialize_to_parquet`), binary columns (`DataValue::Bytes`) are automatically encoded as an Arrow `StructArray` with schema:
27
-
28
- $$\text{Struct}\langle\text{bytes}: \text{Binary},\ \text{path}: \text{Utf8}\rangle$$
29
-
30
- This matches the standard HuggingFace Dataset Viewer image/audio schema, enabling inline previews on `huggingface.co` without post-processing.
31
-
32
- ---
33
-
34
- ## 3. Public Python Serialization Helpers (`serialization.py`)
35
-
36
- - **`serialize_to_parquet(columns, output_path=None, hf_image_struct=True) -> bytes`**: Encodes columnar data into ZSTD-compressed Apache Parquet bytes (`PAR1`) with the GIL released (`py.detach()`).
37
- - **`serialize_to_arrow(columns, output_path=None) -> bytes`**: Encodes columnar data into Apache Arrow IPC File Format bytes (`ARROW1`).
38
- - **`serialize_to_jsonl(columns, output_path=None) -> bytes`**: Encodes columnar data into UTF-8 line-delimited JSONL records.
 
1
+ # Columnar Serialization (Parquet, Arrow IPC & JSONL)
2
+
3
+ [`data/src/serialize.rs`](https://github.com/cloverx-id/luminamoon/blob/main/data/src/serialize.rs) and [`python/luminamoon/data/serialization.py`](https://github.com/cloverx-id/luminamoon/blob/main/python/luminamoon/data/serialization.py) serialize Python dictionaries and columnar tables into Apache Parquet (ZSTD), Apache Arrow IPC (`ARROW1`), and line-delimited JSONL (`NDJSON`) with the Python GIL released.
4
+
5
+ ---
6
+
7
+ ## 1. Unified Columnar Type System (`DataValue`)
8
+
9
+ `data/src/serialize.rs` maps Python sequences and dictionaries into an 8-variant `DataValue` enum:
10
+
11
+ | Variant | Python Input Types | Arrow / Parquet Target Type (`hf_image_struct = true`) |
12
+ | :--- | :--- | :--- |
13
+ | `DataValue::Null` | `None` | Nullable slot in target column array |
14
+ | `DataValue::Text(String)` | `str` (non-JSON) | `DataType::Utf8` (`StringArray`) |
15
+ | `DataValue::Bytes(Vec<u8>)` | `bytes`, `bytearray` | `DataType::Struct(bytes: Binary, path: Utf8)` (or `DataType::Binary` when `hf_image_struct = false`) |
16
+ | `DataValue::Json(String)` | Valid JSON object/array `str` | `DataType::Utf8` in Parquet/Arrow; parsed JSON value in `.jsonl` |
17
+ | `DataValue::Int(i64)` | `int` | `DataType::Int64` (`Int64Array`) |
18
+ | `DataValue::Float(f64)` | `float` | `DataType::Float64` (`Float64Array`) |
19
+ | `DataValue::Bool(bool)` | `bool` | `DataType::Boolean` (`BooleanArray`) |
20
+ | `DataValue::List(Vec<DataValue>)` | Nested sequences | JSON-encoded array representation |
21
+
22
+ ---
23
+
24
+ ## 2. HuggingFace Dataset Viewer Image Struct Compatibility
25
+
26
+ When `hf_image_struct = True` (default in `serialize_to_parquet`), binary columns (`DataValue::Bytes`) are automatically encoded as an Arrow `StructArray` with schema:
27
+
28
+ $$\text{Struct}\langle\text{bytes}: \text{Binary},\ \text{path}: \text{Utf8}\rangle$$
29
+
30
+ This matches the standard HuggingFace Dataset Viewer image/audio schema, enabling inline previews on `huggingface.co` without post-processing.
31
+
32
+ ---
33
+
34
+ ## 3. Public Python Serialization Helpers (`serialization.py`)
35
+
36
+ - **`serialize_to_parquet(columns, output_path=None, hf_image_struct=True) -> bytes`**: Encodes columnar data into ZSTD-compressed Apache Parquet bytes (`PAR1`) with the GIL released (`py.detach()`).
37
+ - **`serialize_to_arrow(columns, output_path=None) -> bytes`**: Encodes columnar data into Apache Arrow IPC File Format bytes (`ARROW1`).
38
+ - **`serialize_to_jsonl(columns, output_path=None) -> bytes`**: Encodes columnar data into UTF-8 line-delimited JSONL records.
docs/dataloader/preram/README.md CHANGED
@@ -1,37 +1,37 @@
1
- # Dual-Layout StringTensor & Async Resumable Pipelines
2
-
3
- [`data/src/string_tensor.rs`](https://github.com/cloverx-id/luminamoon/blob/main/data/src/string_tensor.rs) and [`python/luminamoon/data/adapters.py`](https://github.com/cloverx-id/luminamoon/blob/main/python/luminamoon/data/adapters.py) provide complementary CPU and GPU string layouts alongside asynchronous background batch prefetching for PyTorch and standalone training loops.
4
-
5
- ---
6
-
7
- ## 1. Dual-Layout `StringTensor` (`src/string_tensor.rs`)
8
-
9
- `StringTensor` bridges CPU string processing and GPU tensor ingestion via two complementary memory representations:
10
-
11
- ### 1.1 CPU Layout: 16-Byte Umbra / DuckDB `StringView16`
12
- ```rust
13
- #[repr(C)]
14
- #[derive(Clone, Copy, Debug, PartialEq, Eq)]
15
- pub struct StringView16 {
16
- pub(crate) len: u32,
17
- pub(crate) prefix_or_inline: [u8; 4],
18
- pub(crate) suffix_or_offset: [u8; 8],
19
- }
20
- ```
21
-
22
- - **Inlined Strings ($\text{len} \le 12$ bytes)**: Stored completely inside the 16-byte struct (`prefix_or_inline` + `suffix_or_offset`) with zero heap pointer dereferences.
23
- - **Pool-Referenced Strings ($\text{len} > 12$ bytes)**: Stores the first 4 bytes in `prefix_or_inline` (enabling single-register 32-bit prefix rejection in `filter_by_prefix` and `fast_eq`), alongside a 4-byte `offset` and 4-byte `buffer_id` into `Arc<Vec<u8>>`.
24
-
25
- ### 1.2 GPU & Arrow Layout: Contiguous Offsets + Payload
26
- - `to_gpu_buffers()` emits a contiguous `uint32` offset array `[0, off_1, ..., off_N]` of length $N + 1$ and a flat `uint8` UTF-8 byte buffer.
27
- - Directly exported to PyTorch (`to_torch_gpu(device)`), NumPy (`to_numpy_gpu()`), Apache Arrow (`to_arrow(zero_copy=True)` via `pa.StringArray.from_buffers`), and Polars (`to_polars()`).
28
-
29
- ---
30
-
31
- ## 2. Asynchronous Prefetching & PyTorch Integration (`adapters.py`)
32
-
33
- 1. **`ResumablePipeline`**:
34
- - Wraps any Python iterable or streaming dataset with single-pass `FusedPipeline` execution, `StateTracker` 128-byte Ping-Pong coordinate tracking, and background thread prefetching (`prefetch_batches > 0` via bounded `queue.Queue`).
35
- - Calling `pipe.commit()` atomically persists both the active `StateTracker` batch coordinate (`Slot A` / `Slot B`) and `StreamDeduplicator` WAL.
36
- 2. **`create_resumable_collate`**:
37
- - Generates a drop-in `collate_fn` for `torch.utils.data.DataLoader` that extracts text fields, runs `FusedPipeline` (or modular `TextCleaner` -> `QualityFilter` -> `StreamDeduplicator` -> `PIIMasker`), and returns either tokenized PyTorch tensors or a `StringTensor`.
 
1
+ # Dual-Layout StringTensor & Async Resumable Pipelines
2
+
3
+ [`data/src/string_tensor.rs`](https://github.com/cloverx-id/luminamoon/blob/main/data/src/string_tensor.rs) and [`python/luminamoon/data/adapters.py`](https://github.com/cloverx-id/luminamoon/blob/main/python/luminamoon/data/adapters.py) provide complementary CPU and GPU string layouts alongside asynchronous background batch prefetching for PyTorch and standalone training loops.
4
+
5
+ ---
6
+
7
+ ## 1. Dual-Layout `StringTensor` (`src/string_tensor.rs`)
8
+
9
+ `StringTensor` bridges CPU string processing and GPU tensor ingestion via two complementary memory representations:
10
+
11
+ ### 1.1 CPU Layout: 16-Byte Umbra / DuckDB `StringView16`
12
+ ```rust
13
+ #[repr(C)]
14
+ #[derive(Clone, Copy, Debug, PartialEq, Eq)]
15
+ pub struct StringView16 {
16
+ pub(crate) len: u32,
17
+ pub(crate) prefix_or_inline: [u8; 4],
18
+ pub(crate) suffix_or_offset: [u8; 8],
19
+ }
20
+ ```
21
+
22
+ - **Inlined Strings ($\text{len} \le 12$ bytes)**: Stored completely inside the 16-byte struct (`prefix_or_inline` + `suffix_or_offset`) with zero heap pointer dereferences.
23
+ - **Pool-Referenced Strings ($\text{len} > 12$ bytes)**: Stores the first 4 bytes in `prefix_or_inline` (enabling single-register 32-bit prefix rejection in `filter_by_prefix` and `fast_eq`), alongside a 4-byte `offset` and 4-byte `buffer_id` into `Arc<Vec<u8>>`.
24
+
25
+ ### 1.2 GPU & Arrow Layout: Contiguous Offsets + Payload
26
+ - `to_gpu_buffers()` emits a contiguous `uint32` offset array `[0, off_1, ..., off_N]` of length $N + 1$ and a flat `uint8` UTF-8 byte buffer.
27
+ - Directly exported to PyTorch (`to_torch_gpu(device)`), NumPy (`to_numpy_gpu()`), Apache Arrow (`to_arrow(zero_copy=True)` via `pa.StringArray.from_buffers`), and Polars (`to_polars()`).
28
+
29
+ ---
30
+
31
+ ## 2. Asynchronous Prefetching & PyTorch Integration (`adapters.py`)
32
+
33
+ 1. **`ResumablePipeline`**:
34
+ - Wraps any Python iterable or streaming dataset with single-pass `FusedPipeline` execution, `StateTracker` 128-byte Ping-Pong coordinate tracking, and background thread prefetching (`prefetch_batches > 0` via bounded `queue.Queue`).
35
+ - Calling `pipe.commit()` atomically persists both the active `StateTracker` batch coordinate (`Slot A` / `Slot B`) and `StreamDeduplicator` WAL.
36
+ 2. **`create_resumable_collate`**:
37
+ - Generates a drop-in `collate_fn` for `torch.utils.data.DataLoader` that extracts text fields, runs `FusedPipeline` (or modular `TextCleaner` -> `QualityFilter` -> `StreamDeduplicator` -> `PIIMasker`), and returns either tokenized PyTorch tensors or a `StringTensor`.
docs/dataloader/python_ffi_and_runtime/README.md CHANGED
@@ -1,29 +1,29 @@
1
- # PyO3 FFI Bridge & Modular Python Runtime
2
-
3
- [`data/src/py_bridge.rs`](https://github.com/cloverx-id/luminamoon/blob/main/data/src/py_bridge.rs) and the modular [`python/luminamoon/data/`](https://github.com/cloverx-id/luminamoon/blob/main/python/luminamoon/data/__init__.py) package connect the native Rust SIMD and columnar engine with Python under the CPython Stable ABI (`abi3-py310` and free-threaded `abi3t`).
4
-
5
- ---
6
-
7
- ## 1. Native PyO3 Classes & Functions (`src/py_bridge.rs`)
8
-
9
- All native symbols are compiled into `_luminamoon_optimizer_rs` and re-exported through `python/luminamoon/data/_ffi.py`:
10
-
11
- | Rust PyO3 Symbol | Python Wrapper (`luminamoon.data`) | GIL Detached (`py.detach`) | Core Responsibility |
12
- | :--- | :--- | :--- | :--- |
13
- | `PyStreamCoordinate` | `StreamCoordinate` (`checkpoint.py`) | N/A (32B value) | 32-byte little-endian stream coordinate with `to_bytes()` / `from_bytes()`. |
14
- | `PyStateTracker` | `StateTracker` (`checkpoint.py`) | Yes | 128-byte Dual-Slot Ping-Pong (`Slot A` / `Slot B`) atomic checkpoint manager (`read`, `update`, `commit`). |
15
- | `PyStreamDeduplicator` | `StreamDeduplicator` (`dedup.py`) | Yes | Two-stage `xxh3_64` SwissTable deduplicator with `.dedup` WAL (`is_unique`, `filter_batch`, `commit`, `rollback`). |
16
- | `PyMinHashDeduplicator` | `MinHashDeduplicator` (`minhash.py`) | Yes | 64-permutation MinHash LSH near-duplicate detector (`is_unique`, `similarity`, `filter_batch`). |
17
- | `PyQualityFilter` | `QualityFilter` (`quality.py`) | Yes | Parallel Rayon Shannon entropy, repetition, and symbol ratio filter (`is_high_quality`, `metrics`, `filter_batch`). |
18
- | `PyStringTensor` | `StringTensor` (`string_tensor.py`) | Yes | Dual-layout 16B CPU `StringView16` and contiguous GPU/Arrow buffer container. |
19
- | `fused_pipeline_process_batch` | `FusedPipeline` (`fused.py`) | Yes | Single-pass Rayon + SIMD text cleaning, quality filtering, and deduplication returning `StringTensor`. |
20
- | `serialize_table_to_bytes` | `serialize_to_*` (`serialization.py`) | Yes | Columnar serialization to Parquet (`PAR1`), Arrow IPC (`ARROW1`), and JSONL. |
21
-
22
- ---
23
-
24
- ## 2. Modular Pure-Python Fallback Layer (`python/luminamoon/data/fallbacks/`)
25
-
26
- When running in environments without the compiled native binary, `_ffi.py` transparently falls back to the modular `fallbacks/` subpackage:
27
-
28
- - `cleaner.py`, `quality.py`, `dedup.py`, `minhash.py`, `masker.py`, `coordinate.py`, `tracker.py`, `string_tensor.py`, `fused.py`, `serialization.py`, and `helpers.py`.
29
- - Preserves API and binary format compatibility (including 32-byte `StreamCoordinate` and 128-byte IEEE CRC32 Ping-Pong `.meta` checkpoints).
 
1
+ # PyO3 FFI Bridge & Modular Python Runtime
2
+
3
+ [`data/src/py_bridge.rs`](https://github.com/cloverx-id/luminamoon/blob/main/data/src/py_bridge.rs) and the modular [`python/luminamoon/data/`](https://github.com/cloverx-id/luminamoon/blob/main/python/luminamoon/data/__init__.py) package connect the native Rust SIMD and columnar engine with Python under the CPython Stable ABI (`abi3-py310` and free-threaded `abi3t`).
4
+
5
+ ---
6
+
7
+ ## 1. Native PyO3 Classes & Functions (`src/py_bridge.rs`)
8
+
9
+ All native symbols are compiled into `_luminamoon_optimizer_rs` and re-exported through `python/luminamoon/data/_ffi.py`:
10
+
11
+ | Rust PyO3 Symbol | Python Wrapper (`luminamoon.data`) | GIL Detached (`py.detach`) | Core Responsibility |
12
+ | :--- | :--- | :--- | :--- |
13
+ | `PyStreamCoordinate` | `StreamCoordinate` (`checkpoint.py`) | N/A (32B value) | 32-byte little-endian stream coordinate with `to_bytes()` / `from_bytes()`. |
14
+ | `PyStateTracker` | `StateTracker` (`checkpoint.py`) | Yes | 128-byte Dual-Slot Ping-Pong (`Slot A` / `Slot B`) atomic checkpoint manager (`read`, `update`, `commit`). |
15
+ | `PyStreamDeduplicator` | `StreamDeduplicator` (`dedup.py`) | Yes | Two-stage `xxh3_64` SwissTable deduplicator with `.dedup` WAL (`is_unique`, `filter_batch`, `commit`, `rollback`). |
16
+ | `PyMinHashDeduplicator` | `MinHashDeduplicator` (`minhash.py`) | Yes | 64-permutation MinHash LSH near-duplicate detector (`is_unique`, `similarity`, `filter_batch`). |
17
+ | `PyQualityFilter` | `QualityFilter` (`quality.py`) | Yes | Parallel Rayon Shannon entropy, repetition, and symbol ratio filter (`is_high_quality`, `metrics`, `filter_batch`). |
18
+ | `PyStringTensor` | `StringTensor` (`string_tensor.py`) | Yes | Dual-layout 16B CPU `StringView16` and contiguous GPU/Arrow buffer container. |
19
+ | `fused_pipeline_process_batch` | `FusedPipeline` (`fused.py`) | Yes | Single-pass Rayon + SIMD text cleaning, quality filtering, and deduplication returning `StringTensor`. |
20
+ | `serialize_table_to_bytes` | `serialize_to_*` (`serialization.py`) | Yes | Columnar serialization to Parquet (`PAR1`), Arrow IPC (`ARROW1`), and JSONL. |
21
+
22
+ ---
23
+
24
+ ## 2. Modular Pure-Python Fallback Layer (`python/luminamoon/data/fallbacks/`)
25
+
26
+ When running in environments without the compiled native binary, `_ffi.py` transparently falls back to the modular `fallbacks/` subpackage:
27
+
28
+ - `cleaner.py`, `quality.py`, `dedup.py`, `minhash.py`, `masker.py`, `coordinate.py`, `tracker.py`, `string_tensor.py`, `fused.py`, `serialization.py`, and `helpers.py`.
29
+ - Preserves API and binary format compatibility (including 32-byte `StreamCoordinate` and 128-byte IEEE CRC32 Ping-Pong `.meta` checkpoints).
docs/dataloader/simd_and_postprocess/README.md CHANGED
@@ -1,62 +1,62 @@
1
- # CPU SIMD Kernels & Rayon Normalization
2
-
3
- [`data/src/simd_cpu.rs`](https://github.com/cloverx-id/luminamoon/blob/main/data/src/simd_cpu.rs) and [`data/src/postprocess.rs`](https://github.com/cloverx-id/luminamoon/blob/main/data/src/postprocess.rs) provide vectorized byte scanning and multi-core text normalization without external crate dependencies.
4
-
5
- ---
6
-
7
- ## 1. Instruction Set Support & Vector Widths (`src/simd_cpu.rs`)
8
-
9
- `luminamoon-dataloader` detects host CPU capabilities at startup and caches the active `CpuIsa` in a lock-free `OnceLock<CpuIsa>`:
10
-
11
- | Architecture Target | Enum Variant | Registers | Bytes / Cycle | Intrinsics Used |
12
- | :--- | :--- | :--- | :--- | :--- |
13
- | **x86 / x86_64 AVX-512BW** | `CpuIsa::Avx512` | ZMM (`__m512i`) | 64 bytes | `_mm512_set1_epi8`, `_mm512_loadu_si512`, `_mm512_cmpeq_epi8_mask` |
14
- | **x86 / x86_64 AVX2** | `CpuIsa::Avx2` | YMM (`__m256i`) | 32 bytes | `_mm256_set1_epi8`, `_mm256_loadu_si256`, `_mm256_cmpeq_epi8`, `_mm256_movemask_epi8` |
15
- | **ARM64 NEON** | `CpuIsa::Neon` | V-Reg (`uint8x16_t`) | 16 bytes | `vdupq_n_u8`, `vld1q_u8`, `vceqq_u8`, `vmaxvq_u8` |
16
- | **Portable Fallback** | `CpuIsa::Scalar` | General Purpose | 1 byte | Iterator `position` / `rposition` |
17
-
18
- ---
19
-
20
- ## 2. Vectorized Byte Scanning Kernels
21
-
22
- 1. **`find_byte_fast(haystack: &[u8], needle: u8) -> Option<usize>`**:
23
- - Scans forward in 64-byte (`AVX-512BW`), 32-byte (`AVX2`), or 16-byte (`NEON`) unaligned chunks, locating the first matching byte offset via hardware bitmask `trailing_zeros()`.
24
- - Powers `find_newline_fast` for `.jsonl`/`.txt` line splitting and whitespace boundary detection.
25
- 2. **`rfind_byte_fast(haystack: &[u8], needle: u8) -> Option<usize>`**:
26
- - Scans backward from the end of a byte slice using `leading_zeros()` on the comparison mask.
27
- - Powers `rfind_newline_fast` to locate the exact last complete line boundary in a byte buffer in $O(1)$ vector steps.
28
- 3. **`contains_byte_fast(haystack: &[u8], needle: u8) -> bool`**:
29
- - Fast-path guard in `postprocess.rs` that skips string reallocation during CRLF (`\r\n`) normalization whenever a record contains no `\r` (`0x0D`) byte.
30
-
31
- ---
32
-
33
- ## 3. 64-Byte Cache-Line Aligned Buffer (`CacheAlignedBuffer`)
34
-
35
- ```rust
36
- #[repr(align(64))]
37
- #[derive(Debug, Clone)]
38
- pub struct CacheAlignedBuffer {
39
- pub data: Vec<u8>,
40
- }
41
- ```
42
-
43
- Rounds buffer capacities up to 64-byte cache-line multiples (`(cap + 63) & !63`) to avoid split-line penalties during SIMD scanning and batch serialization.
44
-
45
- ---
46
-
47
- ## 4. Parallel Rayon Normalization Pipeline (`src/postprocess.rs`)
48
-
49
- `TextCleaner` and `FusedPipeline` govern parallel per-record sanitization across raw text batches:
50
-
51
- ```mermaid
52
- flowchart LR
53
- Input["Raw Shard Bytes"] --> Scan["SIMD Newline Scan (AVX-512BW / AVX2 / NEON)"]
54
- Scan --> Extract["Rayon Parallel JSON Field Extraction"]
55
- Extract --> Clean["Fast CRLF Normalization & Whitespace Trim"]
56
- Clean --> Output["Valid Length: Normalized UTF-8 Batch"]
57
- Clean --> Truncate["Exceeds Max: Snap to UTF-8 Boundary"]
58
- Truncate --> Output
59
- Clean --> Drop["Below Min / Empty: Filter Out Record"]
60
- ```
61
-
62
- - **Order-Preserving Parallel Execution**: `process_batch_parallel_indexed` uses Rayon's indexed `par_iter().map(...)` to normalize batches across all CPU cores while preserving exact deterministic record ordering for `StreamCoordinate` progression.
 
1
+ # CPU SIMD Kernels & Rayon Normalization
2
+
3
+ [`data/src/simd_cpu.rs`](https://github.com/cloverx-id/luminamoon/blob/main/data/src/simd_cpu.rs) and [`data/src/postprocess.rs`](https://github.com/cloverx-id/luminamoon/blob/main/data/src/postprocess.rs) provide vectorized byte scanning and multi-core text normalization without external crate dependencies.
4
+
5
+ ---
6
+
7
+ ## 1. Instruction Set Support & Vector Widths (`src/simd_cpu.rs`)
8
+
9
+ `luminamoon-dataloader` detects host CPU capabilities at startup and caches the active `CpuIsa` in a lock-free `OnceLock<CpuIsa>`:
10
+
11
+ | Architecture Target | Enum Variant | Registers | Bytes / Cycle | Intrinsics Used |
12
+ | :--- | :--- | :--- | :--- | :--- |
13
+ | **x86 / x86_64 AVX-512BW** | `CpuIsa::Avx512` | ZMM (`__m512i`) | 64 bytes | `_mm512_set1_epi8`, `_mm512_loadu_si512`, `_mm512_cmpeq_epi8_mask` |
14
+ | **x86 / x86_64 AVX2** | `CpuIsa::Avx2` | YMM (`__m256i`) | 32 bytes | `_mm256_set1_epi8`, `_mm256_loadu_si256`, `_mm256_cmpeq_epi8`, `_mm256_movemask_epi8` |
15
+ | **ARM64 NEON** | `CpuIsa::Neon` | V-Reg (`uint8x16_t`) | 16 bytes | `vdupq_n_u8`, `vld1q_u8`, `vceqq_u8`, `vmaxvq_u8` |
16
+ | **Portable Fallback** | `CpuIsa::Scalar` | General Purpose | 1 byte | Iterator `position` / `rposition` |
17
+
18
+ ---
19
+
20
+ ## 2. Vectorized Byte Scanning Kernels
21
+
22
+ 1. **`find_byte_fast(haystack: &[u8], needle: u8) -> Option<usize>`**:
23
+ - Scans forward in 64-byte (`AVX-512BW`), 32-byte (`AVX2`), or 16-byte (`NEON`) unaligned chunks, locating the first matching byte offset via hardware bitmask `trailing_zeros()`.
24
+ - Powers `find_newline_fast` for `.jsonl`/`.txt` line splitting and whitespace boundary detection.
25
+ 2. **`rfind_byte_fast(haystack: &[u8], needle: u8) -> Option<usize>`**:
26
+ - Scans backward from the end of a byte slice using `leading_zeros()` on the comparison mask.
27
+ - Powers `rfind_newline_fast` to locate the exact last complete line boundary in a byte buffer in $O(1)$ vector steps.
28
+ 3. **`contains_byte_fast(haystack: &[u8], needle: u8) -> bool`**:
29
+ - Fast-path guard in `postprocess.rs` that skips string reallocation during CRLF (`\r\n`) normalization whenever a record contains no `\r` (`0x0D`) byte.
30
+
31
+ ---
32
+
33
+ ## 3. 64-Byte Cache-Line Aligned Buffer (`CacheAlignedBuffer`)
34
+
35
+ ```rust
36
+ #[repr(align(64))]
37
+ #[derive(Debug, Clone)]
38
+ pub struct CacheAlignedBuffer {
39
+ pub data: Vec<u8>,
40
+ }
41
+ ```
42
+
43
+ Rounds buffer capacities up to 64-byte cache-line multiples (`(cap + 63) & !63`) to avoid split-line penalties during SIMD scanning and batch serialization.
44
+
45
+ ---
46
+
47
+ ## 4. Parallel Rayon Normalization Pipeline (`src/postprocess.rs`)
48
+
49
+ `TextCleaner` and `FusedPipeline` govern parallel per-record sanitization across raw text batches:
50
+
51
+ ```mermaid
52
+ flowchart LR
53
+ Input["Raw Shard Bytes"] --> Scan["SIMD Newline Scan (AVX-512BW / AVX2 / NEON)"]
54
+ Scan --> Extract["Rayon Parallel JSON Field Extraction"]
55
+ Extract --> Clean["Fast CRLF Normalization & Whitespace Trim"]
56
+ Clean --> Output["Valid Length: Normalized UTF-8 Batch"]
57
+ Clean --> Truncate["Exceeds Max: Snap to UTF-8 Boundary"]
58
+ Truncate --> Output
59
+ Clean --> Drop["Below Min / Empty: Filter Out Record"]
60
+ ```
61
+
62
+ - **Order-Preserving Parallel Execution**: `process_batch_parallel_indexed` uses Rayon's indexed `par_iter().map(...)` to normalize batches across all CPU cores while preserving exact deterministic record ordering for `StreamCoordinate` progression.