if not isinstance(obj.definition, QuantumCircuit): raise QiskitError('Instruction "{}" ' 'definition is {} but expected QuantumCircuit.'.format( obj.name, type(obj.definition))) if obj.definition.global_phase: dimension = 2 ** obj.num_qubits op = self.compose( ScalarOp(dimension, np.exp(1j * float(obj.definition.global_phase))), qargs=qargs) self._data = op.data flat_instr = obj.definition bit_indices = {bit: index for bits in [flat_instr.qubits, flat_instr.clbits] for index, bit in enumerate(bits)} for instr, qregs, cregs in flat_instr: if cregs: raise QiskitError( 'Cannot apply instruction with classical registers: {}'.format( instr.name)) # Get the integer position of the flat register if qargs is None: new_qargs = [bit_indices[tup] for tup in qregs] else: new_qargs = [qargs[bit_indices[tup]] for tup in qregs] self._append_instruction(instr, qargs=new_qargs) # Update docstrings for API docs generate_apidocs(Operator)
return self.obj._to_label(self.obj.array[key]) return LabelIterator(self) def matrix_iter(self, sparse=False): """Return a matrix representation iterator. This is a lazy iterator that converts each row into the Pauli matrix representation only as it is used. To convert the entire table to matrices use the :meth:`to_matrix` method. Args: sparse (bool): optionally return sparse CSR matrices if True, otherwise return Numpy array matrices (Default: False) Returns: MatrixIterator: matrix iterator object for the PauliTable. """ class MatrixIterator(CustomIterator): """Matrix representation iteration and item access.""" def __repr__(self): return "<PauliTable_matrix_iterator at {}>".format(hex(id(self))) def __getitem__(self, key): return self.obj._to_matrix(self.obj.array[key], sparse=sparse) return MatrixIterator(self) # Update docstrings for API docs generate_apidocs(PauliTable)
# --------------------------------------------------------------------- # Label parsing helper functions # --------------------------------------------------------------------- def _split_pauli_label(label): """Split Pauli label into unsigned group label and coefficient label""" span = re.search(r'[IXYZ]+', label).span() pauli = label[span[0]:] coeff = label[:span[0]] if span[1] != len(label): invalid = set(re.sub(r'[IXYZ]+', '', label[span[0]:])) raise QiskitError("Pauli string contains invalid characters " "{} ∉ ['I', 'X', 'Y', 'Z']".format(invalid)) return pauli, coeff def _phase_from_label(label): """Return the phase from a label""" # Returns None if label is invalid label = label.replace('+', '', 1).replace('1', '', 1).replace('j', 'i', 1) phases = {'': 0, '-i': 1, '-': 2, 'i': 3} if label not in phases: raise QiskitError("Invalid Pauli phase label '{}'".format(label)) return phases.get(label) # Update docstrings for API docs generate_apidocs(Pauli)
else: # If the instruction doesn't have a matrix defined we use its # circuit decomposition definition if it exists, otherwise we # cannot compose this gate and raise an error. if obj.definition is None: raise QiskitError("Cannot apply Instruction: {}".format( obj.name)) if not isinstance(obj.definition, QuantumCircuit): raise QiskitError("{} instruction definition is {}; " "expected QuantumCircuit".format( obj.name, type(obj.definition))) qubit_indices = { bit: idx for idx, bit in enumerate(obj.definition.qubits) } for instr, qregs, cregs in obj.definition.data: if cregs: raise QiskitError( "Cannot apply instruction with classical registers: {}" .format(instr.name)) # Get the integer position of the flat register if qargs is None: new_qargs = [qubit_indices[tup] for tup in qregs] else: new_qargs = [qargs[qubit_indices[tup]] for tup in qregs] self._append_instruction(instr, qargs=new_qargs) # Update docstrings for API docs generate_apidocs(SuperOp)
qubit_wise (bool): whether the commutation rule is applied to the whole operator, or on a per-qubit basis. For example: .. code-block:: python >>> op = SparsePauliOp.from_list([("XX", 2), ("YY", 1), ("IZ",2j), ("ZZ",1j)]) >>> op.group_commuting() [SparsePauliOp(["IZ", "ZZ"], coeffs=[0.+2.j, 0.+1j]), SparsePauliOp(["XX", "YY"], coeffs=[2.+0.j, 1.+0.j])] >>> op.group_commuting(qubit_wise=True) [SparsePauliOp(['XX'], coeffs=[2.+0.j]), SparsePauliOp(['YY'], coeffs=[1.+0.j]), SparsePauliOp(['IZ', 'ZZ'], coeffs=[0.+2.j, 0.+1.j])] Returns: List[SparsePauliOp]: List of SparsePauliOp where each SparsePauliOp contains commuting Pauli operators. """ graph = self._create_graph(qubit_wise) # Keys in coloring_dict are nodes, values are colors coloring_dict = rx.graph_greedy_color(graph) groups = defaultdict(list) for idx, color in coloring_dict.items(): groups[color].append(idx) return [self[group] for group in groups.values()] # Update docstrings for API docs generate_apidocs(SparsePauliOp)
# Contract Choi matrices for composition data = np.reshape(np.einsum('iAjB,AkBl->ikjl', first, second), (input_dim * output_dim, input_dim * output_dim)) ret = Choi(data) ret._op_shape = new_shape return ret def tensor(self, other): if not isinstance(other, Choi): other = Choi(other) return self._tensor(self, other) def expand(self, other): if not isinstance(other, Choi): other = Choi(other) return self._tensor(other, self) @classmethod def _tensor(cls, a, b): ret = copy.copy(a) ret._op_shape = a._op_shape.tensor(b._op_shape) ret._data = _bipartite_tensor(a._data, b.data, shape1=a._bipartite_shape, shape2=b._bipartite_shape) return ret # Update docstrings for API docs generate_apidocs(Choi)
ret.table.phase ^= np.mod( np.sum(ret.table.X & ret.table.Z, axis=1), 2).astype(bool) return ret def _pad_with_identity(self, clifford, qargs): """Pad Clifford with identities on other subsystems.""" if qargs is None: return clifford padded = Clifford(StabilizerTable( np.eye(2 * self.num_qubits, dtype=bool)), validate=False) inds = list(qargs) + [self.num_qubits + i for i in qargs] # Pad Pauli array pauli = clifford.table.array for i, pos in enumerate(qargs): padded.table.array[inds, pos] = pauli[:, i] padded.table.array[inds, self.num_qubits + pos] = pauli[:, clifford.num_qubits + i] # Pad phase padded.table.phase[inds] = clifford.table.phase return padded # Update docstrings for API docs generate_apidocs(Clifford)
def _add(self, other, qargs=None): # Since we cannot directly add two channels in the Kraus # representation we try and use the other channels method # or convert to the Choi representation return Kraus(Choi(self)._add(other, qargs=qargs)) def _multiply(self, other): if not isinstance(other, Number): raise QiskitError("other is not a number") ret = copy.copy(self) # If the number is complex we need to convert to general # kraus channel so we multiply via Choi representation if isinstance(other, complex) or other < 0: # Convert to Choi-matrix ret._data = Kraus(Choi(self)._multiply(other))._data return ret # If the number is real we can update the Kraus operators # directly val = np.sqrt(other) kraus_r = None kraus_l = [val * k for k in self._data[0]] if self._data[1] is not None: kraus_r = [val * k for k in self._data[1]] ret._data = (kraus_l, kraus_r) return ret # Update docstrings for API docs generate_apidocs(Kraus)
# Since we cannot directly add two channels in the Stinespring # representation we convert to the Choi representation return Stinespring(Choi(self)._add(other, qargs=qargs)) def _multiply(self, other): if not isinstance(other, Number): raise QiskitError("other is not a number") ret = copy.copy(self) # If the number is complex or negative we need to convert to # general Stinespring representation so we first convert to # the Choi representation if isinstance(other, complex) or other < 1: # Convert to Choi-matrix ret._data = Stinespring(Choi(self)._multiply(other))._data return ret # If the number is real we can update the Kraus operators # directly num = np.sqrt(other) stine_l, stine_r = self._data stine_l = num * self._data[0] stine_r = None if self._data[1] is not None: stine_r = num * self._data[1] ret._data = (stine_l, stine_r) return ret # Update docstrings for API docs generate_apidocs(Stinespring)
def matrix_iter(self, sparse=False): """Return a matrix representation iterator. This is a lazy iterator that converts each row into the Pauli matrix representation only as it is used. To convert the entire table to matrices use the :meth:`to_matrix` method. Args: sparse (bool): optionally return sparse CSR matrices if True, otherwise return Numpy array matrices (Default: False) Returns: MatrixIterator: matrix iterator object for the StabilizerTable. """ class MatrixIterator(CustomIterator): """Matrix representation iteration and item access.""" def __repr__(self): return f"<StabilizerTable_matrix_iterator at {hex(id(self))}>" def __getitem__(self, key): return self.obj._to_matrix(self.obj.array[key], self.obj.phase[key], sparse=sparse) return MatrixIterator(self) # Update docstrings for API docs generate_apidocs(StabilizerTable)
ScalarOp: the scaled identity operator other * self. Raises: QiskitError: if other is not a valid complex number. """ if not isinstance(other, Number): raise QiskitError("other ({}) is not a number".format(other)) ret = self.copy() ret._coeff = other * self.coeff return ret @staticmethod def _pad_with_identity(current, other, qargs=None): """Pad another operator with identities. Args: current (BaseOperator): current operator. other (BaseOperator): other operator. qargs (None or list): qargs Returns: BaseOperator: the padded operator. """ if qargs is None: return other return ScalarOp(current.input_dims()).compose(other, qargs=qargs) # Update docstrings for API docs generate_apidocs(ScalarOp)
return result def _is_valid(self): """Return True if input is a CNOTDihedral element.""" if (self.poly.weight_0 != 0 or len(self.poly.weight_1) != self.num_qubits or len(self.poly.weight_2) != int(self.num_qubits * (self.num_qubits - 1) / 2) or len(self.poly.weight_3) != int(self.num_qubits * (self.num_qubits - 1) * (self.num_qubits - 2) / 6)): return False if ((self.linear).shape != (self.num_qubits, self.num_qubits) or len(self.shift) != self.num_qubits or not np.allclose( (np.linalg.det(self.linear) % 2), 1)): return False if (not (set(self.poly.weight_1.flatten())).issubset( {0, 1, 2, 3, 4, 5, 6, 7}) or not (set(self.poly.weight_2.flatten())).issubset({0, 2, 4, 6}) or not (set(self.poly.weight_3.flatten())).issubset({0, 4})): return False if not (set(self.shift.flatten())).issubset({0, 1}) or not (set( self.linear.flatten())).issubset({0, 1}): return False return True # Update docstrings for API docs generate_apidocs(CNOTDihedral)
input_dims = new_shape.dims_r() output_dims = new_shape.dims_l() if front: data = np.dot(self._data, other.data) else: data = np.dot(other.data, self._data) ret = PTM(data, input_dims, output_dims) ret._op_shape = new_shape return ret def tensor(self, other): if not isinstance(other, PTM): other = PTM(other) return self._tensor(self, other) def expand(self, other): if not isinstance(other, PTM): other = PTM(other) return self._tensor(other, self) @classmethod def _tensor(cls, a, b): ret = copy.copy(a) ret._op_shape = a._op_shape.tensor(b._op_shape) ret._data = np.kron(a._data, b.data) return ret # Update docstrings for API docs generate_apidocs(PTM)
def compose(self, other, qargs=None, front=False): if qargs is None: qargs = getattr(other, "qargs", None) if qargs is not None: return Chi(SuperOp(self).compose(other, qargs=qargs, front=front)) # If no qargs we compose via Choi representation to avoid an additional # representation conversion to SuperOp and then convert back to Chi return Chi(Choi(self).compose(other, front=front)) def tensor(self, other): if not isinstance(other, Chi): other = Chi(other) return self._tensor(self, other) def expand(self, other): if not isinstance(other, Chi): other = Chi(other) return self._tensor(other, self) @classmethod def _tensor(cls, a, b): ret = copy.copy(a) ret._op_shape = a._op_shape.tensor(b._op_shape) ret._data = np.kron(a._data, b.data) return ret # Update docstrings for API docs generate_apidocs(Chi)