Skip to content

Commit 16f3d09

Browse files
authored
Add MPS (Apple Silicon) device support (#7)
* detect current device and be applicable for mps (apple silicon) * calculate NLL with cpu even user specify for using mps * send tensors to cpu at the begging of the run_gtpq * debug for aligning v1.1.0 release * raise error if quantizer is not gptq * fix settings and readme * bugfix: inference * remove unused import * fix: comment No3, 4, and 5 * fix: Comment No.7,8, and, 12 * add new test file for comment No.12 * fix: Comment No6 and 10 * fix Comment No2 * fix: comment No1 * fix No.9; add docs * update lock file * fix conflict * fix conflict in CHANGELOG * add docs for mps
1 parent 1fc5536 commit 16f3d09

30 files changed

Lines changed: 3380 additions & 1568 deletions

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# Change log
22

3+
## [v1.2.0+mps] 2026-06-05
4+
5+
### Apple Silicon / macOS support
6+
7+
- **MPS quantization**: GPTQ (and AutoBit with GPTQ-only candidates) on `device="mps"`; cross-platform `empty_cache()` via new `onecomp/utils/device.py` (`runner.py`, `quantizer/gptq/_gptq.py`, `quantizer/_quantizer.py`)
8+
- **MPS device placement (GPTQ on CPU, QEP correction on MPS)**: With `device="mps"`, `run_gptq` moves the Hessian and weights to **CPU** for the full column-wise GPTQ loop (including inverse-Hessian Cholesky). The main reason is not absent Cholesky kernels on MPS (recent PyTorch supports them); if the GPTQ loop stayed on MPS, `maxq.item()` inside `quantize()` would run once per column—each call waits for pending MPS work to finish and read back a single scalar to the host (per-column host sync), not a full matrix copy per column—and that overhead is often several times slower than CPU on Apple Silicon (~4× in internal benchmarks with PyTorch 2.12). When QEP weight correction runs (`adjust_weight`, typically under `qep=True`), per-layer work stays on **MPS** (e.g. `weight @ delta_hatX`); only the Cholesky solve uses CPU via `_safe_cholesky_and_solve` (one solve per layer). A full CPU fallback for QEP does not materially improve speed. Calibration forwards may still use MPS. Details: README (macOS / MPS).
9+
- **MPS inference**: load saved quantized models on Mac with `QuantizedModelLoader` + Transformers `generate()` (GemLite/vLLM remain Linux + CUDA)
10+
- **macOS `uv sync`**: added `darwin` to `tool.uv.environments`, `--extra mps` for MPS-enabled PyTorch from PyPI; `--extra cpu` is Linux-only (pytorch-cpu index); Linux-only markers on CUDA extras (`cu118``cu130`)
311

412
## [v1.2.0] 2026-06-04 (WIP)
513

README.md

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,30 @@ import torch
100100
print(torch.cuda.is_available())
101101
```
102102

103+
#### ✅ macOS (MPS)
104+
105+
On macOS, install PyTorch from PyPI (default wheels include MPS support). You do **not** need the CUDA index URLs above.
106+
107+
```bash
108+
pip install torch torchvision torchaudio
109+
```
110+
111+
Verify MPS:
112+
```python
113+
import torch
114+
print(torch.backends.mps.is_available())
115+
```
116+
117+
Then install OneComp from PyPI (see step 2 below). GPTQ quantization and Hugging Face `generate()` inference on MPS are supported; vLLM serving requires Linux with an NVIDIA GPU. An editable install from a git clone is **not** required for MPS use — see [for developers (pip)](#for-developers-pip) only if you are contributing to OneComp.
118+
119+
> **MPS device placement (GPTQ vs QEP)**
120+
> With `device="mps"`, calibration and model forward passes can run on the GPU. The bottleneck is usually not missing Cholesky ops on MPS (recent PyTorch builds implement them); the implementation splits work as follows:
121+
>
122+
> - GPTQ (`run_gptq`): Hessian and weights are moved to CPU for the full column-wise loop (including inverse-Hessian Cholesky). If that loop stayed on MPS, `quantize()` would call `maxq.item()` once per column; each call triggers **per-column host sync** (wait for pending MPS ops, then read one scalar—not a full Hessian/weight copy every column)—often several times slower than CPU on Apple Silicon (e.g. ~4× in internal benchmarks with PyTorch 2.12). Keeping GPTQ on CPU avoids that overhead. With `mse=True`, `find_params` also calls `quantize()` in a grid loop and benefits from the same CPU placement.
123+
> - QEP weight correction (`adjust_weight`, when QEP correction runs—typically `qep=True` with error propagation enabled): Per-layer work stays on MPS (e.g. `weight @ delta_hatX`, diagonal damping). Only the Cholesky solve uses CPU via `_safe_cholesky_and_solve` (one solve per layer, not per column); moving all of QEP to CPU does not materially improve speed. The subsequent GPTQ step still uses the CPU path above.
124+
>
125+
> DBF-based AutoBit fallback and multi-GPU quantization are not supported on MPS.
126+
103127
#### 2. Install `onecomp`
104128

105129
Once PyTorch is installed, you can install `onecomp`:
@@ -122,26 +146,41 @@ curl -LsSf https://astral.sh/uv/install.sh | sh
122146

123147
git clone https://github.com/FujitsuResearch/OneCompression.git
124148
cd OneCompression
125-
uv sync --extra cu128 --extra dev --extra visualize
126149
```
127150

128151
The `uv sync` command creates a Python virtual environment and installs all dependent libraries.
129152

153+
#### Linux (CUDA quantization / vLLM)
154+
155+
```bash
156+
uv sync --extra cu128 --extra dev --extra visualize
157+
```
158+
130159
The `--extra cu128` option installs the CUDA-enabled version of PyTorch (along with `torchvision` from the same CUDA index).
131160
Replace `cu128` with the appropriate variant for your environment: `cpu`, `cu118`, `cu121`, `cu124`, `cu126`, `cu128`, or `cu130`.
132161
PyTorch will be automatically downloaded by `uv`, so you do not need to install it beforehand.
133162

163+
#### macOS (development / MPS inference)
164+
165+
```bash
166+
uv sync --extra mps --extra dev --extra visualize
167+
```
168+
169+
On macOS, use `--extra mps` only. CUDA extras (`cu118``cu130`), `--extra cpu` (Linux-only), and `--extra vllm` are not supported on macOS.
170+
After `uv sync`, you can run GPTQ quantization and Hugging Face `generate()` inference on MPS; vLLM serving still requires Linux with an NVIDIA GPU.
171+
See the **MPS device placement (GPTQ vs QEP)** note under [macOS (MPS)](#macos-mps) above for why GPTQ runs on CPU while QEP correction uses MPS.
172+
134173
Adding `--extra dev` installs development tools (black, pre-commit, pytest, pylint).
135174
Adding `--extra visualize` installs matplotlib for visualization features.
136175
Adding `--extra hydra` installs `hydra-core` for the example scripts and `model_validation/` runners that use Hydra-based configuration.
137176

138-
To use vLLM for serving quantized models, add `--extra vllm` together with `--extra cu130`:
177+
To use vLLM for serving quantized models on Linux, add `--extra vllm` together with `--extra cu130`:
139178

140179
```bash
141180
uv sync --extra cu130 --extra dev --extra visualize --extra vllm
142181
```
143182

144-
> **Note:** `--extra vllm` is only compatible with `--extra cu130`. Recent vLLM releases require `torch>=2.10`, whose wheels are only published for the `cu130` index. Combining `--extra vllm` with `cpu` / `cu118` / `cu121` / `cu124` / `cu126` / `cu128` is rejected by `uv` at lock time.
183+
> **Note:** `--extra vllm` is only compatible with `--extra cu130`. Recent vLLM releases require `torch>=2.10`, whose wheels are only published for the `cu130` index. Combining `--extra vllm` with `cpu` / `mps` / `cu118` / `cu121` / `cu124` / `cu126` / `cu128` is rejected by `uv` at lock time.
145184
146185
> **Note:** `--extra vllm` may take a long time on the first run if a pre-built `xformers` wheel is not available for your Python/CUDA combination (e.g. Python 3.13). Using Python 3.12 typically avoids this.
147186
@@ -168,6 +207,8 @@ black --check onecomp/
168207

169208
### for developers (pip)
170209

210+
> **Note:** The editable install below is for developing OneComp from a local clone. **macOS users who only want MPS inference or quantization should use the [for users (pip)](#for-users-pip) flow** (`pip install torch` then `pip install onecomp` from PyPI); `pip install -e` is not needed for MPS.
211+
171212
```bash
172213
git clone <git repository URL>
173214
cd OneCompression
@@ -217,8 +258,10 @@ uv run pre-commit run --all-files
217258

218259
### Building Documentation Locally
219260

261+
`--extra docs` alone is sufficient (no PyTorch `mps` / `cu*` extra required):
262+
220263
```bash
221-
uv sync --extra cu128 --extra dev --extra docs
264+
uv sync --extra docs
222265
uv run mkdocs serve
223266
```
224267

docs/api/quantized_model_loader.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
Loader for quantized models saved by OneComp.
44

5+
On macOS, `load_quantized_model()` places the model on MPS when available
6+
(CUDA > MPS > CPU via `get_default_device()`). Use Transformers `generate()` for
7+
inference; vLLM requires Linux with an NVIDIA GPU. See the
8+
[macOS / MPS guide](../user-guide/mps.md#inference-with-transformers).
9+
510
::: onecomp.quantized_model_loader.QuantizedModelLoader
611
options:
712
show_source: false

docs/getting-started/installation.md

Lines changed: 66 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ This page describes how to install Fujitsu One Compression (OneComp).
55
## Requirements
66

77
- Python 3.12 or later (< 3.14)
8-
- PyTorch (CPU or CUDA)
8+
- PyTorch (CPU, CUDA, or MPS on macOS)
99

1010
## For Users (pip)
1111

@@ -55,15 +55,40 @@ Install the appropriate version of PyTorch for your system.
5555
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu130
5656
```
5757

58-
Check your CUDA version:
58+
=== "macOS (MPS)"
59+
60+
On macOS, install PyTorch from PyPI (default wheels include MPS support).
61+
You do **not** need the CUDA index URLs above.
62+
63+
```bash
64+
pip install torch torchvision torchaudio
65+
```
66+
67+
Verify MPS:
68+
69+
```python
70+
import torch
71+
print(torch.backends.mps.is_available())
72+
```
73+
74+
Then install OneComp (step 2 below). GPTQ quantization and Hugging Face
75+
`generate()` inference on MPS are supported; vLLM serving requires Linux with
76+
an NVIDIA GPU. An editable install from a git clone is **not** required for
77+
MPS use — see [For Developers (pip)](#for-developers-pip) only if you are
78+
contributing to OneComp.
79+
80+
For usage (`device="mps"`, VRAM budget, limitations), see the
81+
[macOS / MPS guide](../user-guide/mps.md).
82+
83+
Check your CUDA version (Linux / Windows with NVIDIA GPU):
5984

6085
```bash
6186
nvcc --version
6287
# or
6388
nvidia-smi
6489
```
6590

66-
Verify PyTorch GPU support:
91+
Verify PyTorch GPU support (CUDA):
6792

6893
```python
6994
import torch
@@ -91,26 +116,45 @@ It provides deterministic, reproducible environments via its lockfile.
91116
# Install uv (macOS or Linux)
92117
curl -LsSf https://astral.sh/uv/install.sh | sh
93118

94-
# Clone and set up
95119
git clone https://github.com/FujitsuResearch/OneCompression.git
96120
cd OneCompression
121+
```
122+
123+
The `uv sync` command creates a virtual environment and installs all dependencies.
124+
125+
### Linux (CUDA quantization / vLLM)
126+
127+
```bash
97128
uv sync --extra cu128 --extra dev --extra visualize
98129
```
99130

100-
The `uv sync` command creates a virtual environment and installs all dependencies (including `torchvision` from the same CUDA index as PyTorch).
101-
Replace `cu128` with the appropriate CUDA variant for your system: `cpu`, `cu118`, `cu121`, `cu124`, `cu126`, `cu128`, or `cu130`.
131+
The `--extra cu128` option installs the CUDA-enabled version of PyTorch (along with `torchvision` from the same CUDA index).
132+
Replace `cu128` with the appropriate variant for your environment: `cpu`, `cu118`, `cu121`, `cu124`, `cu126`, `cu128`, or `cu130`.
133+
PyTorch will be automatically downloaded by `uv`, so you do not need to install it beforehand.
134+
135+
### macOS (development / MPS inference)
136+
137+
```bash
138+
uv sync --extra mps --extra dev --extra visualize
139+
```
140+
141+
On macOS, use `--extra mps` only. CUDA extras (`cu118``cu130`), `--extra cpu` (Linux-only),
142+
and `--extra vllm` are not supported on macOS.
143+
After `uv sync`, you can run GPTQ quantization and Hugging Face `generate()` inference on MPS;
144+
vLLM serving still requires Linux with an NVIDIA GPU.
145+
See the [macOS / MPS guide](../user-guide/mps.md) for device placement and usage details.
102146

103147
Adding `--extra dev` installs development tools (black, pytest, pylint).
104148
Adding `--extra visualize` installs matplotlib for visualization features.
105149

106-
To use vLLM for serving quantized models, add `--extra vllm` together with `--extra cu130`:
150+
To use vLLM for serving quantized models on Linux, add `--extra vllm` together with `--extra cu130`:
107151

108152
```bash
109153
uv sync --extra cu130 --extra dev --extra visualize --extra vllm
110154
```
111155

112156
!!! note "vLLM requires the `cu130` extra"
113-
Recent vLLM releases depend on `torch>=2.10`, whose wheels are only published for the `cu130` index. The `--extra vllm` declaration in `pyproject.toml` therefore conflicts with `cpu`, `cu118`, `cu121`, `cu124`, `cu126`, and `cu128`; combining any of these with `--extra vllm` is rejected by `uv` at lock time.
157+
Recent vLLM releases depend on `torch>=2.10`, whose wheels are only published for the `cu130` index. The `--extra vllm` declaration in `pyproject.toml` therefore conflicts with `cpu`, `mps`, `cu118`, `cu121`, `cu124`, `cu126`, and `cu128`; combining any of these with `--extra vllm` is rejected by `uv` at lock time.
114158

115159
!!! warning
116160
Do **not** install vLLM with `uv pip install vllm` after `uv sync`. Packages installed via `uv pip` are not tracked by the lockfile and will be removed or overwritten by subsequent `uv sync` or `uv run` commands. Always use `--extra vllm` instead.
@@ -138,19 +182,30 @@ uv sync --extra cu130 --extra dev --extra visualize --extra vllm
138182

139183
## For Developers (pip)
140184

185+
!!! note
186+
The editable install below is for developing OneComp from a local clone.
187+
**macOS users who only want MPS inference or quantization should use the
188+
[For Users (pip)](#for-users-pip) flow** (`pip install torch` then
189+
`pip install onecomp` from PyPI); `pip install -e` is not needed for MPS.
190+
141191
```bash
142192
git clone https://github.com/FujitsuResearch/OneCompression.git
143193
cd OneCompression
144194

145-
# Install PyTorch with CUDA support
195+
# First, install PyTorch for your environment
146196
pip install torch --index-url https://download.pytorch.org/whl/cu128
147-
148-
# Install onecomp with development dependencies
197+
# Then install onecomp with development dependencies
149198
pip install -e ".[dev]"
150199
```
151200

201+
Replace `cu128` with the appropriate variant for your environment: `cpu`, `cu118`, `cu121`, `cu124`, `cu126`, `cu128`, or `cu130`.
202+
On macOS, install PyTorch from PyPI instead (see [macOS (MPS)](#step-1-install-pytorch) above).
203+
152204
## Building Documentation Locally
153205

206+
`--extra docs` alone is enough. PyTorch extras (`mps`, `cu*`, `cpu`) are not required
207+
to build or serve the documentation.
208+
154209
```bash
155210
uv sync --extra docs
156211
uv run mkdocs serve

docs/getting-started/quickstart.md

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ quantized model is saved to `TinyLlama-1.1B-...-autobit-<X>bit/` by default.
3131
|------------------------|------------|----------------------------------------------------------|
3232
| `model_id` | (required) | Hugging Face model ID or local path |
3333
| `wbits` | `None` | Target bitwidth. When `None`, estimated from VRAM |
34-
| `total_vram_gb` | `None` | VRAM budget in GB. When `None`, detected from GPU |
34+
| `total_vram_gb` | `None` | VRAM budget in GB. When `None`, detected from CUDA GPU |
3535
| `groupsize` | `128` | GPTQ group size (`-1` to disable) |
36-
| `device` | `"cuda:0"` | Device for computation |
36+
| `device` | `"cuda:0"` | Device for computation (`"mps"` on macOS — see below) |
3737
| `qep` | `True` | Enable QEP (Quantization Error Propagation) |
3838
| `evaluate` | `True` | Calculate perplexity and zero-shot accuracy |
3939
| `eval_original_model` | `False` | Also evaluate the original (unquantized) model |
@@ -66,6 +66,21 @@ Runner.auto_run(
6666
)
6767
```
6868

69+
### macOS (Apple Silicon)
70+
71+
On Mac, set `device="mps"` and pass `total_vram_gb` (VRAM auto-detection uses CUDA only):
72+
73+
74+
```python
75+
Runner.auto_run(
76+
model_id="TinyLlama/TinyLlama-1.1B-intermediate-step-1431k-3T",
77+
device="mps",
78+
total_vram_gb=16,
79+
)
80+
```
81+
See the [macOS / MPS guide](../user-guide/mps.md) for supported features, device
82+
placement (GPTQ vs QEP), and inference with Transformers.
83+
6984
---
7085

7186
## Step-by-step Workflow
@@ -177,3 +192,4 @@ model, tokenizer = load_quantized_model("./output/quantized_model")
177192
- [Configuration](../user-guide/configuration.md) -- detailed explanation of `ModelConfig`, `QEPConfig`, `LPCDConfig`, and `Runner` parameters
178193
- [Examples](../user-guide/examples.md) -- more usage patterns including multi-GPU and chunked calibration
179194
- [Algorithms](../algorithms/overview.md) -- learn about the quantization algorithms available in OneComp
195+
- [macOS / MPS](../user-guide/mps.md) -- Apple Silicon setup, limitations, and inference

docs/user-guide/basic-usage.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,15 @@ model_config = ModelConfig(
6060
| `model_id` | Hugging Face Hub model ID ||
6161
| `path` | Local path to a saved model ||
6262
| `dtype` | Data type (`"float16"`, `"float32"`)| `"float16"` |
63-
| `device` | Device (`"cpu"`, `"cuda"`, `"auto"`)| `"auto"` |
63+
| `device` | Device (`"cpu"`, `"cuda"`, `"mps"`, `"auto"`)| `"auto"` |
6464

6565
You must provide either `model_id` or `path`.
6666

67+
!!! tip "macOS (Apple Silicon)"
68+
Use `device="mps"` for quantization on Mac. `Runner.auto_run` defaults to
69+
`cuda:0`; pass `device="mps"` and `total_vram_gb` explicitly. See the
70+
[macOS / MPS guide](mps.md).
71+
6772
## Step 2: Choose a Quantizer
6873

6974
```python

docs/user-guide/cli.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@ onecomp [-h] [--wbits WBITS] [--total-vram-gb GB] [--groupsize GROUPSIZE]
4343
| Option | Default | Description |
4444
|---------------------------|--------------|----------------------------------------------------------|
4545
| `--wbits WBITS` | `None` (auto)| Target bitwidth. When omitted, estimated from VRAM |
46-
| `--total-vram-gb GB` | `None` (auto)| VRAM budget in GB for bitwidth estimation. When omitted, detected from GPU |
46+
| `--total-vram-gb GB` | `None` (auto)| VRAM budget in GB for bitwidth estimation. When omitted, detected from CUDA GPU. **Required on MPS** when `--wbits` is omitted |
4747
| `--groupsize GROUPSIZE` | `128` | GPTQ group size (`-1` to disable grouping) |
48-
| `--device DEVICE` | `cuda:0` | Device to place the model on |
48+
| `--device DEVICE` | `cuda:0` | Device to place the model on (`mps` on macOS) |
4949
| `--no-qep` | | Disable QEP (enabled by default) |
5050
| `--no-eval` | | Skip perplexity and accuracy evaluation |
5151
| `--eval-original` | | Also evaluate the original (unquantized) model |
@@ -122,6 +122,8 @@ onecomp meta-llama/Llama-2-7b-hf --eval-original
122122
onecomp meta-llama/Llama-2-7b-hf --device cuda:1
123123
```
124124

125+
See the [macOS / MPS guide](mps.md) for supported quantizers and limitations.
126+
125127
## Default Behavior
126128

127129
When run with no options, the `onecomp` command:

docs/user-guide/configuration.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,16 @@ model_config = ModelConfig(
2121
| `model_id` | `str` | Hugging Face Hub model ID | `None` |
2222
| `path` | `str` | Local path to model directory | `None` |
2323
| `dtype` | `str` | Model precision (`"float16"`, `"float32"`)| `"float16"` |
24-
| `device` | `str` | Device placement (`"cpu"`, `"cuda"`, `"auto"`)| `"auto"` |
24+
| `device` | `str` | Device placement (`"cpu"`, `"cuda"`, `"mps"`, `"auto"`)| `"auto"` |
2525

2626
!!! note
2727
Provide exactly one of `model_id` or `path`. A `ValueError` is raised if neither is specified.
2828

29+
!!! note "macOS (MPS)"
30+
On Apple Silicon, set `device="mps"` for GPTQ / AutoBit (GPTQ-only) quantization.
31+
Only GPTQ quantizers are supported on MPS; DBF fallback and multi-GPU are not.
32+
See the [macOS / MPS guide](mps.md) for details.
33+
2934
## Runner
3035

3136
`Runner` is the main entry point for quantization. It manages the full pipeline: loading the model, preparing calibration data, executing quantization, and providing evaluation utilities.
@@ -148,7 +153,7 @@ qep_config = QEPConfig(
148153
| `general` | `bool` | Use generic (architecture-independent) QEP | `False` |
149154
| `percdamp` | `float` | Damping percentage for Hessian regularization | `0.01` |
150155
| `perccorr` | `float` | Correction percentage for error propagation | `0.5` |
151-
| `device` | `str` | GPU device for QEP computations | `"cuda:0"` |
156+
| `device` | `str` | Device for QEP computations (`"cuda"`, `"mps"`, `"cpu"`) | `"cuda:0"` |
152157
| `exclude_layer_keywords` | `list[str]` | Layer keywords excluded from error propagation | `["mlp.down_proj"]` |
153158

154159
!!! tip

docs/user-guide/examples.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,9 @@ outputs = model.generate(**inputs, max_new_tokens=50)
370370
print(tokenizer.decode(outputs[0], skip_special_tokens=True))
371371
```
372372

373+
On macOS, the model is placed on MPS automatically when available. For vLLM serving,
374+
use a Linux machine with an NVIDIA GPU. See the [macOS / MPS guide](mps.md).
375+
373376
## Block-wise PTQ
374377

375378
Apply block-wise post-training quantization to improve accuracy after GPTQ quantization:

0 commit comments

Comments
 (0)