diff --git a/example/QuantumTransduction/ConversionProtocols.py b/example/QuantumTransduction/ConversionProtocols.py index f775557c..0bb2f2c3 100644 --- a/example/QuantumTransduction/ConversionProtocols.py +++ b/example/QuantumTransduction/ConversionProtocols.py @@ -29,6 +29,10 @@ OPTICAL_WAVELENGTH = 1550 # nm def get_conversion_matrix(efficiency: float) -> Qobj: + """ + Args: + efficiency (float): transducer efficiency + """ custom_gate_matrix = np.array([ [1, 0, 0, 0], [0, math.sqrt(1 - efficiency), math.sqrt(efficiency), 0], diff --git a/example/QuantumTransduction/DirectConversion.py b/example/QuantumTransduction/DirectConversion.py index 944c7115..d2686037 100644 --- a/example/QuantumTransduction/DirectConversion.py +++ b/example/QuantumTransduction/DirectConversion.py @@ -18,7 +18,7 @@ from sequence.components.detector import FockDetector from sequence.constants import KET0, KET1 -from example.Transducer_Examples.ConversionProtocols import EmittingProtocol, UpConversionProtocol, DownConversionProtocol +from example.QuantumTransduction.ConversionProtocols import EmittingProtocol, UpConversionProtocol, DownConversionProtocol # GENERAL @@ -105,7 +105,7 @@ def __init__(self, name, timeline, node2): # add receivers transmon.add_receiver(transducer) - transducer.add_output([node2, detector]) + transducer.add_outputs([node2, detector]) # emitting protocol and upconversion protocol self.emitting_protocol = EmittingProtocol(self, name + ".emitting_protocol", timeline, transmon, transducer) @@ -152,7 +152,7 @@ def __init__(self, name, timeline): transducer.attach(self) transducer.attach(self.counter) self.set_first_component(self.transducer_name) - transducer.add_output([transmon, detector]) + transducer.add_outputs([transmon, detector]) # down conversion protocol self.downconversion_protocol = DownConversionProtocol(self, name + ".downconversion_protocol", timeline, transducer, transmon) diff --git a/example/QuantumTransduction/EntanglementSwapping.py b/example/QuantumTransduction/EntanglementSwapping.py index a1d78d71..3f78632f 100644 --- a/example/QuantumTransduction/EntanglementSwapping.py +++ b/example/QuantumTransduction/EntanglementSwapping.py @@ -48,7 +48,6 @@ def trigger(self, detector, info): state_list= [KET1, KET0] TRANSMON_EFFICIENCY = 1 - # Transducer EFFICIENCY_UP = 0.5 @@ -80,7 +79,6 @@ def __init__(self, name, timeline, node2): self.transmon0_name = name + ".transmon0" 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) self.transducer_name = name + ".transducer" transducer = Transducer(name=self.transducer_name, owner=self, timeline=timeline, efficiency=EFFICIENCY_UP) @@ -89,16 +87,14 @@ def __init__(self, name, timeline, node2): transducer.photon_counter = 0 self.counter = Counter() transducer.attach(self.counter) - self.set_first_component(self.transducer_name) transmon0.add_receiver(transducer) self.transmon_name = name + ".transmon" 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]) # NOTE node2 shouldn't be receiver, the quantum channel is skipped + transducer.add_outputs([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) @@ -119,14 +115,12 @@ def __init__(self, name, timeline, src_list: List[str]): detector_name = name + ".detector1" detector = FockDetector(detector_name, timeline, efficiency=0.25) self.add_component(detector) - self.set_first_component(detector_name) detector_name2 = name + ".detector2" detector2 = FockDetector(detector_name2, timeline, efficiency=0.25) self.add_component(detector2) - self.set_first_component(detector_name2) - fock_beam_splitter.add_output([detector, detector2]) + fock_beam_splitter.add_outputs([detector, detector2]) self.counter = Counter() self.counter2 = Counter() @@ -146,14 +140,13 @@ def receive_photon(self, photon: Photon, src_list: List[str]): runtime = 10e12 tl = Timeline(runtime) - nodoprimo_name = "Node1" - nodoterzo_name = "Node3" - - src_list = [nodoprimo_name, nodoterzo_name] + node1_name = "Node1" + node3_name = "Node3" + src_list = [node1_name, node3_name] node2 = EntangleNode("node2", tl, src_list) - node1 = SenderNode(nodoprimo_name, tl, node2) - node3 = SenderNode(nodoterzo_name, tl, node2) + node1 = SenderNode(node1_name, tl, node2) + node3 = SenderNode(node3_name, tl, node2) qc1 = QuantumChannel("qc.node1.node2", tl, attenuation=ATTENUATION, distance=DISTANCE) qc2 = QuantumChannel("qc.node1.node3", tl, attenuation=ATTENUATION, distance=DISTANCE) @@ -222,22 +215,22 @@ def receive_photon(self, photon: Photon, src_list: List[str]): event2 = Event(event_time0, process2) tl.schedule(event2) - process1 = Process(node1.upconversion_protocol, "start", [Photon]) # the parameter shouldn't be a class, it should be an object + process1 = Process(node1.upconversion_protocol, "start", [Photon]) # NOTE 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) - process3 = Process(node3.upconversion_protocol, "start", [Photon]) + process3 = Process(node3.upconversion_protocol, "start", [Photon]) # NOTE the parameter shouldn't be a class, it should be an object event3 = Event(event_time1, process3) tl.schedule(event3) # Node 2 - process4 = Process(node2.swapping_protocol, "start", [Photon]) + process4 = Process(node2.swapping_protocol, "start", [Photon]) # NOTE the parameter shouldn't be a class, it should be an object event_time4 = event_time1 + SWAPPING_DUARTION event4 = Event(event_time4, process4) tl.schedule(event4) - process5 = Process(node2.measure_protocol, "start", [Photon]) + process5 = Process(node2.measure_protocol, "start", [Photon]) # NOTE the parameter shouldn't be a class, it should be an object event_time5 = event_time4 + MEASURE_DURATION event5 = Event(event_time5, process5) tl.schedule(event5) @@ -279,8 +272,8 @@ def receive_photon(self, photon: Photon, src_list: List[str]): print(f"Percentage of Entangled detected by SPD real: {percentage_spd_real:.2f}%") - -#Plot + +# Plot color_blu = '#0047AB' color_red = '#FF0000' diff --git a/example/QuantumTransduction/SwappingProtocols.py b/example/QuantumTransduction/SwappingProtocols.py index 09ea58fa..91969613 100644 --- a/example/QuantumTransduction/SwappingProtocols.py +++ b/example/QuantumTransduction/SwappingProtocols.py @@ -75,12 +75,12 @@ def __init__(self, own: Node, name: str, tl: Timeline, FockBS: FockBeamSplitter2 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 - if photon_count == 1: + if photon_count == 1: selected_receiver = random.choice(receivers) selected_receiver.get(photon) selected_receiver.get_2(photon) @@ -98,7 +98,6 @@ def received_message(self, src: str, msg): - class Measure(Protocol): def __init__(self, own: Node, name: str, tl: Timeline, FockBS: FockBeamSplitter2): super().__init__(own, name) @@ -113,7 +112,6 @@ def __init__(self, own: Node, name: str, tl: Timeline, FockBS: FockBeamSplitter2 self.detector_photon_counter_ideal = 0 self.spd_ideal= 0 - def start(self, photon: Photon) -> None: if self.FockBS._receivers[0].photon_counter == 1 or self.FockBS._receivers[1].photon_counter == 1: @@ -140,7 +138,6 @@ def get_detector_photon_counter_ideal(self): def get_spd_ideal(self): return self.spd_ideal - def received_message(self, src: str, msg): pass @@ -163,7 +160,6 @@ def start(self, photon: Photon) -> None: self.FockBS._receivers[0].set_efficiency(1) self.FockBS._receivers[1].set_efficiency(1) - if photon_count == 1: selected_receiver = random.choice(receivers) @@ -179,7 +175,6 @@ def start(self, photon: Photon) -> None: - class MeasureIdeal(Protocol): def __init__(self, own: Node, name: str, tl: Timeline, FockBS: FockBeamSplitter2): super().__init__(own, name) @@ -191,7 +186,6 @@ def __init__(self, own: Node, name: str, tl: Timeline, FockBS: FockBeamSplitter2 self.detector_photon_counter_ideal = 0 self.spd_ideal= 0 - self.detector_photon_counter_real = 0 self.spd_real= 0 @@ -202,7 +196,6 @@ def start(self, photon: Photon) -> None: if self.FockBS._receivers[0].photon_counter >= 1 or self.FockBS._receivers[1].photon_counter >= 1: self.spd_real += 1 - print(f"Ideal detector photon counter: {self.detector_photon_counter_ideal}") print(f"Ideal SPD: {self.spd_ideal}") @@ -210,6 +203,5 @@ def start(self, photon: Photon) -> None: print(f"Detector photon counter with eta NOT 1 : {self.entanglement_count_real}") print(f"SPD with eta NOT 1: {self.spd_real}") - def received_message(self, src: str, msg): pass \ No newline at end of file diff --git a/sequence/components/beam_splitter.py b/sequence/components/beam_splitter.py index cfdf2f22..a9a57ae6 100644 --- a/sequence/components/beam_splitter.py +++ b/sequence/components/beam_splitter.py @@ -125,30 +125,40 @@ def get(self, photon, **kwargs) -> None: class FockBeamSplitter2(Entity): """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. - + A Fock beam splitter can send a single photon randomly in one of its ports. See https://arxiv.org/abs/2411.11377, Simulation of Quantum Transduction Strategies for Quantum Networks + Attributes: + name (str): the name + owner (Node): the owner + timeline (Timeline): the timeline + efficiency (float): the efficiency of the beamsplitter + photon_counter (int): counter for counting photons + src_list (str): a list of photon source names """ - def __init__(self, name: str, owner: "Node", timeline: "Timeline", efficiency: int, photon_counter: int, src_list: List[str]): + def __init__(self, name: str, owner: "Node", timeline: "Timeline", efficiency: float, photon_counter: int, src_list: List[str]): Entity.__init__(self, name, timeline) - self.name = name self.owner = owner self.timeline = timeline self.efficiency = efficiency self.photon_counter = photon_counter - + self.src_list = src_list + def init(self): assert len(self._receivers) == 2 - def receive_photon_from_scr(self, photon: Photon, source: List[str]) -> None: + def receive_photon_from_src(self, photon: Photon, source: List[str]) -> None: """Receive photon from two end nodes""" self.photon_counter += 1 - def add_output(self, outputs: List): + def add_outputs(self, outputs: List): + """Add outputs, i.e., receivers + + Args: + outputs (list): a list of entities, i.e., detectors + """ for i in outputs: self.add_receiver(i) def send_photon(self, receiver: Entity, photon: Photon) -> None: receiver.get(self.name, photon) - diff --git a/sequence/components/circuit.py b/sequence/components/circuit.py index 596ede84..29789766 100644 --- a/sequence/components/circuit.py +++ b/sequence/components/circuit.py @@ -39,14 +39,6 @@ def t_gate(): return Qobj(mat, dims=[[2], [2]]) -def custom_gate(p: float): - mat = np.array([[1, 0, 0, 0], - [0, 1, 1 - p, 0], - [0, 0, p, 0], - [0, 0, 0, 1]]) - return Qobj(mat, dims=[[2, 2], [2, 2]]) - - def validator(func): def wrapper(self, *args, **kwargs): for q in args: @@ -98,8 +90,7 @@ def get_unitary_matrix(self) -> np.ndarray: "Y": y_gate, "Z": z_gate, "S": s_gate, - "T": t_gate, - "CUSTOM": custom_gate} + "T": t_gate} for gate in self.gates: name, indices, arg = gate if name == 'h': @@ -122,8 +113,6 @@ def get_unitary_matrix(self) -> np.ndarray: qc.add_gate('S', indices[0]) elif name == 'phase': qc.add_gate('PHASEGATE', indices[0], arg_value=arg) - elif name == 'custom': - qc.add_gate('CUSTOM', indices, arg_value=arg) else: raise NotImplementedError self._cache = gate_sequence_product(qc.propagators()).full() @@ -251,17 +240,6 @@ def phase(self, qubit: int, theta: float): self.gates.append(['phase', [qubit], theta]) - @validator - def custom(self, qubit: int, p: float): - """Method to apply custom gate on two qubits. - - Args: - qubit (int): the index of qubit in the circuit. - p (float): parameter for custom gate - """ - - self.gates.append(['custom', [qubit], p]) - @validator def measure(self, qubit: int): """Method to measure quantum bit into classical bit. diff --git a/sequence/components/detector.py b/sequence/components/detector.py index a98b7588..9cf56f05 100644 --- a/sequence/components/detector.py +++ b/sequence/components/detector.py @@ -623,8 +623,8 @@ class FockDetector(Detector): timeline (Timeline): the simulation timeline efficiency (float): the efficiency of the detector wavelength (int): wave length in nm - photon_counter (int): - photon_counter2 (int): + photon_counter (int): counting photon for the non ideal detector + photon_counter2 (int): counting photon for the ideal detector """ def __init__(self, name: str, timeline: "Timeline", efficiency: float, wavelength: int = 0): @@ -641,18 +641,37 @@ def init(self): pass def get(self, photon: Photon = None, **kwargs) -> None: - """Not ideal detector, there is a chance for photon loss.""" + """Not ideal detector, there is a chance for photon loss. + + Args: + photon (Photon): photon + """ if random.random() < self.efficiency: self.photon_counter += 1 def get_2(self, photon: Photon = None, **kwargs) -> None: - """Ideal detector, no photon loss""" + """Ideal detector, no photon loss + + Args: + photon (Photon): photon + """ self.photon_counter2 += 1 - def set_efficiency(self, efficiency): + def set_efficiency(self, efficiency: float): + """Set the efficiency of the fock detector. + + Args: + efficiency (float): the efficiency of the detector + """ self.efficiency = efficiency - def receive_photon(self, src: str, photon: "Photon") -> None: + def receive_photon(self, src: str, photon: Photon) -> None: + """receive a photon + + Args: + src (str): name of the source node + photon (Photon): photon + """ if photon.wavelength == self.wavelength: self.get(photon) else: diff --git a/sequence/components/transducer.py b/sequence/components/transducer.py index 91d358d4..2deee0cf 100644 --- a/sequence/components/transducer.py +++ b/sequence/components/transducer.py @@ -26,7 +26,7 @@ class Transducer(Entity): efficiency (float): the efficiency of the transducer photon_counter (int): photon counter """ - def __init__(self, owner: "Node", name: str, timeline: "Timeline", efficiency: float = 1): + def __init__(self, owner: Node, name: str, timeline: Timeline, efficiency: float = 1): Entity.__init__(self, name, timeline) self.name = name self.owner = owner @@ -37,14 +37,14 @@ def __init__(self, owner: "Node", name: str, timeline: "Timeline", efficiency: f def init(self): assert len(self._receivers) == 2 - def add_output(self, outputs: List): - """Add receivers""" + def add_outputs(self, outputs: List): + """Add outputs, i.e., receivers""" for i in outputs: self.add_receiver(i) - def receive_photon_from_transmon(self, photon: "Photon") -> None: + def receive_photon_from_transmon(self, photon: Photon) -> None: """Receive a photon from the transmon""" self.photon_counter += 1 # NOTE should schedule an UpConvert event in the future and pass on the argument photon to the UpConverion protocol's convert() method - def receive_photon_from_channel(self, photon: "Photon") -> None: + def receive_photon_from_channel(self, photon: Photon) -> None: self.photon_counter += 1 diff --git a/sequence/components/transmon.py b/sequence/components/transmon.py index ba8aea6f..373590da 100644 --- a/sequence/components/transmon.py +++ b/sequence/components/transmon.py @@ -29,7 +29,7 @@ class Transmon(Entity): photon1 (Photon): optical photon """ - def __init__(self, owner: "Node", name: str, timeline: "Timeline", wavelengths: List[int], photon_counter: int, photons_quantum_state: List[tuple], efficiency: float = 1): + def __init__(self, owner: Node, name: str, timeline: Timeline, wavelengths: List[int], photon_counter: int, photons_quantum_state: List[tuple], efficiency: float = 1): Entity.__init__(self, name, timeline) self.name = name self.owner = owner @@ -46,7 +46,9 @@ def __init__(self, owner: "Node", name: str, timeline: "Timeline", wavelengths: def init(self): pass - def add_output(self, outputs: List): + def add_outputs(self, outputs: List): + """Add outputs, i.e., receivers, of the transmon + """ for i in outputs: self.add_receiver(i) @@ -59,7 +61,7 @@ def get(self) -> None: self.input_quantum_state = input_quantum_state self.new_photon0 = new_photon0 self.new_photon1 = new_photon1 - + def receive_photon_from_transducer(self, photon: Photon) -> None: """Receive photon from the transducer @@ -70,5 +72,3 @@ def receive_photon_from_transducer(self, photon: Photon) -> None: def receive_photon(self, photon: Photon) -> None: self.photon_counter += 1 - - \ No newline at end of file diff --git a/tests/components/test_beam_splitter.py b/tests/components/test_beam_splitter.py index e93134a7..1d2c5646 100644 --- a/tests/components/test_beam_splitter.py +++ b/tests/components/test_beam_splitter.py @@ -1,9 +1,11 @@ import numpy as np -from sequence.components.beam_splitter import BeamSplitter +from sequence.components.beam_splitter import BeamSplitter, FockBeamSplitter2 +from sequence.components.detector import FockDetector from sequence.components.photon import Photon from sequence.kernel.timeline import Timeline from sequence.utils.encoding import polarization +from sequence.topology.node import Node np.random.seed(0) SEED = 0 @@ -205,3 +207,28 @@ def test_BeamSplitter_get(): # tl.time += 1 # # assert len(rec_0.log) == NUM_TRIALS + + +def test_FockBeamSplitter2(): + """ Quantum transduction via entanglemenet swapping (node2). + A fock beamsplitter is attached to two detectors + """ + tl = Timeline() + node1 = Node('Node1', tl) + node2 = Node('Node2', tl) # swapping node + node3 = Node('Node3', tl) + src_list = [node1, node3] + + detector1 = FockDetector(node2.name + '.detector1', tl, efficiency=0.5) + detector2 = FockDetector(node2.name + '.detector2', tl, efficiency=0.5) + + fockbeamsplitter2 = FockBeamSplitter2('fock_beamsplitter2', node2, tl, efficiency=0.9, photon_counter=0, src_list=src_list) + fockbeamsplitter2.add_outputs([detector1, detector2]) + + photon = Photon('photon', tl) + + photon_number = 10 + for _ in range(photon_number): + fockbeamsplitter2.receive_photon_from_src(photon, src_list) + + assert fockbeamsplitter2.photon_counter == photon_number diff --git a/tests/components/test_detector.py b/tests/components/test_detector.py index faf11246..daebdfb0 100644 --- a/tests/components/test_detector.py +++ b/tests/components/test_detector.py @@ -329,3 +329,16 @@ def get_generator(self): times = qsd.get_photon_times() assert len(times[0]) == NUM_TRIALS + + +def test_FockDetector(): + efficiency = 0.9 + tl = Timeline() + fock_detector = FockDetector("fock detector", tl, efficiency=efficiency) + NUM_TRIALS = 1000 + for _ in range(NUM_TRIALS): + fock_detector.get() + fock_detector.get_2() + + ratio = fock_detector.photon_counter / fock_detector.photon_counter2 + assert efficiency - 0.05 < ratio < efficiency + 0.05 diff --git a/tests/components/test_transducer.py b/tests/components/test_transducer.py new file mode 100644 index 00000000..14e1219b --- /dev/null +++ b/tests/components/test_transducer.py @@ -0,0 +1,39 @@ +import numpy as np +from sequence.components.transducer import Transducer +from sequence.kernel.timeline import Timeline + +def test_Transducer(): + + class Owner: + def __init__(self): + self.generator = np.random.default_rng(seed=0) + + def get_generator(self): + return self.generator + + class FakeReceiver: + pass + + class FakePhoton: + pass + + tl = Timeline() + owner = Owner() + efficiency = 1 + transducer = Transducer(owner, "transducer", tl, efficiency) + + fake_receiver = FakeReceiver() + transducer.add_outputs([fake_receiver]) + + PHOTON_NUMBER = 10 + for _ in range(PHOTON_NUMBER): + photon = FakePhoton() + transducer.receive_photon_from_transmon(photon) + assert transducer.photon_counter == PHOTON_NUMBER + + transducer.photon_counter = 0 + PHOTON_NUMBER = 10 + for _ in range(PHOTON_NUMBER): + photon = FakePhoton() + transducer.receive_photon_from_channel(photon) + assert transducer.photon_counter == PHOTON_NUMBER diff --git a/tests/components/test_transmon.py b/tests/components/test_transmon.py new file mode 100644 index 00000000..c8dc39fb --- /dev/null +++ b/tests/components/test_transmon.py @@ -0,0 +1,44 @@ +import numpy as np +from sequence.components.transmon import Transmon +from sequence.kernel.timeline import Timeline +from sequence.topology.node import Node +from sequence.constants import KET0, KET1 + + +def test_Transmon(): + + class Owner: + def __init__(self): + self.generator = np.random.default_rng(seed=0) + + def get_generator(self): + return self.generator + + class FakeReceiver: + pass + + class FakePhoton: + pass + + MICROWAVE_WAVELENGTH = 999308 # nm + OPTICAL_WAVELENGTH = 1550 # nm + state_list= [KET1, KET0] + TRANSMON_EFFICIENCY = 1 + + tl = Timeline() + owner = Owner() + fake_receiver = FakeReceiver() + wavelengths = [MICROWAVE_WAVELENGTH, OPTICAL_WAVELENGTH] + transmon = Transmon('transmon', owner, tl, wavelengths=wavelengths, photon_counter=0, + photons_quantum_state=state_list, efficiency=TRANSMON_EFFICIENCY) + + transmon.add_outputs([fake_receiver]) + transmon.get() + expect = [0, 0, 1, 0] + assert np.array_equal(transmon.input_quantum_state, expect) + + NUM_PHOTONS = 10 + for _ in range(NUM_PHOTONS): + fake_photon = FakePhoton + transmon.receive_photon_from_transducer(fake_photon) + assert transmon.photon_counter == NUM_PHOTONS