blob: ce1966fe68ce56bc50f2aaf2c988ffb75d1c1f85 [file] [log] [blame]
Saurab Dulal8ae870a2018-07-31 05:17:49 +00001# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
2#!/usr/bin/env python
3#
4# Copyright (C) 2015-2019, The University of Memphis
5#
6# This file is part of Mini-NDN.
7# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
8#
9# Mini-NDN is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# Mini-NDN is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with Mini-NDN, e.g., in COPYING.md file.
21# If not, see <http://www.gnu.org/licenses/>.
22
23'''
24This module will compute link state, hyperbolic and geohyperbolic
25routes and their costs from the given Mini-NDN topology
26'''
27
28import heapq
29import math
30from collections import defaultdict
31from math import sin, cos, sinh, cosh, acos, acosh
32import json
33import operator
34
35from mininet.log import info, debug, error
36
37UNKNOWN_DISTANCE = -1
38HYPERBOLIC_COST_ADJUSTMENT_FACTOR = 1000
39
40class CalculateRoutes():
41 """
42 Creates a route calculation object, which is used to compute routes from a node to
43 every other nodes in a given topology topology using hyperbolic or geohyperbolic
44 routing algorithm
45
46 :param NetObject netObj: Mininet net object
47 :param RoutingType routingType: (optional) Routing algorithm, link-state or hr etc
48 """
49 def __init__(self, netObj, routingType):
50 self.adjacenctMatrix = defaultdict(dict)
51 self.nodeDict = defaultdict(dict)
52 self.routingType = routingType
53 for host in netObj.hosts:
54 radius = float(host.params['params']['radius'])
55 angles = [float(x) for x in host.params['params']['angle'].split(',')]
56 self.nodeDict[host.name][radius] = angles
57
58 for link in netObj.topo.links_conf:
59 linkDelay = int(link.linkDict['delay'].replace("ms", ""))
60 self.adjacenctMatrix[link.h1][link.h2] = linkDelay
61 self.adjacenctMatrix[link.h2][link.h1] = linkDelay
62
63 def getNestedDictionary(self):
64 return defaultdict(self.getNestedDictionary)
65
66 def getRoutes(self, nFaces):
67 resultMatrix = self.getNestedDictionary()
68 routes = defaultdict(list)
69
70 if self.routingType == "link-state":
71 if nFaces == 1:
72 resultMatrix = self.computeDijkastra() # only best routes.
73 else:
74 resultMatrix = self.computeDijkastraAll() # all possible routes
75 elif self.routingType == "hr":
76 # Note: For hyperbolic, only way to find the best routes is by computing all possible
77 # routes and getting the best one.
78 resultMatrix = self.computeHyperbolic()
79 else:
80 info("Routing type not supported\n")
81 return []
82
83 for node in resultMatrix:
84 for destinationNode in resultMatrix[node]:
85 # Sort node - destination via neighbor based on their cost
86 tempDict = resultMatrix[node][destinationNode]
87 shortedTempDict = sorted(tempDict.items(), key=operator.itemgetter(1))
88 # nFaces option gets n-best faces based on shortest distance, default is all
89 if nFaces == 0:
90 for item in shortedTempDict:
91 viaNeighbor = item[0]
92 cost = item[1]
93 routes[node].append([destinationNode, str(cost), viaNeighbor])
94 else:
95 for index, item in enumerate(shortedTempDict):
96 if index >= nFaces:
97 break
98 viaNeighbor = item[0]
99 cost = item[1]
100 routes[node].append([destinationNode, str(cost), viaNeighbor])
101
102 debug("-routes----", routes)
103 return routes
104
105 def getNodeNames(self):
106 return [k for k in self.nodeDict]
107
108 def computeHyperbolic(self):
109 paths = self.getNestedDictionary()
110 nodeNames = self.getNodeNames()
111 for node in self.nodeDict:
112 neighbors = [k for k in self.adjacenctMatrix[node]]
113 for viaNeighbor in neighbors:
114 others = list(set(nodeNames) - set(viaNeighbor) - set(node))
115 paths[node][viaNeighbor][viaNeighbor] = 0
116 # Compute distance from neighbors to no-neighbors
117 for destinationNode in others:
118 hyperbolicDistance = getHyperbolicDistance(self.nodeDict[viaNeighbor],
119 self.nodeDict[destinationNode])
120 hyperbolicCost = int(HYPERBOLIC_COST_ADJUSTMENT_FACTOR \
121 * round(hyperbolicDistance, 6))
122 paths[node][destinationNode][viaNeighbor] = hyperbolicCost
123
124 debug("Shortest Distance Matrix: {}".format(json.dumps(paths)))
125 return paths
126
127 def computeDijkastra(self):
128 """
129 Dijkstra computation: Compute all the shortest paths from nodes to the destinations.
130 And fills the distance matrix with the corresponding source to destination cost
131 """
132 distanceMatrix = self.getNestedDictionary()
133 nodeNames = self.getNodeNames()
134 for node in nodeNames:
135 others = list(set(nodeNames) - set(node))
136 for destinationNode in others:
137 cost, path = dijkstra(self.adjacenctMatrix, node, destinationNode)
138 viaNeighbor = path[1]
139 distanceMatrix[node][destinationNode][viaNeighbor] = cost
140
141 debug("Shortest Distance Matrix: {}".format(json.dumps(distanceMatrix)))
142 return distanceMatrix
143
144 def computeDijkastraAll(self):
145 """
146 Multi-path Dijkastra computation: Compute all the shortest paths from nodes to the
147 destinations via all of its neighbors individually. And fills the distanceMatrixViaNeighbor
148 with a corresponding source to its destination cost
149
150 Important: distanceMatrixViaNeighbor represents the shortest distance from a source to a
151 destination via specific neighbors
152 """
153 distanceMatrixViaNeighbor = self.getNestedDictionary()
154 nodeNames = self.getNodeNames()
155 for node in nodeNames:
156 neighbors = [k for k in self.adjacenctMatrix[node]]
157 for viaNeighbor in neighbors:
158 directCost = self.adjacenctMatrix[node][viaNeighbor]
159 distanceMatrixViaNeighbor[node][viaNeighbor][viaNeighbor] = directCost
160 others = list(set(nodeNames) - set(viaNeighbor) - set(node))
161 for destinationNode in others:
162 nodeNeighborCost = self.adjacenctMatrix[node][viaNeighbor]
163 # path variable is not used for now
164 cost, path = dijkstra(self.adjacenctMatrix, viaNeighbor, destinationNode, node)
165 if cost != 0 and path != None:
166 totalCost = cost + nodeNeighborCost
167 distanceMatrixViaNeighbor[node][destinationNode][viaNeighbor] = totalCost
168
169 debug("Shortest Distance Matrix: {}".format(json.dumps(distanceMatrixViaNeighbor)))
170 return distanceMatrixViaNeighbor
171
172def dijkstra(graph, start, end, ignoredNode = None):
173 """
174 Compute shortest path and cost from a given source to a destination
175 using Dijkstra algorithm
176
177 :param Graph graph: given network topology/graph
178 :param Start start: source node in a given network graph/topology
179 :end End end: destination node in a given network graph/topology
180 :param Node ignoredNode: node to ignore computing shortest path from
181 """
182 queue = [(0, start, [])]
183 seen = set()
184 while True:
185 (cost, v, path) = heapq.heappop(queue)
186 if v not in seen:
187 path = path + [v]
188 seen.add(v)
189 if v == end:
190 debug("Distance from {} to {} is {}".format(start, end, cost))
191 return cost, path
192 for (_next, c) in graph[v].iteritems():
193 if _next != ignoredNode: # Ignore path going via ignoreNode
194 heapq.heappush(queue, (cost + c, _next, path))
195
196 if not queue: # return if no path exist from source - destination except via ignoreNode
197 debug("Distance from {} to {} is {}".format(start, end, cost))
198 return cost, None
199
200def calculateAngularDistance(angleVectorI, angleVectorJ):
201 """
202 For hyperbolic/geohyperbolic routing algorithm, this function computes angular distance between
203 two nodes. A node can have two or more than two angular coordinates.
204
205 :param AngleVectorI angleVectorI: list of angular coordinate of a give node I
206 :param AngleVectorJ angleVectorJ: list of angular coordinate of a give node J
207
208 ref: https://en.wikipedia.org/wiki/N-sphere#Spherical_coordinates
209
210 """
211 innerProduct = 0.0
212 if len(angleVectorI) != len(angleVectorJ):
213 error("Angle vector sizes do not match")
214 return UNKNOWN_DISTANCE
215
216 # Calculate x0 of the vectors
217 x0i = cos(angleVectorI[0])
218 x0j = cos(angleVectorJ[0])
219
220 # Calculate xn of the vectors
221 xni = sin(angleVectorI[len(angleVectorI) - 1])
222 xnj = sin(angleVectorJ[len(angleVectorJ) - 1])
223
224 # Do the aggregation of the (n-1) coordinates (if there is more than one angle)
225 # i.e contraction of all (n-1)-dimensional angular coordinates to one variable
226 for k in range(0, len(angleVectorI)-1):
227 xni *= sin(angleVectorI[k])
228 xnj *= sin(angleVectorJ[k])
229
230 innerProduct += (x0i * x0j) + (xni * xnj)
231
232 if (len(angleVectorI) > 1):
233 for m in range(1, len(angleVectorI)):
234 # Calculate euclidean coordinates given the angles and assuming R_sphere = 1
235 xmi = cos(angleVectorI[m])
236 xmj = cos(angleVectorJ[m])
237 for l in range (0, m):
238 xmi *= sin(angleVectorI[l])
239 xmj *= sin(angleVectorJ[l])
240
241 innerProduct += xmi * xmj
242
243 # ArcCos of the inner product gives the angular distance
244 # between two points on a d-dimensional sphere
245 angularDist = acos(innerProduct)
246 debug("Angular distance from {} to {} is {}".format(angleVectorI, angleVectorJ, angularDist))
247 return angularDist
248
249def getHyperbolicDistance(sourceNode, destNode):
250 """
251 Return hyperbolic or geohyperbolic distance between two nodes. The distance is computed
252 on the basis of following algorithm/mathematics
253 ref: https://en.wikipedia.org/wiki/Hyperbolic_geometry
254 """
255 r1 = [key for key in sourceNode][0]
256 r2 = [key for key in destNode][0]
257
258 zeta = 1.0
259 dtheta = calculateAngularDistance(sourceNode[r1], destNode[r2])
260 hyperbolicDistance = (1./zeta) * acosh(cosh(zeta * r1) * cosh(zeta * r2) -\
261 sinh(zeta * r1) * sinh(zeta * r2) * cos(dtheta))
262
263 debug("Distance from {} to {} is {}".format(sourceNode, destNode, hyperbolicDistance))
264 return hyperbolicDistance