mxframe 0.2.3__tar.gz → 0.5.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. mxframe-0.5.0/CHANGELOG.md +160 -0
  2. mxframe-0.5.0/PKG-INFO +188 -0
  3. mxframe-0.5.0/README.md +153 -0
  4. {mxframe-0.2.3 → mxframe-0.5.0}/__init__.py +35 -6
  5. {mxframe-0.2.3 → mxframe-0.5.0}/aot_kernels.py +271 -13
  6. {mxframe-0.2.3 → mxframe-0.5.0}/custom_ops.py +169 -46
  7. {mxframe-0.2.3 → mxframe-0.5.0}/docs/vision-and-architecture.md +17 -40
  8. mxframe-0.5.0/kernels.mojopkg +0 -0
  9. mxframe-0.5.0/kernels_aot/libmxkernels_aot_gpu.so +0 -0
  10. mxframe-0.5.0/mxframe.egg-info/PKG-INFO +188 -0
  11. {mxframe-0.2.3 → mxframe-0.5.0}/mxframe.egg-info/requires.txt +2 -0
  12. {mxframe-0.2.3 → mxframe-0.5.0}/pyproject.toml +65 -63
  13. mxframe-0.2.3/CHANGELOG.md +0 -178
  14. mxframe-0.2.3/PKG-INFO +0 -572
  15. mxframe-0.2.3/README.md +0 -539
  16. mxframe-0.2.3/kernels.mojopkg +0 -0
  17. mxframe-0.2.3/kernels_aot/libmxkernels_aot_gpu.so +0 -0
  18. mxframe-0.2.3/mxframe.egg-info/PKG-INFO +0 -572
  19. {mxframe-0.2.3 → mxframe-0.5.0}/MANIFEST.in +0 -0
  20. {mxframe-0.2.3 → mxframe-0.5.0}/_modidx.py +0 -0
  21. {mxframe-0.2.3 → mxframe-0.5.0}/compiler.py +0 -0
  22. {mxframe-0.2.3 → mxframe-0.5.0}/kernels_aot/__init__.py +0 -0
  23. {mxframe-0.2.3 → mxframe-0.5.0}/kernels_aot/libmxkernels_aot.so +0 -0
  24. {mxframe-0.2.3 → mxframe-0.5.0}/lazy_expr.py +0 -0
  25. {mxframe-0.2.3 → mxframe-0.5.0}/lazy_frame.py +0 -0
  26. {mxframe-0.2.3 → mxframe-0.5.0}/mxframe.egg-info/SOURCES.txt +0 -0
  27. {mxframe-0.2.3 → mxframe-0.5.0}/mxframe.egg-info/dependency_links.txt +0 -0
  28. {mxframe-0.2.3 → mxframe-0.5.0}/mxframe.egg-info/top_level.txt +0 -0
  29. {mxframe-0.2.3 → mxframe-0.5.0}/optimizer.py +0 -0
  30. {mxframe-0.2.3 → mxframe-0.5.0}/plan_validation.py +0 -0
  31. {mxframe-0.2.3 → mxframe-0.5.0}/setup.cfg +0 -0
  32. {mxframe-0.2.3 → mxframe-0.5.0}/setup.py +0 -0
  33. {mxframe-0.2.3 → mxframe-0.5.0}/sql_frontend.py +0 -0
