Source code for squanch.errors

import numpy as np

from squanch import gates

__all__ = ["QError", "AttenuationError", "RandomUnitaryError", "SystematicUnitaryError"]


[docs]class QError: '''A generalized quantum error model'''
[docs] def __init__(self, qchannel): ''' Base initialization class; extend in child methods by overwriting along with QError.__init__(self, qchannel) :param qchannel: the quantum channel this error model is being used on ''' self.qchannel = qchannel
[docs] def apply(self, qubit): ''' Applies the error to the transmitted qubit. Overwrite this method in child classes while maintaining the Qubit->(Qubit | None) signature :param Qubit qubit: the qubit being withdrawn from the quantum channel with channel.get(); possibly None :return: the modified qubit ''' if qubit is not None: # Implement error logic here pass return qubit
[docs]class AttenuationError(QError): '''Simulate the possible loss of a qubit in a fiber optic channel due to attenuation effects'''
[docs] def __init__(self, qchannel, attenuation_coefficient = -0.16): ''' Instatiate the error class :param QChannel qchannel: parent quantum channel :param float attenuation_coefficient: attenuation of fiber in dB/km; default: -.16 dB/km, from Yin, et al ''' QError.__init__(self, qchannel) decibel_loss = qchannel.length * attenuation_coefficient self.attenuation = 10 ** (decibel_loss / 10) # Total attenuation along the fiber
[docs] def apply(self, qubit): ''' Simulates possible loss + measurement of qubit :param Qubit qubit: qubit from quantum channel :return: either unchanged qubit or None ''' if np.random.rand() > self.attenuation and qubit is not None: # Photon was lost due to attenuation effects; collapse state and return nothing qubit.measure() qubit = None return qubit
[docs]class RandomUnitaryError(QError): '''Simualates a random rotation along X and Z with a Gaussian distribution of rotation angles'''
[docs] def __init__(self, qchannel, variance): ''' Instatiate the error class :param QChannel qchannel: parent quantum channel :param float variance: variance to use in the Gaussian sampling of X and Z rotation angles ''' QError.__init__(self, qchannel) self.variance = variance
[docs] def apply(self, qubit): ''' Simulates random rotations on X and Z of a qubit :param Qubit qubit: qubit from quantum channel :return: rotated qubit ''' if qubit is not None: x_angle, z_angle = np.random.normal(0, self.variance, 2) gates.RX(qubit, x_angle) gates.RZ(qubit, z_angle) return qubit
[docs]class SystematicUnitaryError(QError): '''Simulates a random unitary error that is the same for each qubit'''
[docs] def __init__(self, qchannel, operator = None, variance = None): ''' Instantiate the systematic unitary error class :param QChannel qchannel: parent quantum channel :param np.array operator: :param float variance: ''' QError.__init__(self, qchannel) assert operator is not None or variance is not None, \ "Provide either a random operator or a variance value to use for sampling." if operator is not None: self.operator = operator elif variance is not None: x_angle, z_angle = np.random.normal(0, variance, 2) Rx = np.cos(x_angle / 2.0) * gates._I - 1j * np.sin(x_angle / 2.0) * gates._X Rz = np.cos(z_angle / 2.0) * gates._I - 1j * np.sin(z_angle / 2.0) * gates._Z self.operator = np.dot(Rz, Rx)
[docs] def apply(self, qubit): ''' Simulates the application of the unitary error :param Qubit qubit: qubit from quantum channel :return: rotated qubit ''' if qubit is not None: qubit.qsystem.apply(self.operator) return qubit