Routing with Entanglement

In this example, we see how to add a custom routing function that considers the entanglement in the network.

We configure the network as follows:

1
2
A <==> node_1; A <==> node_2
B <==> node_1; B <==> node_2
 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
nodes = ['A', 'node_1', 'node_2', 'B']
network.use_hop_by_hop = False
network.set_delay = 0.1
network.start(nodes)

A = Host('A')
A.add_connection('node_1')
A.add_connection('node_2')
A.start()

node_1 = Host('node_1')
node_1.add_connection('A')
node_1.add_connection('B')
node_1.start()

node_2 = Host('node_2')
node_2.add_connection('A')
node_2.add_connection('B')
node_2.start()

B = Host('B')
B.add_connection('node_1')
B.add_connection('node_2')
B.start()

hosts = [A, node_1, node_2, B]
for h in hosts:
    network.add_host(h)

We add a protocol for a Host to constantly generate entanglement.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
def generate_entanglement(host):
    """
    Generate entanglement if the host has nothing to process (i.e. is idle).
    """
    while True:
        if host.is_idle():
            host_connections = host.get_connections()
            for connection in host_connections:
                if connection['type'] == 'quantum':
                    num_epr_pairs = len(host.get_epr_pairs(connection['connection']))
                    if num_epr_pairs < 4:
                        host.send_epr(connection['connection'], await_ack=True)
        time.sleep(5)

The routing algorithm works as follows.

  1. Build a graph with the vertices the Hosts and the edges the connections

  2. The weight of each edge is the inverse of the amount of entanglement shared on that link (using a shortest path works best with networkx)

  3. When there is no entanglement then add a large weight to that edge

  4. Compute the shortest path on this newly generated graph from sender to receiver and return the route

 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
def routing_algorithm(di_graph, source, target):
    """
    Entanglement based routing function. Note: any custom routing function must
    have exactly these three parameters and must return a list ordered by the steps
    in the route.

    Args:
        di_graph (networkx DiGraph): The directed graph representation of the network.
        source (str): The sender ID
        target (str: The receiver ID
    Returns:
        (list): The route ordered by the steps in the route.
    """

    entanglement_network = nx.DiGraph()
    nodes = di_graph.nodes()
    # Generate entanglement network
    for node in nodes:
        host = network.get_host(node)
        host_connections = host.get_connections()
        for connection in host_connections:
            if connection['type'] == 'quantum':
                num_epr_pairs = len(host.get_epr_pairs(connection['connection']))
                if num_epr_pairs == 0:
                    entanglement_network.add_edge(host.host_id, connection['connection'], weight=1000)
                else:
                    entanglement_network.add_edge(host.host_id, connection['connection'], weight=1. / num_epr_pairs)

    try:
        route = nx.shortest_path(entanglement_network, source, target, weight='weight')
        print('-------' + str(route) + '-------')
        return route
    except Exception as e:
        Logger.get_instance().error(e)

To test the routing, we send superdense messages from A to B after we allow the network to build up some entanglement

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
node_1.run_protocol(generate_entanglement)
node_2.run_protocol(generate_entanglement)

print('---- BUILDING ENTANGLEMENT   ----')
# Let the network build up entanglement
time.sleep(15)
print('---- DONE BUILDING ENTANGLEMENT   ----')

choices = ['00', '11', '10', '01']
for _ in range(5):
    print('----  sending superdense  ----')
    A.send_superdense(B.host_id, random.choice(choices), await_ack=True)
    time.sleep(1)

The full example is 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
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
from qunetsim.components import Host
from qunetsim.components import Network
from qunetsim.objects import Logger
import networkx
import time


def generate_entanglement(host):
    """
    Generate entanglement if the host has nothing to process (i.e. is idle).
    """
    while True:
        if host.is_idle():
            host_connections = host.get_connections()
            for connection in host_connections:
                if connection['type'] == 'quantum':
                    num_epr_pairs = len(host.get_epr_pairs(connection['connection']))
                    if num_epr_pairs < 4:
                        host.send_epr(connection['connection'], await_ack=True)
        time.sleep(5)


def routing_algorithm(di_graph, source, target):
    """
    Entanglement based routing function. Note: any custom routing function must
    have exactly these three parameters and must return a list ordered by the steps
    in the route.

    Args:
        di_graph (networkx DiGraph): The directed graph representation of the network.
        source (str): The sender ID
        target (str: The receiver ID
    Returns:
        (list): The route ordered by the steps in the route.
    """
    # Generate entanglement network
    entanglement_network = nx.DiGraph()
    nodes = di_graph.nodes()
    for node in nodes:
        host = network.get_host(node)
        host_connections = host.get_connections()
        for connection in host_connections:
            if connection['type'] == 'quantum':
                num_epr_pairs = len(host.get_epr_pairs(connection['connection']))
                if num_epr_pairs == 0:
                    entanglement_network.add_edge(host.host_id, connection['connection'], weight=1000)
                else:
                    entanglement_network.add_edge(host.host_id, connection['connection'], weight=1. / num_epr_pairs)

    try:
        route = nx.shortest_path(entanglement_network, source, target, weight='weight')
        print('-------' + str(route) + '-------')
        return route
    except Exception as e:
        Logger.get_instance().error(e)


def main():
    network = Network.get_instance()
    network.quantum_routing_algo = routing_algorithm
    nodes = ['A', 'node_1', 'node_2', 'B']
    network.use_hop_by_hop = False
    network.set_delay = 0.2
    network.start(nodes)

    A = Host('A')
    A.add_connection('node_1')
    A.add_connection('node_2')
    A.start()

    node_1 = Host('node_1')
    node_1.add_connection('A')
    node_1.add_connection('B')
    node_1.start()

    node_2 = Host('node_2')
    node_2.add_connection('A')
    node_2.add_connection('B')
    node_2.start()

    B = Host('B')
    B.add_connection('node_1')
    B.add_connection('node_2')
    B.start()

    hosts = [A, node_1, node_2, B]
    for h in hosts:
        network.add_host(h)

    node_1.run_protocol(generate_entanglement)
    node_2.run_protocol(generate_entanglement)

    print('---- BUILDING ENTANGLEMENT   ----')
    # Let the network build up entanglement
    time.sleep(15)
    print('---- DONE BUILDING ENTANGLEMENT   ----')

    choices = ['00', '11', '10', '01']
    for _ in range(5):
        print('----  sending superdense  ----')
        A.send_superdense(B.host_id, random.choice(choices), await_ack=True)
        time.sleep(1)

    # Let the network run for 40 seconds
    time.sleep(40)
    print('stopping')
    network.stop(stop_hosts=True)