Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Replace QermitPauli internals with UnitaryTableau #201

Open
wants to merge 25 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
00fc8ba
Use only apply_gate
daniel-mills-cqc Dec 10, 2024
b3c29a1
Remove commute_coeff
daniel-mills-cqc Dec 10, 2024
1086f7c
Use qubit pauli tensor in is_measureable
daniel-mills-cqc Dec 10, 2024
d30f8f2
Add from qubit_pauli_tensor
daniel-mills-cqc Dec 10, 2024
897d6cf
Use qubit pauli tensors within reduce_qubits
daniel-mills-cqc Dec 10, 2024
26827e4
Use qubit pauli tensor in equality check
daniel-mills-cqc Dec 10, 2024
8822eae
Remove is_identity
daniel-mills-cqc Dec 10, 2024
5855016
Use qubit pauli tensor in dagger
daniel-mills-cqc Dec 10, 2024
e2ce262
Remove random pauli
daniel-mills-cqc Dec 10, 2024
fe435cd
Use qubit pauli tensor in __hash__
daniel-mills-cqc Dec 10, 2024
2ff6a3b
Remove pre_multiply
daniel-mills-cqc Dec 10, 2024
7659343
Use qubit pauli tensor in from_pauli_list
daniel-mills-cqc Dec 10, 2024
4c924a2
Use qubit pauli tensor in get_control_circuit
daniel-mills-cqc Dec 10, 2024
312eb2b
Use qubit pauli tensor in circuit
daniel-mills-cqc Dec 10, 2024
f44846e
Use Op rather than OpType in apply_gate
daniel-mills-cqc Dec 11, 2024
ef4f65b
Correct wrong phase
daniel-mills-cqc Dec 11, 2024
a4ca938
Use UnitaryTableau
daniel-mills-cqc Dec 11, 2024
7657e2d
Remove unnecessary imports
daniel-mills-cqc Dec 11, 2024
9af038b
Repair high compute test
daniel-mills-cqc Dec 11, 2024
c92f429
Add qulacs dependency
daniel-mills-cqc Dec 11, 2024
87ed732
Move qulacs dependency
daniel-mills-cqc Dec 11, 2024
a1eaf53
Add projectq
daniel-mills-cqc Dec 11, 2024
a285f2b
Reduce projectq version dep
daniel-mills-cqc Dec 11, 2024
a71de8e
Revert projectq dependency change
daniel-mills-cqc Dec 12, 2024
ec3aa13
Do not use init
daniel-mills-cqc Dec 12, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,854 changes: 1,183 additions & 671 deletions poetry.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ furo = { version = "^2024.8.6", optional = true }
myst-nb = { version = "^1.1.2", optional = true}
sphinx-autodoc-typehints = { version = "^2.5.0", optional = true }
jupyter-sphinx = { version = "^0.5.3", optional = true}
notebook = "^7.3.1"

