Replay MPI calls per lane in vector forward mode (#3347)

* Replay MPI calls per lane in vector forward mode

In forward mode the derivative of MPI_Bcast, MPI_Send, MPI_Recv,
MPI_Allreduce and MPI_Reduce (of a sum) is the same call on the shadow
buffers. At vector width > 1, handleMPI asserted width 1; replay the call
once per lane instead, and report the other MPI functions as unsupported.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M5d6m8eQfhUdHDMXaDuYq6

* Test vector forward mode of mpi_allreduce_/mpi_bcast_

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M5d6m8eQfhUdHDMXaDuYq6

* Replay MPI calls per lane in the existing forward-mode rules

Instead of a separate vector-mode path in front of handleMPI, every
forward-mode rule (Isend/Irecv, Wait, Waitall, Send/Ssend, Recv, Bcast,
Reduce, Allreduce, Gather, Scatter, Allgather) now emits its shadow call
through one helper, createMPIForwardCall. The helper replaces the buffer
arguments with their shadows and replays the call once per lane at
width > 1. All other arguments, including the Fortran `ierr`, are passed
as in the original call, and an inactive constant buffer (MPI_IN_PLACE,
MPI_STATUS_IGNORE) is passed unchanged.

So Isend/Irecv/Wait/Waitall/Gather/Scatter/Allgather also work in vector
forward mode now. As a side effect, the forward rules of Waitall and
Allgather pass all arguments of the call: before, they dropped the
statuses argument and the Fortran `ierr`. Reverse mode at width > 1
reports an unsupported-derivative error instead of asserting.

Assisted-by: Claude Code (Opus 5.5)

* Test vector forward mode of mpi_allreduce/mpi_bcast from Fortran

Two lanes with rank-dependent seeds, so that the tangent sum of the
allreduce and the root tangent of the bcast differ per lane. Without
the change, Enzyme asserts `getWidth() == 1` in handleMPI.

Assisted-by: Claude Code (Opus 5.5)

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
3 files changed
tree: a64a08f446a07ad53b6fba703045ba30a5fe7010
  1. .devcontainer/
  2. .github/
  3. .packaging/
  4. enzyme/
  5. integration/
  6. .gitattributes
  7. .gitignore
  8. CITATION.bib
  9. CONTRIBUTING.md
  10. fortitude.toml
  11. fpm.toml
  12. LICENSE
  13. Readme.md
  14. zizmor.yml
Readme.md

The Enzyme High-Performance Automatic Differentiator of LLVM and MLIR

Enzyme is a plugin that performs automatic differentiation (AD) of statically analyzable LLVM and MLIR.

Enzyme can be used by calling __enzyme_autodiff on a function to be differentiated as shown below. Running the Enzyme transformation pass then replaces the call to __enzyme_autodiff with the gradient of its first argument.

double foo(double);

double grad_foo(double x) {
    return __enzyme_autodiff(foo, x);
}

Enzyme is highly-efficient and its ability to perform AD on optimized code allows Enzyme to meet or exceed the performance of state-of-the-art AD tools.

Detailed information on installing and using Enzyme can be found on our website: https://enzyme.mit.edu.

A short example of how to install Enzyme is below:

cd /path/to/Enzyme/enzyme
mkdir build && cd build
cmake -G Ninja .. -DLLVM_DIR=/path/to/llvm/lib/cmake/llvm -DLLVM_EXTERNAL_LIT=/path/to/lit/lit.py
ninja

Or, install Enzyme using a package manager:

Homebrew

brew install enzyme

Spack

spack install enzyme

Nix

nix-shell -p enzyme

To get involved or if you have questions, please join our mailing list.

If using this code in an academic setting, please cite the following three papers (first for Enzyme as a whole, second for GPU+optimizations, and third for AD of all other parallel programs (OpenMP, MPI, Julia Tasks, etc.)):

@inproceedings{NEURIPS2020_9332c513,
 author = {Moses, William and Churavy, Valentin},
 booktitle = {Advances in Neural Information Processing Systems},
 editor = {H. Larochelle and M. Ranzato and R. Hadsell and M. F. Balcan and H. Lin},
 pages = {12472--12485},
 publisher = {Curran Associates, Inc.},
 title = {Instead of Rewriting Foreign Code for Machine Learning, Automatically Synthesize Fast Gradients},
 url = {https://proceedings.neurips.cc/paper/2020/file/9332c513ef44b682e9347822c2e457ac-Paper.pdf},
 volume = {33},
 year = {2020}
}
@inproceedings{10.1145/3458817.3476165,
author = {Moses, William S. and Churavy, Valentin and Paehler, Ludger and H\"{u}ckelheim, Jan and Narayanan, Sri Hari Krishna and Schanen, Michel and Doerfert, Johannes},
title = {Reverse-Mode Automatic Differentiation and Optimization of GPU Kernels via Enzyme},
year = {2021},
isbn = {9781450384421},
publisher = {Association for Computing Machinery},
address = {New York, NY, USA},
url = {https://doi.org/10.1145/3458817.3476165},
doi = {10.1145/3458817.3476165},
booktitle = {Proceedings of the International Conference for High Performance Computing, Networking, Storage and Analysis},
articleno = {61},
numpages = {16},
keywords = {CUDA, LLVM, ROCm, HPC, AD, GPU, automatic differentiation},
location = {St. Louis, Missouri},
series = {SC '21}
}
@inproceedings{10.5555/3571885.3571964,
author = {Moses, William S. and Narayanan, Sri Hari Krishna and Paehler, Ludger and Churavy, Valentin and Schanen, Michel and H\"{u}ckelheim, Jan and Doerfert, Johannes and Hovland, Paul},
title = {Scalable Automatic Differentiation of Multiple Parallel Paradigms through Compiler Augmentation},
year = {2022},
isbn = {9784665454445},
publisher = {IEEE Press},
booktitle = {Proceedings of the International Conference on High Performance Computing, Networking, Storage and Analysis},
articleno = {60},
numpages = {18},
keywords = {automatic differentiation, tasks, OpenMP, compiler, Julia, parallel, Enzyme, C++, RAJA, hybrid parallelization, MPI, distributed, LLVM},
location = {Dallas, Texas},
series = {SC '22}
}

Julia bindings, Rust bindings, and Fortran bindings are available for Enzyme.