diff --git a/example/Transducer_Examples/ConversionProtocols.py b/example/Transducer_Examples/ConversionProtocols.py new file mode 100644 index 00000000..3b79594a --- /dev/null +++ b/example/Transducer_Examples/ConversionProtocols.py @@ -0,0 +1,173 @@ +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.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 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 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 + + +ket1 = (0.0 + 0.0j, 1.0 + 0.0j) +ket0 = (1.0 + 0.0j, 0.0 + 0.0j) + +MICROWAVE_WAVELENGTH = 999308 # nm +OPTICAL_WAVELENGTH = 1550 # nm + +def get_conversion_matrix(efficiency: float) -> Qobj: + custom_gate_matrix = np.array([ + [1, 0, 0, 0], + [0, math.sqrt(1 - efficiency), math.sqrt(efficiency), 0], + [0, math.sqrt(efficiency), math.sqrt(1 - efficiency), 0], + [0, 0, 0, 1] + ]) + return Qobj(custom_gate_matrix, dims=[[4], [4]]) + + + +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.transmon.get() + + if self.transmon.photons_quantum_state[0] == ket1: + if random.random() < self.transmon.efficiency: + self.transmon._receivers[0].receive_photon_from_transmon(self.transmon.new_photon0) + self.transmon.photon_counter += 1 + else: + pass + + else: + print("The transmon is in the state 00, or 01, it doesn't emit microwave photons") + + print(f"Microwave photons emitted by the Transmon at Tx: {self.transmon.photon_counter}") + + + + def received_message(self, src: str, msg): + pass + + + +class UpConversionProtocol(Protocol): + + "Protocol for Up-conversion of an input microwave photon into an output optical photon" + + 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: + custom_gate = get_conversion_matrix(self.transducer.efficiency) + + transmon_state_vector = np.array(self.transmon.input_quantum_state).reshape((4, 1)) + photon_state = Qobj(transmon_state_vector, dims=[[4], [1]]) + + new_photon_state = custom_gate * photon_state + self.transducer.quantum_state = new_photon_state.full().flatten() + + print(f"Transducer at Tx quantum state: {self.transducer.quantum_state}") + + if random.random() < self.transducer.efficiency: + photon.wavelength = OPTICAL_WAVELENGTH + self.transducer._receivers[0].receive_photon(self.node, photon) + print("Successful up-conversion") + self.transducer.output_quantum_state = [0.0 + 0.0j, 0.0 + 0.0j, 1.0 + 0.0j, 0.0 + 0.0j] + print(f"State after successful up-conversion: {self.transducer.output_quantum_state}") + else: + photon.wavelength = MICROWAVE_WAVELENGTH + self.transducer._receivers[1].get(photon) + print("FAILED up-conversion") + else: + print("No photon to up-convert") + + def received_message(self, src: str, msg): + pass + + + +class DownConversionProtocol(Protocol): + + "Protocol for Down-conversion of an input optical photon into an output microwave photon" + + def __init__(self, own: "Node", name: str, tl: "Timeline", transducer: "Transducer", transmon: "Transmon"): + super().__init__(own, name) + self.owner = own + self.name = name + self.tl = tl + self.transducer = transducer + + def start(self, photon: "Photon") -> None: + if self.transducer.photon_counter > 0: + + + transducer_state = [0.0 + 0.0j, 0.0 + 0.0j, 1.0 + 0.0j, 0.0 + 0.0j] + custom_gate = get_conversion_matrix(self.transducer.efficiency) + + transducer_state_vector = np.array(transducer_state).reshape((4, 1)) + transducer_state = Qobj(transducer_state_vector, dims=[[4], [1]]) + + new_transducer_state = custom_gate * transducer_state + self.transducer.quantum_state = new_transducer_state.full().flatten() + + print(f"Transducer at Rx quantum state: {self.transducer.quantum_state}") + + if random.random() < self.transducer.efficiency: + photon.wavelength = MICROWAVE_WAVELENGTH + self.transducer._receivers[0].receive_photon_from_transducer(photon) + print("Successful down-conversion") + self.transducer.output_quantum_state = [0.0 + 0.0j, 0.0 + 0.0j, 1.0 + 0.0j, 0.0 + 0.0j] + print(f"State after successful down-conversion: {self.transducer.output_quantum_state}") + else: + photon.wavelength = OPTICAL_WAVELENGTH + self.transducer._receivers[1].get(photon) + print("FAILED down-conversion") + else: + print("No photon to down-convert") + + def received_message(self, src: str, msg): + pass + + + + diff --git a/example/Transducer_Examples/DirectConversion.py b/example/Transducer_Examples/DirectConversion.py new file mode 100644 index 00000000..6f2d89ba --- /dev/null +++ b/example/Transducer_Examples/DirectConversion.py @@ -0,0 +1,309 @@ +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 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 * + +from example.Transducer_Examples.ConversionProtocols import EmittingProtocol +from example.Transducer_Examples.ConversionProtocols import UpConversionProtocol +from example.Transducer_Examples.ConversionProtocols import DownConversionProtocol + +from sequence.kernel.quantum_manager import QuantumManager +import sequence.components.circuit as Circuit + + +#GENERAL + +NUM_TRIALS = 50 +FREQUENCY = 1e9 +MICROWAVE_WAVELENGTH = 999308 # nm +OPTICAL_WAVELENGTH = 1550 # nm +MEAN_PHOTON_NUM=1 + +# Timeline +START_TIME = 0 +EMISSION_DURATION = 10 # ps +CONVERSION_DURATION = 10 # ps +PERIOD = EMISSION_DURATION + CONVERSION_DURATION + CONVERSION_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_EFFICIENCY = 1 + +# Transducer +EFFICIENCY_UP = 0.5 +EFFICIENCY_DOWN = 0.5 + +# Fock Detector +MICROWAVE_DETECTOR_EFFICIENCY_Rx = 1 +MICROWAVE_DETECTOR_EFFICIENCY_Tx = 1 +OPTICAL_DETECTOR_EFFICIENCY = 1 + +# Channel +ATTENUATION = 0 +DISTANCE = 1e3 + + + + + + + +#NODES OF THE NETWORK + + +class SenderNode(Node): + def __init__(self, name, timeline, node2): + super().__init__(name, timeline) + + + #Hardware setup + + self.transmon_name = name + ".transmon" + transmon = Transmon(name=self.transmon_name, owner=self, timeline=timeline, wavelength=[MICROWAVE_WAVELENGTH, OPTICAL_WAVELENGTH], photon_counter=0, efficiency=TRANSMON_EFFICIENCY, photons_quantum_state= state_list) + self.add_component(transmon) + self.set_first_component(self.transmon_name) + + + self.transducer_name = name + ".transducer" + transducer = Transducer(name=self.transducer_name, owner=self, timeline=timeline, efficiency=EFFICIENCY_UP) + self.add_component(transducer) + transducer.attach(self) + transducer.photon_counter = 0 + self.counter = Counter() + transducer.attach(self.counter) + self.set_first_component(self.transducer_name) + + + transmon.add_receiver(transducer) + + + detector_name = name + ".fockdetector1" + detector = FockDetector(detector_name, timeline, wavelength=MICROWAVE_WAVELENGTH, efficiency=MICROWAVE_DETECTOR_EFFICIENCY_Tx) + self.add_component(detector) + self.set_first_component(detector_name) + self.counter = Counter() + detector.attach(self.counter) + + transducer.add_output([node2, detector]) + + self.emitting_protocol = EmittingProtocol(self, name + ".emitting_protocol", timeline, transmon, transducer) + self.upconversion_protocol = UpConversionProtocol(self, name + ".upconversion_protocol", timeline, transducer, node2, transmon) + + + + +class ReceiverNode(Node): + def __init__(self, name, timeline): + super().__init__(name, timeline) + + self.transducer2_name = name + ".transducer2" + transducer2 = Transducer(name=self.transducer2_name, owner=self, timeline=timeline, efficiency=EFFICIENCY_DOWN) + self.add_component(transducer2) + transducer2.attach(self) + transducer2.photon_counter = 0 + self.counter = Counter() + transducer2.attach(self.counter) + self.set_first_component(self.transducer2_name) + + detector2_name = name + ".fockdetector2" + detector2 = FockDetector(detector2_name, timeline, wavelength=OPTICAL_WAVELENGTH, efficiency=OPTICAL_DETECTOR_EFFICIENCY) + self.add_component(detector2) + self.counter2 = Counter() + detector2.attach(self.counter2) + + self.transmon_name2 = name + ".transmon2" + transmon2 = Transmon(name=self.transmon_name2, owner=self, timeline=timeline, wavelength=[MICROWAVE_WAVELENGTH, OPTICAL_WAVELENGTH], photons_quantum_state= state_list, photon_counter=0, efficiency=1) + self.add_component(transmon2) + self.set_first_component(self.transmon_name2) + + transducer2.add_output([transmon2,detector2]) + print(f"Transducer2 output: {transducer2._receivers}") + + self.downconversion_protocol = DownConversionProtocol(self, name + ".downconversion_protocol", timeline, transducer2, transmon2) + + def receive_photon(self, src, photon): + self.components[self.transducer2_name].receive_photon_from_channel(photon) + + +#MAIN + +if __name__ == "__main__": + + runtime = 10e12 + tl = Timeline(runtime) + + node2 = ReceiverNode("node2", tl) + node1 = SenderNode("node1", tl, node2) + + qc1 = QuantumChannel("qc.node1.node2", tl, attenuation=ATTENUATION, distance=DISTANCE) + qc1.set_ends(node1, node2.name) + + if EFFICIENCY_UP >= 0 and EFFICIENCY_UP <= 1 and EFFICIENCY_DOWN >= 0 and EFFICIENCY_DOWN <= 1: + pass + else: + print("Error: the efficiency must be between 0 and 1") + exit(1) + + tl.init() + + total_photons_successful = 0 + total_transducer_count = 0 + + + #Plot1 + failed_up_conversions = [] + failed_down_conversions = [] + successful_conversions = [] + + #Plot2 + ideal_photons = [] + emitted_photons = [] + converted_photons = [] + + + cumulative_time = START_TIME + + print(f"--------------------") + + print(f"Direct Quantum Transduction Protocol starts, the qubit that we are going to convert is: {ket1}") + + for trial in range(NUM_TRIALS): + + print(f"--------------------") + print(f"Trial {trial}:") + + tl.run() + + + #Node1 + transmon = node1.get_components_by_type("Transmon")[0] + transmon_count = transmon.photon_counter + transducer = node1.get_components_by_type("Transducer")[0] + transducer_count = transducer.photon_counter + detector = node1.get_components_by_type("FockDetector")[0] + detector_count = detector.photon_counter + + #Node2 + transducer2 = node2.get_components_by_type("Transducer")[0] + transmon2 = node2.get_components_by_type("Transmon")[0] + transmon2_count = transmon2.photon_counter + detector2 = node2.get_components_by_type("FockDetector")[0] + detector2_count = detector2.photon_counter + + process0 = Process(node1.emitting_protocol, "start", []) + event_time0 = (cumulative_time + EMISSION_DURATION) + event0 = Event(event_time0, process0) + tl.schedule(event0) + + process1 = Process(node1.upconversion_protocol, "start", [Photon]) + event_time1 = (event_time0 + CONVERSION_DURATION) + event1 = Event(event_time1, process1) + tl.schedule(event1) + + process2 = Process(node2.downconversion_protocol, "start", [Photon]) + event_time2 =(event_time1 + CONVERSION_DURATION) + event2 = Event(event_time2, process2) + tl.schedule(event2) + + failed_up_conversions.append(detector_count) + failed_down_conversions.append(detector2_count) + successful_conversions.append(transmon2_count) + + print(f"Number of photons converted at time {tl.time}: {transmon2_count}") + + #reset timeline + tl.time = 0 + tl.init() + + total_photons_successful += transmon2_count + total_transducer_count += transducer_count + cumulative_time += PERIOD + + ideal_photons.append(trial + 1) + emitted_photons.append(total_transducer_count) + converted_photons.append(total_photons_successful) + + #Reset counters + transmon.photon_counter = 0 + transmon2.photon_counter = 0 + transducer.photon_counter = 0 + detector.photon_counter = 0 + detector2.photon_counter = 0 + transducer2.photon_counter = 0 + + + + + + #RESULTS + + print(f"- - - - - - - - - -") + print(f"Period: {PERIOD}") + + total_photons_to_be_converted = NUM_TRIALS-1 + print(f"Total number of photons converted: {total_photons_successful}") + print(f"Total number of photons EMITTED: {total_transducer_count}") + + conversion_percentage = (total_photons_successful / total_photons_to_be_converted) * 100 if total_photons_to_be_converted > 0 else 0 + print(f"Conversion efficiency of DQT protocol with no-idealities of transmon: {conversion_percentage:.2f}%") + + conversion_percentage_2 = (total_photons_successful / total_transducer_count) * 100 if total_photons_to_be_converted > 0 else 0 + print(f"Conversion efficiency of DQT protocol: {conversion_percentage_2:.2f}%") + + failed_down_conversions_adjusted = [x + 1 for x in failed_down_conversions] + successful_conversions_adjusted = [y + 2 for y in successful_conversions] + + print(f"- - - - - - - - - -") + + time_points = [i * PERIOD for i in range(NUM_TRIALS)] + + + + +results_matrix = np.zeros((NUM_TRIALS, 3)) +for i in range(NUM_TRIALS): + if failed_up_conversions[i] != 0: + results_matrix[i, 0] = 1 + if failed_down_conversions[i] != 0: + results_matrix[i, 1] = 1 + if successful_conversions[i] != 0: + results_matrix[i, 2] = 1 + +fig, (ax1, ax2) = plt.subplots(2, 1, sharex=True, figsize=(12, 8), gridspec_kw={'height_ratios': [4, 1]}) + +ax1.plot(time_points, ideal_photons, 'o-', label="Ideal Successfully Converted Photons", color='darkblue') +ax1.plot(time_points, converted_photons, 'o-', label="Successfully Converted Photons", color='#FF00FF') +ax1.set_ylabel("Photon Number", fontsize=24) +ax1.set_title("Photon Conversion over Time", fontsize=24, fontweight='bold') +ax1.legend(fontsize=24, loc='upper left') +ax1.grid(True) +ax1.tick_params(axis='both', labelsize=18) + +ax2.bar(time_points, results_matrix[:, 0], color='#ED213C', label='Failed Up', alpha=0.7, width=PERIOD * 0.8) +ax2.bar(time_points, results_matrix[:, 1], color='blue', label='Failed Down', alpha=0.7, bottom=results_matrix[:, 0], width=PERIOD * 0.8) +ax2.bar(time_points, results_matrix[:, 2], color='#119B70', label='Successful', alpha=0.7, bottom=results_matrix[:, 0] + results_matrix[:, 1], width=PERIOD * 0.8) +ax2.set_xlabel(r"Time ($\mu$s)", fontsize=24) +ax2.legend(fontsize=18, loc='upper left') +ax2.tick_params(axis='both', labelsize=12) +ax2.yaxis.set_visible(False) +ax2.legend(fontsize=12, loc='upper left') +ax2.tick_params(axis='both', labelsize=12) + +plt.tight_layout() +plt.show() diff --git a/example/Transducer_Examples/EntanglementSwapping.py b/example/Transducer_Examples/EntanglementSwapping.py new file mode 100644 index 00000000..1818b19f --- /dev/null +++ b/example/Transducer_Examples/EntanglementSwapping.py @@ -0,0 +1,369 @@ +import random +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 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 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 + + +# GENERAL +NUM_TRIALS = 50 +FREQUENCY = 1e9 +MICROWAVE_WAVELENGTH = 999308 # nm +OPTICAL_WAVELENGTH = 1550 # nm +MEAN_PHOTON_NUM = 1 + +# Timeline +START_TIME = 0 +EMISSION_DURATION = 10 # mus +ENTANGLEMENT_GENERATION_DURATION = 10 #mus +SWAPPING_DUARTION = 1 # mus +MEASURE_DURATION = 9 # mus +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_EFFICIENCY = 1 + + +# Transducer +EFFICIENCY_UP = 0.5 + +# Fock Detector +MICROWAVE_DETECTOR_EFFICIENCY_Rx = 1 +MICROWAVE_DETECTOR_EFFICIENCY_Tx = 1 +OPTICAL_DETECTOR_EFFICIENCY = 1 + +# Channel +ATTENUATION = 0 +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): + 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) + 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) + self.add_component(transducer) + transducer.attach(self) + 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, wavelength=[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]) + + 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): + 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) + self.add_component(fock_beam_splitter) + self.set_first_component(self.fock_beam_splitter_name) + + 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]) + + self.counter = Counter() + self.counter2 = Counter() + + detector.attach(self.counter) + detector2.attach(self.counter2) + + 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): + 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" + + src_list = [nodoprimo_name, nodoterzo_name] + + + node2 = EntangleNode("node2", tl, src_list) + node1 = SenderNode(nodoprimo_name, tl, node2) + node3 = SenderNode(nodoterzo_name, tl, node2) + + qc1 = QuantumChannel("qc.node1.node2", tl, attenuation=ATTENUATION, distance=DISTANCE) + qc2 = QuantumChannel("qc.node1.node3", tl, attenuation=ATTENUATION, distance=DISTANCE) + qc1.set_ends(node1, node2.name) + qc2.set_ends(node1, node3.name) + + tl.init() + + cumulative_time = START_TIME + + # List to store results + times = [] + detector_photon_counters_real = [] + spd_reals = [] + detector_photon_counters_ideal = [] + spd_ideals = [] + total_emitted_photons = NUM_TRIALS + + 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 + + + + #Process scheduling + + process0 = Process(node1.emitting_protocol, "start", []) + event_time0 = (cumulative_time+EMISSION_DURATION) + event0 = Event(event_time0, process0) + tl.schedule(event0) + + process2 = Process(node3.emitting_protocol, "start", []) + event2 = Event(event_time0, process2) + tl.schedule(event2) + + process1 = Process(node1.upconversion_protocol, "start", [Photon]) + event_time1 = event_time0 + ENTANGLEMENT_GENERATION_DURATION + event1 = Event(event_time1, process1) + tl.schedule(event1) + + process3 = Process(node3.upconversion_protocol, "start", [Photon]) + event3 = Event(event_time1, process3) + tl.schedule(event3) + + process4 = Process(node2.swapping_protocol, "start", [Photon]) + event_time4 = event_time1 + SWAPPING_DUARTION + event4 = Event(event_time4, process4) + tl.schedule(event4) + + process5 = Process(node2.measure_protocol, "start", [Photon]) + event_time5 = event_time4 + MEASURE_DURATION + event5 = Event(event_time5, process5) + tl.schedule(event5) + + 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() + spd_ideal = node2.measure_protocol.get_spd_ideal() + + print(f"CUMULATIVE: Detector photon counter with IDEAL (cumulative): {detector_photon_counter_ideal}") + print(f"CUMULATIVE: SPD IDEAL: {spd_ideal}") + + 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) + spd_reals.append(spd_real) + 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 + percentage_detector_counters_ideal = (detector_photon_counter_ideal / total_emitted_photons) * 100 + print(f"Percentage of Entangled pairs generated (PHOTON COUNTER IDEAL): {percentage_detector_counters_ideal:.2f}%") + + 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}%") + + percentage_spd_real= (spd_real / total_emitted_photons) * 100 + print(f"Percentage of Entangled detected by SPD real: {percentage_spd_real:.2f}%") + + + +#Plot + +color_blu = '#0047AB' +color_red = '#FF0000' + + +plt.figure(figsize=(12, 6)) +plt.plot(times, detector_photon_counters_real, 'o-', color='#FF00FF', label='PCD Real') +plt.plot(times, detector_photon_counters_ideal, 'o-', color='darkblue', label='PCD Ideal') +plt.xlabel(r"Time ($\mu$s)", fontsize=32) +plt.ylabel('PCD Counts', fontsize=32) +plt.legend(fontsize=30) +plt.tick_params(axis='both', which='major', labelsize=28) + +plt.grid(True) +plt.title('Ideal vs Real PCD Counts Over Time', fontsize=32, fontweight='bold') +plt.show() + +plt.figure(figsize=(12, 6)) +plt.plot(times, spd_reals, 'o-', color='#FF00FF', label='SPD Real') +plt.plot(times, spd_ideals, 'o-', color='darkblue', label='SPD Ideal') +plt.xlabel(r"Time ($\mu$s)", fontsize=32) +plt.ylabel('SPD Counts', fontsize=32) +plt.legend(fontsize=30) +plt.tick_params(axis='both', which='major', labelsize=28) + +plt.grid(True) +plt.title('Ideal vs Real SPD Counts Over Time', fontsize=32, fontweight='bold') +plt.show() + + + diff --git a/example/Transducer_Examples/SwappingProtocols.py b/example/Transducer_Examples/SwappingProtocols.py new file mode 100644 index 00000000..625484c3 --- /dev/null +++ b/example/Transducer_Examples/SwappingProtocols.py @@ -0,0 +1,214 @@ +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 + + + + +ket1 = (0.0 + 0.0j, 1.0 + 0.0j) +ket0 = (1.0 + 0.0j, 0.0 + 0.0j) + +MICROWAVE_WAVELENGTH = 999308 # nm +OPTICAL_WAVELENGTH = 1550 # nm + + + +class UpConversionProtocolEntangle(Protocol): + def __init__(self, own: "Node", name: str, tl: "Timeline", transducer: "Transducer", node: "Node"): + super().__init__(own, name) + self.owner = own + self.name = name + self.tl = tl + self.transducer = transducer + 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") + + self.transducer.output_quantum_state = [0.0 + 0.0j, 0.0 + 0.0j, 1.0 + 0.0j, 0.0 + 0.0j] + + else: + photon.wavelength = MICROWAVE_WAVELENGTH + self.transducer._receivers[1].receive_photon(photon) + print("FAILED up-conversion") + + + def received_message(self, src: str, msg): + pass + + + +class Swapping(Protocol): + def __init__(self, own: "Node", name: str, tl: "Timeline", FockBS: "FockBeamSplitter"): + 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 + photon_count = self.FockBS.photon_counter + + if photon_count == 1: + selected_receiver = random.choice(receivers) + selected_receiver.get(photon) + + selected_receiver.get_2(photon) + + elif photon_count == 2: + selected_receiver = random.choice(receivers) + selected_receiver.get(photon) + selected_receiver.get(photon) + + selected_receiver.get_2(photon) + selected_receiver.get_2(photon) + + def received_message(self, src: str, msg): + pass + + + + +class Measure(Protocol): + def __init__(self, own: "Node", name: str, tl: "Timeline", FockBS: "FockBeamSplitter"): + super().__init__(own, name) + self.owner = own + self.name = name + self.tl = tl + self.FockBS = FockBS + + self.detector_photon_counter_real = 0 + self.spd_real= 0 + + 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: + self.detector_photon_counter_real += 1 + + if self.FockBS._receivers[0].photon_counter >= 1 or self.FockBS._receivers[1].photon_counter >= 1: + self.spd_real += 1 + + if self.FockBS._receivers[0].photon_counter2 == 1 or self.FockBS._receivers[1].photon_counter2 == 1: + self.detector_photon_counter_ideal += 1 + + if self.FockBS._receivers[0].photon_counter2 >= 1 or self.FockBS._receivers[1].photon_counter2 >= 1: + self.spd_ideal += 1 + + def get_detector_photon_counter_real(self): + return self.detector_photon_counter_real + + def get_spd_real(self): + return self.spd_real + + def get_detector_photon_counter_ideal(self): + return self.detector_photon_counter_ideal + + def get_spd_ideal(self): + return self.spd_ideal + + + def received_message(self, src: str, msg): + pass + + +class SwappingIdeal(Protocol): + def __init__(self, own: "Node", name: str, tl: "Timeline", FockBS: "FockBeamSplitter"): + 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 + photon_count = self.FockBS.photon_counter + + real_efficiency_0 = self.FockBS._receivers[0].efficiency + real_efficiency_1 = self.FockBS._receivers[1].efficiency + + self.FockBS._receivers[0].set_efficiency(1) + self.FockBS._receivers[1].set_efficiency(1) + + + if photon_count == 1: + selected_receiver = random.choice(receivers) + selected_receiver.get_2(photon) + + elif photon_count == 2: + selected_receiver = random.choice(receivers) + selected_receiver.get_2(photon) + selected_receiver.get_2(photon) + + self.FockBS._receivers[0].set_efficiency(real_efficiency_0) + self.FockBS._receivers[1].set_efficiency(real_efficiency_1) + + + + +class MeasureIdeal(Protocol): + def __init__(self, own: "Node", name: str, tl: "Timeline", FockBS: "FockBeamSplitter"): + super().__init__(own, name) + self.owner = own + self.name = name + self.tl = tl + self.FockBS = FockBS + + self.detector_photon_counter_ideal = 0 + self.spd_ideal= 0 + + + self.detector_photon_counter_real = 0 + self.spd_real= 0 + + 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 + + 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}") + + 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/example/Transducer_Examples/TransductionComponent.py b/example/Transducer_Examples/TransductionComponent.py new file mode 100644 index 00000000..fc31cab6 --- /dev/null +++ b/example/Transducer_Examples/TransductionComponent.py @@ -0,0 +1,186 @@ +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.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 sequence.kernel.event import Event +from sequence.kernel.process import Process +import sequence.utils.log as log +import matplotlib.pyplot as plt +from sequence.components.detector import Detector +from typing import List +import sequence.components.circuit as Circuit +from qutip import Qobj + +ket1 = (0.0 + 0.0j, 1.0 + 0.0j) +ket0 = (1.0 + 0.0j, 0.0 + 0.0j) + +class Counter: + def __init__(self): + self.count = 0 + + def trigger(self, detector, info): + self.count += 1 + + + +class Transmon(Entity): + + """Class modeling a transmon qubit. + + The Transmon class can be configured to emit microwave photons. + + """ + + def __init__(self, owner: "Node", name: str, timeline: "Timeline", wavelength: List[int], photon_counter: int, photons_quantum_state: List[tuple], efficiency=1): + Entity.__init__(self, name, timeline) + self.name = name + self.owner = owner + self.timeline = timeline + self.wavelength = wavelength + self.photon_counter = photon_counter + self.photons_quantum_state = photons_quantum_state + self.efficiency=efficiency + + def init(self): + pass + + def add_output(self, outputs: List): + for i in outputs: + self.add_receiver(i) + + def get(self) -> None: + + new_photon0 = Photon(name=self.name, + timeline=self.timeline, + wavelength=self.wavelength[0], + quantum_state=self.photons_quantum_state[0]) + + new_photon1 = Photon(name=self.name, + timeline=self.timeline, + wavelength=self.wavelength[1], + quantum_state=self.photons_quantum_state[1] + ) + + input_photons = [new_photon0, new_photon1] + input_quantum_state= np.kron(self.photons_quantum_state[0], self.photons_quantum_state[1]) + 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: + self.photon_counter += 1 + + def receive_photon(self, photon: "Photon") -> None: + self.photon_counter += 1 + + +class Transducer(Entity): + + """Class modeling a transducer. + A transducer can operate in two modes: up-conversion and down-conversion. + In up-conversion it can convert microwave photons to optical photons. + In down-conversion it can convert optical photons to microwave photons. + + """ + def __init__(self, owner: "Node", name: str, timeline: "Timeline", efficiency=1, photon_counter=int): + Entity.__init__(self, name, timeline) + self.name = name + self.owner = owner + self.timeline = timeline + self.efficiency = efficiency + self.photon_counter = photon_counter + + + def init(self): + assert len(self._receivers) == 2 + + def add_output(self, outputs: List): + for i in outputs: + self.add_receiver(i) + + def receive_photon_from_transmon(self, photon: "Photon") -> None: + self.photon_counter += 1 + + def receive_photon_from_channel(self, photon: "Photon") -> None: + self.photon_counter += 1 + + + + +class FockDetector(Detector): + + """Class modeling a Fock detector. + A Fock detector can detect the number of photons in a given mode. + """ + + def __init__(self, name: str, timeline: "Timeline", efficiency=1, wavelength=int, encoding_type=fock): + super().__init__(name, timeline, efficiency) + self.name = name + self.photon_counter = 0 + self.photon_counter2 = 0 + self.wavelength = wavelength + self.encoding_type = encoding_type + self.timeline = timeline + self.efficiency = efficiency + + def init(self): + pass + + def get(self, photon=None, **kwargs) -> None: + if random.random() < self.efficiency: + self.photon_counter += 1 + + def get_2(self, photon=None, **kwargs) -> None: + self.photon_counter2 += 1 + + def set_efficiency(self, efficiency): + self.efficiency = efficiency + + + def receive_photon(self, src: str, photon: "Photon") -> None: + if photon.wavelength == self.wavelength: + self.get(photon) + else: + pass + + + +class FockBeamSplitter(Entity): + + """Class modeling a Fock beam splitter. + A Fock beam splitter can send a single photon randomly in one of its ports. + + """ + def __init__(self, name: str, owner: "Node", timeline: "Timeline", efficiency:int, 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 + + def init(self): + assert len(self._receivers) == 2 + + def receive_photon_from_scr(self, photon: "Photon", source: List[str]) -> None: + 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: + receiver.get(self.name, photon) + + diff --git a/sequence/components/circuit.py b/sequence/components/circuit.py index 5135ae77..596ede84 100644 --- a/sequence/components/circuit.py +++ b/sequence/components/circuit.py @@ -1,8 +1,3 @@ -"""Models for simulation of quantum circuit. - -This module introduces the QuantumCircuit class. The qutip library is used to calculate the unitary matrix of a circuit. -""" - from math import e, pi from typing import List, Dict, Union, Optional @@ -44,6 +39,14 @@ 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: @@ -95,7 +98,8 @@ def get_unitary_matrix(self) -> np.ndarray: "Y": y_gate, "Z": z_gate, "S": s_gate, - "T": t_gate} + "T": t_gate, + "CUSTOM": custom_gate} for gate in self.gates: name, indices, arg = gate if name == 'h': @@ -118,6 +122,8 @@ 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() @@ -245,6 +251,17 @@ 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.