Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
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
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,13 @@ def get_pauli_error(t: float, t1: float, t2: float) -> Tuple[float, float, float
if t == 0:
return 0, 0, 0

px = 0.25 * (1 - np.exp(-t / t1))
exp_t1 = np.exp(-t / t1)
exp_t2 = np.exp(-t / t2)

px = 0.25 * (1 - exp_t1)
py = px
pz = 0.5 * (1 - np.exp(-t / t2)) - 0.25 * (1 - np.exp(-t / t1))
pz = 0.5 * (1 - exp_t2) - px

# Clamp values
px = min(max(px, 0.0), 1.0)
py = min(max(py, 0.0), 1.0)
Expand Down
35 changes: 34 additions & 1 deletion src/qce_circuit/addon_stim/noise_settings_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from dataclasses import dataclass, field, asdict, fields, is_dataclass
from typing import Dict, List, Any, get_type_hints
from qce_circuit.utilities.singleton_base import Singleton
from qce_circuit.connectivity.intrf_channel_identifier import IQubitID, QubitIDObj
from qce_circuit.connectivity.intrf_channel_identifier import IQubitID, QubitIDObj, IEdgeID, EdgeIDObj
from qce_circuit.utilities.readwrite_yaml import (
get_yaml_file_path,
write_yaml,
Expand Down Expand Up @@ -77,6 +77,17 @@ def __post_init__(self):
# endregion


@dataclass(frozen=True)
class EdgeNoiseModelParameters:
"""Data class, containing noise model parameters for two-qubit edge."""
two_qubit_gate_error: float = field(default=0.0)

# region Class Methods
def __post_init__(self):
typecast_dataclass_fields(self)
# endregion


@dataclass(frozen=True)
class NoiseSettings:
"""
Expand All @@ -86,8 +97,10 @@ class NoiseSettings:
default_t2: float = field(default=20e-6)
default_assignment_error: float = field(default=0.01)
default_single_qubit_gate_error: float = field(default=0.0)
default_two_qubit_gate_error: float = field(default=0.0)

individual_noise: Dict[IQubitID, QubitNoiseModelParameters] = field(default_factory=dict)
pair_noise: Dict[IEdgeID, EdgeNoiseModelParameters] = field(default_factory=dict)
operation_durations: OperationDurationParameters = field(default_factory=OperationDurationParameters)

# region Class Methods
Expand All @@ -99,11 +112,27 @@ def get_default_noise_settings(self) -> QubitNoiseModelParameters:
single_qubit_gate_error=self.default_single_qubit_gate_error,
)

def get_default_pair_noise_settings(self) -> EdgeNoiseModelParameters:
return EdgeNoiseModelParameters(
two_qubit_gate_error=0.0,
)

def get_noise_settings(self, qubit_id: IQubitID) -> QubitNoiseModelParameters:
if qubit_id in self.individual_noise:
return self.individual_noise[qubit_id]
return self.get_default_noise_settings()

def get_pair_noise_settings(self, edge_id: IEdgeID) -> EdgeNoiseModelParameters:
if edge_id in self.pair_noise:
return self.pair_noise[edge_id]
return self.get_default_pair_noise_settings()

def get_operation_duration(self, identifier: str) -> float:
duration_mapper: Dict[str, float] = self.operation_durations.duration_mapper
if identifier in duration_mapper:
return duration_mapper[identifier]
return self.operation_durations.default_duration

def to_dict(self):
# Manually serialize, especially for the individual_noise dictionary
serialized_data = {
Expand All @@ -112,6 +141,7 @@ def to_dict(self):
"default_assignment_error": self.default_assignment_error,
"default_single_qubit_gate_error": self.default_single_qubit_gate_error,
"individual_noise": {key.id: asdict(value) for key, value in self.individual_noise.items()},
"pair_noise": {key.id: asdict(value) for key, value in self.pair_noise.items()},
"operation_durations": asdict(self.operation_durations),
}
return serialized_data
Expand All @@ -121,14 +151,17 @@ def from_dict(cls, data: Dict):
# Extract and remove the individual_noise from the input dictionary to handle it separately
data_copy = data.copy()
individual_noise_data = data_copy.pop("individual_noise", {})
pair_noise_data = data_copy.pop("pair_noise", {})
operation_durations_data = data_copy.pop("operation_durations", asdict(OperationDurationParameters()))
# Reconstruct the individual_noise dictionary
individual_noise = {QubitIDObj(key): QubitNoiseModelParameters(**value) for key, value in individual_noise_data.items()}
pair_noise = {EdgeIDObj.from_qubit_ids(*key.split("-")): EdgeNoiseModelParameters(**value) for key, value in pair_noise_data.items()}
operation_durations = OperationDurationParameters(**operation_durations_data)
# Construct and return the NoiseSettings instance
return cls(
**data_copy,
individual_noise=individual_noise,
pair_noise=pair_noise,
operation_durations=operation_durations,
)
# endregion
Expand Down
26 changes: 26 additions & 0 deletions src/qce_circuit/connectivity/connectivity_surface_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,32 @@ def contains(self, element: Union[IFeedlineID, IQubitID, IEdgeID]) -> bool:
# endregion


class SurfaceTuna17Layer(Surface17Layer):
"""
Singleton class, implementing ISurfaceCodeLayer interface to describe a surface-17 layout.
Extends Surface-17 layout by setting frequency groups to common mid-frequency range. Suppresses parking constraints.
"""
_frequency_group_lookup: Dict[IQubitID, FrequencyGroupIdentifier] = {
QubitIDObj('D1'): FrequencyGroupIdentifier(_id=FrequencyGroup.MID),
QubitIDObj('D2'): FrequencyGroupIdentifier(_id=FrequencyGroup.MID),
QubitIDObj('D3'): FrequencyGroupIdentifier(_id=FrequencyGroup.MID),
QubitIDObj('D4'): FrequencyGroupIdentifier(_id=FrequencyGroup.MID),
QubitIDObj('D5'): FrequencyGroupIdentifier(_id=FrequencyGroup.MID),
QubitIDObj('D6'): FrequencyGroupIdentifier(_id=FrequencyGroup.MID),
QubitIDObj('D7'): FrequencyGroupIdentifier(_id=FrequencyGroup.MID),
QubitIDObj('D8'): FrequencyGroupIdentifier(_id=FrequencyGroup.MID),
QubitIDObj('D9'): FrequencyGroupIdentifier(_id=FrequencyGroup.MID),
QubitIDObj('Z1'): FrequencyGroupIdentifier(_id=FrequencyGroup.MID),
QubitIDObj('Z2'): FrequencyGroupIdentifier(_id=FrequencyGroup.MID),
QubitIDObj('Z3'): FrequencyGroupIdentifier(_id=FrequencyGroup.MID),
QubitIDObj('Z4'): FrequencyGroupIdentifier(_id=FrequencyGroup.MID),
QubitIDObj('X1'): FrequencyGroupIdentifier(_id=FrequencyGroup.MID),
QubitIDObj('X2'): FrequencyGroupIdentifier(_id=FrequencyGroup.MID),
QubitIDObj('X3'): FrequencyGroupIdentifier(_id=FrequencyGroup.MID),
QubitIDObj('X4'): FrequencyGroupIdentifier(_id=FrequencyGroup.MID),
}


def get_neighbors(element: Union[IQubitID, IEdgeID], connectivity: IConnectivityLayer, order: int = 1) -> List[IQubitID]:
"""
Functionality for both qubit-ID and edge-ID.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,11 @@ def parity_group_z(self) -> List[IParityGroup]:
""":return: (All) parity groups part of Z-stabilizers."""
raise InterfaceMethodException

@property
def parity_groups(self) -> List[IParityGroup]:
""":return: (All) X- and Z-type parity groups."""
return self.parity_group_x + self.parity_group_z

@property
@abstractmethod
def data_qubit_ids(self) -> List[IQubitID]:
Expand Down
36 changes: 22 additions & 14 deletions src/qce_circuit/language/intrf_declarative_circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from abc import ABC, abstractmethod, ABCMeta
from dataclasses import dataclass, field
from multipledispatch import dispatch
from typing import List, Union, Dict, Optional
from typing import List, Union, Dict, Optional, TypeVar, Generic
from enum import Enum, unique
import numpy as np
from numpy.typing import NDArray
Expand Down Expand Up @@ -35,6 +35,9 @@
)


T = TypeVar("T")


@unique
class InitialStateEnum(Enum):
"""Enum class, containing different initial state options. Mostly for cosmetic purposes."""
Expand All @@ -47,13 +50,13 @@ class InitialStateEnum(Enum):


@dataclass(frozen=True)
class InitialStateContainer:
class InitialStateContainer(Generic[T]):
"""
Data class, holding reference to qubits and their initial state.
"""
initial_states: Dict[int, InitialStateEnum]
initial_states: Dict[T, InitialStateEnum]
"""Index pointers to data qubits only."""
ancilla_initial_states: Dict[int, InitialStateEnum] = field(default_factory=dict)
ancilla_initial_states: Dict[T, InitialStateEnum] = field(default_factory=dict)
"""Index pointers to ancilla qubits only."""

# region Class Properties
Expand All @@ -63,7 +66,17 @@ def distance(self) -> int:

@property
def as_array(self) -> np.ndarray:
sorted_indices: List[int] = list(sorted(self.initial_states.keys()))
return self.as_ordered_array(qubit_order=None)
# endregion

# region Class Methods
def get_initial_state(self, qubit_index: T) -> InitialStateEnum:
return self.initial_states[qubit_index]

def as_ordered_array(self, qubit_order: Optional[List[T]] = None) -> np.ndarray:
sorted_indices: List[T] = qubit_order
if sorted_indices is None:
sorted_indices: List[T] = list(self.initial_states.keys())
# Maps initial state to binary
to_bit_conversion: Dict[InitialStateEnum, int] = {
InitialStateEnum.ZERO: 0,
Expand All @@ -74,13 +87,8 @@ def as_array(self) -> np.ndarray:
InitialStateEnum.PLUS_I: 1,
}
return np.asarray([to_bit_conversion[self.initial_states[index]] for index in sorted_indices])
# endregion

# region Class Methods
def get_initial_state(self, qubit_index: int) -> InitialStateEnum:
return self.initial_states[qubit_index]

def get_data_qubit_operation(self, qubit_index: int, initial_state_index: int, **kwargs) -> ICircuitOperation:
def get_data_qubit_operation(self, qubit_index: T, initial_state_index: T, **kwargs) -> ICircuitOperation:
"""
:param qubit_index: Index corresponding to qubit-ID in circuit. Will be passed to operation constructor.
:param initial_state_index: Index corresponding to qubit-ID in initial state container.
Expand All @@ -99,7 +107,7 @@ def get_data_qubit_operation(self, qubit_index: int, initial_state_index: int, *
**kwargs,
)

def get_ancilla_qubit_operation(self, qubit_index: int, initial_state_index: int, **kwargs) -> ICircuitOperation:
def get_ancilla_qubit_operation(self, qubit_index: T, initial_state_index: T, **kwargs) -> ICircuitOperation:
"""
:param qubit_index: Index corresponding to qubit-ID in circuit. Will be passed to operation constructor.
:param initial_state_index: Index corresponding to qubit-ID in initial state container.
Expand All @@ -118,7 +126,7 @@ def get_ancilla_qubit_operation(self, qubit_index: int, initial_state_index: int
**kwargs,
)

def get_operation(self, qubit_index: int, initial_state: InitialStateEnum, **kwargs) -> ICircuitOperation:
def get_operation(self, qubit_index: T, initial_state: InitialStateEnum, **kwargs) -> ICircuitOperation:
"""
:param qubit_index: Index corresponding to qubit-ID in circuit. Will be passed to operation constructor.
:param initial_state: Initial state enum.
Expand Down Expand Up @@ -149,7 +157,7 @@ def from_ordered_list(cls, initial_states: List[InitialStateEnum], ancilla_initi
if ancilla_initial_states is None:
ancilla_initial_states = []

return InitialStateContainer(
return InitialStateContainer[int](
initial_states={i: state for i, state in enumerate(initial_states)},
ancilla_initial_states={i: state for i, state in enumerate(ancilla_initial_states)},
)
Expand Down
119 changes: 112 additions & 7 deletions src/qce_circuit/library/surface_code/surface_code_connectivity.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from qce_circuit.connectivity.connectivity_surface_code import (
ParityGroup,
StabilizerType,
SurfaceTuna17Layer,
)
from qce_circuit.connectivity.generic_gate_sequence import (
IGenericSurfaceCodeLayer,
Expand Down Expand Up @@ -345,20 +346,124 @@ def __init__(self):
# endregion


class Surface17Round4Code(GenericSurfaceCode, IGenericSurfaceCodeLayer, metaclass=SingletonABCMeta):

# region Class Constructor
def __init__(self):
super().__init__(
gate_sequences=[
GateSequenceLayer(
_park_operations=[
],
_gate_operations=[
Operation.type_gate(EdgeIDObj(QubitIDObj('X3'), QubitIDObj('D7'))),
Operation.type_gate(EdgeIDObj(QubitIDObj('Z4'), QubitIDObj('D8'))),
Operation.type_gate(EdgeIDObj(QubitIDObj('Z1'), QubitIDObj('D4'))),
Operation.type_gate(EdgeIDObj(QubitIDObj('X2'), QubitIDObj('D5'))),
Operation.type_gate(EdgeIDObj(QubitIDObj('X1'), QubitIDObj('D1'))),
Operation.type_gate(EdgeIDObj(QubitIDObj('Z2'), QubitIDObj('D6'))),
],
),
GateSequenceLayer(
_park_operations=[
],
_gate_operations=[
Operation.type_gate(EdgeIDObj(QubitIDObj('X3'), QubitIDObj('D8'))),
Operation.type_gate(EdgeIDObj(QubitIDObj('Z4'), QubitIDObj('D5'))),
Operation.type_gate(EdgeIDObj(QubitIDObj('Z1'), QubitIDObj('D1'))),
Operation.type_gate(EdgeIDObj(QubitIDObj('X2'), QubitIDObj('D6'))),
Operation.type_gate(EdgeIDObj(QubitIDObj('X1'), QubitIDObj('D2'))),
Operation.type_gate(EdgeIDObj(QubitIDObj('Z2'), QubitIDObj('D3'))),
],
),
GateSequenceLayer(
_park_operations=[
],
_gate_operations=[
Operation.type_gate(EdgeIDObj(QubitIDObj('X3'), QubitIDObj('D4'))),
Operation.type_gate(EdgeIDObj(QubitIDObj('Z4'), QubitIDObj('D9'))),
Operation.type_gate(EdgeIDObj(QubitIDObj('Z1'), QubitIDObj('D5'))),
Operation.type_gate(EdgeIDObj(QubitIDObj('X2'), QubitIDObj('D2'))),
Operation.type_gate(EdgeIDObj(QubitIDObj('Z3'), QubitIDObj('D7'))),
Operation.type_gate(EdgeIDObj(QubitIDObj('X4'), QubitIDObj('D8'))),
],
),
GateSequenceLayer(
_park_operations=[
],
_gate_operations=[
Operation.type_gate(EdgeIDObj(QubitIDObj('X3'), QubitIDObj('D5'))),
Operation.type_gate(EdgeIDObj(QubitIDObj('Z4'), QubitIDObj('D6'))),
Operation.type_gate(EdgeIDObj(QubitIDObj('Z1'), QubitIDObj('D2'))),
Operation.type_gate(EdgeIDObj(QubitIDObj('X2'), QubitIDObj('D3'))),
Operation.type_gate(EdgeIDObj(QubitIDObj('Z3'), QubitIDObj('D4'))),
Operation.type_gate(EdgeIDObj(QubitIDObj('X4'), QubitIDObj('D9'))),
],
),
],
parity_group_z=[
ParityGroup(
_parity_type=StabilizerType.STABILIZER_Z,
_ancilla_qubit=QubitIDObj('Z1'),
_data_qubits=[QubitIDObj('D1'), QubitIDObj('D2'), QubitIDObj('D4'), QubitIDObj('D5')]
),
ParityGroup(
_parity_type=StabilizerType.STABILIZER_Z,
_ancilla_qubit=QubitIDObj('Z2'),
_data_qubits=[QubitIDObj('D3'), QubitIDObj('D6')]
),
ParityGroup(
_parity_type=StabilizerType.STABILIZER_Z,
_ancilla_qubit=QubitIDObj('Z3'),
_data_qubits=[QubitIDObj('D4'), QubitIDObj('D7')]
),
ParityGroup(
_parity_type=StabilizerType.STABILIZER_Z,
_ancilla_qubit=QubitIDObj('Z4'),
_data_qubits=[QubitIDObj('D5'), QubitIDObj('D6'), QubitIDObj('D8'), QubitIDObj('D9')]
),
],
parity_group_x=[
ParityGroup(
_parity_type=StabilizerType.STABILIZER_X,
_ancilla_qubit=QubitIDObj('X1'),
_data_qubits=[QubitIDObj('D1'), QubitIDObj('D2')]
),
ParityGroup(
_parity_type=StabilizerType.STABILIZER_X,
_ancilla_qubit=QubitIDObj('X2'),
_data_qubits=[QubitIDObj('D2'), QubitIDObj('D3'), QubitIDObj('D5'), QubitIDObj('D6')]
),
ParityGroup(
_parity_type=StabilizerType.STABILIZER_X,
_ancilla_qubit=QubitIDObj('X3'),
_data_qubits=[QubitIDObj('D4'), QubitIDObj('D5'), QubitIDObj('D7'), QubitIDObj('D8')]
),
ParityGroup(
_parity_type=StabilizerType.STABILIZER_X,
_ancilla_qubit=QubitIDObj('X4'),
_data_qubits=[QubitIDObj('D8'), QubitIDObj('D9')]
),
],
surface_code_layer=SurfaceTuna17Layer(),
)
# endregion


if __name__ == '__main__':
from qce_circuit.visualization.visualize_layout.display_connectivity import (
plot_gate_sequences,
plot_stabilizer_specific_gate_sequences,
)
import matplotlib.pyplot as plt

# plot_stabilizer_specific_gate_sequences(
# description=Surface13ARound4Code(),
# )
# plot_stabilizer_specific_gate_sequences(
# description=Surface13BRound4Code(),
# )
plot_stabilizer_specific_gate_sequences(
description=Surface13ARound4Code(),
)
plot_stabilizer_specific_gate_sequences(
description=Surface13BRound4Code(),
)
plot_stabilizer_specific_gate_sequences(
description=Surface17Round8Code(),
description=Surface17Round4Code(),
)
plt.show()
Loading