Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Specific sequence of gate applications and QASM3 conversion results in "only length-1 arrays can be converted to Python scalars" #11558

Open
ljkitt opened this issue Jan 14, 2024 · 6 comments
Labels
bug Something isn't working mod: qasm3 Related to OpenQASM 3 import or export

Comments

@ljkitt
Copy link

ljkitt commented Jan 14, 2024

Environment

  • Qiskit Terra version: 0.45.1
  • Python version: 3.10.1

What is happening?

I've tried to simplify my circuit as much as I can and this is what I've settled with. I have a circuit with 2 qubits. The circuit has a swap gate followed by a Y gate followed by at least 18 other gates (it doesn't seem to matter what type of gate? but I'm just using Z gates in the provided example) followed by a Y gate. Attempting to convert this circuit to Qasm3 results in the following error:

Traceback (most recent call last):
  Cell In[81], line 35
    qc = qasm3.loads(qasm3.dumps(qc))
  File /opt/conda/lib/python3.10/site-packages/qiskit/qasm3/__init__.py:194 in dumps
    return Exporter(**kwargs).dumps(circuit)
  File /opt/conda/lib/python3.10/site-packages/qiskit/qasm3/exporter.py:181 in dumps
    self.dump(circuit, stream)
  File /opt/conda/lib/python3.10/site-packages/qiskit/qasm3/exporter.py:195 in dump
    builder.build_program()
  File /opt/conda/lib/python3.10/site-packages/qiskit/qasm3/exporter.py:445 in build_program
    return ast.Program(self.build_header(), self.build_global_statements())
  File /opt/conda/lib/python3.10/site-packages/qiskit/qasm3/exporter.py:552 in build_global_statements
    quantum_instructions = self.build_quantum_instructions(context.data)
  File /opt/conda/lib/python3.10/site-packages/qiskit/qasm3/exporter.py:781 in build_quantum_instructions
    nodes = [self.build_gate_call(instruction)]
  File /opt/conda/lib/python3.10/site-packages/qiskit/qasm3/exporter.py:977 in build_gate_call
    parameters = [
  File /opt/conda/lib/python3.10/site-packages/qiskit/qasm3/exporter.py:978 in <listcomp>
    ast.StringifyAndPray(pi_check(self._rebind_scoped_parameters(param), output="qasm"))
  File /opt/conda/lib/python3.10/site-packages/qiskit/circuit/tools/pi_check.py:176 in pi_check
    complex_inpt = complex(inpt)
TypeError: only length-1 arrays can be converted to Python scalars

How can we reproduce the issue?

Run the following:

import qiskit
from qiskit import QuantumCircuit, ClassicalRegister, QuantumRegister
from qiskit.circuit.library.standard_gates import *

qr = QuantumRegister(2, name='qr')
cr = ClassicalRegister(2, name='cr')
qc = QuantumCircuit(qr, cr, name='qc')
qc.append(SwapGate(), qargs=[qr[0], qr[1]], cargs=[])
qc.append(YGate(), qargs=[qr[1]], cargs=[])
qc.append(ZGate(), qargs=[qr[1]], cargs=[])
qc.append(ZGate(), qargs=[qr[1]], cargs=[])
qc.append(ZGate(), qargs=[qr[1]], cargs=[])
qc.append(ZGate(), qargs=[qr[1]], cargs=[])
qc.append(ZGate(), qargs=[qr[1]], cargs=[])
qc.append(ZGate(), qargs=[qr[1]], cargs=[])
qc.append(ZGate(), qargs=[qr[1]], cargs=[])
qc.append(ZGate(), qargs=[qr[1]], cargs=[])
qc.append(ZGate(), qargs=[qr[1]], cargs=[])
qc.append(ZGate(), qargs=[qr[1]], cargs=[])
qc.append(ZGate(), qargs=[qr[1]], cargs=[])
qc.append(ZGate(), qargs=[qr[1]], cargs=[])
qc.append(ZGate(), qargs=[qr[1]], cargs=[])
qc.append(ZGate(), qargs=[qr[1]], cargs=[])
qc.append(ZGate(), qargs=[qr[1]], cargs=[])
qc.append(ZGate(), qargs=[qr[1]], cargs=[])
qc.append(ZGate(), qargs=[qr[1]], cargs=[])
qc.append(ZGate(), qargs=[qr[1]], cargs=[])
qc.append(YGate(), qargs=[qr[1]], cargs=[])
qc.measure(qr,cr)

from qiskit import transpile
qc = transpile(qc, basis_gates=None, optimization_level=3, coupling_map=None)

from qiskit import qasm3
qasm3.dumps(qc)

What should happen?

The circuit should execute normally without error.

Any suggestions?

No response

@ljkitt ljkitt added the bug Something isn't working label Jan 14, 2024
@jakelishman
Copy link
Member

On each of these issues, please can you try updating to the most recent version of Qiskit and seeing which of the problems persist? I don't know if any are fixed, but that's always the first diagnostic step.

@ljkitt
Copy link
Author

ljkitt commented Jan 14, 2024

Sorry, I didn't notice I wasn't on the latest version there! I reran each on 0.45.1 and still encountered the same bugs. I'll edit my reports to this version.

@jakelishman
Copy link
Member

Thanks - it's good to know.

This appears to be related to the UnitaryGate operation that the transpiler at level 3 uses during optimisation. One of the things we do is to collect all 2q runs into a single matrix (the UnitaryGate), then directly resynthesise that back into basis gates. When no basis gates are supplied, we output the UnitaryGate directly, since in Qiskit's data model, it's a valid gate - simulators often have it in their basis gates.

The problem comes because UnitaryGate somewhat breaks the params model of Qiskit, by having an entire NumPy array in there (the matrix). The OpenQASM 2 exporter detects that it can't handle the parameter and sensibly doesn't attempt to emit one, but the OpenQASM 3 exporter doesn't have the same handling - in general, OpenQASM 3 is more expressive, so there's more that could be represented.

The bug doesn't appear on main, but that's not really because we've fixed the OpenQASM 3-export problem, it's just changes in the transpiler have meant that we no longer collapse the gate chain into a UnitaryGate in the same way.

For the time being, if OpenQASM 3 export is necessary for you, you might need to set some basis_gates, or use optimization_level=2. For what it's worth, calling transpile with basis_gates=None and coupling_map=None won't do all that much - it might make a few simplifications here are there, but largely it needs some basis gates or a coupling map (or better yet, a full backend) before its most powerful passes can know what to do.

@jakelishman
Copy link
Member

Here's a minimal reproducer that reproduces the complete bug directly on main, as well as Qiskits 0.25 and 0.45:

import numpy as np
import qiskit.qasm3

qc = qiskit.QuantumCircuit(1)
qc.unitary(np.eye(2), [0])
qiskit.qasm3.dumps(qc)

@jakelishman jakelishman added the mod: qasm3 Related to OpenQASM 3 import or export label Jan 15, 2024
@dlyongemallo
Copy link

I encountered this exact same bug, but I wasn't using transpilation. You already have the minimal repro case, but for the record I was trying to manually create the Hardy state 1/sqrt(3)(|00> + |01> + |10> by doing the following:

#!/usr/bin/env python3

from qiskit import QuantumCircuit
from qiskit.quantum_info.operators import Operator
from math import sqrt
import numpy as np
import qiskit.qasm3

circuit = qiskit.QuantumCircuit(2)
op = Operator([
    [sqrt(1/3),  sqrt(2/3)],
    [sqrt(2/3), -sqrt(1/3)]
])
circuit.unitary(op, [0])
circuit.ch(0, 1)
circuit.cx(1, 0)
qiskit.qasm3.dumps(circuit)

I can stick to OpenQASM 2 export for now, but is there a workaround for OpenQASM 3?

@jakelishman
Copy link
Member

The best workaround I can think of would be to tranpsile the circuit with no coupling map and a basis gates containing stuff in the Qiskit standard library - something like

import qiskit.qasm3
from qiskit.circuit.library.standard_gates import get_standard_gate_name_mapping

basis_gates = list(get_standard_gate_name_mapping())

qiskit.qasm3.dumps(qiskit.transpile(qc, basis_gates=basis_gates, optimization_level=0))

It's not ideal, since you'll lose the structure of your circuit, and we're for sure interested in improving the exporter so a) it doesn't choke on gates it should be able to handle and b) it does this kind of gate decomposition automatically to match the header files it's exporting against better. That's on the roadmap, somewhat encompassed by #10737.

Fwiw, OpenQASM 3 is mostly an interoperation language for Qiskit, and we can't generally round-trip our circuits losslessly through it. If you're looking for a way to persist circuits to files, you might want to look at qiskit.qpy.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working mod: qasm3 Related to OpenQASM 3 import or export
Projects
None yet
Development

No branches or pull requests

3 participants