Metadata-Version: 2.4
Name: pennylane-metal
Version: 0.2.0
Summary: PennyLane pure- and mixed-state simulator built on Metal 4
Keywords: pennylane,quantum,simulation,metal,gpu,apple-silicon
Author-Email: Derek Lei <leiderek12@gmail.com>
License-Expression: Apache-2.0
License-File: LICENSE
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: GPU
Classifier: Intended Audience :: Science/Research
Classifier: Operating System :: MacOS :: MacOS X
Classifier: Programming Language :: C++
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Scientific/Engineering :: Physics
Project-URL: Homepage, https://github.com/dereklei12/pennylane-metal
Project-URL: Documentation, https://github.com/dereklei12/pennylane-metal#readme
Project-URL: Repository, https://github.com/dereklei12/pennylane-metal
Project-URL: Issues, https://github.com/dereklei12/pennylane-metal/issues
Requires-Python: >=3.10
Requires-Dist: pennylane>=0.42
Requires-Dist: numpy>=1.21
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-benchmark; extra == "dev"
Requires-Dist: pennylane-lightning>=0.42; extra == "dev"
Requires-Dist: black; extra == "dev"
Requires-Dist: ruff; extra == "dev"
Description-Content-Type: text/markdown

# PennyLane Metal

[![PyPI version](https://img.shields.io/pypi/v/pennylane-metal.svg)](https://pypi.org/project/pennylane-metal/)
[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE)
[![CI](https://github.com/dereklei12/pennylane-metal/actions/workflows/ci.yml/badge.svg)](https://github.com/dereklei12/pennylane-metal/actions/workflows/ci.yml)

`metal.qubit` is a PennyLane quantum simulator built exclusively on Metal
4. Pure circuits use a state vector while noisy circuits use an independent
density-matrix execution model. Both consume the same quantum IR and metallib;
state evolution, channels, measurements, and sampling are implemented in Metal
Shading Language.

## Requirements

- macOS 26.0 or newer on Apple silicon
- A Metal 4-capable Apple GPU (M1 or newer)
- Python 3.10–3.14

There is no Metal 3 path and no earlier macOS deployment target.

## Installation

```bash
pip install pennylane-metal
```

Binary wheels are published for macOS 26+ (arm64) and CPython 3.10–3.14.

### Building from source

Building from the sdist or a git checkout additionally requires:

- Xcode 26 with the Metal Toolchain component
- CMake 3.24 or newer

Install the optional Xcode component if `xcrun --toolchain Metal --find metal`
fails:

```bash
xcodebuild -downloadComponent MetalToolchain
```

Then:

```bash
pip install .                # or: pip install -e '.[dev]' for development
```

## Usage

```python
import pennylane as qml

dev = qml.device("metal.qubit", wires=2)

@qml.qnode(dev)
def bell_state():
    qml.H(0)
    qml.CNOT([0, 1])
    return qml.probs(wires=[0, 1])

print(bell_state())  # [0.5, 0.0, 0.0, 0.5]
```

The default `memory_budget="auto"` policy admits an execution only when its
estimated peak fits within 80% of Metal's recommended working set. An explicit
byte count applies a custom budget; `memory_budget=None` disables only this soft
budget and still enforces kernel addressing and `maxBufferLength`:

```python
from pennylane.tape import QuantumScript
from pennylane_metal import max_supported_qubits

dev = qml.device("metal.qubit", wires=12, memory_budget="auto")
tape = QuantumScript([], [qml.probs(wires=[0])])

print(max_supported_qubits("density_matrix"))
print(dev.estimate_resources(tape).peak_metal_bytes)
dev.release_resources()
```

`estimate_resources` accepts an already-preprocessed `QuantumScript`, matching
the objects passed by PennyLane to `execute`. The low-level
`MetalSimulator.estimate_batch_resources(program, batch_count)` API exposes the
nine-slot native-batch plan; `assess_resources` accepts both scalar and batch
estimates.

PennyLane decomposes only operations outside the native IR. The native set
includes common one-qubit gates, `Rot`, `U2`, `U3`, CNOT/CZ/SWAP,
`CRot`/CRX/CRY/CRZ, iSWAP/SISWAP/PSWAP/ECR, Toffoli and multi-controlled X,
Ising interactions, MultiRZ, PauliRot, the single- and double-excitation
families, FermionicSWAP, OrbitalRotation, state preparation, and arbitrary
dense or diagonal unitaries. Hermitian and projector observables are
diagonalized into the same IR. `SparseHamiltonian` remains CSR and is applied
directly by a Metal sparse expectation kernel.

Analytic state, reduced density matrix, probability, expectation, variance,
purity, von Neumann entropy, and mutual information measurements are
supported. Metal computes the reduced density matrices; entropy eigenvalues
are classical spectral postprocessing through macOS Accelerate. Finite-shot
probability, expectation, variance, sample, and counts measurements use Metal
sampling. Parameter broadcasting is expanded into scalar quantum programs;
homogeneous pure-state batches containing analytic expectation or variance
measurements then execute as one native batched Metal state buffer. Each
simulator retains a fixed-role batch workspace, so a warmed compatible batch
performs no workspace-buffer allocations and smaller batches preserve the high
water mark. Mixed-state, finite-shot, state-preparation, heterogeneous-topology,
and other measurement batches retain the scalar fallback.
Mid-circuit measurements, reset, postselection, and classical conditionals are
rejected explicitly.

Noisy circuits support `QubitDensityMatrix`, arbitrary multi-wire
`QubitChannel` Kraus maps, `PauliError`, `BitFlip`, `PhaseFlip`,
`DepolarizingChannel`, `AmplitudeDamping`, `GeneralizedAmplitudeDamping`,
`PhaseDamping`, `ThermalRelaxationError`, and `ResetError`. The density-matrix
model is selected internally and never routes state evolution through a CPU
simulator. Density inputs are checked for Hermiticity, trace, and positive
semidefiniteness without a qubit-count shortcut.

Analytic expectation values support native Metal adjoint Jacobians and VJPs for
the native parameterized pure-state gates, including multi-parameter
Rot/U2/U3/CRot, Ising interactions, MultiRZ, PauliRot, PSWAP, excitation gates,
FermionicSWAP, and OrbitalRotation. The reverse pass, analytic gate
derivatives, and parallel reductions run in Metal. Noisy circuits use
PennyLane parameter-shift or finite differences, with every forward evaluation
remaining on the Metal density-matrix path.

The device keeps a bounded topology-template cache and refreshes only runtime
parameters, matrices, eigenvalues, sparse values, shots, and seeds on a hit.
Metal precomputes local gate matrices, fuses consecutive one-qubit gates on the
same wire for forward evolution, reuses adjoint workspaces, and builds large
sampling CDFs with a hierarchical parallel scan. These scheduling details are
internal and do not change PennyLane's execution or differentiation protocols.

## Architecture

```text
PennyLane QuantumScript
        |
        v
Python compiler: validate, decompose, map wires
        |
        v
QuantumProgram IR: opcodes, wires, parameters, measurements
        |
        v
SimulationResources: peak memory plan and device admission
        |
        v
MetalProgramBuffers
        |
        +--> MetalSimulator (pure state)
        |
        +--> MetalDensitySimulator (mixed state)
        |
        v
MetalContext
        |
        v
MTL4CommandQueue / MTL4CommandBuffer / MTL4ArgumentTable
        |
        v
Metal 4 kernels: evolution, measurement, sampling, adjoint/VJP
```

There is one device, one IR, and one shader library. Pure and mixed execution
models are internal numerical strategies, not public backend variants. Tile,
batch, and standard are not public backend classes; future scheduling
strategies belong inside the runtime.
Application-specific QNN layers, image patches, classifiers, optimizers, and
training tapes are intentionally outside this repository.

See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for the detailed ownership and
data-flow rules.

## Development

```bash
cmake -S . -B build \
  -DCMAKE_PREFIX_PATH="$PWD/.venv/lib/python3.10/site-packages" \
  -DPython_EXECUTABLE="$PWD/.venv/bin/python"
cmake --build build -j
.venv/bin/pytest
```

Run the full suite with Metal API Validation enabled after changing command
encoding, argument bindings, or residency management. The environment variable
must be set before Python initializes Metal:

```bash
MTL_DEBUG_LAYER=1 NSUnbufferedIO=YES .venv/bin/pytest
```

### Benchmarks

The benchmark runner contains a small set of representative end-to-end
comparisons rather than one case per operation:

```bash
# Common RY/RZ/CNOT workload against lightning.qubit
.venv/bin/python benchmarks/benchmark_metal.py --suite statevector

# Adjoint Jacobian for an RX/RY/CNOT workload against lightning.qubit
.venv/bin/python benchmarks/benchmark_metal.py --suite adjoint

# Native Rot and DoubleExcitation against their explicit Metal decompositions
.venv/bin/python benchmarks/benchmark_metal.py --suite native-gates

# Thermal-relaxation density evolution against default.mixed
.venv/bin/python benchmarks/benchmark_metal.py --suite density

# CSR SparseHamiltonian expectation against lightning.qubit
.venv/bin/python benchmarks/benchmark_metal.py --suite sparse
```

Use `--suite all --iterations 5` for a short representative sweep. Individual
suites accept `--qubits`, `--depth`, `--iterations`, and `--warmups` overrides.
State-vector, adjoint, and sparse suites use `lightning.qubit` as the default
reference; select `--reference default.qubit` or `--reference none` when
needed. Reported times include PennyLane preprocessing, IR compilation,
command encoding, GPU execution, synchronization, and result formatting.

The simulator uses `complex64` storage and two ping-pong state buffers. There is
no fixed product-level qubit limit: admission is calculated from the kernel
address width, the current device's `maxBufferLength`, the selected memory
budget, program scratch space, shots, and measurement output. State-vector
storage scales as `2**n`; density-matrix storage scales as `4**n` and is
normally memory-bound much earlier. Density evolution uses a two-dimensional
row/column grid with 64-bit linear addresses, so it is not restricted by the
old 32-bit flattened-grid boundary at 15 qubits.

Resource failures are raised as `MetalResourceError` by the low-level runtime
and translated to PennyLane `DeviceError` by `MetalQubit`. The error names
the limiting resource and reports the estimated peak and configured budget.
