Skip to content

Code Bug with Synthesizing Permutation Matrix #4928

Closed

Description

@JeanZSJ

URL to the relevant course

https://quantum.cloud.ibm.com/docs/en/tutorials/shors-algorithm

Select all that apply

  • new content request
  • typo
  • code bug
  • out-of-date content
  • broken link
  • other

Describe the fix or the content request.

I am running these two blocks on my local jupyter notebook.

def mod_mult_gate(b, N):
    """
    Modular multiplication gate from permutation matrix.
    """
    if gcd(b, N) > 1:
        print(f"Error: gcd({b},{N}) > 1")
    else:
        n = floor(log(N - 1, 2)) + 1
        U = np.full((2**n, 2**n), 0)
        for x in range(N):
            U[b * x % N][x] = 1
        for x in range(N, 2**n):
            U[x][x] = 1
        G = UnitaryGate(U)
        G.name = f"M_{b}"
        return G

# Let's build M2 using the permutation matrix definition
M2_other = mod_mult_gate(2, 15)

# Add it to a circuit
circ = QuantumCircuit(4)
circ.compose(M2_other, inplace=True)
circ = circ.decompose()

# Transpile the circuit and get the depth
coupling_map = CouplingMap.from_line(4)
pm = generate_preset_pass_manager(coupling_map=coupling_map)
transpiled_circ = pm.run(circ)

print(f"qubits: {circ.num_qubits}")
print(
    f"2q-depth: {transpiled_circ.depth(lambda x: x.operation.num_qubits==2)}"
)
print(f"2q-size: {transpiled_circ.size(lambda x: x.operation.num_qubits==2)}")
print(f"Operator counts: {transpiled_circ.count_ops()}")
transpiled_circ.decompose().draw(
    output="mpl", fold=-1, style="clifford", idle_wires=False
)

However, instead of getting:

qubits: 4
2q-depth: 94
2q-size: 96
Operator counts: OrderedDict({'cx': 45, 'swap': 32, 'u': 24, 'u1': 7, 'u3': 4, 'unitary': 3, 'circuit-335': 1, 'circuit-338': 1, 'circuit-341': 1, 'circuit-344': 1, 'circuit-347': 1, 'circuit-350': 1, 'circuit-353': 1, 'circuit-356': 1, 'circuit-359': 1, 'circuit-362': 1, 'circuit-365': 1, 'circuit-368': 1, 'circuit-371': 1, 'circuit-374': 1, 'circuit-377': 1, 'circuit-380': 1})

I got:

---------------------------------------------------------------------------
PanicException                            Traceback (most recent call last)
Cell In[24], line 7
      5 circ = QuantumCircuit(4)
      6 circ.compose(M2_other, inplace=True)
----> 7 circ = circ.decompose()
      9 # Transpile the circuit and get the depth
     10 coupling_map = CouplingMap.from_line(4)

File ~/.venv/lib/python3.13/site-packages/qiskit/circuit/quantumcircuit.py:3843, in QuantumCircuit.decompose(self, gates_to_decompose, reps)
   3841 pass_ = Decompose(gates_to_decompose, apply_synthesis=True)
   3842 for _ in range(reps):
-> 3843     dag = pass_.run(dag)
   3845 # do not copy operations, this is done in the conversion with circuit_to_dag
   3846 return dag_to_circuit(dag, copy_operations=False)

File ~/.venv/lib/python3.13/site-packages/qiskit/transpiler/passes/basis/decompose.py:74, in Decompose.run(self, dag)
     71     decomposition = control_flow.map_blocks(self.run, node.op)
     72     dag.substitute_node(node, decomposition, inplace=True)
---> 74 elif getattr(node.op, "definition", None) is None:
     75     # if we try to synthesize, turn the node into a DAGCircuit and run HLS
     76     if self.apply_synthesis:
     77         node_as_dag = _node_to_dag(node)

File ~/.venv/lib/python3.13/site-packages/qiskit/circuit/instruction.py:296, in Instruction.definition(self)
    294 """Return definition in terms of other basic gates."""
    295 if self._definition is None:
--> 296     self._define()
    297 return self._definition

File ~/.venv/lib/python3.13/site-packages/qiskit/circuit/library/generalized_gates/unitary.py:162, in UnitaryGate._define(self)
    157 else:
    158     from qiskit.synthesis.unitary.qsd import (  # pylint: disable=cyclic-import
    159         qs_decomposition,
    160     )
--> 162     self.definition = qs_decomposition(self.to_matrix())
    163     # Since iterative Quantum Shannon Decomposition may provide imprecise matrices,
    164     # we use the Isometry decomposition in this case
    165     # pylint: disable=cyclic-import
    166     from qiskit.quantum_info.operators import Operator

File ~/.venv/lib/python3.13/site-packages/qiskit/synthesis/unitary/qsd.py:122, in qs_decomposition(mat, opt_a1, opt_a2, decomposer_1q, decomposer_2q, _depth)
    119         two_q_decomp = decomposer_2q._inner_decomposer
    120     array = np.asarray(mat, dtype=complex)
    121     return QuantumCircuit._from_circuit_data(
--> 122         qsd.qs_decomposition(array, opt_a1, opt_a2, basis_1q, two_q_decomp)
    123     )
    125 #  _depth (int): Internal use parameter to track recursion depth.
    126 dim = mat.shape[0]

PanicException: called `Option::unwrap()` on a `None` value
Image Image

For new content requests - if the request is accepted, do you want to write the content?

(No answer)

Activity

  1. MeltemTolunay commented on Apr 14, 2026

    @MeltemTolunay
    Contributor

    Thank you for reporting the issue. Which version of Qiskit was used to replicate the error?

  2. JeanZSJ commented on Apr 17, 2026

    @JeanZSJ
    Author

    Hi!

    The environment I used is as follows:

    Qiskit version: 2.3.0
    Python version: 3.13.9

  3. henryzou50 commented on Jun 1, 2026

    @henryzou50
    Collaborator

    Hi JeanZSJ , thanks for the detailed report and for flagging this!

    I looked into it and can confirm the panic does occur with Qiskit 2.3.0:

    PanicException: called `Option::unwrap()` on a `None` value
      at crates/synthesis/src/qsd.rs:416
    

    The good news is that this turned out to be a bug in Qiskit itself, not in the tutorial code. It was a regression in the 2.3.0 quantum Shannon decomposition (the UnitaryGate → qs_decomposition synthesis path), tracked upstream in Qiskit#15610 and fixed by Qiskit#15635. A closely related permutation-matrix synthesis issue (Qiskit#15870) was also resolved.

    I re-ran the tutorial across versions to confirm:

    Qiskit Result
    2.3.0 ❌ Panic
    2.3.1 ✅ Runs successfully
    2.4.1 ✅ Runs successfully

    So the fix is already available in Qiskit 2.3.1 and later. Upgrading (pip install -U qiskit) resolves the error, and the notebook runs end to end.

    Since this is fixed on the Qiskit side and isn't something the tutorial needs to change, I'll go ahead and close this. Thanks again for reporting it, and feel free to reopen if you run into anything else!

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

Metadata

Metadata

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions