Esempio n. 1
0
            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)
Esempio n. 2
0
                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)
Esempio n. 3
0
# ---------------------------------------------------------------------
# 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)
Esempio n. 4
0
        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)
Esempio n. 5
0
            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)
Esempio n. 6
0
        # 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)
Esempio n. 7
0
            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)
Esempio n. 8
0
    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)
Esempio n. 10
0
    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)
Esempio n. 11
0
            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)
Esempio n. 12
0
        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)
Esempio n. 13
0
        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)
Esempio n. 14
0
    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)