Skip to content

Conversation

@weinbe58
Copy link
Member

@weinbe58 weinbe58 commented Nov 25, 2025

because of issue: #512 I am adding a slight modification to HintLen so it will not add hints inside For/IfElse bodies as that is causing some other passes to fail for example in AddressiveUnroll in bloqade-circuit the following example doesn't unroll properly:

from bloqade import qubit, squin
from kirin.dialects import ilist
from bloqade.rewrite.passes.aggressive_unroll import AggressiveUnroll

@squin.kernel(typeinfer=True, fold=True)
def log_depth_ghz():
    size = 8
    q0 = qubit.new()
    squin.h(q0)
    reg = ilist.IList([q0])
    for i in range(size):
        current = len(reg)
        missing = size - current
        if missing > current:
            num_alloc = current
        else:
            num_alloc = missing

        if num_alloc > 0:
            new_qubits = qubit.qalloc(num_alloc)
            squin.broadcast.cx(reg[:num_alloc], new_qubits)
            reg = reg + new_qubits

unroll = AggressiveUnroll(log_depth_ghz.dialects, no_raise=True)
result = unroll.fixpoint(log_depth_ghz)
log_depth_ghz.print()

Before this PR:

func.func @log_depth_ghz() -> !py.NoneType {
  ^0(%log_depth_ghz_self):
  │           %q0 = qubit.new() : !py.Qubit%reg = py.ilist.new(values=(%q0)){elem_type=!py.Qubit} : !py.IList[!py.Qubit, Literal(1,int)]
  │                 squin.gate.h(qubits=%reg)
  │            %0 = qubit.new() : !py.Qubit%new_qubits = py.ilist.new(values=(%0)){elem_type=!Any} : !py.IList[!py.Qubit, Literal(1,int)]
  │                 squin.gate.cx(controls=%reg, targets=%new_qubits)
  │            %1 = qubit.new() : !py.Qubit%new_qubits_1 = py.ilist.new(values=(%1)){elem_type=!Any} : !py.IList[!py.Qubit, Literal(1,int)]
  │                 squin.gate.cx(controls=%reg, targets=%new_qubits_1)
  │            %2 = qubit.new() : !py.Qubit%new_qubits_2 = py.ilist.new(values=(%2)){elem_type=!Any} : !py.IList[!py.Qubit, Literal(1,int)]
  │                 squin.gate.cx(controls=%reg, targets=%new_qubits_2)
  │            %3 = qubit.new() : !py.Qubit%new_qubits_3 = py.ilist.new(values=(%3)){elem_type=!Any} : !py.IList[!py.Qubit, Literal(1,int)]
  │                 squin.gate.cx(controls=%reg, targets=%new_qubits_3)
  │            %4 = qubit.new() : !py.Qubit%new_qubits_4 = py.ilist.new(values=(%4)){elem_type=!Any} : !py.IList[!py.Qubit, Literal(1,int)]
  │                 squin.gate.cx(controls=%reg, targets=%new_qubits_4)
  │            %5 = qubit.new() : !py.Qubit%new_qubits_5 = py.ilist.new(values=(%5)){elem_type=!Any} : !py.IList[!py.Qubit, Literal(1,int)]
  │                 squin.gate.cx(controls=%reg, targets=%new_qubits_5)
  │            %6 = qubit.new() : !py.Qubit%new_qubits_6 = py.ilist.new(values=(%6)){elem_type=!Any} : !py.IList[!py.Qubit, Literal(1,int)]
  │                 squin.gate.cx(controls=%reg, targets=%new_qubits_6)
  │            %7 = qubit.new() : !py.Qubit%new_qubits_7 = py.ilist.new(values=(%7)){elem_type=!Any} : !py.IList[!py.Qubit, Literal(1,int)]
  │                 squin.gate.cx(controls=%reg, targets=%new_qubits_7)
  │            %8 = func.const.none() : !py.NoneTypefunc.return %8

After this PR:

func.func @main() -> !py.NoneType {
  ^0(%main_self):
  │           %q0 = qubit.new() : !py.Qubit%reg = py.ilist.new(values=(%q0)){elem_type=!py.Qubit} : !py.IList[!py.Qubit, Literal(1,int)]
  │                 squin.gate.h(qubits=%reg)
  │            %0 = qubit.new() : !py.Qubit%new_qubits = py.ilist.new(values=(%0)){elem_type=!Any} : !py.IList[!py.Qubit, Literal(1,int)]
  │                 squin.gate.cx(controls=%reg, targets=%new_qubits)
  │            %1 = qubit.new() : !py.Qubit%2 = qubit.new() : !py.Qubit%new_qubits_1 = py.ilist.new(values=(%1, %2)){elem_type=!Any} : !py.IList[!py.Qubit, Literal(2,int)]
  │     %controls = py.ilist.new(values=(%q0, %0)){elem_type=!Any} : !py.IList[!py.Qubit, Literal(2,int)]
  │                 squin.gate.cx(controls=%controls, targets=%new_qubits_1)
  │            %3 = qubit.new() : !py.Qubit%4 = qubit.new() : !py.Qubit%5 = qubit.new() : !py.Qubit%6 = qubit.new() : !py.Qubit%new_qubits_2 = py.ilist.new(values=(%3, %4, %5, %6)){elem_type=!Any} : !py.IList[!py.Qubit, Literal(4,int)]
  │   %controls_1 = py.ilist.new(values=(%q0, %0, %1, %2)){elem_type=!Any} : !py.IList[!py.Qubit, Literal(4,int)]
  │                 squin.gate.cx(controls=%controls_1, targets=%new_qubits_2)
  │            %7 = func.const.none() : !py.NoneTypefunc.return %7
} // func.func main

which is clearly correct

The issue is because the type inference result for reg is wrong inside the for loop that means that current = len(reg) is being replaced with current = 1 which after unrolling propagates an incorrect value for every iteration after unrolling the loop. If you do not add hints inside the for loop body this will not happen so the program unrolls correctly.

In principle we do not need to be as aggressive with this rewrite rule anyways as we do not unroll inner loops, therefore, adding hints inside the body of a loop is not really necessary.

@weinbe58 weinbe58 changed the title Making HintLen less aggressive Making ilist.rewrite.HintLen less aggressive Nov 25, 2025
@github-actions
Copy link
Contributor

github-actions bot commented Nov 25, 2025

☂️ Python Coverage

current status: ✅

Overall Coverage

Lines Covered Coverage Threshold Status
11734 10469 89% 0% 🟢

New Files

No new covered files...

Modified Files

File Coverage Status
src/kirin/dialects/ilist/rewrite/hint_len.py 92% 🟢
TOTAL 92% 🟢

updated for commit: ed3c04e by action🐍

@codecov
Copy link

codecov bot commented Nov 25, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@github-actions
Copy link
Contributor

github-actions bot commented Nov 25, 2025

PR Preview Action v1.6.3
Preview removed because the pull request was closed.
2025-12-01 21:03 UTC

@weinbe58 weinbe58 merged commit a7a3f28 into main Dec 1, 2025
12 of 13 checks passed
@weinbe58 weinbe58 deleted the phil/less-aggressive-hint-len branch December 1, 2025 21:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants