Skip to content

Commit

Permalink
qiskit version
Browse files Browse the repository at this point in the history
  • Loading branch information
Grace committed Sep 14, 2023
1 parent 2e662d7 commit 2a39400
Show file tree
Hide file tree
Showing 9 changed files with 368 additions and 359 deletions.
40 changes: 20 additions & 20 deletions docs/tutorials/QEC_Framework_IEEE_2022.ipynb

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
qiskit-terra>=0.21.2, <0.24.0
qiskit-terra>=0.25.0
qiskit-aer>=0.11.0
pybind11<=2.9.1
PyMatching>=0.6.0,!=2.0.0
Expand Down
135 changes: 73 additions & 62 deletions src/qiskit_qec/operators/base_pauli.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
from typing import List, Optional, Union

import numpy as np

# Must be imported as follows to avoid circular import errors
import qiskit.quantum_info.operators.symplectic.clifford
from qiskit import QiskitError
Expand All @@ -28,6 +27,7 @@
from qiskit.circuit.instruction import Instruction
from qiskit.quantum_info.operators.base_operator import BaseOperator
from qiskit.quantum_info.operators.mixins import AdjointMixin, MultiplyMixin

from qiskit_qec.linear import matrix as mt
from qiskit_qec.linear.symplectic import symplectic_product
from qiskit_qec.utils import pauli_rep
Expand Down Expand Up @@ -103,7 +103,8 @@ def __init__(
self.matrix = matrix
self._num_paulis = self.matrix.shape[0]
if phase_exp is None:
self._phase_exp = np.zeros(shape=(self.matrix.shape[0],), dtype=np.int8)
self._phase_exp = np.zeros(
shape=(self.matrix.shape[0],), dtype=np.int8)
else:
self._phase_exp = np.atleast_1d(phase_exp)
if self._phase_exp.shape[0] != self.matrix.shape[0]:
Expand Down Expand Up @@ -189,22 +190,22 @@ def _x(self, val): # pylint: disable=invalid-name
@property
def z(self):
"""The z array for the symplectic representation."""
return self.matrix[:, self.num_qubits :]
return self.matrix[:, self.num_qubits:]

@z.setter
def z(self, val):
self.matrix[:, self.num_qubits :] = val
self.matrix[:, self.num_qubits:] = val

# @final Add when python >= 3.8
@property
def _z(self): # pylint: disable=invalid-name
"""The z array for the symplectic representation."""
return self.matrix[:, self.num_qubits :]
return self.matrix[:, self.num_qubits:]

# @final Add when python >= 3.8
@_z.setter
def _z(self, val): # pylint: disable=invalid-name
self.matrix[:, self.num_qubits :] = val
self.matrix[:, self.num_qubits:] = val

@property
def num_y(self):
Expand Down Expand Up @@ -298,7 +299,8 @@ def set_syntax(cls, syntax_code: Optional[int] = None, syntax_str: Optional[str]
BasePauli.EXTERNAL_SYNTAX = pauli_rep.INDEX_SYNTAX
else:
if syntax_code not in [0, 1, 2]:
raise QiskitError("Unknown syntax: {syntax_code}. See pauli_rep for options.")
raise QiskitError(
"Unknown syntax: {syntax_code}. See pauli_rep for options.")
BasePauli.EXTERNAL_SYNTAX = syntax_code

@property
Expand Down Expand Up @@ -425,7 +427,8 @@ def compose(

# Validation
if qargs is None and other.num_qubits != self.num_qubits:
raise QiskitError(f"other {type(self).__name__} must be on the same number of qubits.")
raise QiskitError(
f"other {type(self).__name__} must be on the same number of qubits.")

if qargs and other.num_qubits != len(qargs):
raise QiskitError(
Expand Down Expand Up @@ -474,11 +477,11 @@ def _compose(
phase_exp = a._phase_exp + b._phase_exp
if front:
phase_exp += 2 * np.sum(
np.logical_and(amat[:, : b.num_qubits], bmat[:, b.num_qubits :]), axis=1
np.logical_and(amat[:, : b.num_qubits], bmat[:, b.num_qubits:]), axis=1
)
else:
phase_exp += 2 * np.sum(
np.logical_and(bmat[:, : b.num_qubits], amat[:, b.num_qubits :]), axis=1
np.logical_and(bmat[:, : b.num_qubits], amat[:, b.num_qubits:]), axis=1
)
phase_exp = np.mod(phase_exp, 4)

Expand Down Expand Up @@ -628,7 +631,8 @@ def _commutes(self, other: "BasePauli", qargs: List = None) -> np.ndarray:
x1, z1 = self.matrix[:, inds], self.matrix[:, sinds]
else:
# x1, z1 = self.x, self.z
x1, z1 = self.matrix[:, : self.num_qubits], self.matrix[:, self.num_qubits :]
x1, z1 = self.matrix[:,
: self.num_qubits], self.matrix[:, self.num_qubits:]

return np.logical_not(np.sum((x1 & other.z) ^ (z1 & other.x), axis=1) % 2)

Expand Down Expand Up @@ -692,59 +696,62 @@ def evolve(self, other: "BasePauli", qargs: Union[None, List, int] = None, frame
ret = ret.compose(other, front=True, qargs=qargs)
return ret

# Convert Clifford to quantum circuits
if isinstance(other, qiskit.quantum_info.operators.symplectic.clifford.Clifford):
return self._evolve_clifford(other, qargs=qargs, frame=frame)
# # Convert Clifford to quantum circuits
# if isinstance(other, qiskit.quantum_info.operators.symplectic.clifford.Clifford):
# return self._evolve_clifford(other, qargs=qargs, frame=frame)

# Otherwise evolve by the inverse circuit to compute C^dg.P.C
if frame == "s":
return self.copy()._append_circuit(other, qargs=qargs)
return self.copy()._append_circuit(other.inverse(), qargs=qargs)

def _evolve_clifford(
self,
other: qiskit.quantum_info.operators.symplectic.clifford.Clifford,
qargs: Union[None, List, int] = None,
frame: str = "h",
):
"""Heisenberg picture evolution of a Pauli by a Clifford."""
if qargs is None:
idx = slice(None)
num_act = self.num_qubits
else:
idx = list(qargs)
num_act = len(idx)

# Set return to I on qargs
ret = self.copy()
ret._x[:, idx] = False
ret._z[:, idx] = False

# pylint: disable=cyclic-import
from qiskit_qec.operators.pauli import Pauli
from qiskit_qec.operators.pauli_list import PauliList

# Get action of Pauli's from Clifford
if frame == "s":
adj = other.copy()
else:
adj = other.adjoint()
pauli_list = []
for z in self._z[:, idx]:
pauli = Pauli("I" * num_act)
for row in adj.stabilizer[z]:
pauli.compose(Pauli((row.Z[0], row.X[0], 2 * row.phase[0])), inplace=True)
pauli_list.append(pauli)
ret.dot(PauliList(pauli_list), qargs=qargs, inplace=True)

pauli_list = []
for x in self._x[:, idx]:
pauli = Pauli("I" * num_act)
for row in adj.destabilizer[x]:
pauli.compose(Pauli((row.Z[0], row.X[0], 2 * row.phase[0])), inplace=True)
pauli_list.append(pauli)
ret.dot(PauliList(pauli_list), qargs=qargs, inplace=True)
return ret
# Drew said no longer needed
# def _evolve_clifford(
# self,
# other: qiskit.quantum_info.operators.symplectic.clifford.Clifford,
# qargs: Union[None, List, int] = None,
# frame: str = "h",
# ):
# """Heisenberg picture evolution of a Pauli by a Clifford."""
# if qargs is None:
# idx = slice(None)
# num_act = self.num_qubits
# else:
# idx = list(qargs)
# num_act = len(idx)

# # Set return to I on qargs
# ret = self.copy()
# ret._x[:, idx] = False
# ret._z[:, idx] = False

# # pylint: disable=cyclic-import
# from qiskit_qec.operators.pauli import Pauli
# from qiskit_qec.operators.pauli_list import PauliList

# # Get action of Pauli's from Clifford
# if frame == "s":
# adj = other.copy()
# else:
# adj = other.adjoint()
# pauli_list = []
# for z in self._z[:, idx]:
# pauli = Pauli("I" * num_act)
# for row in adj.stab[z]:
# pauli.compose(
# Pauli((row.Z[0], row.X[0], 2 * row.phase[0])), inplace=True)
# pauli_list.append(pauli)
# ret.dot(PauliList(pauli_list), qargs=qargs, inplace=True)

# pauli_list = []
# for x in self._x[:, idx]:
# pauli = Pauli("I" * num_act)
# for row in adj.destabilizer[x]:
# pauli.compose(
# Pauli((row.Z[0], row.X[0], 2 * row.phase[0])), inplace=True)
# pauli_list.append(pauli)
# ret.dot(PauliList(pauli_list), qargs=qargs, inplace=True)
# return ret

# ---------------------------------------------------------------------
# Helper Metods
Expand Down Expand Up @@ -906,7 +913,8 @@ def _append_circuit(
"sdg": _evolve_sdg,
"sinv": _evolve_sdg,
}
basis_2q = {"cx": _evolve_cx, "cz": _evolve_cz, "cy": _evolve_cy, "swap": _evolve_swap}
basis_2q = {"cx": _evolve_cx, "cz": _evolve_cz,
"cy": _evolve_cy, "swap": _evolve_swap}

# Non-Clifford gates
non_clifford = ["t", "tdg", "ccx", "ccz"]
Expand All @@ -922,7 +930,8 @@ def _append_circuit(

# Apply gate if it is a Clifford basis gate
if name in non_clifford:
raise QiskitError(f"Cannot update Pauli with non-Clifford gate {name}")
raise QiskitError(
f"Cannot update Pauli with non-Clifford gate {name}")
if name in basis_1q:
if len(qargs) != 1:
raise QiskitError("Invalid qubits for 1-qubit gate.")
Expand Down Expand Up @@ -1003,7 +1012,8 @@ def _evolve_i(base_pauli: "BasePauli", qubit: int) -> "BasePauli":

def _evolve_x(base_pauli: "BasePauli", qubit: int) -> "BasePauli":
"""Update P -> X.P.X"""
base_pauli._phase_exp += 2 * base_pauli.matrix[:, qubit + base_pauli.num_qubits].T
base_pauli._phase_exp += 2 * \
base_pauli.matrix[:, qubit + base_pauli.num_qubits].T
return base_pauli


Expand Down Expand Up @@ -1050,7 +1060,8 @@ def _evolve_cy(base_pauli: "BasePauli", qctrl: int, qtrgt: int) -> "BasePauli":
z2 = base_pauli.matrix[:, qtrgt + base_pauli.num_qubits].copy()
base_pauli.matrix[:, qtrgt] ^= x1
base_pauli.matrix[:, qtrgt + base_pauli.num_qubits] ^= x1
base_pauli.matrix[:, qctrl + base_pauli.num_qubits] ^= np.logical_xor(x2, z2)
base_pauli.matrix[:, qctrl +
base_pauli.num_qubits] ^= np.logical_xor(x2, z2)
base_pauli._phase_exp += x1 + 2 * np.logical_and(x1, x2).T
return base_pauli

Expand Down
35 changes: 22 additions & 13 deletions src/qiskit_qec/operators/pauli.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from qiskit.quantum_info.operators.mixins import generate_apidocs
from qiskit.quantum_info.operators.scalar_op import ScalarOp
from qiskit.utils.deprecation import deprecate_function

from qiskit_qec.operators.base_pauli import BasePauli
from qiskit_qec.utils import pauli_rep

Expand Down Expand Up @@ -98,7 +99,8 @@ def __init__(
)

elif isinstance(data, str):
matrix, phase_exp = pauli_rep.str2symplectic(data, qubit_order=input_qubit_order)
matrix, phase_exp = pauli_rep.str2symplectic(
data, qubit_order=input_qubit_order)
elif isinstance(data, ScalarOp):
matrix, phase_exp = pauli_rep.scalar_op2symplectic(
data, output_encoding=pauli_rep.INTERNAL_PHASE_ENCODING
Expand All @@ -109,10 +111,12 @@ def __init__(
if z is None:
# Using old Pauli initialization with positional args instead of kwargs
z = data
matrix, phase_exp = self._from_split_array_deprecated(z, x, phase_exp)
matrix, phase_exp = self._from_split_array_deprecated(
z, x, phase_exp)

elif label is not None: # DEPRECATED
matrix, phase_exp = self._from_label_deprecated(label, qubit_order=input_qubit_order)
matrix, phase_exp = self._from_label_deprecated(
label, qubit_order=input_qubit_order)
else:
raise QiskitError("Invalid input data for Pauli.")

Expand Down Expand Up @@ -196,11 +200,11 @@ def x(self, val):
@property
def z(self):
"""The z vector for the Pauli."""
return self.matrix[:, self.num_qubits :][0]
return self.matrix[:, self.num_qubits:][0]

@z.setter
def z(self, val):
self.matrix[:, self.num_qubits :][0] = val
self.matrix[:, self.num_qubits:][0] = val

# ---------------------------------------------------------------------
# Magic Methods
Expand All @@ -217,7 +221,7 @@ def __str__(self):
and self.num_qubits > self._truncate__
and BasePauli.EXTERNAL_SYNTAX == pauli_rep.PRODUCT_SYNTAX
):
front = self[-self._truncate__ :].to_label()
front = self[-self._truncate__:].to_label()
return front + "..."
return self.to_label()

Expand Down Expand Up @@ -318,7 +322,7 @@ def _getitem(self, i):
Returns:
Pauli: Pauli acting on qubit i
"""
return Pauli(self.matrix[0][i : self.matrix.shape[1] : self.num_qubits])
return Pauli(self.matrix[0][i: self.matrix.shape[1]: self.num_qubits])

def _fast_getitem_str(self, i):
"""Get Pauli for qubit i
Expand Down Expand Up @@ -517,7 +521,8 @@ def to_instruction(self):
)

if len(pauli) == 1:
gate = {"I": IGate(), "X": XGate(), "Y": YGate(), "Z": ZGate()}[pauli]
gate = {"I": IGate(), "X": XGate(), "Y": YGate(),
"Z": ZGate()}[pauli]
else:
gate = PauliGate(pauli)
if not phase_exp:
Expand Down Expand Up @@ -696,7 +701,8 @@ def instrs2symplectic(instr: Union[Instruction, QuantumCircuit]):
instr = instr.definition

# Initialize identity Pauli
ret = Pauli(np.zeros((1, 2 * instr.num_qubits), dtype=bool), phase_exp=0)
ret = Pauli(np.zeros((1, 2 * instr.num_qubits),
dtype=bool), phase_exp=0)
# Add circuit global phase if specified
if instr.global_phase:
ret._phase_exp = pauli_rep.cpx2exp(
Expand All @@ -712,7 +718,7 @@ def instrs2symplectic(instr: Union[Instruction, QuantumCircuit]):
if not isinstance(dinstr, (Barrier, Delay)):
next_instr = BasePauli(*Pauli.instrs2symplectic(dinstr))
if next_instr is not None:
qargs = [tup.index for tup in qregs]
qargs = [instr.find_bit(tup)[0] for tup in qregs]
ret = ret.compose(next_instr, qargs=qargs)
return ret.matrix, ret._phase_exp

Expand Down Expand Up @@ -948,7 +954,8 @@ def insert_paulis(self, indices=None, paulis=None, pauli_labels=None):
"""
if pauli_labels is not None:
if paulis is not None:
raise QiskitError("Please only provide either `paulis` or `pauli_labels`")
raise QiskitError(
"Please only provide either `paulis` or `pauli_labels`")
if isinstance(pauli_labels, str):
pauli_labels = list(pauli_labels)
# since pauli label is in reversed order.
Expand Down Expand Up @@ -1032,7 +1039,8 @@ def pauli_single(cls, num_qubits, index, pauli_label):
Pauli: single qubit pauli
"""
tmp = Pauli(pauli_label)
ret = Pauli((np.zeros(num_qubits, dtype=bool), np.zeros(num_qubits, dtype=bool)))
ret = Pauli((np.zeros(num_qubits, dtype=bool),
np.zeros(num_qubits, dtype=bool)))
ret.x[index] = tmp.x[0]
ret.z[index] = tmp.z[0]
ret.phase = tmp.phase
Expand All @@ -1057,7 +1065,8 @@ def random(cls, num_qubits, seed=None):
Pauli: the random pauli
"""
# pylint: disable=cyclic-import
from qiskit.quantum_info.operators.symplectic.random import random_pauli
from qiskit.quantum_info.operators.symplectic.random import \
random_pauli

return random_pauli(num_qubits, group_phase=False, seed=seed)

Expand Down
Loading

0 comments on commit 2a39400

Please sign in to comment.