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

Circuit.unitary breaks in the presente of two-qubit gate initialized by Gate.controlled_by #1279

Open
renatomello opened this issue Mar 20, 2024 · 2 comments
Labels
bug Something isn't working
Milestone

Comments

@renatomello
Copy link
Contributor

Code to reproduce bug:

from qibo import Circuit, gates

circuit = Circuit(2)
circuit.add(gates.H(0).controlled_by(1))

circuit.unitary()
@renatomello renatomello added the bug Something isn't working label Mar 20, 2024
@rnhmjoj
Copy link

rnhmjoj commented Jun 6, 2024

gates.Unitary has the same problem, It seems the matrix representation is not updated after calling controlled_by:

>>> q.gates.X(1).controlled_by(0).matrix()
array([[1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j],
       [0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j]])
>>> q.gates.Unitary(np.array([[0,1],[1,0]])).controlled_by(0).matrix()
array([[0.+0.j, 1.+0.j],
       [1.+0.j, 0.+0.j]])

@rnhmjoj
Copy link

rnhmjoj commented Jun 8, 2024

To investigate a bit I made a custom gate by subclassing gates.Gate and implemented the .matrix() method myself.
I thus discovered what seems to be an inconsistency with controlled gates in how matrix() is interpreted.
When using the numpy backed it seems that .matrix_fused() calls .matrix() expecting the full 2ⁿ×2ⁿ controlled matrix representation, while .apply_gate() wants the 2×2 matrix.

With this hack I can work around the issue consistently:

class FGate(q.gates.Gate):
    '''
    The F(α, β) gate is an multi-controlled gate defined by:

         F(α,β) |1..10> = α|1..10>
         F(α,β) |1..11> = β|1..10>

    Its matrix representation is diag(1,..,1,α,β).
    '''
    def __init__(self, diag, target, name='F'):
        super().__init__()
        self._values = diag
        self.unitary = True
        self.target_qubits = (target,)
        self.name = 'fgate'
        self.draw_label = name
        controls = range(target)
        if controls:
            self.control_qubits = tuple(controls)
            self.is_controlled_by = True

    def matrix(self, backend=None):
        import inspect
        caller = inspect.stack()[1]

        if caller.function == 'matrix_fused':
            npad = len(self._values) * (2 ** len(self.control_qubits) - 1)
            return np.diag(np.concatenate([np.ones(npad), self._values]))
        elif caller.function == 'apply_gate':
            return np.diag(self._values)

@MatteoRobbiati MatteoRobbiati added this to the Qibo 0.2.9 milestone Jun 11, 2024
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

3 participants