def controlled(U, ctrl=(1,), dim=None): r"""Controlled gate. Returns the (t+1)-qudit controlled-U gate, where t == length(ctrl). U has to be a square matrix. ctrl is an integer vector defining the control nodes. It has one entry k per control qudit, denoting the required computational basis state :math:`|k\rangle` for that particular qudit. Value k == -1 denotes no control. dim is the dimensions vector for the control qudits. If not given, all controls are assumed to be qubits. Examples: controlled(NOT, [1]) gives the standard CNOT gate. controlled(NOT, [1, 1]) gives the Toffoli gate. """ # Ville Bergholm 2009-2011 # TODO generalization, uniformly controlled gates? if isscalar(dim): dim = (dim,) # scalar into a tuple t = len(ctrl) if dim == None: dim = qubits(t) # qubits by default if t != len(dim): raise ValueError('ctrl and dim vectors have unequal lengths.') if any(array(ctrl) >= array(dim)): raise ValueError('Control on non-existant state.') yes = 1 # just the diagonal for k in range(t): if ctrl[k] >= 0: temp = zeros(dim[k]) temp[ctrl[k]] = 1 # control on k yes = kron(yes, temp) else: yes = kron(yes, ones(dim[k])) # no control on this qudit no = 1 - yes T = prod(dim) dim = list(dim) if isinstance(U, lmap): d1 = dim + list(U.dim[0]) d2 = dim + list(U.dim[1]) U = U.data else: d1 = dim + [U.shape[0]] d2 = dim + [U.shape[1]] # controlled gates only make sense for square matrices U (we need an identity transformation for the 'no' cases!) U_dim = U.shape[0] S = U_dim * T out = sparse.spdiags(kron(no, ones(U_dim)), 0, S, S) + sparse.kron(sparse.spdiags(yes, 0, T, T), U) return lmap(out, (d1, d2))
def walsh(n): """Walsh-Hadamard gate. Returns the Walsh-Hadamard gate for n qubits. The returned lmap is dense. """ # Ville Bergholm 2009-2010 from base import H U = 1 for k in range(n): U = kron(U, H) dim = qubits(n) return lmap(U, (dim, dim))