SemiAdder fails to decompose under catalyst.passes.graph_decomposition + capture=True; built-in rule bytecode is unloadable and cannot be (re)built

Open
#3,177 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
55/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
python
Domain
compilers

Research direction

Start in frontend/catalyst/decomposition/precompile_decomposition_rules.py, especially the COMPILER_OPS_FOR_DECOMPOSITION loop and compile_decomposition_rules assertion. Reproduce the deterministic failure with precompile_decomp_rules("/tmp/rules.mlirbc"), then verify the graph_decomposition SemiAdder MWE. Done means the built-in rules can be regenerated and loaded, and SemiAdder no longer aborts during full compilation.

Written by the indexing model from the issue text.

Description

Summary

Under the catalyst.passes.graph_decomposition + qml.qjit(capture=True), decomposing SemiAdder aborts

The immediate abort has two coupled symptoms:

  1. the compiler fails to load the built-in decomposition-rule bytecode (... the rules file could not be parsed, SIGABRT), and
  2. the graph solver then can't find a rule for SemiAdder (GraphSolverFailedError).

The root cause is that the built-in decomposition-rule bytecode is currently unloadable and cannot be (re)built: the regenerator precompile_decomp_rules crashes deterministically, and the bytecode file is keyed to the git revision, so any mismatch degrades to an opaque fatal error rather than a graceful fallback.

Trivial circuits that only need on-demand-compiled rules (e.g. S + CNOT) still compile and run fine, so the failure is specific to operators that rely on the built-in rule library.

Environment
  • catalyst 0.16.0-dev87 (main; also on branch qottmann/capture-operator2-solution-nodes, commit e39692ae6)
  • pennylane 0.46.0-dev88
  • jax 0.7.1
  • macOS arm64
MWE 1 — user-facing symptom: SemiAdder via graph_decomposition
import pennylane as qp
from catalyst import qjit
from catalyst.passes import graph_decomposition

qp.decomposition.enable_graph()

@qjit(capture=True)
@graph_decomposition(gate_set={qp.CNOT, qp.Toffoli, qp.PauliX, qp.Hadamard, qp.PhaseShift})
@qp.qnode(qp.device("null.qubit", wires=5))
def circuit():
    qp.SemiAdder(x_wires=[0, 1], y_wires=[2, 3], work_wires=[4])
    return qp.expval(qp.Z(0))

circuit()

Observed:

catalyst.utils.exceptions.CompileError: catalyst failed with error code -6:
failed to load built-in decomposition rules from
'.../resources/decomposition_rules_<revision>.mlirbc': the rules file could not be parsed

libc++abi: terminating due to uncaught exception of type
DecompGraph::Core::GraphSolverFailedError: Decomposition rule not found for operator
'id: SemiAdder{}{work_wires:1,x_wires:2,y_wires:2}{}'. Tried rules:
  - __builtin__semi_adder_SemiAdder{}{work_wires:1,x_wires:2,y_wires:2}{}
...
13 catalyst  ... catalyst::quantum::GraphDecompositionPass::runOnOperation() + 3196

target="mlir" (frontend capture only) succeeds and emits IR; the abort happens once the GraphDecompositionPass actually runs (full compile).

Contrast — a circuit that does not need the built-in library compiles and runs fine:

@qjit(capture=True)
@graph_decomposition(gate_set={qp.PhaseShift, qp.CNOT, qp.GlobalPhase, qp.RX, qp.RZ, qp.Hadamard})
@qp.qnode(qp.device("null.qubit", wires=2))
def ok():
    qp.S(0)
    qp.CNOT([0, 1])
    return qp.expval(qp.Z(0))

ok()   # -> 0.0
MWE 2 — root cause: the built-in rule bytecode cannot be (re)built

The bytecode regenerator crashes deterministically, independent of any build artifact:

from catalyst.decomposition.precompile_decomposition_rules import precompile_decomp_rules

precompile_decomp_rules("/tmp/rules.mlirbc")
AssertionError: Graph-based decomposition expects an Operator2 instance,
got <class 'pennylane.ops.qubit.parametric_ops_single_qubit.RY'>
of type <class 'pennylane.core.operator.meta.OperatorMeta'>
  at frontend/catalyst/decomposition/precompile_decomposition_rules.py:143

The COMPILER_OPS_FOR_DECOMPOSITION loop passes an operator class (e.g. RX/RY/RZ) — which isinstance(op, Operator2) treats as non-Operator2 — into compile_decomposition_rules, which then asserts it expected an Operator2 instance.

Written with the help of genAI.

Dominant language
Python
Stars
234
Forks
84
Avg merge
2d 15h
Merged PRs (30d)
66

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from PennyLaneAI/catalyst

All issues in PennyLaneAI/catalyst

Similar issues

More Python issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.