Skip to content

Base Module API Reference

This page documents the base classes and functions in the skq.base module. These components form the foundation of the SKQ framework.

Base Classes

skq.base.Operator

Bases: ndarray

Base class for Quantum Operators. Gates, density matrices, hamiltonians, etc. are all operators. The operator must be a 2D matrix. :param input_array: Input array to create the operator. Will be converted to a NumPy array.

Source code in src/skq/base.py
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
class Operator(np.ndarray):
    """
    Base class for Quantum Operators.
    Gates, density matrices, hamiltonians, etc. are all operators.
    The operator must be a 2D matrix.
    :param input_array: Input array to create the operator. Will be converted to a NumPy array.
    """

    def __new__(cls, input_array):
        arr = np.asarray(input_array, dtype=complex)
        obj = arr.view(cls)
        assert obj.is_2d(), "Quantum operator must be a 2D matrix."
        assert obj.is_square(), "Quantum operator must be a square matrix."
        assert obj.is_at_least_nxn(n=1), "Gate must be at least a 1x1 matrix."
        return obj

    def encodes(self, other: np.ndarray) -> bool:
        """Progress quantum state"""
        return np.array(self @ other, dtype=complex)

    def decodes(self, other: np.ndarray) -> bool:
        """Reverse quantum state"""
        return np.array(self.conjugate_transpose() @ other, dtype=complex)

    def is_square(self) -> bool:
        """Operator is a square matrix."""
        return self.shape[0] == self.shape[1]

    def is_2d(self) -> bool:
        """Operator is a 2D matrix."""
        return len(self.shape) == 2

    def is_at_least_nxn(self, n: int) -> bool:
        """Operator is at least an n x n matrix."""
        rows, cols = self.shape
        return rows >= n and cols >= n

    def is_power_of_n_shape(self, n: int) -> bool:
        """
        Operator shape is a power of n.
        Qubits: n=2, Qutrits: n=3, Ququarts: n=4, Qupents: n=5, etc.
        :param n: Number to check for power of n shape.
        """

        def _is_power_of_n(x, n):
            if x < 1:
                return False
            while x % n == 0:
                x //= n
            return x == 1

        rows, cols = self.shape
        return _is_power_of_n(rows, n) and _is_power_of_n(cols, n)

    def is_hermitian(self) -> bool:
        """Check if the operator is Hermitian: U = U^dagger"""
        return np.allclose(self, self.conjugate_transpose())

    def is_identity(self) -> bool:
        """Check if the operator is the identity matrix."""
        return np.allclose(self, np.eye(self.shape[0]))

    def is_equal(self, other) -> bool:
        """Check if the operator is effectively equal to another operator."""
        return np.allclose(self, other, atol=1e-8)

    def num_levels(self) -> int:
        """Number of rows. Used for checking valid shapes."""
        return self.shape[0]

    def conjugate_transpose(self) -> np.ndarray:
        """
        Conjugate transpose (i.e. Hermitian adjoint or 'dagger operation') of the operator.
        1. Transpose the matrix
        2. Take the complex conjugate of each element (Flip the sign of the imaginary part)
        """
        return self.T.conj()

    def eigenvalues(self) -> np.ndarray:
        """
        Eigenvalues of the operator.
        Hermitian operators use eigvalsh for stable and faster computation.
        """
        return np.linalg.eigvalsh(self) if self.is_hermitian() else np.linalg.eigvals(self)

    def eigenvectors(self) -> np.ndarray:
        """
        Eigenvectors of the operator.
        Hermitian operators use eigh for stable and faster computation.
        """
        return np.linalg.eigh(self)[1] if self.is_hermitian() else np.linalg.eig(self)[1]

    def frobenius_norm(self) -> float:
        """Compute the Frobenius norm"""
        return np.linalg.norm(self)

    def commute(self, other: "Operator") -> bool:
        """
        Check if operator commute. U and V commute if UV = VU.
        :param other: Operator to check commutation with.
        """
        assert isinstance(other, Operator), "Other object must be a valid Operator."
        assert self.num_levels() == other.num_levels(), "Operators must have the same number of rows for the commutation check."
        return np.allclose(self @ other, other @ self)

    def to_qiskit(self) -> qiskit.quantum_info.Operator:
        """Convert operator to a Qiskit."""
        raise NotImplementedError(f"Conversion to Qiskit Gate is not implemented for {self.__class__.__name__}.")

    def from_qiskit(self, qiskit_operator: qiskit.quantum_info.Operator) -> "Operator":
        """
        Convert a Qiskit operator to scikit-q Operator.
        :param qiskit_operator: Qiskit Operator
        :return: scikit-q Operator object
        """
        raise NotImplementedError(f"Conversion from Qiskit is not implemented for {self.__class__.__name__}.")

    def to_pennylane(self):
        """Convert gate to a PennyLane gate object."""
        raise NotImplementedError(f"Conversion to PennyLane is not implemented for {self.__class__.__name__}.")

    def from_pennylane(self, pennylane_operator: qml.operation.Operation) -> "Operator":
        """
        Convert a PennyLane Operation to scikit-q Operator.
        :param pennylane_gate: PennyLane Operation object.
        :return: scikit-q Operator
        """
        raise NotImplementedError(f"Conversion from PennyLane is not implemented for {self.__class__.__name__}.")

    def __call__(self, other: np.ndarray) -> np.ndarray:
        """Call the operator on a quantum state."""
        return self.encodes(other)

__call__(other)

Call the operator on a quantum state.

Source code in src/skq/base.py
135
136
137
def __call__(self, other: np.ndarray) -> np.ndarray:
    """Call the operator on a quantum state."""
    return self.encodes(other)

commute(other)

Check if operator commute. U and V commute if UV = VU. :param other: Operator to check commutation with.

Source code in src/skq/base.py
102
103
104
105
106
107
108
109
def commute(self, other: "Operator") -> bool:
    """
    Check if operator commute. U and V commute if UV = VU.
    :param other: Operator to check commutation with.
    """
    assert isinstance(other, Operator), "Other object must be a valid Operator."
    assert self.num_levels() == other.num_levels(), "Operators must have the same number of rows for the commutation check."
    return np.allclose(self @ other, other @ self)

conjugate_transpose()

Conjugate transpose (i.e. Hermitian adjoint or 'dagger operation') of the operator. 1. Transpose the matrix 2. Take the complex conjugate of each element (Flip the sign of the imaginary part)

Source code in src/skq/base.py
76
77
78
79
80
81
82
def conjugate_transpose(self) -> np.ndarray:
    """
    Conjugate transpose (i.e. Hermitian adjoint or 'dagger operation') of the operator.
    1. Transpose the matrix
    2. Take the complex conjugate of each element (Flip the sign of the imaginary part)
    """
    return self.T.conj()

decodes(other)

Reverse quantum state

Source code in src/skq/base.py
26
27
28
def decodes(self, other: np.ndarray) -> bool:
    """Reverse quantum state"""
    return np.array(self.conjugate_transpose() @ other, dtype=complex)

eigenvalues()

Eigenvalues of the operator. Hermitian operators use eigvalsh for stable and faster computation.

Source code in src/skq/base.py
84
85
86
87
88
89
def eigenvalues(self) -> np.ndarray:
    """
    Eigenvalues of the operator.
    Hermitian operators use eigvalsh for stable and faster computation.
    """
    return np.linalg.eigvalsh(self) if self.is_hermitian() else np.linalg.eigvals(self)

eigenvectors()

Eigenvectors of the operator. Hermitian operators use eigh for stable and faster computation.

Source code in src/skq/base.py
91
92
93
94
95
96
def eigenvectors(self) -> np.ndarray:
    """
    Eigenvectors of the operator.
    Hermitian operators use eigh for stable and faster computation.
    """
    return np.linalg.eigh(self)[1] if self.is_hermitian() else np.linalg.eig(self)[1]

encodes(other)

Progress quantum state

Source code in src/skq/base.py
22
23
24
def encodes(self, other: np.ndarray) -> bool:
    """Progress quantum state"""
    return np.array(self @ other, dtype=complex)

frobenius_norm()

Compute the Frobenius norm

Source code in src/skq/base.py
 98
 99
100
def frobenius_norm(self) -> float:
    """Compute the Frobenius norm"""
    return np.linalg.norm(self)

from_pennylane(pennylane_operator)

Convert a PennyLane Operation to scikit-q Operator. :param pennylane_gate: PennyLane Operation object. :return: scikit-q Operator

Source code in src/skq/base.py
127
128
129
130
131
132
133
def from_pennylane(self, pennylane_operator: qml.operation.Operation) -> "Operator":
    """
    Convert a PennyLane Operation to scikit-q Operator.
    :param pennylane_gate: PennyLane Operation object.
    :return: scikit-q Operator
    """
    raise NotImplementedError(f"Conversion from PennyLane is not implemented for {self.__class__.__name__}.")

from_qiskit(qiskit_operator)

Convert a Qiskit operator to scikit-q Operator. :param qiskit_operator: Qiskit Operator :return: scikit-q Operator object

Source code in src/skq/base.py
115
116
117
118
119
120
121
def from_qiskit(self, qiskit_operator: qiskit.quantum_info.Operator) -> "Operator":
    """
    Convert a Qiskit operator to scikit-q Operator.
    :param qiskit_operator: Qiskit Operator
    :return: scikit-q Operator object
    """
    raise NotImplementedError(f"Conversion from Qiskit is not implemented for {self.__class__.__name__}.")

is_2d()

Operator is a 2D matrix.

Source code in src/skq/base.py
34
35
36
def is_2d(self) -> bool:
    """Operator is a 2D matrix."""
    return len(self.shape) == 2

is_at_least_nxn(n)

Operator is at least an n x n matrix.

Source code in src/skq/base.py
38
39
40
41
def is_at_least_nxn(self, n: int) -> bool:
    """Operator is at least an n x n matrix."""
    rows, cols = self.shape
    return rows >= n and cols >= n

is_equal(other)

Check if the operator is effectively equal to another operator.

Source code in src/skq/base.py
68
69
70
def is_equal(self, other) -> bool:
    """Check if the operator is effectively equal to another operator."""
    return np.allclose(self, other, atol=1e-8)

is_hermitian()

Check if the operator is Hermitian: U = U^dagger

Source code in src/skq/base.py
60
61
62
def is_hermitian(self) -> bool:
    """Check if the operator is Hermitian: U = U^dagger"""
    return np.allclose(self, self.conjugate_transpose())

is_identity()

Check if the operator is the identity matrix.

Source code in src/skq/base.py
64
65
66
def is_identity(self) -> bool:
    """Check if the operator is the identity matrix."""
    return np.allclose(self, np.eye(self.shape[0]))

is_power_of_n_shape(n)

Operator shape is a power of n. Qubits: n=2, Qutrits: n=3, Ququarts: n=4, Qupents: n=5, etc. :param n: Number to check for power of n shape.

Source code in src/skq/base.py
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
def is_power_of_n_shape(self, n: int) -> bool:
    """
    Operator shape is a power of n.
    Qubits: n=2, Qutrits: n=3, Ququarts: n=4, Qupents: n=5, etc.
    :param n: Number to check for power of n shape.
    """

    def _is_power_of_n(x, n):
        if x < 1:
            return False
        while x % n == 0:
            x //= n
        return x == 1

    rows, cols = self.shape
    return _is_power_of_n(rows, n) and _is_power_of_n(cols, n)

is_square()

Operator is a square matrix.

Source code in src/skq/base.py
30
31
32
def is_square(self) -> bool:
    """Operator is a square matrix."""
    return self.shape[0] == self.shape[1]

num_levels()

Number of rows. Used for checking valid shapes.

Source code in src/skq/base.py
72
73
74
def num_levels(self) -> int:
    """Number of rows. Used for checking valid shapes."""
    return self.shape[0]

to_pennylane()

Convert gate to a PennyLane gate object.

Source code in src/skq/base.py
123
124
125
def to_pennylane(self):
    """Convert gate to a PennyLane gate object."""
    raise NotImplementedError(f"Conversion to PennyLane is not implemented for {self.__class__.__name__}.")

to_qiskit()

Convert operator to a Qiskit.

Source code in src/skq/base.py
111
112
113
def to_qiskit(self) -> qiskit.quantum_info.Operator:
    """Convert operator to a Qiskit."""
    raise NotImplementedError(f"Conversion to Qiskit Gate is not implemented for {self.__class__.__name__}.")

skq.base.HermitianOperator

Bases: Operator

Hermitian operator (observable) class. :param input_array: Input array to create the operator. Will be converted to a NumPy array.

Source code in src/skq/base.py
140
141
142
143
144
145
146
147
148
149
150
class HermitianOperator(Operator):
    """
    Hermitian operator (observable) class.
    :param input_array: Input array to create the operator. Will be converted to a NumPy array.
    """

    def __new__(cls, input_array):
        obj = super().__new__(cls, input_array)
        assert obj.is_hermitian(), "Hermitian operator must be Hermitian."
        assert obj.is_at_least_nxn(n=2), "Hermitian operator must be at least a 2x2 matrix."
        return obj