|
| 1 | +# Running AlphaFold 3 on Apple Silicon GPU (Metal / MPS) |
| 2 | + |
| 3 | +> **Status: experimental feasibility path.** This mode runs AlphaFold 3 |
| 4 | +> inference natively on the Apple Silicon integrated GPU through the community |
| 5 | +> [`jax-mps`](https://pypi.org/project/jax-mps/) Metal plugin for JAX. It is |
| 6 | +> **not** an officially supported or numerically certified configuration. The |
| 7 | +> NVIDIA CUDA path is unchanged and remains the reference. Use this for research, |
| 8 | +> local development, and prototyping on Macs — not for production or benchmarking |
| 9 | +> against the published CUDA results. |
| 10 | +
|
| 11 | +This guide describes a **native build with no Docker**. Docker Desktop on macOS |
| 12 | +runs a Linux VM that cannot see the Mac GPU, so the containerized recipe in |
| 13 | +[`installation.md`](installation.md) can only ever reach the CPU. To use the |
| 14 | +Apple GPU you must build AlphaFold 3 natively, as described below. |
| 15 | + |
| 16 | +If you only want CPU-only inference on a Mac (much slower, but officially |
| 17 | +described), follow the "Running AlphaFold 3 without a GPU" section of |
| 18 | +[`installation.md`](installation.md) instead. |
| 19 | + |
| 20 | +## What works and what to expect |
| 21 | + |
| 22 | +Validated in July 2026 across a 52-target campaign (all 12 molecule-type classes: |
| 23 | +monomers, homo/hetero oligomers, protein–DNA, protein–RNA, and ligand/ion |
| 24 | +complexes, plus token-scaling targets up to ~2,200 tokens) on an **M2 Ultra Mac |
| 25 | +Studio (24-core CPU, 76-core GPU, 192 GB unified memory)**, macOS 15. |
| 26 | + |
| 27 | +- **Correctness:** the full data pipeline (HMMER MSA + templates) and inference |
| 28 | + run end to end; predictions match the CUDA reference closely for the cases |
| 29 | + tested. Inference is **bit-reproducible run-to-run** with fixed seeds. |
| 30 | +- **Attention:** only the portable `xla` and `xla_chunked` implementations work |
| 31 | + on Metal. `triton` and `cudnn` require NVIDIA GPUs and are rejected early. |
| 32 | +- **Memory is the main limit** (see [Performance and memory](#performance-and-memory)). |
| 33 | + Peak GPU (unified) memory grows with the model's padded token bucket and |
| 34 | + reaches ~167 GiB at ~2,200 tokens on a 192 GB machine — that is the practical |
| 35 | + single-structure ceiling on this hardware. |
| 36 | +- **Large targets are slow.** Per-seed inference scales roughly cubically with |
| 37 | + tokens and hits a steep Metal-specific penalty above ~1,500 tokens. |
| 38 | + |
| 39 | +## Prerequisites |
| 40 | + |
| 41 | +- Apple Silicon Mac (M1/M2/M3/M4 family). More unified memory = larger tractable |
| 42 | + structures; 64 GB is a reasonable floor, 128–192 GB recommended for large |
| 43 | + complexes. |
| 44 | +- macOS 14 or newer. |
| 45 | +- Xcode Command Line Tools (provides `clang`, `make`, `patch`): |
| 46 | + |
| 47 | + ```sh |
| 48 | + xcode-select --install |
| 49 | + ``` |
| 50 | + |
| 51 | +- A recent CMake (≥ 3.28) and a Python 3.11–3.13 toolchain. The steps below use |
| 52 | + [`uv`](https://docs.astral.sh/uv/); a Conda/Miniforge environment works |
| 53 | + equally well (see [Alternative: Conda](#alternative-conda-environment)). |
| 54 | + |
| 55 | +## Step 1 — Install the HMMER suite (for the MSA data pipeline) |
| 56 | + |
| 57 | +HMMER is only needed if you run the data pipeline (i.e. not with |
| 58 | +`--norun_data_pipeline`). Prefer a user-local source build of HMMER 3.4 with the |
| 59 | +sequence-truncation patch that ships in this repository |
| 60 | +(`docker/jackhmmer_seq_limit.patch`); a plain HMMER build also works but is not |
| 61 | +the preferred configuration. |
| 62 | + |
| 63 | +```sh |
| 64 | +# Pick a location OUTSIDE the git checkout. |
| 65 | +export AF3_REPO="$PWD" # path to your alphafold3 clone |
| 66 | +export HMMER_PREFIX="$HOME/af3-tools/hmmer-3.4" |
| 67 | +mkdir -p "$HOME/af3-tools/hmmer-source" && cd "$HOME/af3-tools/hmmer-source" |
| 68 | + |
| 69 | +curl -LO http://eddylab.org/software/hmmer/hmmer-3.4.tar.gz |
| 70 | +echo "ca70d94fd0cf271bd7063423aabb116d42de533117343a9b27a65c17ff06fbf3 hmmer-3.4.tar.gz" | shasum -a 256 -c |
| 71 | +tar -xzf hmmer-3.4.tar.gz |
| 72 | +cd hmmer-3.4 |
| 73 | +patch -p0 < "$AF3_REPO/docker/jackhmmer_seq_limit.patch" |
| 74 | +./configure --prefix="$HMMER_PREFIX" |
| 75 | +make -j"$(sysctl -n hw.perflevel0.logicalcpu)" |
| 76 | +make install |
| 77 | +"$HMMER_PREFIX/bin/jackhmmer" -h | head -1 # sanity check |
| 78 | +``` |
| 79 | + |
| 80 | +You will pass the binaries to `run_alphafold.py` in Step 6 via the |
| 81 | +`--jackhmmer_binary_path`, `--nhmmer_binary_path`, `--hmmalign_binary_path`, |
| 82 | +`--hmmsearch_binary_path`, and `--hmmbuild_binary_path` flags (or put |
| 83 | +`$HMMER_PREFIX/bin` first on your `PATH`). |
| 84 | + |
| 85 | +## Step 2 — Get the code, model weights, and databases |
| 86 | + |
| 87 | +1. Clone the repository (if you have not already): |
| 88 | + |
| 89 | + ```sh |
| 90 | + git clone https://github.com/google-deepmind/alphafold3.git |
| 91 | + cd alphafold3 |
| 92 | + ``` |
| 93 | + |
| 94 | +2. Obtain the model weights by following the process in the |
| 95 | + [main README](https://github.com/google-deepmind/alphafold3). Keep the |
| 96 | + weights **outside** the git checkout. |
| 97 | +3. Download the genetic databases (see [`installation.md`](installation.md) and |
| 98 | + `fetch_databases.py`). These are large (hundreds of GB); place them on fast |
| 99 | + local storage. |
| 100 | + |
| 101 | +## Step 3 — Build and install AlphaFold 3 natively |
| 102 | + |
| 103 | +This compiles the AlphaFold 3 C++/CMake extension and installs the Python |
| 104 | +package. Upstream already supports the `darwin arm64` build environment. |
| 105 | + |
| 106 | +```sh |
| 107 | +uv venv --python 3.12 |
| 108 | +source .venv/bin/activate |
| 109 | +uv sync # builds and installs alphafold3 (scikit-build-core + cmake) |
| 110 | +uv run build_data # builds the CCD / chemical-component data used at runtime |
| 111 | +``` |
| 112 | + |
| 113 | +`uv sync` installs the base dependencies, which pin `jax==0.9.1` (CPU wheel on |
| 114 | +macOS). That JAX cannot reach the Apple GPU — Step 4 replaces it. |
| 115 | + |
| 116 | +Verify the base install with the data-pipeline test: |
| 117 | + |
| 118 | +```sh |
| 119 | +uv run python run_alphafold_data_test.py |
| 120 | +``` |
| 121 | + |
| 122 | +## Step 4 — Install the Apple Silicon GPU backend |
| 123 | + |
| 124 | +Replace the base JAX with the Metal-capable stack pinned in |
| 125 | +[`requirements/apple-silicon-gpu.txt`](../requirements/apple-silicon-gpu.txt): |
| 126 | + |
| 127 | +```sh |
| 128 | +uv pip install -r requirements/apple-silicon-gpu.txt |
| 129 | +# equivalently: uv pip install jax==0.10.2 jaxlib==0.10.2 jax-mps==0.10.9 |
| 130 | +``` |
| 131 | + |
| 132 | +Confirm JAX now sees the Metal device: |
| 133 | + |
| 134 | +```sh |
| 135 | +python -c "import jax; print([d.platform for d in jax.local_devices()])" |
| 136 | +# -> ['mps'] (a one-time 'Platform mps is experimental' warning is expected) |
| 137 | +``` |
| 138 | + |
| 139 | +`pip check` will report exactly one warning that AlphaFold 3 pins `jax==0.9.1`. |
| 140 | +That single mismatch is expected; any other mismatch means the environment is |
| 141 | +inconsistent. |
| 142 | + |
| 143 | +## Step 5 — Verify device selection |
| 144 | + |
| 145 | +Run the launcher's device-selection unit tests (fast, no GPU needed): |
| 146 | + |
| 147 | +```sh |
| 148 | +uv run python run_alphafold_device_test.py |
| 149 | +``` |
| 150 | + |
| 151 | +These confirm the MPS backend is selectable and — importantly — that the CUDA |
| 152 | +path is unchanged. |
| 153 | + |
| 154 | +## Step 6 — Run inference on the Apple GPU |
| 155 | + |
| 156 | +Select the Metal backend with `--jax_backend mps` and a portable attention |
| 157 | +implementation. `xla` is the validated choice; `xla_chunked` uses less memory |
| 158 | +but is slower. |
| 159 | + |
| 160 | +```sh |
| 161 | +uv run python run_alphafold.py \ |
| 162 | + --json_path=/path/to/fold_input.json \ |
| 163 | + --model_dir=/path/to/weights \ |
| 164 | + --db_dir=/path/to/databases \ |
| 165 | + --output_dir=/path/to/output \ |
| 166 | + --jax_backend=mps \ |
| 167 | + --gpu_device=0 \ |
| 168 | + --flash_attention_implementation=xla \ |
| 169 | + --jackhmmer_binary_path="$HMMER_PREFIX/bin/jackhmmer" \ |
| 170 | + --nhmmer_binary_path="$HMMER_PREFIX/bin/nhmmer" \ |
| 171 | + --hmmalign_binary_path="$HMMER_PREFIX/bin/hmmalign" \ |
| 172 | + --hmmsearch_binary_path="$HMMER_PREFIX/bin/hmmsearch" \ |
| 173 | + --hmmbuild_binary_path="$HMMER_PREFIX/bin/hmmbuild" |
| 174 | +``` |
| 175 | + |
| 176 | +For inference on a pre-computed input (skipping the MSA pipeline and HMMER), add |
| 177 | +`--norun_data_pipeline` and drop the HMMER flags. |
| 178 | + |
| 179 | +## Performance and memory |
| 180 | + |
| 181 | +Measured on the M2 Ultra (192 GB). Numbers are per diffusion sample ("seed") and |
| 182 | +scale with the **padded token bucket**, not the exact token count. Treat them as |
| 183 | +order-of-magnitude guidance; other Apple Silicon chips will differ. |
| 184 | + |
| 185 | +| Padded tokens | Peak GPU (unified) memory | Inference time / seed | |
| 186 | +| ------------: | ------------------------: | --------------------: | |
| 187 | +| 256 | ~36 GiB | ~29 s | |
| 188 | +| 512 | ~50 GiB | ~90 s | |
| 189 | +| 768 | ~73 GiB | ~3.9 min | |
| 190 | +| 1,024 | ~101 GiB | ~8.6 min | |
| 191 | +| 1,536 | ~155 GiB | ~26.6 min | |
| 192 | +| ~2,180 | ~167 GiB (+ ~11 GiB swap) | ~3.9 hours | |
| 193 | + |
| 194 | +Key points: |
| 195 | + |
| 196 | +- **Unified memory is the ceiling.** GPU tensors live in unified memory and are |
| 197 | + reported by macOS as "Memory Used"; they are **not** visible as process RSS |
| 198 | + (the Python process itself stays near ~7 GiB). Do not rely on an RSS-based |
| 199 | + memory limiter to cap GPU use. On a 192 GB machine, ~2,200 tokens |
| 200 | + (~167 GiB + light swap) is the practical single-structure limit; larger |
| 201 | + structures will run out of memory. Scale your expectation to your Mac's memory. |
| 202 | +- **Inference cost is roughly cubic** in tokens (triangle attention dominates), |
| 203 | + with an additional steep Metal-specific penalty beyond ~1,500 tokens. Small to |
| 204 | + medium structures (≤ ~1,000 tokens) are comfortable; multi-thousand-token |
| 205 | + complexes are expensive. Use `xla_chunked` to trade speed for lower memory. |
| 206 | +- **The MSA / data pipeline runs on the CPU** and its cost depends on the number |
| 207 | + of unique chains and whether RNA is present (≈ 7–35 min in the campaign), not |
| 208 | + on token count. Set the HMMER thread counts so that |
| 209 | + (concurrent searches × threads-per-search) ≈ the number of performance cores |
| 210 | + to avoid oversubscription (`sysctl hw.perflevel0.logicalcpu`). |
| 211 | +- **No persistent JAX compilation cache on Metal.** `--jax_compilation_cache_dir` |
| 212 | + does not persist across processes on the MPS backend, so every process pays the |
| 213 | + compile cost for each new token bucket. Amortize it by folding many inputs in |
| 214 | + one process (e.g. `--input_dir`) rather than launching one process per target. |
| 215 | +- **Thermals** were nominal on the Mac Studio throughout a ~40 h campaign (no |
| 216 | + throttling). Laptops with less cooling headroom will behave differently. |
| 217 | + |
| 218 | +## Alternative: Conda environment |
| 219 | + |
| 220 | +If you prefer Conda/Miniforge over `uv`, the same result is achieved with: |
| 221 | + |
| 222 | +```sh |
| 223 | +conda create -n af3-mps python=3.13 cmake -y |
| 224 | +conda activate af3-mps |
| 225 | +pip install --no-build-isolation -e . # or: pip install . |
| 226 | +build_data |
| 227 | +pip install -r requirements/apple-silicon-gpu.txt |
| 228 | +``` |
| 229 | + |
| 230 | +Then run `run_alphafold.py` with the same MPS flags as in Step 6. |
| 231 | + |
| 232 | +## Troubleshooting |
| 233 | + |
| 234 | +- **`No local MPS device was found for --jax_backend=mps`** — `jax-mps` is not |
| 235 | + installed or JAX was not upgraded. Re-run Step 4 and re-check |
| 236 | + `jax.local_devices()`. |
| 237 | +- **`--flash_attention_implementation must be set to "xla" or "xla_chunked"`** — |
| 238 | + `triton`/`cudnn` are NVIDIA-only; use `xla` (or `xla_chunked`) on Metal. |
| 239 | +- **Out-of-memory / heavy swapping on large targets** — reduce the structure |
| 240 | + size, switch to `--flash_attention_implementation=xla_chunked`, or run on a Mac |
| 241 | + with more unified memory. See the memory table above. |
| 242 | +- **`pip check` reports a jax version mismatch** — one warning about the |
| 243 | + `jax==0.9.1` pin is expected (see Step 4); investigate any other mismatch. |
0 commit comments