def __init__( self, mixture: Iterable[Tuple[float, np.ndarray]], key: Union[str, value.MeasurementKey, None] = None, validate: bool = False, ): mixture = list(mixture) if not mixture: raise ValueError('MixedUnitaryChannel must have at least one unitary.') if not protocols.approx_eq(sum(p[0] for p in mixture), 1): raise ValueError('Unitary probabilities must sum to 1.') m0 = mixture[0][1] num_qubits = np.log2(m0.shape[0]) if not num_qubits.is_integer() or m0.shape[1] != m0.shape[0]: raise ValueError( f'Input mixture of shape {m0.shape} does not ' 'represent a square operator over qubits.' ) self._num_qubits = int(num_qubits) for i, op in enumerate(p[1] for p in mixture): if not op.shape == m0.shape: raise ValueError( f'Inconsistent unitary shapes: op[0]: {m0.shape}, op[{i}]: {op.shape}' ) if validate and not linalg.is_unitary(op): raise ValueError(f'Element {i} of mixture is non-unitary.') self._mixture = mixture if not isinstance(key, value.MeasurementKey) and key is not None: key = value.MeasurementKey(key) self._key = key
def __init__(self, matrix: np.ndarray, *, qid_shape: Optional[Iterable[int]] = None) -> None: """Initializes a matrix gate. Args: matrix: The matrix that defines the gate. qid_shape: The shape of state tensor that the matrix applies to. If not specified, this value is inferred by assuming that the matrix is supposed to apply to qubits. """ if len(matrix.shape) != 2 or matrix.shape[0] != matrix.shape[1]: raise ValueError('`matrix` must be a square 2d numpy array.') if qid_shape is None: n = int(np.round(np.log2(matrix.shape[0] or 1))) if 2**n != matrix.shape[0]: raise ValueError( f'Matrix width ({matrix.shape[0]}) is not a power of 2 and ' f'qid_shape is not specified.') qid_shape = (2, ) * n self._matrix = matrix self._qid_shape = tuple(qid_shape) m = int(np.prod(self._qid_shape)) if self._matrix.shape != (m, m): raise ValueError('Wrong matrix shape for qid_shape.\n' f'Matrix shape: {self._matrix.shape}\n' f'qid_shape: {self._qid_shape}\n') if not linalg.is_unitary(matrix): raise ValueError(f'Not a unitary matrix: {self._matrix}')
def __init__(self, matrix: np.ndarray) -> None: """ Initializes the 2-qubit matrix gate. Args: matrix: The matrix that defines the gate. """ if matrix.shape != (2, 2) or not linalg.is_unitary(matrix): raise ValueError('Not a 2x2 unitary matrix: {}'.format(matrix)) self._matrix = matrix
def _strat_apply_mixture(self, val: Any, qubits: Sequence['cirq.Qid']) -> bool: mixture = protocols.mixture(val, None) if mixture is None: return NotImplemented if not all(linalg.is_unitary(m) for _, m in mixture): return NotImplemented # coverage: ignore probabilities, unitaries = zip(*mixture) index = self.prng.choice(len(unitaries), p=probabilities) return self._strat_act_from_single_qubit_decompose( matrix_gates.MatrixGate(unitaries[index]), qubits )
def __init__(self, matrix: Optional[np.ndarray]) -> None: """ Initializes the 2-qubit matrix gate. Args: matrix: The matrix that defines the gate. Child classes can instead instead implement the matrix method and pass in None. """ if matrix is not None and (matrix.shape != (2, 2) or not linalg.is_unitary(matrix)): raise ValueError('Not a 2x2 unitary matrix: {}'.format(matrix)) self._matrix = matrix
def __init__(self, matrix: np.ndarray) -> None: """ Initializes the 2-qubit matrix gate. Args: matrix: The matrix that defines the gate. """ if (len(matrix.shape) != 2 or matrix.shape[0] != matrix.shape[1] or not linalg.is_unitary(matrix)): raise ValueError( 'Not a 2x2 (or d x d) unitary matrix: {}'.format(matrix)) self._matrix = matrix
def __init__(self, matrix: np.ndarray) -> None: """ Initializes the 2-qubit matrix gate. Args: matrix: The matrix that defines the gate. """ if matrix.shape not in [(cirq.QUDIT_LEVELS, cirq.QUDIT_LEVELS) ] or not linalg.is_unitary(matrix): raise ValueError( 'Unitary matrix has wrong dimensions: {}'.format(matrix)) self._matrix = matrix
def __init__( self, matrix: np.ndarray, *, name: str = None, qid_shape: Optional[Iterable[int]] = None, unitary_check_rtol: float = 1e-5, unitary_check_atol: float = 1e-8, ) -> None: """Initializes a matrix gate. Args: matrix: The matrix that defines the gate. name: The optional name of the gate to be displayed. qid_shape: The shape of state tensor that the matrix applies to. If not specified, this value is inferred by assuming that the matrix is supposed to apply to qubits. unitary_check_rtol: The relative tolerance for checking whether the supplied matrix is unitary. See `cirq.is_unitary`. unitary_check_atol: The absolute tolerance for checking whether the supplied matrix is unitary. See `cirq.is_unitary`. Raises: ValueError: If the matrix is not a square numpy array, if the matrix does not match the `qid_shape`, if `qid_shape` is not supplied and the matrix dimension is not a power of 2, or if the matrix not unitary (to the supplied precisions). """ if len(matrix.shape) != 2 or matrix.shape[0] != matrix.shape[1]: raise ValueError('`matrix` must be a square 2d numpy array.') if qid_shape is None: n = int(np.round(np.log2(matrix.shape[0] or 1))) if 2**n != matrix.shape[0]: raise ValueError( f'Matrix width ({matrix.shape[0]}) is not a power of 2 and ' f'qid_shape is not specified.') qid_shape = (2, ) * n self._matrix = matrix self._qid_shape = tuple(qid_shape) self._name = name m = int(np.prod(self._qid_shape, dtype=np.int64)) if self._matrix.shape != (m, m): raise ValueError('Wrong matrix shape for qid_shape.\n' f'Matrix shape: {self._matrix.shape}\n' f'qid_shape: {self._qid_shape}\n') if not linalg.is_unitary( matrix, rtol=unitary_check_rtol, atol=unitary_check_atol): raise ValueError(f'Not a unitary matrix: {self._matrix}')
def from_unitary(u: np.ndarray) -> Optional['SingleQubitCliffordGate']: """Creates Clifford gate with given unitary (up to global phase). Args: u: 2x2 unitary matrix of a Clifford gate. Returns: SingleQubitCliffordGate, whose matrix is equal to given matrix (up to global phase), or `None` if `u` is not a matrix of a single-qubit Clifford gate. """ if u.shape != (2, 2) or not linalg.is_unitary(u): return None x = protocols.unitary(pauli_gates.X) z = protocols.unitary(pauli_gates.Z) x_to = _to_pauli_transform(u @ x @ u.conj().T) z_to = _to_pauli_transform(u @ z @ u.conj().T) if x_to is None or z_to is None: return None return SingleQubitCliffordGate.from_double_map({pauli_gates.X: x_to, pauli_gates.Z: z_to})
def decompose_multi_controlled_rotation( matrix: np.ndarray, controls: List['cirq.Qid'], target: 'cirq.Qid') -> List['cirq.Operation']: """Implements action of multi-controlled unitary gate. Returns a sequence of operations, which is equivalent to applying single-qubit gate with matrix `matrix` on `target`, controlled by `controls`. Result is guaranteed to consist exclusively of 1-qubit, CNOT and CCNOT gates. If matrix is special unitary, result has length `O(len(controls))`. Otherwise result has length `O(len(controls)**2)`. References: [1] Barenco, Bennett et al. Elementary gates for quantum computation. 1995. https://arxiv.org/pdf/quant-ph/9503016.pdf Args: matrix - 2x2 numpy unitary matrix (of real or complex dtype). controls - control qubits. targets - target qubits. Returns: A list of operations which, applied in a sequence, are equivalent to applying `MatrixGate(matrix).on(target).controlled_by(*controls)`. """ assert is_unitary(matrix) assert matrix.shape == (2, 2) if len(controls) == 0: return [ops.MatrixGate(matrix).on(target)] elif len(controls) == 1: return _decompose_single_ctrl(matrix, controls[0], target) elif is_special_unitary(matrix): return _decompose_su(matrix, controls, target) else: return _decompose_recursive(matrix, 1.0, controls, target, [])
def _has_unitary_(self) -> bool: return linalg.is_unitary(self.matrix())
def _unitary_(self) -> np.ndarray: m = self.matrix() if linalg.is_unitary(m): return m raise ValueError(f'{self} is not unitary')
def _has_unitary_(self) -> bool: m = self.matrix() return m is not NotImplemented and linalg.is_unitary(m)
def test_random_unitary(): u1 = random_unitary(2) u2 = random_unitary(2) assert is_unitary(u1) assert is_unitary(u2) assert not np.allclose(u1, u2)