monoprop

Parallelism and distribution

Scaling by partitioning the operator across cores and across MPI ranks.

monoprop scales the same way on one node and on many: it partitions the operator into disjoint pieces and applies each gate to every partition in lock-step, exchanging only the terms that cross a partition boundary. Two axes compose:

  • Across cores (default): one single-threaded partition per physical core, within a single process. This is on automatically, and can be configured with environment variables (see below).
  • Across nodes (opt-in): MPI ranks, each of which partitions further across its own cores. Distributing the operator and its graph across ranks lets you simulate and variationally optimise systems larger than one node's memory.

Operator partitioning (single node)

By default monoprop splits the operator into one partition per physical core and gives each partition a pinned worker thread that runs it serially. Each partition keeps its own small, cache-resident term index; a gate is applied to all partitions at once, synchronised by a lightweight barrier, and anticommuting terms whose partner lives in another partition are resolved through a per-gate exchange. When the partitions are pinned and span more than one L3 domain, that barrier fans in within each domain and then across domains, so both the arrival counter and the release store stay inside one L3 slice instead of crossing the socket interconnect.

Runtime environment variables

VariableDefaultMeaning
monoprop_NUM_THREADSone partition per physical coreCaps the number of partitions. Set it to run fewer partitions than cores.
monoprop_PARTITIONSautoauto = one partition per core (capped by monoprop_NUM_THREADS); an integer N = exactly N partitions; off = one partition holding the whole operator.
monoprop_PARTITION_PINNINGon0/false/no disables pinning each partition to a core. Has an effect only on Linux.
monoprop_COMM_PROFILEoff1/true/yes makes each transport print one COMMPROF line per rank to stderr when it is destroyed, accounting for where its collectives' wall time went. Diagnostic only; off costs one branch per collective phase.
# Run 8 partitions instead of one-per-core:
export monoprop_NUM_THREADS=8

MPI distribution (multi-node)

MPI partitions the operator and graph across ranks, composing with per-rank partitioning into one flat world of R × S partitions (R ranks, S partitions each). MPI communication is serialised through each rank's first partition, bracketed by the intra-rank barriers.

Single-node (MPI.COMM_SELF)

from mpi4py import MPI
sim = MajoranaPropagator(..., comm=MPI.COMM_SELF)

Multi-node (MPI.COMM_WORLD)

Replace the communicator and launch with mpiexec:

from mpi4py import MPI
sim = MajoranaPropagator(..., comm=MPI.COMM_WORLD)
mpiexec -n 8 uv run python your_script.py

A pure-MPI rank keeps its whole share in one partition unless monoprop_NUM_THREADS is set, so an MPI user who has not asked for threads gets one partition per rank.

Enabling MPI

MPI is off by default, so the prebuilt PyPI wheels run single-rank and the communicators above only distribute work after a from-source build with MPI enabled. See Building from source for the full build instructions.

On this page