Quantum channel: the link to transmit qubits
Quantum channels can transmit a QuantumModel
(qubit) from a node to another.
It has the following attributions:
name
: the channel’s name.length
: the physcial length of the channel. Default length is 0delay
: the propagation delay. The time delay from sending to receiving.delay
can be a float or aDelayModel
. Default delay is 0s.drop_rate
: the probability of losing the transmitting qubit. Default drop rate is 0.bandwidth
: the number of qubits to be sent per second. If thebandwidth
is reached, further qubits will be put into a buffer (and causes a buffer delay). Default bandwidth isNone
(infinite).max_buffer_size
: the maximum send buffer size. If the buffer is full, further qubits will be dropped. Default buffer size isNone
(infinite).``decoherence_rate
: the decoherence rate, have different meanings in qubit or entanglement models.transfer_error_model_args
: other attributions for the error model.
It is easy to generate a quantum channel:
from qns.entity.node.node import QNode
from qns.entity.qchannel.qchannel import QuantumChannel
n2 = QNode("n2")
n1 = QNode("n1")
l1 = QuantumChannel(name="l1", bandwidth=3, delay=0.2, drop_rate=0.1, max_buffer_size=5)
# add the qchannel
n1.add_qchannel(l1)
n2.add_qchannel(l1)
# get_qchannel can return the quantum channel by its destination
assert(l1 == n1.get_qchannel(n2))
s = Simulator(0, 10, 1000)
# install QNodes will also install all channels
n1.install(s)
n2.install(s)
s.run()
Send and receive qubits
It is easy to send a qubit using send
method:
n1 = QNode("n1")
n2 = QNode("n2")
l1 = QuantumChannel(name="l1")
n1.add_qchannel(l1)
n2.add_qchannel(l1)
# install and initiate the simulator
# ...
qubit = Qubit()
# use the send method to send qubit
l1.send(qubit = qubit, next_hop = n2)
The receiving may be complex. The destination node will be noticed by an event called RecvQubitPacket
. It has the following fields:
t
: the receiving timeqchannel
: the related quantum channelqubit
: the receiving qubitdest
: the destination
This packet needs to be processed in the handle
method of the applications:
class SendApp(Application):
def __init__(self, dest: QNode, qchannel: QuantumChannel, send_rate=1000):
super().__init__()
self.dest = dest
self.qchannel = qchannel
self.send_rate = send_rate
# initiate: generate the first send event
def install(self, node: QNode, simulator: Simulator):
super().install(node, simulator)
# get start time
time_list.append(simulator.ts)
t = simulator.ts
event = func_to_event(t, self.send_qubit)
self._simulator.add_event(event)
def send_qubit(self):
# generate a qubit
qubit = Qubit()
# send the qubit
self.qchannel.send(qubit=qubit, next_hop=self.dest)
# calculate the next sending time
t = self._simulator.current_time + \
self._simulator.time(sec=1 / self.send_rate)
# insert the next send event to the simulator
event = func_to_event(t, self.send_qubit)
self._simulator.add_event(event)
class RecvApp(Application):
def handle(self, node: QNode, event: Event):
if isinstance(event, RecvQubitPacket):
qubit = event.qubit
qchannel = event.qchannel
recv_time = event.t
# handling the receiving qubit
# ...
# generate quantum nodes
n1 = QNode("n1")
n2 = QNode("n2") # add the RecvApp
# generate a quantum channel
l1 = QuantumChannel(name="l1")
n1.add_qchannel(l1)
n2.add_qchannel(l1)
# add apps
n1.add_apps(SendApp(dest = n2, qchannel = l1))
n2.add_apps(RecvApp())
# initiate the simulator
s = Simulator(0, 10, 10000) # from 0 to 10 seconds
n1.install(s)
n2.install(s)
# run the simulation
s.run()
Error models in transmission
Errors can be introduced during sending qubits. The error is handled in function transfer_error_model
, which takes the channel length
and other parameters as input. Those parameters shows the quantum channel’s attributions (such as the optical fiber’s decay), and they can be set using transfer_error_model_args
. This parameter should be in the directory form.
Here is an example:
# Extend the qubit model to handle transfer error
class QubitWithError(Qubit):
def transfer_error_model(self, length: float, **kwargs):
# get the decay attribution
decay = kwargs.get("decay", 0)
# handle error
lkm = length / 1000
theta = random.random() * lkm * decay * np.pi / 4
operation = np.array([[np.cos(theta), - np.sin(theta)], [np.sin(theta), np.cos(theta)]], dtype=np.complex128)
# change the state vector
self.state.state = np.dot(operation, self.state.state)
n1 = QNode("n1")
n2 = QNode("n2")
# the error model attribution: decay 0.2db/KM
l1 = QuantumChannel(name="l1", transfer_error_model_args={"decay": 0.2})
n1.add_qchannel(l1)
n2.add_qchannel(l1)
# generate a qubit in ``QubitWithError`` model
qubit = QubitWithError()
# send the qubit
l1.send(qubit=qubit, next_hop=n2)
Qubit Loss Quantum Channel
qns.entity.qchannel.QubitLossChannel
is a usually used quantum channel model, that it will drop qubits randomly, following this possibility: \(1-(1-p_{\text{init}})*10^{- \miu \cdot length / 10}\), where \(p_{\text{init}}\) is the initial drop probability of generating a qubit, \(\miu\) is the attenuation rate, and :math”length is the channel length.