@@ -0,0 +1,160 @@
1
+ # 📋 Changelog
2
+
3
+ All notable changes to MXFrame are documented here.
4
+ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
5
+
6
+ ---
7
+
8
+ ## [0.5.0] — 2026-07-11
9
+
10
+ Full narrative: [v0.5.0 release notes](RELEASE_NOTES_0.5.0.md).
11
+
12
+ ### ⚡ Native GPU UTF-8 predicates
13
+ - Added AOT `utf8_startswith_mask_gpu`, `utf8_contains_mask_gpu`, `utf8_equal_mask_gpu`, and packed-literal `utf8_isin_mask_gpu` kernels over Arrow validity, offsets, and byte buffers.
14
+ - Supports sliced arrays, nulls, empty strings/patterns, duplicate literals, and multibyte UTF-8 while matching Arrow filter semantics.
15
+ - GPU expression dispatch covers string `==`, `!=`, `startswith`, literal `contains`, and non-null literal `isin`; unsupported/null-membership cases retain Arrow fallback.
16
+ - Added permanent CPU/GPU Phase 6 tests and `pixi run bench-utf8` comparisons against PyArrow and Polars.
17
+ - At 1M rows, packed `isin` and `contains` beat local Arrow and Polars; exact equality remains faster in Polars' optimized CPU path, so no universal string-performance claim is made.
18
+
19
+ ### ⚡ Weak-query GPU fusion
20
+ - Q6 dispatches `range5_sum_product_f32_gpu` instead of materializing five masks and a product array.
21
+ - Q4 uses `semi_range_group_count_i32_gpu` for fused qualifying-key marking, semi-join probing, and grouped counts.
22
+ - Q13 uses `key_match_counts_i32_gpu` to emit one compact count per customer without materializing a left join.
23
+ - Q21 uses `single_late_supplier_counts_i32_gpu` for exact `(order, supplier)` deduplication and deterministic compact supplier counts.
24
+ - Representative 1M/10M GPU results versus Polars: Q4 **3.4/28.6 vs 20.2/105.4 ms**; Q13 **1.8/28.5 vs 26.5/442.0 ms**; Q21 **6.6/49.8 vs 30.2/99.1 ms**.
25
+
26
+ ### 📊 Bounded 22-query benchmark
27
+ - Every query and engine now runs in its own process, releasing Arrow, Pandas, and CUDA allocations between measurements.
28
+ - Added per-engine timeouts, five-query progress groups, merged CSV output, and optional synchronized `cudf.pandas` workers.
29
+ - MX CPU beat Polars on **22/22 at 1M** and **18/22 at 10M**.
30
+ - MX GPU beat Polars on **15/17 comparable paths at 1M** and **14/17 at 10M**.
31
+ - Q8 GPU remains `N/A:JIT`; Q15/Q17/Q18/Q20 GPU workers exceeded the bounded timeout.
32
+ - cuDF was unavailable locally, so RAPIDS remains `N/A` and no RAPIDS parity claim is made.
33
+
34
+ ### 🎬 Visualization and identity
35
+ - Q4, Q6, Q12, Q13, Q14, and Q21 visualize their logical rewrite, dispatch boundary, and step-by-step Mojo kernel algorithm.
36
+ - Added a cool-gray MX mark whose rising X stroke is shaped as orange lightning.
37
+ - Verified responsive rendering and zero horizontal overflow in Playwright.
38
+
39
+ ### Remaining performance-parity work
40
+ - Replace Q8's JIT path and fix Q15/Q17/Q18/Q20 GPU timeouts.
41
+ - Improve Q1 and Q14 GPU pipelines at 10M.
42
+ - Run synchronized 1M/10M benchmarks on a compatible RAPIDS/cuDF environment.
43
+
44
+ ### Remaining feature-parity work
45
+ - Native GPU UTF-8 dictionary construction and string/date gather.
46
+ - GPU windows and additional semi/anti/cross/as-of/range joins.
47
+ - Broader regex/string, datetime, nested-type, reshape, SQL, streaming, and I/O pushdown support.
48
+ - Cross-generation NVIDIA testing plus AMD and Apple Silicon validation.
49
+
50
+ ---
51
+
52
+ ## [0.4.0] — 2026-07-11
53
+
54
+ ### 🔗 String and composite joins
55
+ - Join keys from both tables now use one shared Arrow dictionary before dispatch, so equal strings receive identical dense `int32` IDs regardless of encounter order.
56
+ - Multi-column joins jointly densify shared per-column codes, avoiding mixed-radix overflow and supporting mixed string/integer composite keys.
57
+ - SQL null semantics are preserved: null keys receive side-specific IDs and never match, including left joins.
58
+ - Compact nonnegative integer joins retain their direct `int32` fast path; sparse and non-integer keys are safely densified.
59
+ - Fixed GPU upload-cache aliasing by retaining each cached NumPy host owner, preventing recycled host pointers from returning stale device buffers during joined-table gather.
60
+ - String encoding runs in Arrow on CPU; resulting IDs use the Mojo AOT CPU/GPU join kernels. Native GPU UTF-8 dictionary construction remains future work.
61
+
62
+ ---
63
+
64
+ ## [0.3.0] — 2026-07-10
65
+
66
+ ### 📚 Docs & Benchmarks
67
+ - README restructured: 543 lines → 119 lines. Intro, quick start, and 4-row teaser only — all detail moved to dedicated docs.
68
+ - New `docs/benchmarks.md`: full 22-query TPC-H tables (1 M + 10 M rows), Mojo kernel catalogue, current limitations, roadmap, and reproduce instructions.
69
+ - New `docs/api.md`: LazyFrame method reference, expression syntax, SQL frontend, supported operations, project structure.
70
+ - `docs/vision-and-architecture.md`: fixed garbled sections 2.5/2.6/3; success criteria table repaired; all milestones marked ✅.
71
+ - `docs/archive/` added to `.gitignore` — old dev notes moved there, not published.
72
+ - All stale `Mojo 0.26.2` / `MAX Graph shape-cached join` references updated to reflect v0.2.3 reality (Mojo 26.4, AOT ctypes everywhere).
73
+
74
+ ### 📊 Benchmark updates
75
+ - Q5/Q7/Q8/Q10/Q13/Q19 re-measured with per-run join-cache clearing (fresh-join). Previous v0.2.0 numbers for those queries were warm join-cache hits.
76
+ - Summary corrected: CPU 20/22 (Q13 loses fresh-join), GPU 12/22 (Q5/Q7 no longer win at 1 M without cache warmup).
77
+ - Q8 GPU marked `—` (MAX Graph `year()` groupby JIT too slow to benchmark; Phase 6 target).
78
+
79
+ ### 🔨 Infrastructure
80
+ - `modular >= 26.4` requirement added to `pyproject.toml` runtime deps and Dependencies table in README.
81
+ - `docs/archive/` gitignored; previously committed archive files removed from history.
82
+
83
+ ---
84
+
85
+ ## [0.2.3] — 2026-07-09
86
+
87
+ ### ⚡ Performance
88
+ - **Phase 4 — Device-resident GPU join pipeline** (`join_count_i32_gpu` + `join_scatter_i32_gpu`):
89
+ GPU inner joins now bypass MAX Graph JIT entirely. Cold-start join latency drops from ~300 ms (MAX session init + JIT) to ~70 ms. `AOTKernelsGPU.hash_join()` caches key arrays via `_cached_upload`; `AOTKernelsGPU.gather_table()` gathers all numeric columns with a single shared device-resident index upload.
90
+ - **Phase 5 — GPU sort + top-k** (`sort_topk_i32_gpu`): `ORDER BY … LIMIT k` fuses sort and limit in a single AOT bitonic sort kernel, downloading only `k × 4` bytes instead of `N × 4`. `_apply_post_ops_custom` peek-ahead passes `topk=k` directly to the sort kernel.
91
+ - **Phase 3 — Vectorised rank mapping**: `_encode_sort_key` replaces the per-unique-value Python `for` loop with a single vectorised NumPy scatter (`rank_of[sorted_order] = np.arange(n_unique)`). ~1.5× speedup for sort-key encoding at 5 M rows.
92
+
93
+ ### 🛠 Fixed
94
+ - **GPU sort+limit crash**: `_apply_sort_custom` was calling `_gpu_gather_table` which referenced `gather_f32` as a MAX Graph custom op — that kernel was never registered in the mojopkg. Any `ORDER BY` on `device="gpu"` would crash. Now routes through `_aot_gpu.gather_table` (AOT ctypes, no MAX Graph).
95
+ - `_apply_sort_custom` CPU path now uses `np.argpartition` (O(N) partial sort) when `topk < N`, instead of a full O(N log N) argsort.
96
+
97
+ ### 🔨 Infrastructure
98
+ - **Mojo/MAX 26.4 migration**: all `kernels/*.mojo` and `kernels_aot/*.mojo` migrated from 26.2 API (`fn` → `def`, `from tensor import` → `from extensibility import`, `DeviceContextPtr` → `DeviceContext`, `enqueue_function_experimental` → `enqueue_function`).
99
+ - **`kernels/_atomic.mojo`** (new): drop-in replacement for the missing `std.os.atomic.Atomic`. Implements `fetch_add`, `min`, `max`, `compare_exchange` via MLIR `llvm.atomicrmw` / `llvm.cmpxchg` instructions — portable across NVIDIA and AMD; works in both `mojo package` (JIT) and `mojo build --emit shared-lib` (AOT) contexts.
100
+ - All compiled artifacts rebuilt with Mojo 1.0.0b2 (modular 26.4): `kernels.mojopkg`, `libmxkernels_aot.so`, `libmxkernels_aot_gpu.so`.
101
+
102
+ ---
103
+
104
+ ## [0.2.2] — 2026-07-08
105
+
106
+ ### 🔨 Infrastructure
107
+ - First release targeting **Mojo/MAX 26.4** (`modular = "26.4.*"` in `pixi.toml`).
108
+ - **`kernels/_atomic.mojo`** shim introduced to replace the removed `std.os.atomic` module.
109
+ - All kernel sources migrated to 26.4 syntax; compiled artefacts rebuilt.
110
+ - `pyproject.toml` `runtime` extra pinned to `modular>=26.4` to prevent 26.2 wheels from being installed by Colab/pip users.
111
+
112
+ ---
113
+
114
+ ## [0.2.1] — 2026-06-xx
115
+
116
+ ### 🛠 Fixed
117
+ - TestPyPI re-upload to validate wheel packaging. No functional changes.
118
+
119
+ ---
120
+
121
+ ## [0.2.0] — 2026-05-xx
122
+
123
+ ### ✨ Added
124
+ - **Phase 1 — GPU buffer cache + masked global min/max**:
125
+ - `masked_global_min_f32_gpu` / `masked_global_max_f32_gpu` Mojo AOT kernels.
126
+ - `AOTKernelsGPU` device-buffer cache (`_cached_upload`, eviction, `clear_buf_cache`) — eliminates redundant PCIe uploads on repeated hot queries.
127
+ - `group_mean_f32` GPU kernel (sum + count on GPU, divide on CPU).
128
+ - **Phase 2 — Masked global aggregation for any expression**:
129
+ - `_compute_masked_global_agg` extended to handle any inner expression via `_eval_expr_arrow` (e.g. `a*(1-b)`, `case_when`, `startswith`, `isin`) — SUM reduction stays GPU-resident.
130
+ - Join-only plans emit `"join_mojo_shortcut"` provenance (not `"pyarrow_shortcut"`) — Q19 restructured to single-pass join+filter+global_agg.
131
+ - **Phase 3 — GPU integer key encoding**:
132
+ - `group_encode_i32_gpu` open-addressing hash-table kernel for integer group keys; replaces PyArrow `dictionary_encode` for integer-keyed group-by on GPU.
133
+ - **Audit tool** (`scripts/audit_gpu_paths.py`): classifies each of 22 TPC-H queries as `GPU-CLEAN` / `MOJO-CPU` / `FALLBACK` via `last_compile_provenance["path"]`.
134
+
135
+ ### ⚡ Performance
136
+ - **22/22 TPC-H queries GPU-CLEAN** (100% Mojo-backed on GPU, zero PyArrow fallbacks in the reduction and join hot paths).
137
+
138
+ ---
139
+
140
+ ## [0.1.2] — 2026-04-22
141
+
142
+ ### 🛠 Fixed
143
+ - `pip install "mxframe[runtime]"` now gives a working install. The `[runtime]` extra pulls `modular>=25.5`, which provides the `max.engine` / `max.driver` modules that `mxframe.compiler` imports at module load. (Pixi users get `modular` from the Modular conda channel and should install `mxframe` without the extra.)
144
+ - `sqlglot` import in `mxframe.sql_frontend` is now lazy: `import mxframe` no longer requires the `[sql]` extra. Calling `mxframe.sql(…)` without `sqlglot` installed raises a clear `ImportError` with the install hint.
145
+ - `__init__.py` `__version__` now matches `pyproject.toml`.
146
+
147
+ ---
148
+
149
+ ## [0.1.1] — 2026-04-22
150
+
151
+ ### 🛠 Fixed
152
+ - Packaging: `readme` now points to `README.md` so PyPI shows the full project page with benchmarks and quickstart (was accidentally showing the internal vision/architecture doc).
153
+ - CI: `AOTKernelsGPU` init no longer hard-fails on CPU-only runners (`CUDA driver unavailable` is caught, `self._aot_gpu` falls back to `None`).
154
+ - CPU-only import and compute path validated in a clean venv without `modular`.
155
+
156
+ ---
157
+
158
+ ## [0.1.0] — 2026-04-21
159
+
160
+ Initial public release.
mxframe-0.5.0/PKG-INFO ADDED
@@ -0,0 +1,188 @@
1
+ Metadata-Version: 2.4
2
+ Name: mxframe
3
+ Version: 0.5.0
4
+ Summary: GPU-accelerated DataFrame engine with Python ergonomics and Mojo AOT kernels.
5
+ Author-email: Abhishek Sreesaila <abhisheksreesaila@users.noreply.github.com>
6
+ License-Expression: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/abhisheksreesaila/mxframe
8
+ Project-URL: Repository, https://github.com/abhisheksreesaila/mxframe
9
+ Keywords: dataframe,gpu,mojo,arrow,analytics,tpch
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Topic :: Scientific/Engineering :: Information Analysis
16
+ Classifier: Topic :: Database
17
+ Requires-Python: >=3.12
18
+ Description-Content-Type: text/markdown
19
+ Requires-Dist: pyarrow>=14.0
20
+ Requires-Dist: numpy>=1.24
21
+ Requires-Dist: pandas>=2.0
22
+ Provides-Extra: runtime
23
+ Requires-Dist: modular>=26.4; extra == "runtime"
24
+ Provides-Extra: polars
25
+ Requires-Dist: polars>=0.20; extra == "polars"
26
+ Provides-Extra: sql
27
+ Requires-Dist: sqlglot>=25.0; extra == "sql"
28
+ Provides-Extra: dev
29
+ Requires-Dist: build>=1.2; extra == "dev"
30
+ Requires-Dist: pytest>=7.0; extra == "dev"
31
+ Requires-Dist: polars>=0.20; extra == "dev"
32
+ Requires-Dist: sqlglot>=25.0; extra == "dev"
33
+ Requires-Dist: duckdb>=0.9; extra == "dev"
34
+ Requires-Dist: twine>=6.0; extra == "dev"
35
+
36
+ <img src="visualize/mx-spark.svg" alt="MXFrame MX lightning mark" width="72">
37
+
38
+ # 🚀 MXFrame
39
+
40
+ > **GPU-accelerated DataFrames — Python ergonomics, Mojo AOT kernels.**
41
+
42
+ MXFrame is a DataFrame query engine that pairs a Polars-style Python API with pre-compiled Mojo AOT kernels. The current GPU build is validated on **NVIDIA**; AMD and Apple Silicon validation remain on the roadmap.
43
+
44
+ [![TPC-H](https://img.shields.io/badge/TPC--H-22%2F22%20queries-brightgreen)](docs/benchmarks.md)
45
+ [![Python](https://img.shields.io/badge/python-3.12%2B-blue)](pyproject.toml)
46
+ [![Platform](https://img.shields.io/badge/platform-linux--x86__64-lightgrey)](pyproject.toml)
47
+ [![License](https://img.shields.io/badge/license-Apache%202.0-blue)](LICENSE)
48
+
49
+ ---
50
+
51
+ ## ✨ Why MXFrame?
52
+
53
+ | | pandas | Polars | cuDF (RAPIDS) | **MXFrame** |
54
+ |---|---|---|---|---|
55
+ | GPU support | ❌ | ❌ | ✅ NVIDIA | ✅ **NVIDIA validated** |
56
+ | Compiled kernels | ❌ | ✅ Rust | ✅ CUDA | ✅ **Mojo AOT** |
57
+ | Install complexity | pip | pip | CUDA + RAPIDS stack | **pip or pixi** |
58
+ | TPC-H coverage | reference | ✅ | ✅ | ✅ **22/22** |
59
+ | Portable kernel design | ❌ | CPU | CUDA | ✅ NVIDIA today; AMD/Apple planned |
60
+
61
+ MXFrame uses a portable Mojo kernel architecture rather than CUDA-specific source. Kernels compile once to shared libraries and dispatch directly at runtime.
62
+
63
+ ### What changed in v0.4 and v0.5
64
+
65
+ - **v0.4.0 — correct string/composite joins:** shared dictionaries assign identical dense IDs across both join sides; mixed composite keys and SQL null semantics are preserved.
66
+ - **v0.5.0 — native GPU UTF-8 predicates:** `startswith`, literal `contains`, string equality/inequality, and packed non-null literal `isin` operate directly on Arrow offsets and byte buffers.
67
+ - **v0.5.0 — weak-query fusion:** dedicated compact GPU paths remove expanded intermediates from Q4, Q6, Q13, and Q21.
68
+ - **v0.5.0 — bounded benchmarking and visualization:** every query/engine runs in an isolated process; Q4, Q6, Q12, Q13, Q14, and Q21 have step-by-step kernel visualizations.
69
+
70
+ See [v0.5.0 release notes](RELEASE_NOTES_0.5.0.md) for measured gains, limitations, and the parity roadmap.
71
+
72
+ ---
73
+
74
+ ## ⚡ Quick Start
75
+
76
+ ```sh
77
+ # 1. Install pixi (Modular's package manager)
78
+ curl -fsSL https://pixi.sh/install.sh | bash
79
+
80
+ # 2. Clone and set up
81
+ git clone https://github.com/abhisheksreesaila/mxframe
82
+ cd mxframe
83
+ pixi install
84
+
85
+ # 3. Verify GPU is working
86
+ pixi run python3 scripts/_check_gpu.py
87
+ ```
88
+
89
+ ```python
90
+ import pyarrow as pa
91
+ from mxframe import LazyFrame, Scan, col, lit
92
+
93
+ data = pa.table({
94
+ "dept": pa.array(["eng", "eng", "mkt", "mkt", "eng"]),
95
+ "salary": pa.array([120.0, 95.0, 80.0, 110.0, 130.0], pa.float32()),
96
+ "age": pa.array([32, 28, 35, 29, 40], pa.int32()),
97
+ })
98
+
99
+ result = (
100
+ LazyFrame(Scan(data))
101
+ .filter(col("age") > lit(28))
102
+ .groupby("dept")
103
+ .agg(
104
+ col("salary").sum().alias("total_salary"),
105
+ col("age").count().alias("headcount"),
106
+ )
107
+ .sort(col("total_salary"), descending=True)
108
+ .compute(device="gpu") # or "cpu"
109
+ )
110
+ print(result.to_pandas())
111
+ ```
112
+
113
+ ```
114
+ dept total_salary headcount
115
+ 0 eng 345.0 3
116
+ 1 mkt 110.0 1
117
+ ```
118
+
119
+ ---
120
+
121
+ ## 📊 Performance — bounded v0.5.0 benchmark
122
+
123
+ RTX 3090, warm median of three runs. Every query and engine ran in its own process so Arrow, Pandas, and CUDA allocations were released between measurements.
124
+
125
+ - **1M:** MX CPU beat Polars on **22/22**; MX GPU beat Polars on **15/17 comparable** paths.
126
+ - **10M:** MX CPU beat Polars on **18/22**; MX GPU beat Polars on **14/17 comparable** paths.
127
+ - Q8 GPU remains `N/A:JIT`; Q15, Q17, Q18, and Q20 GPU workers exceeded the bounded timeout.
128
+ - cuDF was unavailable on this machine, so RAPIDS remains `N/A` and no RAPIDS parity claim is made.
129
+
130
+ ### 1M representative queries
131
+
132
+ | Query | MX CPU | MX GPU | Polars | Best MX speedup |
133
+ |---|---:|---:|---:|---:|
134
+ | Q4 · Order priority | 12.8 ms | **3.4 ms** | 20.2 ms | **5.9× GPU** |
135
+ | Q6 · Discounted revenue | **6.7 ms** | 6.8 ms | 10.8 ms | **1.6× CPU** |
136
+ | Q12 · Shipping modes | **0.6 ms** | 6.1 ms | 26.6 ms | **44.3× CPU** |
137
+ | Q13 · Customer distribution | 20.4 ms | **1.8 ms** | 26.5 ms | **14.7× GPU** |
138
+ | Q21 · Waiting suppliers | 27.1 ms | **6.6 ms** | 30.2 ms | **4.6× GPU** |
139
+
140
+ ### 10M representative queries
141
+
142
+ | Query | MX CPU | MX GPU | Polars | Best MX speedup |
143
+ |---|---:|---:|---:|---:|
144
+ | Q4 · Order priority | 265.1 ms | **28.6 ms** | 105.4 ms | **3.7× GPU** |
145
+ | Q9 · Product profit | **2.0 ms** | 16.5 ms | 109.7 ms | **54.9× CPU** |
146
+ | Q12 · Shipping modes | **1.5 ms** | 8.2 ms | 128.0 ms | **85.3× CPU** |
147
+ | Q13 · Customer distribution | 368.8 ms | **28.5 ms** | 442.0 ms | **15.5× GPU** |
148
+ | Q21 · Waiting suppliers | 1068.7 ms | **49.8 ms** | 99.1 ms | **2.0× GPU** |
149
+
150
+ Full matrices: [1M CSV](scripts/bench_results_1M.csv) · [10M CSV](scripts/bench_results_10M.csv) · [methodology and kernel catalogue](docs/benchmarks.md)
151
+
152
+ ## What remains for parity
153
+
154
+ **Performance parity:** replace Q8's JIT path; fix Q15/Q17/Q18/Q20 GPU timeouts; improve Q1 and Q14 GPU execution; run synchronized 1M/10M measurements on a compatible RAPIDS/cuDF environment.
155
+
156
+ **Feature parity:** native GPU UTF-8 dictionary construction and string/date gather; GPU windows; semi/anti/as-of/range joins; broader string/regex, datetime, nested-type, reshape, SQL, streaming, and I/O pushdown coverage.
157
+
158
+ ---
159
+
160
+ ## 📚 Docs
161
+
162
+ | | |
163
+ |---|---|
164
+ | [**v0.5.0 Release Notes**](RELEASE_NOTES_0.5.0.md) | v0.4/v0.5 gains, benchmark evidence, known gaps, and parity roadmap |
165
+ | [**API Reference**](docs/api.md) | LazyFrame, expressions, SQL frontend, supported operations, running tests |
166
+ | [**Benchmarks**](docs/benchmarks.md) | Full TPC-H tables, kernel catalogue, limitations, roadmap, reproduce instructions |
167
+ | [**Architecture**](docs/vision-and-architecture.md) | Design philosophy, internal layers, how MAX Graph fits in |
168
+ | [**Kernel Visualizer**](visualize/mxframe_pipeline.html) | Interactive plans, dispatch boundaries, GPU threads, and output masks |
169
+ | [**Contributing**](CONTRIBUTING.md) | Dev setup, writing Mojo kernels, adding queries |
170
+
171
+ ---
172
+
173
+ ## 📦 Dependencies
174
+
175
+ | Package | Required | Purpose |
176
+ |---|---|---|
177
+ | `pyarrow >= 14` | ✅ | Column storage, zero-copy NumPy bridge |
178
+ | `numpy >= 1.24` | ✅ | Vectorized pre/post processing |
179
+ | `pandas >= 2.0` | ✅ | Reference implementations |
180
+ | `modular >= 26.4` | GPU only | MAX Engine runtime, Mojo GPU dispatch |
181
+ | `polars >= 0.20` | optional | Benchmark comparison |
182
+ | `sqlglot >= 25` | optional | SQL frontend parsing |
183
+
184
+ ---
185
+
186
+ ## 📄 License
187
+
188
+ Apache 2.0 — see [LICENSE](LICENSE).
@@ -0,0 +1,153 @@
1
+ <img src="visualize/mx-spark.svg" alt="MXFrame MX lightning mark" width="72">
2
+
3
+ # 🚀 MXFrame
4
+
5
+ > **GPU-accelerated DataFrames — Python ergonomics, Mojo AOT kernels.**
6
+
7
+ MXFrame is a DataFrame query engine that pairs a Polars-style Python API with pre-compiled Mojo AOT kernels. The current GPU build is validated on **NVIDIA**; AMD and Apple Silicon validation remain on the roadmap.
8
+
9
+ [![TPC-H](https://img.shields.io/badge/TPC--H-22%2F22%20queries-brightgreen)](docs/benchmarks.md)
10
+ [![Python](https://img.shields.io/badge/python-3.12%2B-blue)](pyproject.toml)
11
+ [![Platform](https://img.shields.io/badge/platform-linux--x86__64-lightgrey)](pyproject.toml)
12
+ [![License](https://img.shields.io/badge/license-Apache%202.0-blue)](LICENSE)
13
+
14
+ ---
15
+
16
+ ## ✨ Why MXFrame?
17
+
18
+ | | pandas | Polars | cuDF (RAPIDS) | **MXFrame** |
19
+ |---|---|---|---|---|
20
+ | GPU support | ❌ | ❌ | ✅ NVIDIA | ✅ **NVIDIA validated** |
21
+ | Compiled kernels | ❌ | ✅ Rust | ✅ CUDA | ✅ **Mojo AOT** |
22
+ | Install complexity | pip | pip | CUDA + RAPIDS stack | **pip or pixi** |
23
+ | TPC-H coverage | reference | ✅ | ✅ | ✅ **22/22** |
24
+ | Portable kernel design | ❌ | CPU | CUDA | ✅ NVIDIA today; AMD/Apple planned |
25
+
26
+ MXFrame uses a portable Mojo kernel architecture rather than CUDA-specific source. Kernels compile once to shared libraries and dispatch directly at runtime.
27
+
28
+ ### What changed in v0.4 and v0.5
29
+
30
+ - **v0.4.0 — correct string/composite joins:** shared dictionaries assign identical dense IDs across both join sides; mixed composite keys and SQL null semantics are preserved.
31
+ - **v0.5.0 — native GPU UTF-8 predicates:** `startswith`, literal `contains`, string equality/inequality, and packed non-null literal `isin` operate directly on Arrow offsets and byte buffers.
32
+ - **v0.5.0 — weak-query fusion:** dedicated compact GPU paths remove expanded intermediates from Q4, Q6, Q13, and Q21.
33
+ - **v0.5.0 — bounded benchmarking and visualization:** every query/engine runs in an isolated process; Q4, Q6, Q12, Q13, Q14, and Q21 have step-by-step kernel visualizations.
34
+
35
+ See [v0.5.0 release notes](RELEASE_NOTES_0.5.0.md) for measured gains, limitations, and the parity roadmap.
36
+
37
+ ---
38
+
39
+ ## ⚡ Quick Start
40
+
41
+ ```sh
42
+ # 1. Install pixi (Modular's package manager)
43
+ curl -fsSL https://pixi.sh/install.sh | bash
44
+
45
+ # 2. Clone and set up
46
+ git clone https://github.com/abhisheksreesaila/mxframe
47
+ cd mxframe
48
+ pixi install
49
+
50
+ # 3. Verify GPU is working
51
+ pixi run python3 scripts/_check_gpu.py
52
+ ```
53
+
54
+ ```python
55
+ import pyarrow as pa
56
+ from mxframe import LazyFrame, Scan, col, lit
57
+
58
+ data = pa.table({
59
+ "dept": pa.array(["eng", "eng", "mkt", "mkt", "eng"]),
60
+ "salary": pa.array([120.0, 95.0, 80.0, 110.0, 130.0], pa.float32()),
61
+ "age": pa.array([32, 28, 35, 29, 40], pa.int32()),
62
+ })
63
+
64
+ result = (
65
+ LazyFrame(Scan(data))
66
+ .filter(col("age") > lit(28))
67
+ .groupby("dept")
68
+ .agg(
69
+ col("salary").sum().alias("total_salary"),
70
+ col("age").count().alias("headcount"),
71
+ )
72
+ .sort(col("total_salary"), descending=True)
73
+ .compute(device="gpu") # or "cpu"
74
+ )
75
+ print(result.to_pandas())
76
+ ```
77
+
78
+ ```
79
+ dept total_salary headcount
80
+ 0 eng 345.0 3
81
+ 1 mkt 110.0 1
82
+ ```
83
+
84
+ ---
85
+
86
+ ## 📊 Performance — bounded v0.5.0 benchmark
87
+
88
+ RTX 3090, warm median of three runs. Every query and engine ran in its own process so Arrow, Pandas, and CUDA allocations were released between measurements.
89
+
90
+ - **1M:** MX CPU beat Polars on **22/22**; MX GPU beat Polars on **15/17 comparable** paths.
91
+ - **10M:** MX CPU beat Polars on **18/22**; MX GPU beat Polars on **14/17 comparable** paths.
92
+ - Q8 GPU remains `N/A:JIT`; Q15, Q17, Q18, and Q20 GPU workers exceeded the bounded timeout.
93
+ - cuDF was unavailable on this machine, so RAPIDS remains `N/A` and no RAPIDS parity claim is made.
94
+
95
+ ### 1M representative queries
96
+
97
+ | Query | MX CPU | MX GPU | Polars | Best MX speedup |
98
+ |---|---:|---:|---:|---:|
99
+ | Q4 · Order priority | 12.8 ms | **3.4 ms** | 20.2 ms | **5.9× GPU** |
100
+ | Q6 · Discounted revenue | **6.7 ms** | 6.8 ms | 10.8 ms | **1.6× CPU** |
101
+ | Q12 · Shipping modes | **0.6 ms** | 6.1 ms | 26.6 ms | **44.3× CPU** |
102
+ | Q13 · Customer distribution | 20.4 ms | **1.8 ms** | 26.5 ms | **14.7× GPU** |
103
+ | Q21 · Waiting suppliers | 27.1 ms | **6.6 ms** | 30.2 ms | **4.6× GPU** |
104
+
105
+ ### 10M representative queries
106
+
107
+ | Query | MX CPU | MX GPU | Polars | Best MX speedup |
108
+ |---|---:|---:|---:|---:|
109
+ | Q4 · Order priority | 265.1 ms | **28.6 ms** | 105.4 ms | **3.7× GPU** |
110
+ | Q9 · Product profit | **2.0 ms** | 16.5 ms | 109.7 ms | **54.9× CPU** |
111
+ | Q12 · Shipping modes | **1.5 ms** | 8.2 ms | 128.0 ms | **85.3× CPU** |
112
+ | Q13 · Customer distribution | 368.8 ms | **28.5 ms** | 442.0 ms | **15.5× GPU** |
113
+ | Q21 · Waiting suppliers | 1068.7 ms | **49.8 ms** | 99.1 ms | **2.0× GPU** |
114
+
115
+ Full matrices: [1M CSV](scripts/bench_results_1M.csv) · [10M CSV](scripts/bench_results_10M.csv) · [methodology and kernel catalogue](docs/benchmarks.md)
116
+
117
+ ## What remains for parity
118
+
119
+ **Performance parity:** replace Q8's JIT path; fix Q15/Q17/Q18/Q20 GPU timeouts; improve Q1 and Q14 GPU execution; run synchronized 1M/10M measurements on a compatible RAPIDS/cuDF environment.
120
+
121
+ **Feature parity:** native GPU UTF-8 dictionary construction and string/date gather; GPU windows; semi/anti/as-of/range joins; broader string/regex, datetime, nested-type, reshape, SQL, streaming, and I/O pushdown coverage.
122
+
123
+ ---
124
+
125
+ ## 📚 Docs
126
+
127
+ | | |
128
+ |---|---|
129
+ | [**v0.5.0 Release Notes**](RELEASE_NOTES_0.5.0.md) | v0.4/v0.5 gains, benchmark evidence, known gaps, and parity roadmap |
130
+ | [**API Reference**](docs/api.md) | LazyFrame, expressions, SQL frontend, supported operations, running tests |
131
+ | [**Benchmarks**](docs/benchmarks.md) | Full TPC-H tables, kernel catalogue, limitations, roadmap, reproduce instructions |
132
+ | [**Architecture**](docs/vision-and-architecture.md) | Design philosophy, internal layers, how MAX Graph fits in |
133
+ | [**Kernel Visualizer**](visualize/mxframe_pipeline.html) | Interactive plans, dispatch boundaries, GPU threads, and output masks |
134
+ | [**Contributing**](CONTRIBUTING.md) | Dev setup, writing Mojo kernels, adding queries |
135
+
136
+ ---
137
+
138
+ ## 📦 Dependencies
139
+
140
+ | Package | Required | Purpose |
141
+ |---|---|---|
142
+ | `pyarrow >= 14` | ✅ | Column storage, zero-copy NumPy bridge |
143
+ | `numpy >= 1.24` | ✅ | Vectorized pre/post processing |
144
+ | `pandas >= 2.0` | ✅ | Reference implementations |
145
+ | `modular >= 26.4` | GPU only | MAX Engine runtime, Mojo GPU dispatch |
146
+ | `polars >= 0.20` | optional | Benchmark comparison |
147
+ | `sqlglot >= 25` | optional | SQL frontend parsing |
148
+
149
+ ---
150
+
151
+ ## 📄 License
152
+
153
+ Apache 2.0 — see [LICENSE](LICENSE).
@@ -1,6 +1,6 @@
1
1
  """mxframe - GPU-accelerated DataFrames with MAX Engine"""
2
2
 
3
- __version__ = "0.2.3"
3
+ __version__ = "0.5.0"
4
4
 
5
5
  # Lazy expression layer
6
6
  from .lazy_expr import Expr, col, lit, when, row_number
@@ -13,17 +13,41 @@ from .lazy_frame import (
13
13
  DeviceType,
14
14
  )
15
15
 
16
- # Compiler
17
- from .compiler import GraphCompiler
18
-
19
16
  # Optimizer
20
17
  from .optimizer import PlanOptimizer, OptimizationResult, optimize_plan
21
18
 
22
19
  # Plan validation
23
20
  from .plan_validation import PlanValidationError, validate_plan, validate_plan_or_raise
24
21
 
25
- # Custom ops compiler
26
- from .custom_ops import CustomOpsCompiler, KERNELS_PATH, clear_cache
22
+ _RUNTIME_EXPORTS = {"GraphCompiler", "CustomOpsCompiler", "KERNELS_PATH"}
23
+
24
+
25
+ def _runtime_import_error() -> ImportError:
26
+ return ImportError(
27
+ "MXFrame computation requires the Modular MAX runtime. "
28
+ "Install with: pip install 'mxframe[runtime]'"
29
+ )
30
+
31
+
32
+ def __getattr__(name: str):
33
+ if name not in _RUNTIME_EXPORTS:
34
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
35
+ try:
36
+ if name == "GraphCompiler":
37
+ from .compiler import GraphCompiler
38
+ return GraphCompiler
39
+ from .custom_ops import CustomOpsCompiler, KERNELS_PATH
40
+ return CustomOpsCompiler if name == "CustomOpsCompiler" else KERNELS_PATH
41
+ except ImportError as exc:
42
+ raise _runtime_import_error() from exc
43
+
44
+
45
+ def clear_cache() -> None:
46
+ try:
47
+ from .custom_ops import clear_cache as _clear_cache
48
+ except ImportError as exc:
49
+ raise _runtime_import_error() from exc
50
+ _clear_cache()
27
51
 
28
52
  # SQL frontend (optional; requires `sqlglot`).
29
53
  # Imported lazily so `pip install mxframe` works without the sql extra.
@@ -59,6 +83,11 @@ def warmup(device: str = "auto") -> float:
59
83
  import pyarrow as _pa
60
84
  import numpy as _np
61
85
 
86
+ try:
87
+ from .custom_ops import CustomOpsCompiler
88
+ except ImportError as exc:
89
+ raise _runtime_import_error() from exc
90
+
62
91
  t0 = _t.perf_counter()
63
92
 
64
93
  # Step 1: session init (cached -- effectively free on repeat calls)