Quantum memory: the device to store qubits

Quantum memory has two modes: the synchronous and asynchronous mode. In synchronous model, it can not describe the time delay in writing and reading qubits. Users may use write and read functions to operate the memory directly. However, in asynchronous mode, quantum memory works as an independent entity. Quantum nodes uses events to control the memories and get the results.

Synchronous mode

Quantum memory is an entity that can store qubits. It can be equipped to a quantum nodes:

from qns.entity.node.node import QNode
from qns.entity.memory.memory import QuantumMemory

n1 = QNode("n1") # a quantum node named "n1"
m = QuantumMemory("m1")
n1.add_memory(m)

read and write methods are used to store and get a qubit. The read methods will use the qubit’s name or reference as the keyword to search the qubit.

q1 = Qubit()
m.write(q1)
q2 = m.read()

The memory can have a limited size. is_full function returns whether the memory is full:

from qns.entity.node.node import QNode
from qns.entity.memory.memory import QuantumMemory

n1 = QNode("n1") # a quantum node named "n1"
m2 = QuantumMemory("m2", capacity = 10) # a memory can store 10 qubits
n1.add_memory(m2)

m2.is_full() # check if the memory is full

Asynchronous mode

In this mode, quantum nodes (or applications) needs to use MemoryWriteRequestEvent and MemoryReadRequestEvent events to send requests to the quantum memories and collect the results by listening to MemoryWriteResponseEvent and MemoryReadResponseEvent events. In this way, users can model the time delay in reading and writing quantum memories. In asynchronous mode, the quantum memories have an additional input parameter called delay to set the read/write time delay. delay can be a float or a DelayModel.

Here, we give an example of asynchronous mode. The quantum node first install a MemoryResponseApp application to handle the read/write result. During the simulation, the node generates MemoryWriteRequestEvent to save a qubit and use MemoryReadRequestEvent to get the qubit one second later.

class MemoryResponseApp(Application):
    def __init__(self):
        super().__init__()
        self.add_handler(self.MemoryReadhandler, [MemoryReadResponseEvent], [])
        self.add_handler(self.MemoryWritehandler, [MemoryWriteResponseEvent], [])

    def MemoryReadhandler(self, node, event: Event) -> Optional[bool]:
        result = event.result # the saving qubit
        print("self._simulator.tc.sec: {}".format(self._simulator.tc))
        print("result: {}".format(result))
        assert (self._simulator.tc.sec == 1.5)
        assert (result is not None)

    def MemoryWritehandler(self, node, event: Event) -> Optional[bool]:
        result = event.result # if the qubit is saved successfully
        print("self._simulator.tc.sec: {}".format(self._simulator.tc))
        print("result: {}".format(result))
        assert (self._simulator.tc.sec == 0.5)
        assert (result)

n1 = QNode("n1")
app = MemoryReadResponseApp()
n1.add_apps(app)

m = QuantumMemory("m1", delay=0.5)
n1.add_memory(m)

s = Simulator(0, 10, 1000)
n1.install(s)

q1 = Qubit(name="q1")
write_request = MemoryWriteRequestEvent(memory=m, qubit=q1, t=s.time(sec=0), by=n1)
read_request = MemoryReadRequestEvent(memory=m, key="q1", t=s.time(sec=1), by=n1)
s.add_event(write_request)
s.add_event(read_request)
s.run()

Error models in a quantum memory

Also, storage error can be introduced during storage a qubit. The error is handled in function storage_error_model, which takes the storage time and other parameters as the input. Those parameters shows the memory attributions (such as the coherence time), and they can be set using decoherence_rate and store_error_model_args. decoherence_rate is the decoherence rate, while store_error_model_args is a directory that contains other parameters for the error model

from qns.entity.memory.memory import QuantumMemory
from qns.models.epr import WernerStateEntanglement

class ErrorEntanglement(WernerStateEntanglement):
    def storage_error_model(self, t: float, **kwargs):
        # storage error will reduce the fidelity
        t_coh = kwargs.get("t_coh", 1)
        self.w = self.w * np.exp(- t / t_coh)

# memory error attributions: coherence time is 1 second
m3 = QuantumMemory("m3", capacity = 10, decoherence_rate=0.2, store_error_model_args = {"t_coh": 1})

# generate an entanglement and store it
epr1 = ErrorEntanglement(name="epr1")
m3.write(epr1)

# after a while, the fidelity will drop
epr2 = m3.read("epr1")