Skip to content

Commit

Permalink
Started to work on the bb84 example
Browse files Browse the repository at this point in the history
  • Loading branch information
SimonSekavcnik committed Feb 27, 2024
1 parent ba84f96 commit c8bb83d
Show file tree
Hide file tree
Showing 10 changed files with 276 additions and 25 deletions.
4 changes: 4 additions & 0 deletions examples/beam_splitter_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,8 @@
sim.run()

exp = Experiment.get_instance()

print(exp.state.all_fock_probs())
state = exp.state
print(state.mean_photon(mode=[0]))
print(state.mean_photon(mode=[1]))
104 changes: 104 additions & 0 deletions examples/mach_zender_bb84.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
"""
Mach Zender BB84 implementation
"""
from quasi.simulation import Simulation, SimulationType, ModeManager
from quasi.devices.sources import IdealNPhotonSource, IdealCoherentSource
from quasi.devices.control import SimpleTrigger
from quasi.devices.beam_splitters import IdealBeamSplitter
from quasi.devices.phase_shifters import IdealPhaseShifter
from quasi.signals import GenericBoolSignal, GenericQuantumSignal
from quasi.experiment import Experiment
from quasi.backend.fock_first_backend import FockBackendFirst
import numpy as np
from math import pi


sim = Simulation()
sim.set_backend(FockBackendFirst())
sim.set_dimensions(5)

# Create all of the devices
BSa = IdealBeamSplitter("BSa")
BSb = IdealBeamSplitter("BSb")
BSc = IdealBeamSplitter("BSc")
BSd = IdealBeamSplitter("BSd")

PSa = IdealPhaseShifter("PSa")
PSa.set_phi(0)

PSb = IdealPhaseShifter("PSb")
PSb.set_phi(pi/4)

S = IdealNPhotonSource()
S.set_photon_num(0)
trigger = SimpleTrigger()

# Create Signals
signals = {}
signals["trigger"] = GenericBoolSignal()
signals["qsig1"] = GenericQuantumSignal()
signals["qsig2"] = GenericQuantumSignal()
signals["qsig3"] = GenericQuantumSignal()
signals["qsig4"] = GenericQuantumSignal()
signals["qsig5"] = GenericQuantumSignal()
signals["qsig6"] = GenericQuantumSignal()
signals["qsig7"] = GenericQuantumSignal()
signals["qsig8"] = GenericQuantumSignal()
signals["qsig9"] = GenericQuantumSignal()

# Connect trigger and source
trigger.register_signal(signal=signals["trigger"], port_label="trigger")
S.register_signal(signal=signals["trigger"], port_label="trigger")

# connect source and beam splitter a
S.register_signal(signal=signals["qsig1"], port_label="output")
BSa.register_signal(signal=signals["qsig1"], port_label="A")

# connect beam splitter a and phase shifter
BSa.register_signal(signal=signals["qsig2"], port_label="C")
PSa.register_signal(signal=signals["qsig2"], port_label="input")

# connect beam splitter a and b
BSa.register_signal(signal=signals["qsig3"], port_label="D")
BSb.register_signal(signal=signals["qsig3"], port_label="B")

# connect phase shifter and beam splitter b
PSa.register_signal(signal=signals["qsig4"], port_label="output")
BSb.register_signal(signal=signals["qsig4"], port_label="A")

# connect beamspitter b and c
BSb.register_signal(signal=signals["qsig5"], port_label="C")
BSb.register_signal(signal=signals["qsig6"], port_label="D")

BSc.register_signal(signal=signals["qsig5"], port_label="A")
BSc.register_signal(signal=signals["qsig6"], port_label="B")


# Connect beamsplitter c and phase shifter
BSc.register_signal(signal=signals["qsig7"], port_label="C")
PSb.register_signal(signal=signals["qsig7"], port_label="input")

# Connect phase shifter b and beam splitter d
PSb.register_signal(signal=signals["qsig8"], port_label="output")
BSd.register_signal(signal=signals["qsig8"], port_label="A")

# Connect beam splitter c and beam splitter d
BSc.register_signal(signal=signals["qsig9"], port_label="D")
BSd.register_signal(signal=signals["qsig9"], port_label="B")

# Signal going to det 1
dsig1 = GenericQuantumSignal()
dsig2 = GenericQuantumSignal()

BSd.register_signal(signal=dsig1, port_label="C")
BSd.register_signal(signal=dsig2, port_label="D")

sim.run()

exp = Experiment.get_instance()

