Qiskit / qiskit

Qiskit is an open-source SDK for working with quantum computers at the level of extended quantum circuits, operators, and primitives.
https://www.ibm.com/quantum/qiskit
Apache License 2.0
5.3k stars 2.38k forks source link

`qiskit.qasm3.dumps` raise TypeError: only length-1 arrays can be converted to Python scalars #13362

Open harui2019 opened 1 month ago

harui2019 commented 1 month ago

Environment

What is happening?

A numpy type error raised during the execution of qiskit.qasm3.dumps .

Image Image

How can we reproduce the issue?

from qiskit import QuantumCircuit
from qiskit.quantum_info import random_unitary
from qiskit.qasm3 import dumps

qc = QuantumCircuit(4)
for i in range(4):
    qc.h(i)
    qc.append(random_unitary(2),[i])

print(qc.draw())

dumps(qc)

or

from qiskit import QuantumCircuit
from qiskit.quantum_info import random_unitary
from qiskit.qasm3 import dumps

qc = QuantumCircuit(4)
for i in range(4):
    qc.h(i)
    qc.append(random_unitary(2).to_instruction(),[i])

print(qc.draw())

dumps(qc)

What should happen?

I should be able to dump this circuit to QASM3 because qiskit.qasm2.dumps can successfully dump this circuit.

Any suggestions?

This issue appears to be mentioned in https://github.com/Qiskit/qiskit/issues/12013#issuecomment-2038064846

ldi18 commented 2 weeks ago

Hi, I just reproduced the error and would like to work on this as my first contribution to Qiskit.

gadial commented 2 weeks ago

@ldi18 feel free to go ahead. As far as I can see, this is a problem with QASM3 operating on the params of the unitary gates (which amount to its matrix) as opposed to working on the params of its definition circuit (as QASM2 does).

ldi18 commented 2 weeks ago

@gadial When I move the complex-to-string conversion into a function and apply it to each matrix element,

    def complex_to_str(inpt):
        """Convert a complex number to a string with formatting."""
        complex_inpt = complex(inpt)
        real, imag = map(normalize, [complex_inpt.real, complex_inpt.imag])

        jstr = "\\jmath" if output == "latex" else "j"
        if real == "0" and imag != "0":
            str_out = imag + jstr
        elif real != "0" and imag != "0":
            op_str = "+"
            # Remove + if imag negative except for latex fractions
            if complex_inpt.imag < 0 and (output != "latex" or "\\frac" not in imag):
                op_str = ""
            str_out = f"{real}{op_str}{imag}{jstr}"
        else:
            str_out = real
        return str_out

    if isinstance(inpt, np.ndarray):
        return np.reshape([complex_to_str(val) for val in inpt.flatten()], inpt.shape)
    return complex_to_str(inpt)

I get the following qasm3 string:

('OPENQASM 3.0;\n'
 'include "stdgates.inc";\n'
 'gate unitary(_gate_p_0) _gate_q_0 {\n'
 '  U(1.2181930078303413, 3.1803949087168197, 0.3822577065102588) _gate_q_0;\n'
 '}\n'
 'qubit[2] q;\n'
 "unitary([['-0.6309366264965464-0.5240133949751805j'\n"
 "  '0.27200760222784093+0.5033297125761635j']\n"
 " ['0.4256140835502845+0.3823370072532796j'\n"
 "  '0.36164925477003157+0.7361255879892625j']]) q[0];\n")

When importing the string with qasm3.loads(s), it gives the error line 7:9 no viable alternative at input 'unitary(['. Is this due to a missing unitary function in the loads() method? Or should the unitary([[..., ...],[..., ...]]) rather be a U(1.2181930078303413, 3.1803949087168197, 0.3822577065102588) as in the old qasm format?

gadial commented 2 weeks ago

I don't think you should handle the matrix elements. In QASM2 the matrix params do not appear on the output; it adds the definition of the gate (i.e. a circuit composed of basic gates which implements it), and I guess QASM3 should do this as well.