PennyLaneAI / PennyLaneAI/catalyst
Parameter-dependent decomposition rules cannot specialize on constant parameters under capture
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 234
- Forks
- 84
- Avg merge
- 2d 15h
- Merged PRs (30d)
- 66
Description
Summary
Under qml.qjit(capture=True), Catalyst's graph-based decomposition compiles a single, generic decomposition rule per operator type, traced with an abstract parameter, and applies it uniformly via the MLIR decompose-lowering pass. As a result, decomposition rules whose structure depends on the concrete value of a parameter cannot specialize, i.e. the parameter is always abstract at rule-compile time.
The concrete motivating case is the adaptive-precision RZ phase-gradient decomposition here: https://github.com/PennyLaneAI/pennylane/pull/10098. It truncates the SemiAdder width (and drops angles that round to zero) based on the concrete angle bits, guarded by:
if adaptive_precision and not math.is_abstract(phi):
... # truncate angle_wires / phase_grad_wires to the significant width
Under capture=True, phi is always abstract when the rule is compiled, so this branch is dead code: the adder is always full width and the Toffoli/T counts are identical for adaptive_precision=True and False. The large (~70%) Toffoli/T reduction that this rule produces under capture=False silently vanishes under capture.
Why "the angles are constants" does not help
Even when the circuit has no arguments and the angles are compile-time constants, the concreteness is not exploited. The angles are concrete literals in the captured plxpr:
import numpy as np, pennylane as qml, jax
qml.capture.enable()
angles = [f * 2 * np.pi for f in (1/2, 1/4, 3/4, 1/64, 7/8, 2.0**-7)]
def circuit():
for a in angles:
qml.RZ(a, 0)
return qml.expval(qml.Z(0))
print(jax.make_jaxpr(circuit)())
# ... operator[op_cls=RZ ...] 3.141592653589793:f32[] 0:i32[]
# ... operator[op_cls=RZ ...] 1.5707963267948966:f32[] 0:i32[]
# ... operator[op_cls=RZ ...] 0.09817477042468103:f32[] 0:i32[] (etc.)
But the decomposition rule is compiled once, generically, keyed by op-name + wire-count rather than by the concrete angle. The concrete value flows into the compiled rule as a runtime SSA operand (the emitted MLIR computes binary_decimals(phi) at "rule runtime"), so the per-angle structural truncation has nowhere to happen.
The rule still produces correct results, just without the optimization, so this is a silent performance regression relative to capture=False.
Environment
- catalyst
0.16.0-dev86 - pennylane
0.46.0-dev88 - jax
0.7.1 - macOS arm64
written with the help of genAI
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the graph-based decomposition path and the MLIR decompose-lowering pass, then compare how the adaptive-precision RZ decomposition behaves under capture=True and capture=False. Done means concrete constant parameters can enable the intended structural specialization under capture while preserving correct results.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- compilers, performance
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Needs clarification
- Newbie friendliness
- 38/100