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
4.99k stars 2.32k forks source link

`Gate.control(num_ctrl_qubits)` is not working when `num_ctrl_qubits` > 1 #10311

Closed SimoneGasperini closed 4 weeks ago

SimoneGasperini commented 1 year ago

Environment

What is happening?

The call gate.control(num_ctrl_qubits) is raising an unexpected error when gate is one of the 2-qubit interaction RXXGate, RYYGate, RZZGate, RZXGate and num_ctrl_qubits is greater than 1.

How can we reproduce the issue?

from qiskit.circuit import Parameter
from qiskit.circuit.library import RXXGate

p = Parameter('p')
gate = RXXGate(p).control(2)

What should happen?

The code above should work and return a multi-controlled parametric RXXGate.

shivalee12 commented 1 year ago

can you pls assign me I would like to try it out!

ewinston commented 1 year ago

@shivalee12 Thanks!

shivalee12 commented 1 year ago

@ewinston @SimoneGasperini can you pls explain in detail what exactly is the issue

SimoneGasperini commented 9 months ago

@shivalee12 @ewinston Any update on this? I noticed that the same error occurs also for the multi-controlled RZGate:

from qiskit.circuit import Parameter
from qiskit.circuit.library import RZGate

p = Parameter('p')
mcrz = RZGate(p).control(2)

Same happens calling directly the QuantumCircuit.mcrz method with more than 1 control qubit:

from qiskit.circuit import Parameter
from qiskit import QuantumCircuit

p = Parameter('p')
qc = QuantumCircuit(3)
qc.mcrz(p, q_controls=[0, 1], q_target=2)
SimoneGasperini commented 9 months ago

I think that the problem is in the following code: multi_control_rotation_gates.py, lines:370-385. When the number of control qubits n_c is > 1, there is a call to the method RZGate(lam).to_matrix() that works only if lam is a float (not the case when passing a Qiskit Parameter object), raising a TypeError.

Notice that the same occurs for mcrx (see multi_control_rotation_gates.py, lines:254-260) and mrcy (see multi_control_rotation_gates.py, lines:329-335) as well, but only when n_c > 3.

It's still not 100% clear to me how the implementation of these multi-control rotation gates works but I would like to work on this if possible. Maybe @jakelishman can give more details about the issue or any general idea on a potential fix?

jakelishman commented 9 months ago

I don't have any immediate ideas about the best way to go about fixing the synthesis in these cases, because the majority of our controlled-gate synthesis routines work by numeric matrix decomposition. There's presumably abstract, matrix-free methods out there that we can use for these symbolic cases, but off the top of my head I don't know them, so I'd have to go search the literature a little for a sensible default algorithm. I know @alexanderivrii is reworking the organisation and structure of a lot of our controlled-gate synthesis code, so he's most likely to have ideas about a new strategy here.

ewinston commented 8 months ago

Symbolic decompositions could exist but the matrix elements could quickly become complex which may unreasonably slow things down. I'm not sure if there is a clean way to enable the user to make that determination so maybe the easiest thing to do is just raise with a message about requiring parameter binding or similar message.

jakelishman commented 8 months ago

We're going to need to find a way to do symbolic decompositions in some form or another as we move to support more runtime parametrisation of circuits; longer term, we'll need to be able to compile (for example) a crz gate for hardware when the rotation angle is an input angle[16] theta; or whatever. That's going to end up needing the same kind of mathematics.

Cryoris commented 1 month ago

Another solution would be to use the AnnotatedOperation and do RXXGate(p).control(2, annotated=True). Though for that to work we'd have to add parameter support to the AnnotatedOperation (which could e.g. just proxy the base_op's parameters).