There are some inconsistencies coming from these two methods, and I'll list them below:
First, apparently controlled gates cannot receive more controls by using the Gate.controlled_by method, e.g.
In [1]: from qibo import Circuit, gates, set_backend
...: set_backend("numpy")
...:
...: gates.CRY(0, 1, 0.1).controlled_by(2)
[Qibo 0.2.8|INFO|2024-06-11 08:25:51]: Using numpy backend on /CPU:0
[Qibo 0.2.8|ERROR|2024-06-11 08:25:51]: Cannot use controlled_by method on gate <qibo.gates.gates.CRY object at 0x7cef2f413a60> because it is already controlled by (0,).
RuntimeError: Cannot use controlled_by method on gate <qibo.gates.gates.CRY object at 0x7cef2f413a60> because it is already controlled by (0,).
Meanwhile, it is possible to create a $2$-controlled $RY$ gate using
```python
In [3]: gates.RY(1, 0.1).controlled_by(0, 2)
Out[3]: <qibo.gates.gates.RY at 0x7cef2f2f8cd0>
This is an inconsistency because there should be no difference between a $2$-controlled $RY$ gate and a $1$-controlled $CRY$ gate. They are the same gate.
I used $RY$ as an example, but the same thing happens to other gates. For instance,
In [12]: gates.X(0).controlled_by(1, 2, 3)
Out[12]: <qibo.gates.gates.X at 0x7ceed33c5db0>
In [13]: gates.TOFFOLI(1, 2, 0).controlled_by(3)
[Qibo 0.2.8|ERROR|2024-06-11 08:37:52]: Cannot use `controlled_by` method on gate <qibo.gates.gates.TOFFOLI object at 0x7ceed3190850> because it is already controlled by (1, 2).
---------------------------------------------------------------------------
RuntimeError Traceback (most recent call last)
Cell In[13], line 1
----> 1 gates.TOFFOLI(1, 2, 0).controlled_by(3)
File ~/.local/lib/python3.10/site-packages/qibo/gates/abstract.py:311, in Gate.check_controls.<locals>.wrapper(self, *args)
309 def wrapper(self, *args):
310 if self.control_qubits:
--> 311 raise_error(
312 RuntimeError,
313 "Cannot use `controlled_by` method "
314 + f"on gate {self} because it is already "
315 + f"controlled by {self.control_qubits}.",
316 )
317 return func(self, *args)
File ~/.local/lib/python3.10/site-packages/qibo/config.py:46, in raise_error(exception, message)
39 """Raise exception with logging error.
40
41 Args:
42 exception (Exception): python exception.
43 message (str): the error message.
44 """
45 log.error(message)
---> 46 raise exception(message)
RuntimeError: Cannot use `controlled_by` method on gate <qibo.gates.gates.TOFFOLI object at 0x7ceed3190850> because it is already controlled by (1, 2).
The first issue leads to a second issue. When defining controlled $RY$ gates using the controlled_by method, somehow the gate class is changed to gates.CRY if only one control is passed. This leads to wrong gate counts when using the Circuit.gates_of_type method. Example:
There are some inconsistencies coming from these two methods, and I'll list them below:
Gate.controlled_by
method, e.g.[Qibo 0.2.8|INFO|2024-06-11 08:25:51]: Using numpy backend on /CPU:0 [Qibo 0.2.8|ERROR|2024-06-11 08:25:51]: Cannot use
controlled_by
method on gate <qibo.gates.gates.CRY object at 0x7cef2f413a60> because it is already controlled by (0,).RuntimeError Traceback (most recent call last) Cell In[1], line 4 1 from qibo import Circuit, gates, set_backend 2 set_backend("numpy") ----> 4 gates.CRY(0, 1, 0.1).controlled_by(2)
File ~/.local/lib/python3.10/site-packages/qibo/gates/abstract.py:311, in Gate.check_controls..wrapper(self, args)
309 def wrapper(self, args):
310 if self.control_qubits:
--> 311 raise_error(
312 RuntimeError,
313 "Cannot use
controlled_by
method " 314 + f"on gate {self} because it is already " 315 + f"controlled by {self.control_qubits}.", 316 ) 317 return func(self, *args)File ~/.local/lib/python3.10/site-packages/qibo/config.py:46, in raise_error(exception, message) 39 """Raise exception with logging error. 40 41 Args: 42 exception (Exception): python exception. 43 message (str): the error message. 44 """ 45 log.error(message) ---> 46 raise exception(message)
RuntimeError: Cannot use
controlled_by
method on gate <qibo.gates.gates.CRY object at 0x7cef2f413a60> because it is already controlled by (0,).This is an inconsistency because there should be no difference between a $2$-controlled $RY$ gate and a $1$-controlled $CRY$ gate. They are the same gate.
I used $RY$ as an example, but the same thing happens to other gates. For instance,
controlled_by
method, somehow the gate class is changed togates.CRY
if only one control is passed. This leads to wrong gate counts when using theCircuit.gates_of_type
method. Example:q0: ─RY─RY─RY─ q1: ────o──o── q2: ───────o──
RY: 2
cRY: 1