From f55b519de2217220b643e7ea20211260ef1ea7e7 Mon Sep 17 00:00:00 2001 From: Caitao Zhan Date: Mon, 23 Dec 2024 14:55:22 -0600 Subject: [PATCH] [minor] adding comments to and refactoring DirectConversionProtocol; move the absorptive memory files to a new folder --- .../absorptive_experiment.py | 0 .../absorptive_fidelity.py | 0 .../absorptive_fidelity_graph.py | 0 .../absorptive_graph.py | 0 .../{ => AbsorptiveMemory}/absorptive_rate.py | 0 .../absorptive_rate_graph.py | 0 .../ConversionProtocols.py | 71 ++++++++++++------- .../Transducer_Examples/DirectConversion.py | 49 +++++++------ sequence/components/photon.py | 10 +-- sequence/components/transducer.py | 3 +- sequence/components/transmon.py | 16 +++-- sequence/constants.py | 19 ++--- 12 files changed, 99 insertions(+), 69 deletions(-) rename example/{ => AbsorptiveMemory}/absorptive_experiment.py (100%) rename example/{ => AbsorptiveMemory}/absorptive_fidelity.py (100%) rename example/{ => AbsorptiveMemory}/absorptive_fidelity_graph.py (100%) rename example/{ => AbsorptiveMemory}/absorptive_graph.py (100%) rename example/{ => AbsorptiveMemory}/absorptive_rate.py (100%) rename example/{ => AbsorptiveMemory}/absorptive_rate_graph.py (100%) diff --git a/example/absorptive_experiment.py b/example/AbsorptiveMemory/absorptive_experiment.py similarity index 100% rename from example/absorptive_experiment.py rename to example/AbsorptiveMemory/absorptive_experiment.py diff --git a/example/absorptive_fidelity.py b/example/AbsorptiveMemory/absorptive_fidelity.py similarity index 100% rename from example/absorptive_fidelity.py rename to example/AbsorptiveMemory/absorptive_fidelity.py diff --git a/example/absorptive_fidelity_graph.py b/example/AbsorptiveMemory/absorptive_fidelity_graph.py similarity index 100% rename from example/absorptive_fidelity_graph.py rename to example/AbsorptiveMemory/absorptive_fidelity_graph.py diff --git a/example/absorptive_graph.py b/example/AbsorptiveMemory/absorptive_graph.py similarity index 100% rename from example/absorptive_graph.py rename to example/AbsorptiveMemory/absorptive_graph.py diff --git a/example/absorptive_rate.py b/example/AbsorptiveMemory/absorptive_rate.py similarity index 100% rename from example/absorptive_rate.py rename to example/AbsorptiveMemory/absorptive_rate.py diff --git a/example/absorptive_rate_graph.py b/example/AbsorptiveMemory/absorptive_rate_graph.py similarity index 100% rename from example/absorptive_rate_graph.py rename to example/AbsorptiveMemory/absorptive_rate_graph.py diff --git a/example/Transducer_Examples/ConversionProtocols.py b/example/Transducer_Examples/ConversionProtocols.py index 5dd94e42..f775557c 100644 --- a/example/Transducer_Examples/ConversionProtocols.py +++ b/example/Transducer_Examples/ConversionProtocols.py @@ -13,13 +13,10 @@ 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.transducer import Transducer from sequence.components.transmon import Transmon - +from sequence.constants import KET0, KET1 from sequence.components.detector import Detector from sequence.components.photon import Photon @@ -28,9 +25,6 @@ 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 @@ -44,12 +38,18 @@ def get_conversion_matrix(efficiency: float) -> Qobj: return Qobj(custom_gate_matrix, dims=[[4], [4]]) - class EmittingProtocol(Protocol): """Protocol for emission of single microwave photon by transmon. + + Attributes: + owner (Node): the owner of this protocol, the protocol runs on the owner + name (str): the name of the protocol + tl (Timeline): the simulation timeline + transducer (Transducer): the transducer component + transmon (Transmon): the transmon component """ - def __init__(self, owner: "Node", name: str, tl: "Timeline", transmon="Transmon", transducer="Transducer"): + def __init__(self, owner: "Node", name: str, tl: Timeline, transmon: Transmon, transducer: Transducer): super().__init__(owner, name) self.owner = owner self.name = name @@ -61,7 +61,7 @@ def start(self) -> None: self.transmon.get() - if self.transmon.photons_quantum_state[0] == ket1: + 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 @@ -72,17 +72,23 @@ def start(self) -> None: 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. + + Attributes: + owner (Node): the owner of this protocol, the protocol runs on the owner + name (str): the name of the protocol + tl (Timeline): the simulation timeline + transducer (Transducer): the transducer component + node (Node): the receiver node (where DownConversionProtocol runs) -- NOTE this is an error! node's typing should be a str, instead of Node + transmon (Transmon): the transmon component """ - def __init__(self, owner: "Node", name: str, tl: "Timeline", transducer: "Transducer", node: "Node", transmon: "Transmon"): + def __init__(self, owner: Node, name: str, tl: Timeline, transducer: Transducer, node: Node, transmon: Transmon): super().__init__(owner, name) self.owner = owner self.name = name @@ -91,9 +97,16 @@ def __init__(self, owner: "Node", name: str, tl: "Timeline", transducer: "Transd self.transmon = transmon self.node = node - def start(self, photon: "Photon") -> None: - """start the protocol""" - if self.transducer.photon_counter > 0: + def start(self, photon: Photon) -> None: + """start the protocol + + NOTE (caitao, 12/21/2024): this start() method should be empty. + The content of this function should be at a new convert() method that receives photons from the from the transducer + + Args: + photon (Photon): photon from arrived at the transducer from the transmon + """ + if self.transducer.photon_counter > 0: # NOTE shouldn't use this photon_counter to determine custom_gate = get_conversion_matrix(self.transducer.efficiency) transmon_state_vector = np.array(self.transmon.input_quantum_state).reshape((4, 1)) @@ -106,7 +119,7 @@ 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) + self.transducer._receivers[0].receive_photon(self.node, photon) # NOTE the receiver should be the quantum channel 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}") @@ -121,9 +134,14 @@ 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. + + Attributes: + owner (Node): the owner of this protocol, the protocol runs on the owner + name (str): the name of the protocol + tl (Timeline): the simulation timeline + transducer (Transducer): the transducer component """ def __init__(self, owner: "Node", name: str, tl: "Timeline", transducer: "Transducer", transmon: "Transmon"): @@ -134,10 +152,16 @@ def __init__(self, owner: "Node", name: str, tl: "Timeline", transducer: "Transd self.transducer = transducer def start(self, photon: "Photon") -> None: - """start the protocol""" - if self.transducer.photon_counter > 0: - - transducer_state = [0.0 + 0.0j, 0.0 + 0.0j, 1.0 + 0.0j, 0.0 + 0.0j] + """start the protocol + + NOTE (caitao, 12/21/2024): this start() method should be empty. + The content of this function should be at a new convert() method that receives photons from the from the transducer + Args: + photon (Photon): the photon received at the transducer from the quantum channel + """ + if self.transducer.photon_counter > 0: # NOTE shouldn't use this photon_counter to determine + + transducer_state = [0.0 + 0.0j, 0.0 + 0.0j, 1.0 + 0.0j, 0.0 + 0.0j] # NOTE Why is this the transducer's state custom_gate = get_conversion_matrix(self.transducer.efficiency) transducer_state_vector = np.array(transducer_state).reshape((4, 1)) @@ -164,6 +188,3 @@ def start(self, photon: "Photon") -> None: def received_message(self, src: str, msg): pass - - - diff --git a/example/Transducer_Examples/DirectConversion.py b/example/Transducer_Examples/DirectConversion.py index eab6c07c..24995571 100644 --- a/example/Transducer_Examples/DirectConversion.py +++ b/example/Transducer_Examples/DirectConversion.py @@ -2,7 +2,6 @@ sys.path.append('.') import numpy as np -import sequence.utils.log as log import matplotlib.pyplot as plt from sequence.kernel.timeline import Timeline @@ -14,6 +13,7 @@ from sequence.components.transmon import Transmon from sequence.components.transducer import Transducer 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 @@ -26,7 +26,6 @@ FREQUENCY = 1e9 MICROWAVE_WAVELENGTH = 999308 # nm OPTICAL_WAVELENGTH = 1550 # nm -MEAN_PHOTON_NUM=1 # Timeline START_TIME = 0 @@ -34,16 +33,13 @@ CONVERSION_DURATION = 10 # ps PERIOD = EMISSION_DURATION + CONVERSION_DURATION + CONVERSION_DURATION -# Transmon -ket0 = (1.0 + 0.0j, 0.0 + 0.0j) -ket1 = (0.0 + 0.0j, 1.0 + 0.0j) -state_list= [ket1, ket0] +state_list= [KET1, KET0] TRANSMON_EFFICIENCY = 1 # Transducer -EFFICIENCY_UP = 0.5 -EFFICIENCY_DOWN = 0.5 +EFFICIENCY_UP = 0.6 +EFFICIENCY_DOWN = 0.6 # Fock Detector MICROWAVE_DETECTOR_EFFICIENCY_Rx = 1 @@ -54,6 +50,7 @@ ATTENUATION = 0 DISTANCE = 1e3 + class Counter: def __init__(self): self.count = 0 @@ -62,7 +59,7 @@ def trigger(self, detector, info): self.count += 1 -#NODES OF THE NETWORK +# NODES OF THE NETWORK class SenderNode(Node): @@ -75,23 +72,27 @@ class SenderNode(Node): timeline (Timeline): timeline transmon_name (str): name of the transmon detector_name (str): name of the detector + counter2 (Counter): the counter for the detector transducer_name (str): name of the transducer - + counter (Counter): the counter for the transducer + emitting_protocol (EmittingProtocol): the protocol for emitting photons + upconversion_protocol (UpConversionProtocol): the protocol for up converting the microwave to optical photon """ def __init__(self, name, timeline, node2): super().__init__(name, timeline) # transmon self.transmon_name = name + ".transmon" - transmon = Transmon(name=self.transmon_name, owner=self, timeline=timeline, wavelengths=[MICROWAVE_WAVELENGTH, OPTICAL_WAVELENGTH], photon_counter=0, efficiency=TRANSMON_EFFICIENCY, photons_quantum_state= state_list) + transmon = Transmon(name=self.transmon_name, owner=self, timeline=timeline, wavelengths=[MICROWAVE_WAVELENGTH, OPTICAL_WAVELENGTH], \ + photon_counter=0, efficiency=TRANSMON_EFFICIENCY, photons_quantum_state=state_list) self.add_component(transmon) # detector detector_name = name + ".fockdetector" detector = FockDetector(detector_name, timeline, wavelength=MICROWAVE_WAVELENGTH, efficiency=MICROWAVE_DETECTOR_EFFICIENCY_Tx) self.add_component(detector) - self.counter = Counter() - detector.attach(self.counter) + self.counter2 = Counter() + detector.attach(self.counter2) # transducer self.transducer_name = name + ".transducer" @@ -110,7 +111,6 @@ def __init__(self, name, timeline, node2): self.upconversion_protocol = UpConversionProtocol(self, name + ".upconversion_protocol", timeline, transducer, node2, transmon) - class ReceiverNode(Node): """Receiver node in the Direct Conversion Protocol. @@ -136,7 +136,8 @@ def __init__(self, name, timeline): self.counter = Counter() # transmon - transmon = Transmon(name=self.transmon_name, owner=self, timeline=timeline, wavelengths=[MICROWAVE_WAVELENGTH, OPTICAL_WAVELENGTH], photons_quantum_state=state_list, photon_counter=0, efficiency=1) + transmon = Transmon(name=self.transmon_name, owner=self, timeline=timeline, wavelengths=[MICROWAVE_WAVELENGTH, OPTICAL_WAVELENGTH], \ + photons_quantum_state=state_list, photon_counter=0, efficiency=1) self.add_component(transmon) # fock detector @@ -155,12 +156,11 @@ def __init__(self, name, timeline): # down conversion protocol self.downconversion_protocol = DownConversionProtocol(self, name + ".downconversion_protocol", timeline, transducer, transmon) - def receive_photon(self, src, photon): self.components[self.transducer_name].receive_photon_from_channel(photon) -#MAIN +# MAIN if __name__ == "__main__": @@ -170,7 +170,7 @@ def receive_photon(self, src, photon): node2 = ReceiverNode("node2", tl) node1 = SenderNode("node1", tl, node2) - qc1 = QuantumChannel("qc.node1.node2", tl, attenuation=ATTENUATION, distance=DISTANCE) + qc1 = QuantumChannel("qc.node1.node2", tl, attenuation=ATTENUATION, distance=DISTANCE) # NOTE: this quantum channel is not used qc1.set_ends(node1, node2.name) if EFFICIENCY_UP >= 0 and EFFICIENCY_UP <= 1 and EFFICIENCY_DOWN >= 0 and EFFICIENCY_DOWN <= 1: @@ -205,14 +205,14 @@ def receive_photon(self, src, photon): detector2 = node2.get_components_by_type("FockDetector")[0] print(f"--------------------") - print(f"Direct Quantum Transduction Protocol starts, the qubit that we are going to convert is: {ket1}") + 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}:") - # reset timeline + # reset timeline # NOTE should not reset timeline, instead, each experiment should keep running for some time. tl.time = 0 tl.init() @@ -224,17 +224,17 @@ def receive_photon(self, src, photon): transducer2.photon_counter = 0 detector2.photon_counter = 0 - process0 = Process(node1.emitting_protocol, "start", []) + process0 = Process(node1.emitting_protocol, "start", []) # NOTE: there should only be one event to kick the simulation (following 2 events shouldn't exist here) event_time0 = (cumulative_time + EMISSION_DURATION) event0 = Event(event_time0, process0) tl.schedule(event0) - - process1 = Process(node1.upconversion_protocol, "start", [Photon]) + + process1 = Process(node1.upconversion_protocol, "start", [Photon]) # NOTE: class Photon shouldn't be the parameter, it should be Photon object event_time1 = (event_time0 + CONVERSION_DURATION) event1 = Event(event_time1, process1) tl.schedule(event1) - process2 = Process(node2.downconversion_protocol, "start", [Photon]) + process2 = Process(node2.downconversion_protocol, "start", [Photon]) # NOTE: class Photon shouldn't be the parameter, it should be Photon object event_time2 =(event_time1 + CONVERSION_DURATION) event2 = Event(event_time2, process2) tl.schedule(event2) @@ -257,7 +257,6 @@ def receive_photon(self, src, photon): converted_photons.append(total_photons_successful) - # RESULTS print(f"- - - - - - - - - -") diff --git a/sequence/components/photon.py b/sequence/components/photon.py index 00277b42..45aa8096 100644 --- a/sequence/components/photon.py +++ b/sequence/components/photon.py @@ -4,7 +4,7 @@ Photons may be encoded directly with polarization or time bin schemes, or may herald the encoded state of single atom memories. """ from typing import Dict, Any, List, Union, TYPE_CHECKING -from numpy import log2 +from numpy import log2, ndarray if TYPE_CHECKING: from numpy.random._generator import Generator @@ -15,6 +15,7 @@ from ..components.circuit import Circuit from ..utils.encoding import polarization from ..kernel.quantum_state import FreeQuantumState +from ..constants import EPSILON class Photon: @@ -43,8 +44,7 @@ class Photon: _measure_circuit = Circuit(1) _measure_circuit.measure(0) - def __init__(self, name: str, timeline: "Timeline", wavelength=0, location=None, encoding_type=polarization, - quantum_state=None, use_qm=False): + def __init__(self, name: str, timeline: "Timeline", wavelength=0, location=None, encoding_type=polarization, quantum_state=None, use_qm=False): """Constructor for the photon class. Args: @@ -84,8 +84,8 @@ def __init__(self, name: str, timeline: "Timeline", wavelength=0, location=None, quantum_state = (complex(1), complex(0)) else: assert type(quantum_state) is tuple - assert all([abs(a) <= 1.01 for a in quantum_state]), "Illegal value with abs > 1 in photon state" - assert abs(sum([abs(a) ** 2 for a in quantum_state]) - 1) < 1e-5, "Squared amplitudes do not sum to 1" + assert all([abs(a) < 1 + EPSILON for a in quantum_state]), "Illegal value with abs > 1 in photon state" + assert abs(sum([abs(a) ** 2 for a in quantum_state]) - 1) < EPSILON, "Squared amplitudes do not sum to 1" num_qubits = log2(len(quantum_state)) assert num_qubits == 1, "Length of amplitudes for single photon should be 2" self.quantum_state = FreeQuantumState() diff --git a/sequence/components/transducer.py b/sequence/components/transducer.py index 5297d1c9..91d358d4 100644 --- a/sequence/components/transducer.py +++ b/sequence/components/transducer.py @@ -43,7 +43,8 @@ def add_output(self, outputs: List): self.add_receiver(i) def receive_photon_from_transmon(self, photon: "Photon") -> None: - self.photon_counter += 1 + """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: self.photon_counter += 1 diff --git a/sequence/components/transmon.py b/sequence/components/transmon.py index ce8eba4b..45aec80e 100644 --- a/sequence/components/transmon.py +++ b/sequence/components/transmon.py @@ -24,6 +24,9 @@ class Transmon(Entity): photon_counter (int): photon counter photongs_quantum_state (list): a list of quantum states efficiency (float): the efficiency of the transmon + input_quantum_state (np.array): two qubit state for microwave and optical photon + photon0 (Photon): microwave photon + 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): @@ -35,7 +38,10 @@ def __init__(self, owner: "Node", name: str, timeline: "Timeline", wavelengths: self.wavelengths = wavelengths self.photon_counter = photon_counter self.photons_quantum_state = photons_quantum_state - self.efficiency = efficiency + self.efficiency = efficiency + self.input_quantum_state = None + self.new_photon0 = None + self.new_photon1 = None def init(self): pass @@ -45,9 +51,9 @@ def add_output(self, outputs: List): self.add_receiver(i) def get(self) -> None: - - new_photon0 = Photon(name=self.name, timeline=self.timeline, wavelength=self.wavelengths[0], quantum_state=self.photons_quantum_state[0]) - new_photon1 = Photon(name=self.name, timeline=self.timeline, wavelength=self.wavelengths[1], quantum_state=self.photons_quantum_state[1]) + """Receives a photon""" + new_photon0 = Photon(name=self.name, timeline=self.timeline, wavelength=self.wavelengths[0], quantum_state=self.photons_quantum_state[0]) # microwave + new_photon1 = Photon(name=self.name, timeline=self.timeline, wavelength=self.wavelengths[1], quantum_state=self.photons_quantum_state[1]) # optical input_quantum_state = np.kron(self.photons_quantum_state[0], self.photons_quantum_state[1]) self.input_quantum_state = input_quantum_state @@ -55,6 +61,8 @@ def get(self) -> None: 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 + """ self.photon_counter += 1 def receive_photon(self, photon: "Photon") -> None: diff --git a/sequence/constants.py b/sequence/constants.py index e0d0fed7..7fcebe3a 100644 --- a/sequence/constants.py +++ b/sequence/constants.py @@ -1,28 +1,29 @@ """useful constants""" from typing import Final -from numpy import array - # speed of light in (m / pico second) SPEED_OF_LIGHT: Final = 2e-4 -SQRT_HALF: Final = 0.5 ** 0.5 +# |0> and |1> +KET0: Final = (1, 0) +KET1: Final = (0, 1) # four Bell states -PHI_PLUS: Final = array([SQRT_HALF, 0, 0, SQRT_HALF]) -PHI_MINUS: Final = array([SQRT_HALF, 0, 0, -SQRT_HALF]) -PSI_PLUS: Final = array([0, SQRT_HALF, SQRT_HALF, 0]) -PSI_MINUS: Final = array([0, SQRT_HALF, -SQRT_HALF, 0]) +SQRT_HALF: Final = 0.5 ** 0.5 +PHI_PLUS: Final = (SQRT_HALF, 0, 0, SQRT_HALF) +PHI_MINUS: Final = (SQRT_HALF, 0, 0, -SQRT_HALF) +PSI_PLUS: Final = (0, SQRT_HALF, SQRT_HALF, 0) +PSI_MINUS: Final = (0, SQRT_HALF, -SQRT_HALF, 0) # machine epsilon, i.e., a small number EPSILON: Final = 1e-7 # convert to picosecond -NANOSECOND: Final = 1e3 +NANOSECOND: Final = 1e3 MICROSECOND: Final = 1e6 MILLISECOND: Final = 1e9 -SECOND: Final = 1e12 +SECOND: Final = 1e12 # for timeline formatting NANOSECONDS_PER_MILLISECOND: Final = 1e6