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

Controlled UGate with a parameterized angle fails with 4 or more controls #12135

Open
nepomechie opened this issue Apr 3, 2024 · 5 comments
Open
Labels
bug Something isn't working

Comments

@nepomechie
Copy link

Environment

  • Qiskit version: 1.0.0
  • Python version: 3.11.7
  • Operating system: macOS 14.4.1

What is happening?

A controlled UGate with a parametrized angle k and with 4 (or more) controls throws an error.

Such controlled parametrized gates are useful for constructing certain custom trial states for vqe.

How can we reproduce the issue?

from qiskit import *
from qiskit import QuantumRegister
from qiskit import QuantumCircuit
from qiskit.circuit.library import UGate
from qiskit.circuit import Parameter

k=Parameter("k")
q=QuantumRegister(5)
qc = QuantumCircuit(q)
ccU = UGate(k,0,0).control(4)
qc.append(ccU,[0,1,2,3,4])```


TypeError Traceback (most recent call last)
Cell In[3], line 10
8 q=QuantumRegister(5)
9 qc = QuantumCircuit(q)
---> 10 ccU = UGate(k,0,0).control(4)
11 qc.append(ccU,[0,1,2,3,4])

File ~/Library/Python/3.12/lib/python/site-packages/qiskit/circuit/library/standard_gates/u.py:130, in UGate.control(self, num_ctrl_qubits, label, ctrl_state, annotated)
128 gate.base_gate.label = self.label
129 else:
--> 130 gate = super().control(
131 num_ctrl_qubits=num_ctrl_qubits,
132 label=label,
133 ctrl_state=ctrl_state,
134 annotated=annotated,
135 )
136 return gate

File ~/Library/Python/3.12/lib/python/site-packages/qiskit/circuit/gate.py:121, in Gate.control(self, num_ctrl_qubits, label, ctrl_state, annotated)
117 if not annotated:
118 # pylint: disable=cyclic-import
119 from .add_control import add_control
--> 121 return add_control(self, num_ctrl_qubits, label, ctrl_state)
123 else:
124 return AnnotatedOperation(
125 self, ControlModifier(num_ctrl_qubits=num_ctrl_qubits, ctrl_state=ctrl_state)
126 )

File ~/Library/Python/3.12/lib/python/site-packages/qiskit/circuit/add_control.py:61, in add_control(operation, num_ctrl_qubits, label, ctrl_state)
58 if isinstance(operation, UnitaryGate):
59 # attempt decomposition
60 operation._define()
---> 61 cgate = control(operation, num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state)
62 if operation.label is not None:
63 cgate.base_gate = cgate.base_gate.to_mutable()

File ~/Library/Python/3.12/lib/python/site-packages/qiskit/circuit/add_control.py:187, in control(operation, num_ctrl_qubits, label, ctrl_state)
183 controlled_circ.mcrx(
184 theta, q_control, q_target[bit_indices[qargs[0]]], use_basis_gates=True
185 )
186 elif phi == 0 and lamb == 0:
--> 187 controlled_circ.mcry(
188 theta,
189 q_control,
190 q_target[bit_indices[qargs[0]]],
191 q_ancillae,
192 use_basis_gates=True,
193 )
194 elif theta == 0 and phi == 0:
195 controlled_circ.mcp(lamb, q_control, q_target[bit_indices[qargs[0]]])

File ~/Library/Python/3.12/lib/python/site-packages/qiskit/circuit/library/standard_gates/multi_control_rotation_gates.py:331, in mcry(self, theta, q_controls, q_target, q_ancillae, mode, use_basis_gates)
320 _apply_mcu_graycode(
321 self,
322 theta_step,
(...)
327 use_basis_gates=use_basis_gates,
328 )
329 else:
330 cgate = _mcsu2_real_diagonal(
--> 331 RYGate(theta).to_matrix(),
332 num_controls=len(control_qubits),
333 use_basis_gates=use_basis_gates,
334 )
335 self.compose(cgate, control_qubits + [target_qubit], inplace=True)
336 else:

File ~/Library/Python/3.12/lib/python/site-packages/qiskit/circuit/gate.py:62, in Gate.to_matrix(self)
52 """Return a Numpy.array for the gate unitary matrix.
53
54 Returns:
(...)
59 exception will be raised when this base class method is called.
60 """
61 if hasattr(self, "array"):
---> 62 return self.array(dtype=complex)
63 raise CircuitError(f"to_matrix not defined for this {type(self)}")

File ~/Library/Python/3.12/lib/python/site-packages/qiskit/circuit/library/standard_gates/ry.py:124, in RYGate.array(self, dtype)
122 def array(self, dtype=None):
123 """Return a numpy.array for the RY gate."""
--> 124 cos = math.cos(self.params[0] / 2)
125 sin = math.sin(self.params[0] / 2)
126 return numpy.array([[cos, -sin], [sin, cos]], dtype=dtype)

File ~/Library/Python/3.12/lib/python/site-packages/qiskit/circuit/parameterexpression.py:415, in ParameterExpression.float(self)
413 except (TypeError, RuntimeError) as exc:
414 if self.parameters:
--> 415 raise TypeError(
416 "ParameterExpression with unbound parameters ({}) "
417 "cannot be cast to a float.".format(self.parameters)
418 ) from None
419 # In symengine, if an expression was complex at any time, its type is likely to have
420 # stayed "complex" even when the imaginary part symbolically (i.e. exactly)
421 # cancelled out. Sympy tends to more aggressively recognise these as symbolically
422 # real. This second attempt at a cast is a way of unifying the behaviour to the
423 # more expected form for our users.
424 cval = complex(self)

TypeError: ParameterExpression with unbound parameters (dict_keys([Parameter(k)])) cannot be cast to a float.```

