diff --git a/example/Transducer_Examples/DirectConversion.py b/example/Transducer_Examples/DirectConversion.py index 24995571..944c7115 100644 --- a/example/Transducer_Examples/DirectConversion.py +++ b/example/Transducer_Examples/DirectConversion.py @@ -1,3 +1,6 @@ +""" Quantum transduction via direct conversion +""" + import sys sys.path.append('.') @@ -15,9 +18,7 @@ from sequence.components.detector import FockDetector from sequence.constants import KET0, KET1 -from example.Transducer_Examples.ConversionProtocols import EmittingProtocol -from example.Transducer_Examples.ConversionProtocols import UpConversionProtocol -from example.Transducer_Examples.ConversionProtocols import DownConversionProtocol +from example.Transducer_Examples.ConversionProtocols import EmittingProtocol, UpConversionProtocol, DownConversionProtocol # GENERAL diff --git a/example/Transducer_Examples/EntanglementSwapping.py b/example/Transducer_Examples/EntanglementSwapping.py index 1818b19f..c163d18c 100644 --- a/example/Transducer_Examples/EntanglementSwapping.py +++ b/example/Transducer_Examples/EntanglementSwapping.py @@ -1,35 +1,32 @@ -import random +""" Quantum transduction via entanglement swapping +""" + +import sys +sys.path.append('.') + from sequence.kernel.timeline import Timeline from sequence.components.optical_channel import QuantumChannel -from sequence.protocol import Protocol from sequence.topology.node import Node -from sequence.components.light_source import LightSource -from sequence.utils.encoding import absorptive, single_atom from sequence.components.photon import Photon -from sequence.kernel.entity import Entity -from typing import List, Callable, TYPE_CHECKING -from abc import ABC, abstractmethod -from sequence.components.memory import Memory -from sequence.utils.encoding import fock -import math +from typing import List from sequence.kernel.event import Event from sequence.kernel.process import Process -import sequence.utils.log as log import matplotlib.pyplot as plt -from example.Transducer_Examples.TransductionComponent import Transducer -from example.Transducer_Examples.TransductionComponent import FockDetector -from example.Transducer_Examples.TransductionComponent import Transmon -from example.Transducer_Examples.TransductionComponent import Counter -from example.Transducer_Examples.TransductionComponent import FockBeamSplitter +from sequence.components.transducer import Transducer +from sequence.components.detector import FockDetector +from sequence.components.transmon import Transmon +from sequence.components.beam_splitter import FockBeamSplitter2 +from sequence.constants import KET0, KET1 -from sequence.components.detector import Detector +from example.Transducer_Examples.SwappingProtocols import Swapping, Measure, EmittingProtocol, UpConversionProtocol -from example.Transducer_Examples.SwappingProtocols import UpConversionProtocolEntangle -from example.Transducer_Examples.SwappingProtocols import Swapping -from example.Transducer_Examples.SwappingProtocols import Measure -from sequence.kernel.quantum_manager import QuantumManager -import sequence.components.circuit as Circuit +class Counter: + def __init__(self): + self.count = 0 + + def trigger(self, detector, info): + self.count += 1 # GENERAL @@ -41,16 +38,14 @@ # Timeline START_TIME = 0 -EMISSION_DURATION = 10 # mus -ENTANGLEMENT_GENERATION_DURATION = 10 #mus -SWAPPING_DUARTION = 1 # mus -MEASURE_DURATION = 9 # mus +EMISSION_DURATION = 10 # ps +ENTANGLEMENT_GENERATION_DURATION = 10 #ps +SWAPPING_DUARTION = 1 # ps +MEASURE_DURATION = 9 # ps PERIOD = ENTANGLEMENT_GENERATION_DURATION + SWAPPING_DUARTION + MEASURE_DURATION + EMISSION_DURATION -#Transmon -ket1 = (0.0 + 0.0j, 1.0 + 0.0j) -ket0 = (1.0 + 0.0j, 0.0 + 0.0j) -state_list= [ket1, ket0] +# Transmon +state_list= [KET1, KET0] TRANSMON_EFFICIENCY = 1 @@ -67,70 +62,23 @@ DISTANCE = 1e3 - - -class EmittingProtocol(Protocol): - - "Protocol for emission of single microwave photon by transmon" - - def __init__(self, own: "Node", name: str, tl: "Timeline", transmon="Transmon", transducer="Transducer"): - super().__init__(own, name) - self.owner = own - self.name = name - self.tl = tl - self.transmon = transmon - self.transducer = transducer - - - def start(self) -> None: - - self.transducer.photon_counter += 1 - - print(f"Microwave photons emitted by the Transmon at Tx: {self.transducer.photon_counter}") - - - def received_message(self, src: str, msg): - pass - - - - -class UpConversionProtocol(Protocol): - def __init__(self, own: "Node", name: str, tl: "Timeline", transducer: "Transducer", node: "Node", transmon: "Transmon"): - super().__init__(own, name) - self.owner = own - self.name = name - self.tl = tl - self.transducer = transducer - self.transmon = transmon - self.node = node - - - def start(self, photon: "Photon") -> None: - - if self.transducer.photon_counter > 0: - - if random.random() < self.transducer.efficiency: - self.transducer._receivers[0].receive_photon(self.node, photon) - print("Successful up-conversion") - else: - self.transducer._receivers[1].receive_photon_from_transducer(photon) - print("FAILED up-conversion") - else: - print("No photon to up-convert") - - def received_message(self, src: str, msg): - pass - - # NODES OF THE NETWORK class SenderNode(Node): + """The end nodes. + + Attributes: + transmon0_name (str): the name of the transmon that emits + transducer_name (str): the name of the transducer + transmon_name (str): the name of the transmon + emitting_protocol (EmittingProtocol): + upconversion_protocol (UpConversionProtocol): + """ def __init__(self, name, timeline, node2): super().__init__(name, timeline) self.transmon0_name = name + ".transmon0" - transmon0 = Transmon(name=self.transmon0_name, owner=self, timeline=timeline, wavelength=[MICROWAVE_WAVELENGTH, OPTICAL_WAVELENGTH], photon_counter=0, efficiency=1, photons_quantum_state= state_list) + transmon0 = Transmon(name=self.transmon0_name, owner=self, timeline=timeline, wavelengths=[MICROWAVE_WAVELENGTH, OPTICAL_WAVELENGTH], photon_counter=0, efficiency=1, photons_quantum_state= state_list) self.add_component(transmon0) self.set_first_component(self.transmon0_name) @@ -146,23 +94,25 @@ def __init__(self, name, timeline, node2): transmon0.add_receiver(transducer) self.transmon_name = name + ".transmon" - transmon = Transmon(name=self.transmon_name, owner=self, timeline=timeline, wavelength=[MICROWAVE_WAVELENGTH, OPTICAL_WAVELENGTH], photon_counter=0, efficiency=1, photons_quantum_state= state_list) + transmon = Transmon(name=self.transmon_name, owner=self, timeline=timeline, wavelengths=[MICROWAVE_WAVELENGTH, OPTICAL_WAVELENGTH], photon_counter=0, efficiency=1, photons_quantum_state= state_list) self.add_component(transmon) self.set_first_component(self.transmon_name) - transducer.add_output([node2, transmon]) + transducer.add_output([node2, transmon]) # NOTE node2 shouldn't be receiver, the quantum channel is skipped self.emitting_protocol = EmittingProtocol(self, name + ".emitting_protocol", timeline, transmon0, transducer) self.upconversion_protocol = UpConversionProtocol(self, name + ".upconversion_protocol", timeline, transducer, node2, transmon) class EntangleNode(Node): + """The node in the middle for entanglement swapping + """ def __init__(self, name, timeline, src_list: List[str]): super().__init__(name, timeline) # Hardware setup self.fock_beam_splitter_name = name + ".FockBeamSplitter" - fock_beam_splitter = FockBeamSplitter(name=self.fock_beam_splitter_name, owner=self, timeline=timeline, efficiency=0.5, photon_counter=0, src_list=src_list) + fock_beam_splitter = FockBeamSplitter2(name=self.fock_beam_splitter_name, owner=self, timeline=timeline, efficiency=0.5, photon_counter=0, src_list=src_list) self.add_component(fock_beam_splitter) self.set_first_component(self.fock_beam_splitter_name) @@ -187,20 +137,20 @@ def __init__(self, name, timeline, src_list: List[str]): self.swapping_protocol = Swapping(self, name + ".swapping_protocol", timeline, fock_beam_splitter) self.measure_protocol = Measure(self, name + ".measure_protocol", timeline, fock_beam_splitter) - def receive_photon(self, photon, src_list): + def receive_photon(self, photon: Photon, src_list: List[str]): self.components[self.fock_beam_splitter_name].receive_photon_from_scr(photon, src_list) + if __name__ == "__main__": runtime = 10e12 tl = Timeline(runtime) - nodoprimo_name = "Nodoo1" - nodoterzo_name = "Nodoo3" + nodoprimo_name = "Node1" + nodoterzo_name = "Node3" src_list = [nodoprimo_name, nodoterzo_name] - node2 = EntangleNode("node2", tl, src_list) node1 = SenderNode(nodoprimo_name, tl, node2) node3 = SenderNode(nodoterzo_name, tl, node2) @@ -222,41 +172,49 @@ def receive_photon(self, photon, src_list): spd_ideals = [] total_emitted_photons = NUM_TRIALS + # Node1 and Node3 + transducer = node1.get_components_by_type("Transducer")[0] + transducer_count = transducer.photon_counter + transducer2 = node3.get_components_by_type("Transducer")[0] + transducer2_count = transducer2.photon_counter + transmon0 = node1.get_components_by_type("Transmon")[0] + transmon_count = transmon0.photon_counter # NOTE transmon_count is not used anywhere + transmon = node1.get_components_by_type("Transmon")[1] + transmon_count = transmon.photon_counter + + # Node2 + fock_beam_splitter = node2.get_components_by_type("FockBeamSplitter2")[0] + fock_beam_splitter_count = fock_beam_splitter.photon_counter + detector1 = node2.get_components_by_type("FockDetector")[0] + detector1_count = detector1.photon_counter + detector1_count2 = detector1.photon_counter2 + detector2 = node2.get_components_by_type("FockDetector")[1] + detector2_count = detector2.photon_counter + detector2_count2 = detector2.photon_counter2 + print(f"--------------------") for trial in range(NUM_TRIALS): print(f"--------------------") print(f"Trial {trial}:") - tl.run() - - #Node1 and Node3 - transducer = node1.get_components_by_type("Transducer")[0] - transducer_count = transducer.photon_counter - transducer2 = node3.get_components_by_type("Transducer")[0] - transducer2_count = transducer2.photon_counter - transmon0 = node1.get_components_by_type("Transmon")[0] - transmon_count = transmon0.photon_counter - transmon = node1.get_components_by_type("Transmon")[1] - transmon_count = transmon.photon_counter - - - #Node2 - fock_beam_splitter = node2.get_components_by_type("FockBeamSplitter")[0] - fock_beam_splitter_count = fock_beam_splitter.photon_counter - detector1 = node2.get_components_by_type("FockDetector")[0] - detector1_count = detector1.photon_counter - detector1_count2 = detector1.photon_counter2 - detector2 = node2.get_components_by_type("FockDetector")[1] - detector2_count = detector2.photon_counter - detector2_count2 = detector2.photon_counter2 - - + # Reset timeline + tl.time = 0 + tl.init() - #Process scheduling + # Reset counters + transducer.photon_counter=0 + transducer2.photon_counter=0 + fock_beam_splitter.photon_counter = 0 + detector1.photon_counter = 0 + detector2.photon_counter = 0 + detector1.photon_counter2 = 0 + detector2.photon_counter2 = 0 + # Process scheduling + # Node 1 & 3 process0 = Process(node1.emitting_protocol, "start", []) - event_time0 = (cumulative_time+EMISSION_DURATION) + event_time0 = (cumulative_time + EMISSION_DURATION) event0 = Event(event_time0, process0) tl.schedule(event0) @@ -264,7 +222,7 @@ def receive_photon(self, photon, src_list): event2 = Event(event_time0, process2) tl.schedule(event2) - process1 = Process(node1.upconversion_protocol, "start", [Photon]) + process1 = Process(node1.upconversion_protocol, "start", [Photon]) # the parameter shouldn't be a class, it should be an object event_time1 = event_time0 + ENTANGLEMENT_GENERATION_DURATION event1 = Event(event_time1, process1) tl.schedule(event1) @@ -273,6 +231,7 @@ def receive_photon(self, photon, src_list): event3 = Event(event_time1, process3) tl.schedule(event3) + # Node 2 process4 = Process(node2.swapping_protocol, "start", [Photon]) event_time4 = event_time1 + SWAPPING_DUARTION event4 = Event(event_time4, process4) @@ -283,6 +242,9 @@ def receive_photon(self, photon, src_list): event5 = Event(event_time5, process5) tl.schedule(event5) + # run the simulation + tl.run() + detector_photon_counter_real = node2.measure_protocol.get_detector_photon_counter_real() spd_real = node2.measure_protocol.get_spd_real() detector_photon_counter_ideal =node2.measure_protocol.get_detector_photon_counter_ideal() @@ -294,8 +256,6 @@ def receive_photon(self, photon, src_list): print(f"CUMILATIVE Detector photon counter REAL : {detector_photon_counter_real}") print(f"CUMULATIVE SPD with eta NOT 1 REAL: {spd_real}") - - # Append results times.append(trial * PERIOD) #Time for each trial detector_photon_counters_real.append(detector_photon_counter_real) @@ -303,20 +263,6 @@ def receive_photon(self, photon, src_list): detector_photon_counters_ideal.append(detector_photon_counter_ideal) spd_ideals.append(spd_ideal) - - # Reset timeline - tl.time = 0 - tl.init() - - # Reset counters - transducer.photon_counter=0 - transducer2.photon_counter=0 - fock_beam_splitter.photon_counter = 0 - detector1.photon_counter = 0 - detector2.photon_counter = 0 - detector1.photon_counter2 = 0 - detector2.photon_counter2 = 0 - cumulative_time += PERIOD # Calculate and print the percentage of ideal detector counters @@ -326,7 +272,6 @@ def receive_photon(self, photon, src_list): percentage_spd_ideal= (spd_ideal / total_emitted_photons) * 100 print(f"Percentage of Entangled detected by SPD IDEAL: {percentage_spd_ideal:.2f}%") - percentage_detector_counters_real = (detector_photon_counter_real / total_emitted_photons) * 100 print(f"Percentage of Entangled pairs detected by photon counter real: {percentage_detector_counters_real:.2f}%") diff --git a/example/Transducer_Examples/SwappingProtocols.py b/example/Transducer_Examples/SwappingProtocols.py index 625484c3..09ea58fa 100644 --- a/example/Transducer_Examples/SwappingProtocols.py +++ b/example/Transducer_Examples/SwappingProtocols.py @@ -1,78 +1,80 @@ import random -import numpy as np from sequence.kernel.timeline import Timeline -from sequence.components.optical_channel import QuantumChannel from sequence.protocol import Protocol from sequence.topology.node import Node -from sequence.components.light_source import LightSource from sequence.components.photon import Photon -from sequence.kernel.entity import Entity -from sequence.components.memory import Memory -from sequence.utils.encoding import fock -from sequence.kernel.event import Event -from sequence.kernel.process import Process -import sequence.utils.log as log -import matplotlib.pyplot as plt -from example.Transducer_Examples.TransductionComponent import Transducer -from example.Transducer_Examples.TransductionComponent import FockDetector -from example.Transducer_Examples.TransductionComponent import Transmon -from example.Transducer_Examples.TransductionComponent import Counter -from example.Transducer_Examples.TransductionComponent import FockBeamSplitter - -from sequence.components.detector import Detector from sequence.components.photon import Photon -from sequence.kernel.quantum_manager import QuantumManager -import sequence.components.circuit as Circuit -from qutip import Qobj +from sequence.components.beam_splitter import FockBeamSplitter2 +from sequence.components.transducer import Transducer +from sequence.components.transmon import Transmon +MICROWAVE_WAVELENGTH = 999308 # nm +OPTICAL_WAVELENGTH = 1550 # nm -ket1 = (0.0 + 0.0j, 1.0 + 0.0j) -ket0 = (1.0 + 0.0j, 0.0 + 0.0j) +class EmittingProtocol(Protocol): + + """Protocol for emission of single microwave photon by transmon""" + + def __init__(self, own: Node, name: str, tl: Timeline, transmon: Transmon, transducer: Transducer): + super().__init__(own, name) + self.owner = own + self.name = name + self.tl = tl + self.transmon = transmon + self.transducer = transducer + + def start(self) -> None: + + self.transducer.photon_counter += 1 + print(f"Microwave photons emitted by {self.name}: {self.transducer.photon_counter}") + + def received_message(self, src: str, msg): + pass -MICROWAVE_WAVELENGTH = 999308 # nm -OPTICAL_WAVELENGTH = 1550 # nm +class UpConversionProtocol(Protocol): + """Convert Microwave photon into optical photon.""" -class UpConversionProtocolEntangle(Protocol): - def __init__(self, own: "Node", name: str, tl: "Timeline", transducer: "Transducer", node: "Node"): + def __init__(self, own: "Node", name: str, tl: "Timeline", transducer: "Transducer", node: "Node", transmon: "Transmon"): super().__init__(own, name) self.owner = own self.name = name self.tl = tl self.transducer = transducer + self.transmon = transmon self.node = node - def start(self, photon: "Photon") -> None: - if random.random() < self.transducer.efficiency: - photon.wavelength = OPTICAL_WAVELENGTH - self.transducer._receivers[0].receive_photon(self.node, photon) - print("Successful up-conversion") + def start(self, photon: Photon) -> None: - self.transducer.output_quantum_state = [0.0 + 0.0j, 0.0 + 0.0j, 1.0 + 0.0j, 0.0 + 0.0j] - + if self.transducer.photon_counter > 0: + + if random.random() < self.transducer.efficiency: + self.transducer._receivers[0].receive_photon(photon, [self.node.name]) + print(f"{self.name}: Successful up-conversion") else: - photon.wavelength = MICROWAVE_WAVELENGTH - self.transducer._receivers[1].receive_photon(photon) - print("FAILED up-conversion") - + self.transducer._receivers[1].receive_photon_from_transducer(photon) + print(f"{self.name}: FAILED up-conversion") + else: + print(f"{self.name}: No photon to up-convert") def received_message(self, src: str, msg): pass - class Swapping(Protocol): - def __init__(self, own: "Node", name: str, tl: "Timeline", FockBS: "FockBeamSplitter"): + + """Entanglement swapping in the middle.""" + + def __init__(self, own: Node, name: str, tl: Timeline, FockBS: FockBeamSplitter2): super().__init__(own, name) self.owner = own self.name = name self.tl = tl self.FockBS = FockBS - def start(self, photon: "Photon") -> None: receivers = self.FockBS._receivers @@ -81,7 +83,6 @@ def start(self, photon: "Photon") -> None: if photon_count == 1: selected_receiver = random.choice(receivers) selected_receiver.get(photon) - selected_receiver.get_2(photon) elif photon_count == 2: @@ -99,7 +100,7 @@ def received_message(self, src: str, msg): class Measure(Protocol): - def __init__(self, own: "Node", name: str, tl: "Timeline", FockBS: "FockBeamSplitter"): + def __init__(self, own: Node, name: str, tl: Timeline, FockBS: FockBeamSplitter2): super().__init__(own, name) self.owner = own self.name = name @@ -113,7 +114,7 @@ def __init__(self, own: "Node", name: str, tl: "Timeline", FockBS: "FockBeamSpli self.spd_ideal= 0 - def start(self, photon: "Photon") -> None: + def start(self, photon: Photon) -> None: if self.FockBS._receivers[0].photon_counter == 1 or self.FockBS._receivers[1].photon_counter == 1: self.detector_photon_counter_real += 1 @@ -145,14 +146,14 @@ def received_message(self, src: str, msg): class SwappingIdeal(Protocol): - def __init__(self, own: "Node", name: str, tl: "Timeline", FockBS: "FockBeamSplitter"): + def __init__(self, own: Node, name: str, tl: Timeline, FockBS: FockBeamSplitter2): super().__init__(own, name) self.owner = own self.name = name self.tl = tl self.FockBS = FockBS - def start(self, photon: "Photon") -> None: + def start(self, photon: Photon) -> None: receivers = self.FockBS._receivers photon_count = self.FockBS.photon_counter @@ -180,7 +181,7 @@ def start(self, photon: "Photon") -> None: class MeasureIdeal(Protocol): - def __init__(self, own: "Node", name: str, tl: "Timeline", FockBS: "FockBeamSplitter"): + def __init__(self, own: Node, name: str, tl: Timeline, FockBS: FockBeamSplitter2): super().__init__(own, name) self.owner = own self.name = name @@ -194,7 +195,7 @@ def __init__(self, own: "Node", name: str, tl: "Timeline", FockBS: "FockBeamSpli self.detector_photon_counter_real = 0 self.spd_real= 0 - def start(self, photon: "Photon") -> None: + def start(self, photon: Photon) -> None: if self.FockBS._receivers[0].photon_counter2 == 1 or self.FockBS._receivers[1].photon_counter2 == 1: self.detector_photon_counter_real += 1 diff --git a/sequence/components/beam_splitter.py b/sequence/components/beam_splitter.py index 334e7f99..cfdf2f22 100644 --- a/sequence/components/beam_splitter.py +++ b/sequence/components/beam_splitter.py @@ -9,6 +9,7 @@ if TYPE_CHECKING: from ..kernel.timeline import Timeline + from ..topology.node import Node from numpy import trace @@ -122,7 +123,7 @@ def get(self, photon, **kwargs) -> None: class FockBeamSplitter2(Entity): - """Class modeling a Fock beam splitter. + """Class modeling a Fock beam splitter. The '2' for avoiding naming conflicts. A Fock beam splitter can send a single photon randomly in one of its ports. @@ -140,13 +141,14 @@ def __init__(self, name: str, owner: "Node", timeline: "Timeline", efficiency: i def init(self): assert len(self._receivers) == 2 - def receive_photon_from_scr(self, photon: "Photon", source: List[str]) -> None: + def receive_photon_from_scr(self, photon: Photon, source: List[str]) -> None: + """Receive photon from two end nodes""" self.photon_counter += 1 def add_output(self, outputs: List): for i in outputs: self.add_receiver(i) - def send_photon(self, receiver: "Entity", photon: "Photon") -> None: + def send_photon(self, receiver: Entity, photon: Photon) -> None: receiver.get(self.name, photon) diff --git a/sequence/components/detector.py b/sequence/components/detector.py index 00b0cfd3..a98b7588 100644 --- a/sequence/components/detector.py +++ b/sequence/components/detector.py @@ -621,13 +621,13 @@ class FockDetector(Detector): Attributes: name (str): name of the detector timeline (Timeline): the simulation timeline - efficiency (float): the efficiency + efficiency (float): the efficiency of the detector wavelength (int): wave length in nm photon_counter (int): photon_counter2 (int): """ - def __init__(self, name: str, timeline: "Timeline", efficiency: float, wavelength: int): + def __init__(self, name: str, timeline: "Timeline", efficiency: float, wavelength: int = 0): super().__init__(name, timeline, efficiency) self.name = name self.photon_counter = 0 @@ -640,12 +640,14 @@ def __init__(self, name: str, timeline: "Timeline", efficiency: float, wavelengt def init(self): pass - def get(self, photon=None, **kwargs) -> None: + def get(self, photon: Photon = None, **kwargs) -> None: + """Not ideal detector, there is a chance for photon loss.""" if random.random() < self.efficiency: self.photon_counter += 1 - def get_2(self, photon=None, **kwargs) -> None: - self.photon_counter2 += 1 + def get_2(self, photon: Photon = None, **kwargs) -> None: + """Ideal detector, no photon loss""" + self.photon_counter2 += 1 def set_efficiency(self, efficiency): self.efficiency = efficiency diff --git a/sequence/components/transmon.py b/sequence/components/transmon.py index 45aec80e..ba8aea6f 100644 --- a/sequence/components/transmon.py +++ b/sequence/components/transmon.py @@ -60,12 +60,15 @@ def get(self) -> None: self.new_photon0 = new_photon0 self.new_photon1 = new_photon1 - def receive_photon_from_transducer(self, photon: "Photon") -> None: - """Receive photon from the transducer, called when the receiver node's transducer successfully down convert the photon to microwave + def receive_photon_from_transducer(self, photon: Photon) -> None: + """Receive photon from the transducer + + In the Direct Conversion, called when the receiver node's transducer successfully down convert the photon to microwave + In the Entanglement Swapping, called when the transducer at the end nodes fail to up convert the microwave into photon """ self.photon_counter += 1 - def receive_photon(self, photon: "Photon") -> None: + def receive_photon(self, photon: Photon) -> None: self.photon_counter += 1 \ No newline at end of file