print(exp.state.all_fock_probs())
state = exp.state
mm = ModeManager()
print(state.mean_photon(mode=[mm.get_mode_index(dsig1.mode_id)]))
print(state.mean_photon(mode=[mm.get_mode_index(dsig2.mode_id)]))
6 changes: 3 additions & 3 deletions quasi/backend/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class FockBackend(Backend):
"""

@abstractmethod
def set_number_of_modes(self, mode):
def set_number_of_modes(self, number_of_modes):
"""
Tells the backend number of modes that should be simulated
"""
Expand All @@ -48,13 +48,13 @@ def squeeze(self, z: complex, mode):
"""

@abstractmethod
def displace(self, alpha: complex, mode):
def displace(self, alpha: float, phi: float, mode):
"""
Should return displacement operator
"""

@abstractmethod
def phase_shift(self, phi: complex, mode):
def phase_shift(self, theta: float, mode):
"""
Should return phase shift operator
"""
Expand Down
23 changes: 15 additions & 8 deletions quasi/backend/fock_first_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@

from quasi.backend.backend import FockBackend
from quasi.experiment import Experiment
from quasi._math.fock import a, adagger, squeezing, displacement, beamsplitter
from quasi._math.fock import (a, adagger,
squeezing, displacement,
beamsplitter, phase)


class FockBackendFirst(FockBackend):
Expand All @@ -22,8 +24,6 @@ def initialize(self):
Initialization method is run befor the simulation
"""
self.experiment.prepare_experiment()
#self.experiment.state_init(0, list(range(self.number_of_modes)))
#self.experiment.state_init(0,[0])

def set_number_of_modes(self, number_of_modes):
"""
Expand All @@ -37,7 +37,7 @@ def set_dimensions(self, dimensions):
Set the number of modes
"""
self.experiment.update_dimensions(dimensions)

def create(self, mode):
"""
Return the creation operator
Expand All @@ -54,7 +54,6 @@ def squeeze(self, z: complex, mode):
"""
Return the squeezing operator
"""

return squeezing(abs(z), cmath.phase(z), self.experiment.cutoff)

def displace(self, alpha: float, phi: float, mode):
Expand All @@ -67,8 +66,8 @@ def displace(self, alpha: float, phi: float, mode):
self.experiment.cutoff
)

def phase_shift(self, phi: complex, mode):
pass
def phase_shift(self, theta: float, mode):
return phase(theta, self.experiment.cutoff)

def number(self, mode):
pass
Expand All @@ -77,7 +76,15 @@ def apply_operator(self, operator, modes):
self.experiment.add_operation(operator, [*modes])

def initialize_number_state(self, n: int, mode: int):
"""
Initialize the number state
"""
self.experiment.state_init(n, [mode])

def beam_splitter(self, theta=0, phi=0):
return beamsplitter(theta, phi, self.experiment.cutoff).transpose((0,2,1,3))
"""
Returns the beamsplitter operator
"""
return beamsplitter(
theta, phi, self.experiment.cutoff).transpose(
(0, 2, 1, 3))
37 changes: 25 additions & 12 deletions quasi/devices/beam_splitters/ideal_beam_splitter.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@

from quasi.gui.icons import icon_list
from quasi.simulation import Simulation, SimulationType, ModeManager
from quasi.extra.logging import Loggers, get_custom_logger

logger = get_custom_logger(Loggers.Devices)

