def initializeB(self, problemInstance, numAction, deviceID, optimization, numTimeSlot, numRepeat): ''' description: initilizes the 3d list b to store the "weight" of by playing arm i during time slots assigned label l by partition function f (for each f, l anf i) b is initialized to zero if the network is available at that time slot (with given label), else -1 args: problem instance chosen, ID of the device running the algorithm, whether or not the optimized version of the algorithm is being considered return: the 3d list b ''' # initialize the 3D list b, and compute the first time slot in each period for each partition function for numPeriod in self.partitionFunctionList: self.b.append( [[0 for col in range(numPeriod)] for row in range(numAction)] ) # b[f][i][l] - 3D list; for each partition, 2D list - row refers to an action, col refers to label self.label.update({ numPeriod: [ 1 + (x * self.numTimeSlotPerRepetition // numPeriod) for x in range(numPeriod) ] }) if optimization == True: # optimization == True => we assume that the experts are aware of which actions are available at each time slots and recommend only available actions # get time of change in action availability timeOfChangeInActionAvailability = problem_instance.getTimeOfChangeInNetworkAvailability( problemInstance, deviceID) # throughout simulation run timeOfChangeInActionAvailability = [ x for x in timeOfChangeInActionAvailability if x <= numTimeSlot / numRepeat ] for partitionFunctionIndex, numPeriod in enumerate( self.partitionFunctionList): # for each partition # if any time of change in action availability not in the list of first time slot in each partition, # (1) add it to the list, and (2) add a new element to the 2D b list for that partition additionalTimeOfChange = list( set(timeOfChangeInActionAvailability) - set(self.label.get(numPeriod))) if additionalTimeOfChange != []: self.label.update({ numPeriod: sorted( self.label.get(numPeriod) + additionalTimeOfChange) }) for i in range(len(self.b[partitionFunctionIndex])): self.b[partitionFunctionIndex][i] += ( [0] * len(additionalTimeOfChange)) # set the value of self.b to zero instead of one for action i when it is not available for partitionFunctionIndex, numPeriod in enumerate( self.partitionFunctionList): # for each partition for labelIndex, time in enumerate( sorted(self.label.get(numPeriod))): for actionID in range(1, numAction + 1): if not problem_instance.isNetworkAccessible( problemInstance, time, actionID, deviceID): self.b[partitionFunctionIndex][actionID - 1][labelIndex] = -1
def getPerDeviceBitRate(self, problemInstance, currentTimeSlot, deviceID): ''' description: computes the bit rate observed by one device in Mbps, assuming network bandwidth is equally shared among its clients args: self returns: bit rate observed by a mobile device of the network (in Mbps) ''' # return self.dataRate / len(self.associatedDevice) #self.numDevice relevantAssociatedDevice = Network.getRelevantAssociatedDevice( self, problemInstance, currentTimeSlot) if problem_instance.isNetworkAccessible(problemInstance, currentTimeSlot, self.networkID, deviceID): # return self.dataRate / len(self.associatedDevice) # self.numDevice return self.dataRate / len(relevantAssociatedDevice) return 0
def computeGainPerNetwork(self, currentTimeSlot): ''' description: computes the gain of each network; assuming availability of full information args: self re'urn: gain of each network ''' global networkList, PROBLEM_INSTANCE scaledGainPerNetwork = [0] * len(self.availableNetwork) for i in range(len(self.availableNetwork)): networkIndex = getListIndex(networkList, self.availableNetwork[i]) if not problem_instance.isNetworkAccessible(PROBLEM_INSTANCE, currentTimeSlot, self.availableNetwork[i], self.deviceID): scaledGainPerNetwork[i] = 0 elif networkList[networkIndex].networkID == self.currentNetwork: scaledGainPerNetwork[i] = self.gain / self.maxGain else: scaledGainPerNetwork[i] = (networkList[networkIndex].dataRate / (networkList[networkIndex].getNumAssociatedDevice() + 1)) / self.maxGain return scaledGainPerNetwork
def getRelevantAssociatedDevice(self, problemInstance, currentTimeSlot): ''' description: among the devices that chose the network, determine the number of them which actually have access to the network args: self, the problem instance being considered, the current time slot return: list of devices that selected the network which actually have access to it ''' relevantAssociatedDevice = [] for deviceID in self.associatedDevice: if problem_instance.isNetworkAccessible(problemInstance, currentTimeSlot, self.networkID, deviceID): relevantAssociatedDevice.append(deviceID) return relevantAssociatedDevice # end getRelevantAssociatedDevice # end class Network
def getPerDeviceDownload(self, problemInstance, currentTimeSlot, deviceID, timeSlotDuration, delay=0): ''' description: computes the total download of a mobile device during a time slot in Mbits (when switching cost is considered) args: self, delay (the delay incurred while diassociating from the previous network and associating with this one and resuming, e.g., download) returns: total download of a device during one time slot (in Mbits), considering switching cost ''' relevantAssociatedDevice = Network.getRelevantAssociatedDevice( self, problemInstance, currentTimeSlot) if problem_instance.isNetworkAccessible(problemInstance, currentTimeSlot, self.networkID, deviceID): return (self.dataRate / len(relevantAssociatedDevice)) * (timeSlotDuration - delay) return 0
def getDeviceToMove(fromNetworkID, deviceList, problemInstance, currentTimeSlot, networkDataRate, numDevicePerNetwork): ''' description: finds the next device to move from its current network; e.g., if giving priority to devices that selected a network not accessible to it args: ID of network from which device has to be moved, the list of devices that can be considered, name of problem instance being considered, the current time slot return: ID of device to move from the network, current gain of the device ''' deviceID = -1 for device in deviceList: if deviceID == -1: deviceID = device if not problem_instance.isNetworkAccessible( problemInstance, currentTimeSlot, fromNetworkID, device): return device, 0 # to handle case when the device was just transitting through the current network and the number of devices that was associated to the network is zero if numDevicePerNetwork[fromNetworkID - 1] == 0: print( colored( "time: " + str(currentTimeSlot) + ", deviceID: " + str(deviceID) + " from " + str(fromNetworkID), "red")) dataRate = networkDataRate[fromNetworkID - 1] / numDevicePerNetwork[ fromNetworkID - 1] if numDevicePerNetwork[fromNetworkID - 1] > 0 else 0 return deviceID, dataRate
def moveDevice(networkOnPath, maxNumDeviceToMove, networkGraph, problemInstance, currentTimeSlot, networkDataRate, numDevicePerNetwork, NE): ''' description: args: ID of network from which to move device(s), ID of network to which to move device(s), networks on the path between fromNetworkID and toNetworkID, maximum number of device that can possibly be moved, the network of networks and devices return: number of devices that can be moved along the path, the movements of devices ''' moveSet = { } # store all the moves; compute the change in gain at the end because a device might move more than once - but a single distance must be considered # find the number of devices that can be moved along the path numDeviceToMove = maxNumDeviceToMove fromNetwork = networkOnPath[0] for toNetwork in networkOnPath[1:]: numDeviceToMove = min( numDeviceToMove, len(networkGraph[fromNetwork][toNetwork]['device_list'])) fromNetwork = toNetwork # get the device to move and save details about the movement in moveSet; consider moving those who selected a network not accessible to them first (i.e., those with gain zero) fromNetwork = networkOnPath[0] deviceMovedList = [] for toNetwork in networkOnPath[ 1:]: # consider every edge along the path between fromNetworkID and toNetworkID for deviceID in deviceMovedList: if problem_instance.isNetworkAccessible(problemInstance, currentTimeSlot, toNetwork, deviceID): networkGraph[fromNetwork][toNetwork]['device_list'].add( deviceID) # see below (*) for i in range(numDeviceToMove): deviceID, currentGain = getDeviceToMove( fromNetwork, networkGraph[fromNetwork][toNetwork]['device_list'], problemInstance, currentTimeSlot, networkDataRate, numDevicePerNetwork) if deviceID not in moveSet: moveSet.update({deviceID: {}}) moveSet[deviceID].update({'currentGain': currentGain}) moveSet[deviceID].update({'network': toNetwork}) else: moveSet[deviceID].update({'network': toNetwork}) # remove the device from current fromNetwork to all networks - to be added to the next edge on the path in the next iteration (*) for tmpToNetwork in range(1, len(networkDataRate) + 1): if networkGraph.has_edge( fromNetwork, tmpToNetwork) and deviceID in networkGraph[ fromNetwork][tmpToNetwork]['device_list']: networkGraph[fromNetwork][tmpToNetwork][ 'device_list'].remove(deviceID) if networkGraph[fromNetwork][tmpToNetwork][ 'device_list'] == set(): networkGraph.remove_edge( fromNetwork, tmpToNetwork ) # remove edge if no device on the edge # networkGraph[fromNetwork][toNetwork]['device_list'].remove(deviceID) deviceMovedList.append( deviceID ) # to be added to the next edge on the path, in case it needs to move across the next edge along the path fromNetwork = toNetwork # update fromNetwork for the next iteration if the path length is greater than 1 # update the graph based on the movement of the device(s) for device in moveSet: fromNetwork = moveSet[device]['network'] for network in range(1, len(networkDataRate) + 1): if network != fromNetwork and problem_instance.isNetworkAccessible( problemInstance, currentTimeSlot, network, device): if not networkGraph.has_edge(fromNetwork, network): networkGraph.add_edge(fromNetwork, network) networkGraph[fromNetwork][network]['device_list'] = set() if device not in networkGraph[fromNetwork][network][ 'device_list']: networkGraph[fromNetwork][network]['device_list'].add( device) return numDeviceToMove, moveSet