utils.py 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. # The code mainly comes from the tutorial of NetSquid:
  2. # https://docs.netsquid.org/latest-release/tutorial.intro.html
  3. import itertools
  4. import math
  5. import random
  6. import netsquid as ns
  7. import netsquid.qubits.ketstates as ks
  8. import numpy
  9. import numpy as np
  10. from netsquid.components import (DephaseNoiseModel, DepolarNoiseModel,
  11. PhysicalInstruction, QuantumChannel,
  12. QuantumProcessor)
  13. from netsquid.components.instructions import (INSTR_MEASURE_BELL, INSTR_X,
  14. INSTR_Z)
  15. from netsquid.components.models.qerrormodels import QuantumErrorModel
  16. from netsquid.components.qsource import QSource, SourceStatus
  17. from netsquid.nodes.connections import Connection
  18. from netsquid.qubits import StateSampler
  19. def set_random_seed(seed):
  20. random.seed(seed)
  21. numpy.random.seed(seed)
  22. ns.set_random_state(seed=seed)
  23. def pairwise(iterable):
  24. """E.g., pairwise([1, 2, 3, 4]) outputs [(1, 2), (2, 3), (3, 4)]
  25. If input size is less or equal to 1, output []
  26. """
  27. a, b = itertools.tee(iterable)
  28. next(b, None)
  29. return zip(a, b)
  30. def pairs(lst):
  31. """Iterate over pairs in a list (circular fashion).
  32. E.g., if `lst=[0, 1, 2, ..., 9]`, the function returns (0, 1) (1, 2) (2, 3) (3, 4) (4, 5) (5, 6) (6, 7) (7, 8) (8, 9) (9, 0).
  33. Reference: https://stackoverflow.com/questions/1257413/iterate-over-pairs-in-a-list-circular-fashion-in-python
  34. """
  35. n = len(lst)
  36. for i in range(n):
  37. yield lst[i], lst[(i + 1) % n]
  38. def fidelity_to_error_param(f, noise_model):
  39. if noise_model == "Depolar":
  40. # Deploarizing channel: E[ρ] = pρ+(1-p)I/2
  41. # f = p + (1 - p)/2 ==> p = -1 + 2f
  42. # The domain of f is [0.5, 1]
  43. assert f >= 0.5 and f <= 1
  44. p = -1 + 2 * f
  45. return 1 - p
  46. elif noise_model == "Dephase":
  47. # Dephasing channel: E[ρ] = pρ+(1-p)ZρZ'
  48. # f = (2 p + 1)/3 ==> p = 1/2 (-1 + 3 f)
  49. # The domain of f is [1/3, 1]
  50. assert f >= 1 / 3 and f <= 1
  51. p = 0.5 * (-1 + 3 * f)
  52. return 1 - p
  53. elif noise_model == "AmplitudeDamping":
  54. # Amplitude damping channel
  55. # f = 2/3 - p/6 + Sqrt[1 - p]/3 ==> p = 2 (1 - 3 f + Sqrt[2] Sqrt[-1 + 3 f])
  56. # The domain of f is [1/2, 1]
  57. # print("AAAAA", f, 2 * (1 - 3 * f + math.sqrt(2) * math.sqrt(-1 + 3 * f)))
  58. assert f >= 1 / 2 and f <= 1
  59. return 2 * (1 - 3 * f + math.sqrt(2) * math.sqrt(-1 + 3 * f))
  60. elif noise_model == "BitFlip":
  61. # Bit flip channel: E[ρ] = pρ+(1-p)XρX'
  62. # f = (2 p + 1)/3 ==> p = 1/2 (-1 + 3 f)
  63. # The domain of f is [1/3, 1]
  64. assert f >= 1 / 3 and f <= 1
  65. p = 0.5 * (-1 + 3 * f)
  66. return 1 - p
  67. else:
  68. print("Error: Unknown error model")
  69. exit(1)
  70. class EntanglingConnectionOnDemand(Connection):
  71. """A connection that generates an entanglement upon receiving a request in port "trigger".
  72. Consists of a midpoint holding a quantum source that connects to
  73. outgoing quantum channels.
  74. Parameters
  75. ----------
  76. fidelity : float
  77. """
  78. # Static variable used in the name of QSource. This guarantees that all the generated qubits' name are distinct.
  79. qsource_index = 1
  80. def __init__(self, noise_model, fidelity):
  81. name = "EntanglingConnection"
  82. name = name + str(EntanglingConnectionOnDemand.qsource_index)
  83. EntanglingConnectionOnDemand.qsource_index += 1
  84. super().__init__(name=name)
  85. qsource = QSource(f"qsource_{name}", StateSampler([ks.b00], [1.0]), num_ports=2, status=SourceStatus.EXTERNAL)
  86. self.add_subcomponent(qsource, name="qsource")
  87. self.fidelity = fidelity
  88. error_parameter = fidelity_to_error_param(fidelity, noise_model)
  89. if noise_model == "Depolar":
  90. noise_model = DepolarNoiseModel(error_parameter, time_independent=True)
  91. elif noise_model == "Dephase":
  92. noise_model = DephaseNoiseModel(error_parameter, time_independent=True)
  93. elif noise_model == "AmplitudeDamping":
  94. noise_model = AmplitudeDampingNoiseModel(error_parameter)
  95. elif noise_model == "BitFlip":
  96. noise_model = BitFlipNoiseModel(error_parameter)
  97. else:
  98. print("Error: Unknown error model")
  99. exit(1)
  100. qchannel_c2a = QuantumChannel("qchannel_C2A", models={"quantum_noise_model": noise_model})
  101. qchannel_c2b = QuantumChannel("qchannel_C2B")
  102. # Add channels and forward quantum channel output to external port output:
  103. self.add_subcomponent(qchannel_c2a, forward_output=[("A", "recv")])
  104. self.add_subcomponent(qchannel_c2b, forward_output=[("B", "recv")])
  105. # Connect qsource output to quantum channel input:
  106. qsource.ports["qout0"].connect(qchannel_c2a.ports["send"])
  107. qsource.ports["qout1"].connect(qchannel_c2b.ports["send"])
  108. def create_qprocessor(num_positions, gate_noise_rate=0, mem_noise_rate=0):
  109. """Factory to create a quantum processor for each node in the repeater chain network.
  110. Has memory positions and the physical instructions necessary for teleportation.
  111. Parameters
  112. ----------
  113. num_positions : int
  114. The number of qubits that the quantum memory can maintain.
  115. gate_noise_rate : float
  116. The probability that quantum operation results will depolarize.
  117. mem_noise_rate : float
  118. The probability that qubits stored in quantum memory will depolarize.
  119. Returns
  120. -------
  121. :class:`~netsquid.components.qprocessor.QuantumProcessor`
  122. A quantum processor to specification.
  123. """
  124. gate_noise_model = DepolarNoiseModel(gate_noise_rate, time_independent=True)
  125. mem_noise_model = DepolarNoiseModel(mem_noise_rate, time_independent=True)
  126. physical_instructions = [
  127. PhysicalInstruction(INSTR_X, duration=1, quantum_noise_model=None),
  128. PhysicalInstruction(INSTR_Z, duration=1, quantum_noise_model=None),
  129. # We have to set `apply_q_noise_after=False` to make sure the noise is added before measurement
  130. # Otherwise the measurement results will be precise
  131. PhysicalInstruction(INSTR_MEASURE_BELL,
  132. duration=7,
  133. quantum_noise_model=gate_noise_model,
  134. apply_q_noise_after=False),
  135. ]
  136. qproc = QuantumProcessor("QuantumProcessor",
  137. num_positions=num_positions,
  138. fallback_to_nonphysical=False,
  139. mem_noise_models=[mem_noise_model] * num_positions,
  140. phys_instructions=physical_instructions)
  141. return qproc
  142. class BitFlipNoiseModel(QuantumErrorModel):
  143. """Bit Flip Noise Model.
  144. (1-gamma) * |PHI><PHI| + gamma * X|PHI><PHI|X.
  145. Parameters
  146. ----------
  147. gamma : float
  148. Bit flip parameter
  149. Raises
  150. ------
  151. ValueError
  152. If gamma is <0 or >1
  153. """
  154. def __init__(self, gamma, **kwargs):
  155. super().__init__(**kwargs)
  156. self._properties.update({'gamma': gamma})
  157. if gamma < 0:
  158. raise ValueError("gamma {} is negative".format(self.gamma))
  159. if gamma > 1:
  160. raise ValueError("gamma {} is larger than one".format(self.gamma))
  161. self._properties.update({'gamma': gamma})
  162. @property
  163. def gamma(self):
  164. return self._properties['gamma']
  165. @gamma.setter
  166. def gamma(self, value):
  167. self._properties['gamma'] = value
  168. def error_operation(self, qubits, delta_time=0, **kwargs):
  169. """Error operation to apply to qubits.
  170. Parameters
  171. ----------
  172. qubits : tuple of :obj:`~netsquid.qubits.qubit.Qubit`
  173. Qubits to apply noise to.
  174. delta_time : float, optional
  175. Time qubits have spent on component [ns].
  176. """
  177. for qubit in qubits:
  178. self.apply_noise(qubit)
  179. def apply_noise(self, qubit):
  180. """Applies noise to the qubit, depending on gamma."""
  181. # Check whether the memory is empty, if so we do nothing
  182. if qubit is None:
  183. return
  184. # Apply noise
  185. ns.qubits.qubitapi.apply_pauli_noise(qubit, (1 - self.gamma, self.gamma, 0, 0))
  186. class AmplitudeDampingNoiseModel(QuantumErrorModel):
  187. """Amplitude Damping Noise model
  188. Parameters
  189. ----------
  190. gamma : float
  191. Damping parameter
  192. Raises
  193. ------
  194. ValueError
  195. If gamma is <0 or >1
  196. """
  197. def __init__(self, gamma, **kwargs):
  198. super().__init__(**kwargs)
  199. self._properties.update({'gamma': gamma})
  200. if gamma < 0:
  201. raise ValueError("gamma {} is negative".format(self.gamma))
  202. if gamma > 1:
  203. raise ValueError("gamma {} is larger than one".format(self.gamma))
  204. self._properties.update({'gamma': gamma})
  205. @property
  206. def gamma(self):
  207. return self._properties['gamma']
  208. @gamma.setter
  209. def gamma(self, value):
  210. self._properties['gamma'] = value
  211. def error_operation(self, qubits, delta_time=0, **kwargs):
  212. """Error operation to apply to qubits.
  213. Parameters
  214. ----------
  215. qubits : tuple of :obj:`~netsquid.qubits.qubit.Qubit`
  216. Qubits to apply noise to.
  217. delta_time : float, optional
  218. Time qubits have spent on component [ns].
  219. """
  220. for qubit in qubits:
  221. self.apply_noise(qubit)
  222. def apply_noise(self, qubit):
  223. """Applies noise to the qubit, depending on gamma."""
  224. # Check whether the memory is empty, if so we do nothing
  225. if qubit is None:
  226. return
  227. # Apply noise
  228. ns.qubits.qubitapi.amplitude_dampen(qubit, gamma=self.gamma, prob=1)