class IdealBeamSplitter(GenericDevice):
"""
Expand All @@ -38,32 +40,43 @@ class IdealBeamSplitter(GenericDevice):
power_average = 0
power_peak = 0
reference = None

@wait_input_compute
def compute_outputs(self, *args, **kwargs):
simulation = Simulation.get_instance()
if simulation.simulation_type is SimulationType.FOCK:
self.simulate_fock()


def simulate_fock(self):
"""
Fock Simulation
"""
logger.info("Beam Splitter - %s - executing", self.name)
simulation = Simulation.get_instance()
backend = simulation.get_backend()
mm = ModeManager()
m_id_a = self.ports["A"].signal.mode_id
m_id_b = self.ports["B"].signal.mode_id

# Utilize a helper function to streamline mode ID retrieval or creation
def get_or_create_mode_id(port):
if port.signal is None:
logger.info("Beam Splitter - %s - empty signal, new mode generated", self.name)
return mm.create_new_mode()
return port.signal.mode_id

m_id_a = get_or_create_mode_id(self.ports["A"])
m_id_b = get_or_create_mode_id(self.ports["B"])

# Apply beam splitter operation in a more concise manner
op = backend.beam_splitter(theta=pi/4, phi=0)
backend.apply_operator(
op,
[mm.get_mode_index(m_id_a),
mm.get_mode_index(m_id_b)]
)
backend.apply_operator(op, [mm.get_mode_index(m_id_a),
mm.get_mode_index(m_id_b)])

# Get the modes or create empty modes
m_id_a = self.ports["A"].signal.mode_id
m_id_b = self.ports["B"].signal.mode_id
# Simplify signal content setting using a loop to avoid repetition
for port, mode_id in zip(
[self.ports["C"], self.ports["D"]], [m_id_a, m_id_b]):
if port.signal is not None:
logger.info("Beam Splitter - %s - assisning mode %s to signal on port %s",
self.name, mm.get_mode_index(mode_id), port.label)
port.signal.set_contents(timestamp=0, mode_id=mode_id)
port.signal.set_computed()

1 change: 1 addition & 0 deletions quasi/devices/phase_shifters/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .ideal_phase_shifter import IdealPhaseShifter
86 changes: 86 additions & 0 deletions quasi/devices/phase_shifters/ideal_phase_shifter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
"""
Ideal Phase Shifter
"""
from quasi.devices import (GenericDevice,
wait_input_compute,
coordinate_gui,
ensure_output_compute)
from quasi.devices.port import Port
from quasi.signals import (GenericSignal,
GenericFloatSignal,
GenericQuantumSignal)
from quasi.extra.logging import Loggers, get_custom_logger
from quasi.gui.icons import icon_list
from quasi.simulation import Simulation, SimulationType, ModeManager

logger = get_custom_logger(Loggers.Devices)

class IdealPhaseShifter(GenericDevice):
"""
Implements Ideal Phase Shifter
"""

ports = {
"theta": Port(label="theta", direction="input", signal=None,
signal_type=GenericFloatSignal, device=None),
"input": Port(label="input", direction="input", signal=None,
signal_type=GenericQuantumSignal, device=None),
"output": Port(label="output", direction="output", signal=None,
signal_type=GenericQuantumSignal, device=None)
}

gui_icon = icon_list.LASER
gui_tags = ["ideal"]
gui_name = "Ideal Coherent Photon Source"
gui_documentation = "ideal_phase_shifter.md"

power_peak = 0
power_average = 0
reference = None

def set_phi(self, phi):
"""
Sets the phi for the phase shifter
"""
theta_sig = GenericFloatSignal()
theta_sig.set_float(phi)
self.register_signal(signal=theta_sig, port_label="theta")
theta_sig.set_computed()

@ensure_output_compute
@coordinate_gui
@wait_input_compute
def compute_outputs(self, *args, **kwargs):
simulation = Simulation.get_instance()
if simulation.simulation_type is SimulationType.FOCK:
self.simulate_fock()

def simulate_fock(self):
"""
Fock Simulation
"""
logger.info("Beam Splitter - %s - executing", self.name)
simulation = Simulation.get_instance()
backend = simulation.get_backend()

# Get the mode manager
mm = ModeManager()
# Generate new mode
theta = self.ports["theta"].signal.contents
mode = self.ports["input"].signal.mode_id
logger.info("Phase Shifter - %s - received mode %s from signal on port %s",
self.name, mm.get_mode_index(mode),
self.ports["output"].label)

# Initialize photon number state in the mode
operator = backend.phase_shift(theta, mm.get_mode_index(mode))
backend.apply_operator(operator, [mm.get_mode_index(mode)])

logger.info("Phase Shifter - %s - assisning mode %s to signal on port %s",
self.name, mm.get_mode_index(mode),
self.ports["output"].label)

self.ports["output"].signal.set_contents(
timestamp=0,
mode_id=mode)
self.ports["output"].signal.set_computed()
1 change: 0 additions & 1 deletion quasi/devices/sources/ideal_coherent_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@ def simulate_fock(self):
phi = self.ports["phi"].signal.contents

# Initialize photon number state in the mode
# backend.initialize_number_state(photon_num, [mm.get_mode_index(mode)])
operator = backend.displace(alpha, phi, mm.get_mode_index(mode))
backend.apply_operator(operator, [mm.get_mode_index(mode)])

Expand Down
6 changes: 5 additions & 1 deletion quasi/devices/sources/ideal_n_photon_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,10 @@ class IdealNPhotonSource(GenericDevice):
power_average = 0
reference = None

def set_photon_num(self, photon_num:int):
def set_photon_num(self, photon_num: int):
"""
Set the number of photons the source should emit in a pulse
"""
photon_num_sig = GenericIntSignal()
photon_num_sig.set_int(photon_num)
self.register_signal(signal=photon_num_sig, port_label="photon_num")
Expand All @@ -66,6 +69,7 @@ def set_photon_num(self, photon_num:int):
def compute_outputs(self, *args, **kwargs):
simulation = Simulation.get_instance()
if simulation.simulation_type is SimulationType.FOCK:
print("SOURCE")
self.simulate_fock()

def simulate_fock(self):
Expand Down
Loading

0 comments on commit c8bb83d

Please sign in to comment.