def compose(self, other, qargs=None, front=False): """Return the composition channel self∘other. Args: other (SparsePauliOp): an operator object. qargs (list or None): a list of subsystem positions to compose other on. front (bool or None): If False compose in standard order other(self(input)) otherwise compose in reverse order self(other(input)) [default: False] Returns: SparsePauliOp: The composed operator. Raises: QiskitError: if other cannot be converted to an Operator or has incompatible dimensions. """ # pylint: disable=invalid-name if qargs is None: qargs = getattr(other, 'qargs', None) if not isinstance(other, SparsePauliOp): other = SparsePauliOp(other) # Validate composition dimensions and qargs match self._op_shape.compose(other._op_shape, qargs, front) # Implement composition of the Pauli table x1, x2 = PauliTable._block_stack(self.table.X, other.table.X) z1, z2 = PauliTable._block_stack(self.table.Z, other.table.Z) c1, c2 = PauliTable._block_stack(self.coeffs, other.coeffs) if qargs is not None: ret_x, ret_z = x1.copy(), z1.copy() x1 = x1[:, qargs] z1 = z1[:, qargs] ret_x[:, qargs] = x1 ^ x2 ret_z[:, qargs] = z1 ^ z2 table = np.hstack([ret_x, ret_z]) else: table = np.hstack((x1 ^ x2, z1 ^ z2)) # Take product of coefficients and add phase correction coeffs = c1 * c2 # We pick additional phase terms for the products # X.Y = i * Z, Y.Z = i * X, Z.X = i * Y # Y.X = -i * Z, Z.Y = -i * X, X.Z = -i * Y if front: plus_i = (x1 & ~z1 & x2 & z2) | (x1 & z1 & ~x2 & z2) | (~x1 & z1 & x2 & ~z2) minus_i = (x2 & ~z2 & x1 & z1) | (x2 & z2 & ~x1 & z1) | (~x2 & z2 & x1 & ~z1) else: minus_i = (x1 & ~z1 & x2 & z2) | (x1 & z1 & ~x2 & z2) | (~x1 & z1 & x2 & ~z2) plus_i = (x2 & ~z2 & x1 & z1) | (x2 & z2 & ~x1 & z1) | (~x2 & z2 & x1 & ~z1) coeffs *= 1j**np.array(np.sum(plus_i, axis=1), dtype=int) coeffs *= (-1j)**np.array(np.sum(minus_i, axis=1), dtype=int) return SparsePauliOp(table, coeffs)
def from_list(obj): """Construct from a list [(pauli_str, coeffs)]""" obj = list(obj) # To convert zip or other iterable num_qubits = len(PauliTable._from_label(obj[0][0])) size = len(obj) coeffs = np.zeros(size, dtype=complex) labels = np.zeros(size, dtype='<U{}'.format(num_qubits)) for i, item in enumerate(obj): labels[i] = item[0] coeffs[i] = item[1] table = PauliTable.from_labels(labels) return SparsePauliOp(table, coeffs)
def pauli_basis(num_qubits, weight=False, pauli_list=False): """Return the ordered PauliTable or PauliList for the n-qubit Pauli basis. Args: num_qubits (int): number of qubits weight (bool): if True optionally return the basis sorted by Pauli weight rather than lexicographic order (Default: False) pauli_list (bool): if True, the return type becomes PauliList, otherwise PauliTable. Returns: PauliTable, PauliList: the Paulis for the basis """ if pauli_list: pauli_1q = PauliList(["I", "X", "Y", "Z"]) else: warnings.warn( "The pauli_basis function with PauliTable output is deprecated as of Qiskit Terra " "0.19.0 and will be removed no sooner than 3 months after the releasedate. " "Use PauliList by pauli_list=True instead.", DeprecationWarning, stacklevel=2, ) pauli_1q = PauliTable( np.array( [[False, False], [True, False], [True, True], [False, True]], dtype=bool)) if num_qubits == 1: return pauli_1q pauli = pauli_1q for _ in range(num_qubits - 1): pauli = pauli_1q.tensor(pauli) if weight: return pauli.sort(weight=True) return pauli
def pauli_basis(num_qubits, weight=False, pauli_list=False): """Return the ordered PauliTable or PauliList for the n-qubit Pauli basis. Args: num_qubits (int): number of qubits weight (bool): if True optionally return the basis sorted by Pauli weight rather than lexicographic order (Default: False) pauli_list (bool): if True, the return type becomes PauliList, otherwise PauliTable. Returns: PauliTable, PauliList: the Paulis for the basis """ if pauli_list: pauli_1q = PauliList(["I", "X", "Y", "Z"]) else: warnings.warn( "The return type of 'pauli_basis' will change from PauliTable to PauliList in a " "future release of Qiskit Terra. Returning PauliTable is deprecated as of " "Qiskit Terra 0.19, and will be removed in a future release. To immediately switch " "to the new behaviour, pass the keyword argument 'pauli_list=True'.", FutureWarning, stacklevel=2, ) pauli_1q = PauliTable( np.array( [[False, False], [True, False], [True, True], [False, True]], dtype=bool)) if num_qubits == 1: return pauli_1q pauli = pauli_1q for _ in range(num_qubits - 1): pauli = pauli_1q.tensor(pauli) if weight: return pauli.sort(weight=True) return pauli
def __init__(self, data, coeffs=None): """Initialize an operator object. Args: data (PauliTable): Pauli table of terms. coeffs (np.ndarray): complex coefficients for Pauli terms. Raises: QiskitError: If the input data or coeffs are invalid. """ if isinstance(data, SparsePauliOp): table = data._table coeffs = data._coeffs else: table = PauliTable(data) if coeffs is None: coeffs = np.ones(table.size, dtype=np.complex) # Initialize PauliTable self._table = table # Initialize Coeffs self._coeffs = np.asarray(coeffs, dtype=complex) if self._coeffs.shape != (self._table.size, ): raise QiskitError("coeff vector is incorrect shape for number" " of Paulis {} != {}".format(self._coeffs.shape, self._table.size)) # Initialize BaseOperator super().__init__(self._table._input_dims, self._table._output_dims)
def _from_label(label): """Return the symplectic representation of a Pauli stabilizer string""" # Check if first character is '+' or '-' phase = False if label[0] in ['-', '+']: phase = (label[0] == '-') label = label[1:] return PauliTable._from_label(label), phase
def _to_label(pauli, phase): """Return the Pauli stabilizer string from symplectic representation.""" # pylint: disable=arguments-differ # Cast in symplectic representation # This should avoid a copy if the pauli is already a row # in the symplectic table label = PauliTable._to_label(pauli) if phase: return '-' + label return '+' + label
def compose(self, other, qargs=None, front=False): if qargs is None: qargs = getattr(other, "qargs", None) if not isinstance(other, SparsePauliOp): other = SparsePauliOp(other) # Validate composition dimensions and qargs match self._op_shape.compose(other._op_shape, qargs, front) # Implement composition of the Pauli table x1, x2 = PauliTable._block_stack(self.table.X, other.table.X) z1, z2 = PauliTable._block_stack(self.table.Z, other.table.Z) c1, c2 = PauliTable._block_stack(self.coeffs, other.coeffs) if qargs is not None: ret_x, ret_z = x1.copy(), z1.copy() x1 = x1[:, qargs] z1 = z1[:, qargs] ret_x[:, qargs] = x1 ^ x2 ret_z[:, qargs] = z1 ^ z2 table = np.hstack([ret_x, ret_z]) else: table = np.hstack((x1 ^ x2, z1 ^ z2)) # Take product of coefficients and add phase correction coeffs = c1 * c2 # We pick additional phase terms for the products # X.Y = i * Z, Y.Z = i * X, Z.X = i * Y # Y.X = -i * Z, Z.Y = -i * X, X.Z = -i * Y if front: plus_i = (x1 & ~z1 & x2 & z2) | (x1 & z1 & ~x2 & z2) | (~x1 & z1 & x2 & ~z2) minus_i = (x2 & ~z2 & x1 & z1) | (x2 & z2 & ~x1 & z1) | (~x2 & z2 & x1 & ~z1) else: minus_i = (x1 & ~z1 & x2 & z2) | (x1 & z1 & ~x2 & z2) | (~x1 & z1 & x2 & ~z2) plus_i = (x2 & ~z2 & x1 & z1) | (x2 & z2 & ~x1 & z1) | (~x2 & z2 & x1 & ~z1) coeffs *= 1j**np.array(np.sum(plus_i, axis=1), dtype=int) coeffs *= (-1j)**np.array(np.sum(minus_i, axis=1), dtype=int) return SparsePauliOp(table, coeffs)
def _to_matrix(pauli, phase, sparse=False): """"Return the Pauli stabilizer matrix from symplectic representation. Args: pauli (array): symplectic Pauli vector. phase (bool): the phase value for the Pauli. sparse (bool): if True return a sparse CSR matrix, otherwise return a dense Numpy array (Default: False). Returns: array: if sparse=False. csr_matrix: if sparse=True. """ # pylint: disable=arguments-differ mat = PauliTable._to_matrix(pauli, sparse=sparse, real_valued=True) if phase: mat *= -1 return mat
def __getitem__(self, key): sumopcoeff = self.obj.coeff * self.obj.primitive.coeffs[key] mat = PauliTable._to_matrix( self.obj.primitive.table.array[key], sparse=sparse) return sumopcoeff * mat
def pauli(self, value): if not isinstance(value, PauliTable): value = PauliTable(value) self._array[:, :] = value._array
def __getitem__(self, key): coeff = self.obj.coeffs[key] mat = PauliTable._to_matrix(self.obj.table.array[key], sparse=sparse) return coeff * mat
def __getitem__(self, key): coeff = self.obj.coeffs[key] pauli = PauliTable._to_label(self.obj.table.array[key]) return (pauli, coeff)
def table(self, value): if not isinstance(value, PauliTable): value = PauliTable(value) self._table.array = value.array
def table(self): """DEPRECATED - Return the the PauliTable.""" return PauliTable(np.column_stack((self.paulis.x, self.paulis.z)))
def table(self, value): if not isinstance(value, PauliTable): value = PauliTable(value) self._pauli_list = PauliList(value)
def pauli(self): """Return PauliTable""" return PauliTable(self._array)