Skip to content

Implement ZNE local-random unitary folding#2956

Open
jk20342 wants to merge 13 commits into
PennyLaneAI:mainfrom
jk20342:zne-random-local-folding
Open

Implement ZNE local-random unitary folding#2956
jk20342 wants to merge 13 commits into
PennyLaneAI:mainfrom
jk20342:zne-random-local-folding

Conversation

@jk20342

@jk20342 jk20342 commented Jun 17, 2026

Copy link
Copy Markdown

Context:
The local-random folding option for mitigate_with_zne was wired through the enum and op but the MLIR randomLocalFolding was a stub

Description of the Change:
Implemented randomLocalFolding in the --lower-mitigation pass: each gate is wrapped in a fold loop where every iteration flips a runtime coin (__catalyst__rt__random_double() < 0.5) deciding whether to insert a G G^\dagger pair. Added the __catalyst__rt__random_double runtime CAPI (backed by the ExecutionContext PRNG) and unblocked folding="local-random" in the frontend.

Benefits:
local-random folding now works end-to-end, with per-gate fold counts randomized at runtime and reproducible under qjit(seed=...).

Possible Drawbacks:
Folding shares the single qjit seed, so it can't be seeded independently of measurement readout yet.

Related GitHub Issues:
#755

@github-actions github-actions Bot added the external PRs where the author is not a part of PennyLane Org (or part of external contributors team) label Jun 17, 2026
@dime10

dime10 commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Thank you for the PR @jk20342 🎉

Before reviewing code, my main concern was figuring out what the feature is even supposed to do.

each gate is wrapped in a fold loop where every iteration flips a runtime coin (__catalyst__rt__random_double() < 0.5) deciding whether to insert a G G^\dagger pair

It sounds like in your PR each gate is wrapped in a loop that iterates (scaling_factor-1)/2 number of times, and each iteration inserts an additional GG† pair with 50% probability. That implies that the total size of the circuit is roughly scaled by 1 + num_iterations*num_gates_inserted*insertion_probability or 1 + (scaling_factor-1)/2 * 2 * 50% which equals (1+scale_factor)/2.


The issue is pretty sparse about the desired behaviour, and mostly refers to the UF documentation. Reading through it I'm getting the following relevant info:

For any noise scaling function, if scale_factor is equal to 1, the input circuit is unchanged and it is subject to the base noise of the backend.

This is satisfied.

When scale_factor is an odd integer, the number of gates is scaled exactly as dictated by the value of scale_factor.

Looking at the odd integer scale factors section, this is not 100% clear to me, but it sounds like odd scaling factors might lead to a predictably uniformly scaled circuit? The example in that section, which also uses local-random folding, does not show any randomness either (admittedly it is very small).

Unfortunately, the API docs for the random folding function don't go into details on how the sampling is performed exactly, except for:

Returns a new folded circuit by applying the map G -> G G^dag G to a subset of gates of the input circuit, different indices randomly sampled without replacement.

So gate indices (from the circuit) are sampled randomly (probability distribution not specified, although there is an optional fidelity mechanism that might affect this) without replacement.

I think the "index sampling without replacement" part could support the hypothesis that random folding with a scale factor of 3 would indeed lead to a uniformly scaled circuit.

The folded circuit has a number of gates approximately equal to scale_factor * n where n is the number of gates in the input circuit.

What is clearer I think is that the proposed implementation doesn't meet this scaling factor target, since it generates (1+scale_factor)/2 * n many gates, rather than scale_factor * n.


One interpretation is that the random folding for odd integer scale factors does not behave any differently than the "local-all" folding, and that the randomness is only meaningful for fractional scale_factors (currently not supported in the Catalyst implementation), or perhaps when the fidelity dict is used.

What are your thoughts on this? Perhaps the best thing to do would be to reach out to mitiq maintainers, dig into their source code, or play around with their tools to figure out the intended behaviour.

@jk20342

jk20342 commented Jun 17, 2026

Copy link
Copy Markdown
Author

Hey @dime10 took a look at the mitiq docs https://mitiq.readthedocs.io/en/stable/guide/zne-3-options.html and https://mitiq.readthedocs.io/en/stable/_modules/mitiq/zne/scaling/folding.html from my understanding the rule is if the scale factor is an odd integer 1+2n then all gates are folded n times -> G - G (G G^\dagger)^((sf-1)/2): and a generic real \lambda = 1 + 2(n + \\delta), \\delta < 1 all gates folded n time and a subset is folded one more time. So the randomness only enters from the fractional remainder \delta

going to make some changes to allow real sf \ge 1 then pass base + \\delta to the mitigation.zne op and implement unconditional base folds + one random_double() < \\delta fold

that looks to be correct behavior to me if you could verify on your end :)

@jk20342

jk20342 commented Jun 17, 2026

Copy link
Copy Markdown
Author

ie per gate:
fold base = floor((sf-1)/2) times unconditionally then
fold one extra time with probability \delta = (sf-1)/2 - base,
then the original gate.

@jk20342

jk20342 commented Jun 18, 2026

Copy link
Copy Markdown
Author

I would leave the fidelity-weighted variant out though since catalyst doesnt expose per-gate fidelities yet

@dime10

dime10 commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Great, sounds like we have the same interpretation then :) Your proposal sounds reasonable!

@jk20342

jk20342 commented Jun 19, 2026

Copy link
Copy Markdown
Author

@dime10 made the changes :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

external PRs where the author is not a part of PennyLane Org (or part of external contributors team)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants