#
# Copyright © 2022-2023 University of Strasbourg. All Rights Reserved.
# Copyright © 2032-2024 QPerfect. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import numpy as np
from typing import Union
import mimiqcircuits as mc
from functools import reduce
def convert_pauli(x: str) -> mc.Gate:
if x == "X":
return mc.GateX()
elif x == "Z":
return mc.GateZ()
elif x == "Y":
return mc.GateY()
elif x == "I":
return mc.GateID()
else:
raise ValueError(f"Not a valid Pauli character '{x}'")
[docs]
class PauliString(mc.Gate):
"""PauliString.
N-qubit tensor product of Pauli operators.
The PauliString gate can represent any N-qubit tensor product of operators
of the form
P_1 ⊗ P_2 ⊗ P_3 ⊗ ... ⊗ P_N,
where each P_i ∈ { I, X, Y, Z } is a Pauli operator, including the identity.
This gate can be initialized by passing as argument a string of length N
where each element is either 'I', 'X', 'Y', or 'Z'.
For example, PauliString("IXXYZ")
Examples:
>>> from mimiqcircuits import *
>>> g= PauliString("XYZ")
>>> g.matrix()
[0, 0, 0, 0, 0, 0, -0.0 - 1.0*I, 0]
[0, 0, 0, 0, 0, 0, 0, 0.0 + 1.0*I]
[0, 0, 0, 0, 0.0 + 1.0*I, 0, 0, 0]
[0, 0, 0, 0, 0, -0.0 - 1.0*I, 0, 0]
[0, 0, -0.0 - 1.0*I, 0, 0, 0, 0, 0]
[0, 0, 0, 0.0 + 1.0*I, 0, 0, 0, 0]
[0.0 + 1.0*I, 0, 0, 0, 0, 0, 0, 0]
[0, -0.0 - 1.0*I, 0, 0, 0, 0, 0, 0]
<BLANKLINE>
>>> c= Circuit()
>>> c.push(PauliString("XYZ"),0,1,2)
3-qubit circuit with 1 instructions:
└── XYZ @ q[0,1,2]
<BLANKLINE>
>>> c.decompose()
3-qubit circuit with 3 instructions:
├── X @ q[0]
├── Y @ q[1]
└── Z @ q[2]
<BLANKLINE>
"""
_name = "PauliString"
_num_qregs = 1
_parnames = ()
def __init__(self, pauli: Union[str, chr]):
self.pauli = str(pauli)
super().__init__()
self._num_qubits = len(self.pauli)
self._qregsizes = [
self._num_qubits,
]
self._parnames = "pauli"
if self._num_qubits < 1:
raise ValueError("Pauli string cannot be empty.")
if any(x not in "IXYZ" for x in self.pauli):
raise ValueError("Pauli string can only contain I, X, Y, or Z.")
[docs]
@classmethod
def from_string(cls, pauli_expr: str):
return cls(pauli_expr)
def __str__(self):
return f"PauliString({self.pauli})"
@property
def num_qubits(self):
return self._num_qubits
@property
def parnames(self):
return self._parnames
[docs]
def inverse(self):
# Inverse of a Pauli string is itself
return PauliString(self.pauli)
def _matrix(self):
# Generate the matrix representation of the Pauli string
ops = [convert_pauli(self.pauli[i]).matrix() for i in range(self.num_qubits)]
return reduce(np.kron, ops)
[docs]
def unwrapped_matrix(self):
# Return the matrix representation of the Pauli string
return self._matrix()
[docs]
def is_identity(self):
# Check if the Pauli string represents the identity operator
return all(p == "I" for p in self.pauli)
def _power(self, pwr):
# Elevate Pauli string to an integer power
if pwr % 2 == 0:
return PauliString("I" * self.num_qubits)
elif pwr % 1 == 0:
return self
else:
raise ValueError("Pauli strings can only be elevated to an integer power.")
def _decompose(self, circ, qreg, _, zvars):
# Decompose the Pauli string into individual gates
for i, q in enumerate(qreg):
circ.push(convert_pauli(self.pauli[i]), [q])
return circ
def __str__(self):
tail = " ⊗ "
pauli_str = tail.join(self.pauli)
return f"{self.pauli}"
[docs]
def evaluate(self, d):
return self
[docs]
def isidentity(self):
return all(p == "I" for p in self.pauli)
[docs]
def asciiwidth(self, qubits, bits, zvars):
if len(qubits) == 1:
return len(str(self.pauli[0])) + 2
else:
max_label_length = max(
len(f"{i}: {self.pauli[i]}") for i in range(len(self.pauli))
)
return max_label_length + 2
__all__ = ["PauliString"]