[tool.poetry.extras]
tests = [
Expand Down
59 changes: 33 additions & 26 deletions qermit/coherent_pauli_checks/pauli_sampler.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from pytket import Circuit
from pytket._tket.unit_id import UnitID
from pytket.circuit import Bit, CircBox, Command, OpType, Qubit
from pytket.pauli import Pauli, QubitPauliString
from pytket.pauli import Pauli, QubitPauliString, QubitPauliTensor

from qermit.noise_model.noise_model import NoiseModel
from qermit.noise_model.qermit_pauli import QermitPauli
Expand Down Expand Up @@ -124,9 +124,8 @@ def add_pauli_checks_to_circbox(
# is not Clifford. It could be worth raising a clearer
# error.
end_stabiliser.apply_gate(
clifford_command.op.type,
clifford_command.qubits,
params=clifford_command.op.params,
op=clifford_command.op,
qubits=clifford_command.qubits,
)

pauli_check_circuit.add_barrier(command.args + [control_qubit])
Expand Down Expand Up @@ -189,10 +188,13 @@ def sample(self, circ: Circuit) -> List[QermitPauli]:
:return: Z Pauli string of length equal to the circuit.
"""
return [
QermitPauli(
Z_list=[1] * circ.n_qubits,
X_list=[0] * circ.n_qubits,
qubit_list=circ.qubits,
QermitPauli.from_qubit_pauli_tensor(
QubitPauliTensor(
string=QubitPauliString(
map={qubit: Pauli.Z for qubit in circ.qubits}
),
coeff=1,
)
)
]

Expand All @@ -207,10 +209,13 @@ def sample(self, circ: Circuit) -> List[QermitPauli]:
:return: X Pauli string of length equal to the circuit.
"""
return [
QermitPauli(
Z_list=[0] * circ.n_qubits,
X_list=[1] * circ.n_qubits,
qubit_list=circ.qubits,
QermitPauli.from_qubit_pauli_tensor(
QubitPauliTensor(
string=QubitPauliString(
map={qubit: Pauli.X for qubit in circ.qubits}
),
coeff=1,
)
)
]

Expand Down Expand Up @@ -239,21 +244,21 @@ def sample(
:return: Random Pauli of length equal to the size of the circuit.
"""
# TODO: Make sure sampling is done without replacement

stabiliser_list: List[QermitPauli] = []
while len(stabiliser_list) < self.n_checks:
Z_list = [self.rng.integers(2) for _ in circ.qubits]
X_list = [self.rng.integers(2) for _ in circ.qubits]

# Avoids using the identity string as it commutes with all errors
if any(Z == 1 for Z in Z_list) or any(X == 1 for X in X_list):
stabiliser_list.append(
QermitPauli(
Z_list=Z_list,
X_list=X_list,
qubit_list=circ.qubits,
)
)
qpt = QubitPauliTensor(
string=QubitPauliString(
map={
qubit: self.rng.choice(
numpy.array([Pauli.X, Pauli.Y, Pauli.Z, Pauli.I])
)
for qubit in circ.qubits
}
),
coeff=1,
)
if qpt != QubitPauliTensor():
stabiliser_list.append(QermitPauli.from_qubit_pauli_tensor(qpt))

return stabiliser_list

Expand Down Expand Up @@ -347,6 +352,8 @@ def sample(
# )

return [
QermitPauli.from_qubit_pauli_string(smallest_commute_prob_pauli)
QermitPauli.from_qubit_pauli_tensor(
QubitPauliTensor(string=smallest_commute_prob_pauli, coeff=1)
)
for smallest_commute_prob_pauli in smallest_commute_prob_pauli_list
]
59 changes: 33 additions & 26 deletions qermit/noise_model/noise_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from numpy.typing import NDArray
from pytket import Circuit, Qubit
from pytket.circuit import OpType
from pytket.pauli import Pauli, QubitPauliString
from pytket.pauli import Pauli, QubitPauliString, QubitPauliTensor
from scipy.linalg import fractional_matrix_power # type: ignore

from .qermit_pauli import QermitPauli
Expand Down Expand Up @@ -99,8 +99,8 @@ def to_ptm(self) -> Tuple[NDArray, Dict[Tuple[Pauli, ...], int]]:
# PTM entry as a sum pf error weights multiplied by +/-1
# Depending on commutation relations.
for pauli_tuple, index in pauli_index.items():
pauli = QermitPauli.from_pauli_iterable(
pauli_iterable=pauli_tuple,
pauli = QermitPauli.from_pauli_list(
pauli_list=list(pauli_tuple),
qubit_list=[Qubit(i) for i in range(self.n_qubits)],
)

Expand All @@ -111,14 +111,18 @@ def to_ptm(self) -> Tuple[NDArray, Dict[Tuple[Pauli, ...], int]]:
ptm[index][index] += self.identity_error_rate

for error, error_rate in self.distribution.items():
error_pauli = QermitPauli.from_pauli_iterable(
pauli_iterable=error,
error_pauli = QermitPauli.from_pauli_list(
pauli_list=list(error),
qubit_list=[Qubit(i) for i in range(self.n_qubits)],
)

ptm[index][index] += error_rate * QermitPauli.commute_coeff(
pauli_one=pauli, pauli_two=error_pauli
commute_coeff = (
1
if pauli.qubit_pauli_tensor.commutes_with(
error_pauli.qubit_pauli_tensor
)
else -1
)
ptm[index][index] += error_rate * commute_coeff

# Some checks that the form of the PTM is correct.
identity = tuple(Pauli.I for _ in range(self.n_qubits))
Expand Down Expand Up @@ -181,17 +185,21 @@ def from_ptm(
# is the matrix of commutation values.
commutation_matrix = np.zeros(ptm.shape)
for pauli_one_tuple, index_one in pauli_index.items():
pauli_one = QermitPauli.from_pauli_iterable(
pauli_iterable=pauli_one_tuple,
pauli_one = QermitPauli.from_pauli_list(
pauli_list=list(pauli_one_tuple),
qubit_list=[Qubit(i) for i in range(len(pauli_one_tuple))],
)
for pauli_two_tuple, index_two in pauli_index.items():
pauli_two = QermitPauli.from_pauli_iterable(
pauli_iterable=pauli_two_tuple,
pauli_two = QermitPauli.from_pauli_list(
pauli_list=list(pauli_two_tuple),
qubit_list=[Qubit(i) for i in range(len(pauli_two_tuple))],
)
commutation_matrix[index_one][index_two] = QermitPauli.commute_coeff(
pauli_one=pauli_one, pauli_two=pauli_two
commutation_matrix[index_one][index_two] = (
1
if pauli_one.qubit_pauli_tensor.commutes_with(
pauli_two.qubit_pauli_tensor
)
else -1
)

error_rate_list = np.matmul(ptm.diagonal(), np.linalg.inv(commutation_matrix))
Expand Down Expand Up @@ -607,7 +615,8 @@ def counter_propagate(
for _ in range(n_counts):
pauli_error = self.random_propagate(cliff_circ, **kwargs)

if not pauli_error.is_identity:
# Check if the error is the identity.
if pauli_error.qubit_pauli_tensor.string != QubitPauliString():
error_counter.update([pauli_error])

return error_counter
Expand All @@ -627,13 +636,13 @@ def random_propagate(
:raises Exception: Raised if direction is invalid.
:return: Resulting logical error.
"""

# Create identity error.
qubit_list = cliff_circ.qubits
pauli_error = QermitPauli(
Z_list=[0] * len(qubit_list),
X_list=[0] * len(qubit_list),
qubit_list=qubit_list,
pauli_error = QermitPauli.from_qubit_pauli_tensor(
QubitPauliTensor(
string=QubitPauliString(
map={qubit: Pauli.I for qubit in cliff_circ.qubits}
),
coeff=1,
)
)

# Commands are ordered in reverse or original order depending on which
Expand All @@ -656,9 +665,8 @@ def random_propagate(
if direction == Direction.forward:
# Apply gate to total error.
pauli_error.apply_gate(
op_type=command.op.type,
op=command.op,
qubits=cast(List[Qubit], command.args),
params=command.op.params,
)
# Add noise operation if appropriate.
if command.op.type in self.noisy_gates:
Expand All @@ -685,9 +693,8 @@ def random_propagate(
# which has the same effect on the pauli as pushing through the
# dagger.
pauli_error.apply_gate(
op_type=command.op.dagger.type,
op=command.op.dagger,
qubits=cast(List[Qubit], command.args),
params=command.op.dagger.params,
)

return pauli_error
Loading
Loading