What should happen?

If the number of controls is less than 4, then no error is thrown:

ccU = UGate(k,0,0).control(3)
qc.append(ccU,[0,1,2,3])```

<qiskit.circuit.instructionset.InstructionSet at 0x107709330>```

Similar behavior is expected for 4 or more controls.

Any suggestions?

Thanks for your help.

CC:@diemilio

@nepomechie nepomechie added the bug Something isn't working label Apr 3, 2024
@levbishop
Copy link
Member

Perhaps related to #11993

@jakelishman
Copy link
Member

This is because of the eager attempts to synthesise controlled gates in arbitrary controls - chances are that before 4 controls, we have a particular path through the synthesis that simplifies everything down to something we can do symbolically, but not in this case.

As an immediate workaround, depending on when you will call assign_parameters, you might be able to set annotated=True in the call to control - if you assign the parameters before transpilation, that may well work for you. Fwiw, no matter how we improve this in the future, the synthesis of parametric multi-controlled gates is likely never going to work efficiently in a symbolic manner, because the decomposition algorithms for multicontrolled boxes usually make heavy use of various numerics of them to produce more efficient syntheses.

@ShellyGarion
Copy link
Member

ShellyGarion commented Apr 10, 2024

Perhaps related to #11993

This bug cannot be related to #11993 since this error already occurs in Qiskit 1.0.2 while this PR was merged only last week (and will be released in Qiksit 1.1)

@ShellyGarion
Copy link
Member

I think it may be related to this PR: #9688

in the code of mcrz (as well as mcrx and mcry) this argument: lam: ParameterValueType


is overridden by: lam (float): angle lambda

In fact, mcrz never actually had ParameterExpression, it was always overridden by float.

I also mentioned it in issue #12048

@king-p3nguin
Copy link
Contributor

king-p3nguin commented Apr 21, 2024

Perhaps this is because the function _mcsu2_real_diagonal generates gates from a numpy array.

def _mcsu2_real_diagonal(
unitary: np.ndarray,
num_controls: int,
ctrl_state: Optional[str] = None,
use_basis_gates: bool = False,
) -> QuantumCircuit:

If you try to use parameters to construct MCRX gate, it fails here

How to reproduce:

from qiskit import QuantumCircuit
from qiskit.circuit.parameter import Parameter

qc = QuantumCircuit(4)

th = Parameter('θ')

qc.mcrx(th, [0, 1, 2], 3, use_basis_gates=False)

qcinv = qc.inverse()

from qiskit.quantum_info import Operator
from qiskit.quantum_info.operators.predicates import is_identity_matrix

print(is_identity_matrix(Operator(qc).dot(qcinv.inverse()).data))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

5 participants