Backends

As quantum networking and quantum internet becomes a more popular field, we expect to see a rise in the number of quantum network simulation tools as we see now with quantum computing simulation software. In QuNetSim, we have prepared for this by structuring the network backend so that it is modular. With this, one can simply import their own network and qubit backends, implement the necessary methods and it (hopefully) will work right out of the box.

Currently we are working with three backends: SimulaQron, ProjectQ and EQSN, a simulator that has been developed by the TQSD team.

If you do not chose a backend, the default backend will be SimulaQron. However, you can chose the backend explicitly, by creating one of the backend objects and passing it as an argument to the Hosts and network. An example of how to chose the EQSN backend is shown in the code snippet below.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import numpy as np
# import the eqsn backend
from qunetsim.backends import EQSNBackend

# create the EQSN backend object
backend = EQSNBackend()

# Initialize a network and define the host IDs in the network
network = Network.get_instance()
nodes = ['Alice', 'Bob', 'Eve']

# Start the network with the hosts and explicitly set the backend
network.start(nodes, backend)

# Initialize the host Alice with the right backend
host_alice = Host('Alice', backend)

host_alice.add_connection('Bob')
host_alice.start()

# Initialize the host Bob with the right backend
host_bob = Host('Bob', backend)

host_bob.add_connection('Alice')
host_bob.add_connection('Eve')
host_bob.start()

# Initialize the host Eve with the right backend
host_eve = Host('Eve', backend)
host_eve.add_connection('Bob')
host_eve.start()

# Add the hosts to the network
network.add_host(host_alice)
network.add_host(host_bob)
network.add_host(host_eve)

To chose a different backend, just initialize the backend variable with another backend object from the QuNetSim backend package.

Writing your own Backend

Instead of using a provided backend, it is also possible to write an own backend. In this section, we describe how the interfacing to a backend works.

Note

We include ProjectQ as a development package which may not install properly without a C++ compiler. See ProjectQ documentation for how to install ProjectQ with just the Python version. The issue can be resolved in most cases by running pip install projectq --global-option=--without-cppsimulator and then installing the dev requirements again, but this will only install the ProjectQ Python simulator, and not the C version.

The interface is described in the file backends.backend. The interface to the backend is a class. There can be multiple instances of this class at once, and it has to be thread safe. This class also has to store all information which are necessary for the backend, if needed. To make these information thread safe, the file backend.RWLock provides a read write lock and the file backends.SafeDict provides a thread safe dictionary with some basic functionality. In the functions start and stop, the backend can be initialized if necessary and be destructed.

The create_qubit function creates a qubit in the backend. It then has to create an object of type Qubit. The variable qubit of the object Qubit can be used to store some backend specific information. Always, if a gate is called, this information can be accessed.

The function send_qubit_to(qubit, from_host_id, to_host_id) transmits a qubit from one host to the other. Also, if the backend dose not need any knowledge of the owner of the qubit, the backend has to change the owner of the Qubit object. Therefore, most backends will need a dictionary which maps host_ids to host objects. We recommend using the SafeDict class for it and calling the variable _host. The implementation for this function, would than look the following:

1
2
3
4
5
6
def send_qubit_to(self, qubit, from_host_id, to_host_id):

    # Backend specific stuff

    new_host = self._hosts.get_from_dict(to_host_id)
    qubit.set_new_host(new_host)

The functions create_EPR and receive_EPR are responsible for creating entangled pairs. Calling create_EPR will return a single Qubit, calling receive_EPR on the other Host will then give the second Qubit belonging to the pair. It might seem weird, that there are two function for this task, and not a real Qubit is sent over the network. Most backends will have to create two qubits in the create_EPR function and buffer one of the qubits till the receive_EPR function is called.

All other functions which are part of the interface should be self-explanatory.