schedulerH.py 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. # -*- coding: Latin-1 -*-
  2. from heapq import heappush, heappop, heapify
  3. from logger import *
  4. class SchedulerH(object):
  5. """
  6. Scheduler class itself
  7. """
  8. def __init__(self, models, epsilon, totalModels):
  9. """
  10. Constructor
  11. :param models: all models in the simulation
  12. """
  13. self.heap = []
  14. self.id_fetch = [None] * totalModels
  15. for model in models:
  16. self.id_fetch[model.model_id] = [model.timeNext, model.model_id, True, model]
  17. heappush(self.heap, self.id_fetch[model.model_id])
  18. self.invalids = 0
  19. self.maxInvalids = len(models)*2
  20. self.epsilon = epsilon
  21. def schedule(self, model):
  22. """
  23. Schedule a model
  24. :param model: the model to schedule
  25. """
  26. #assert debug("Scheduling " + str(model))
  27. # Create the entry, as we have accepted the model
  28. elem = [model.timeNext, model.model_id, False, model]
  29. try:
  30. self.id_fetch[model.model_id] = elem
  31. except IndexError:
  32. # A completely new model
  33. self.id_fetch.append(elem)
  34. self.maxInvalids += 2
  35. # Check if it requires to be scheduled
  36. self.id_fetch[model.model_id][2] = True
  37. heappush(self.heap, self.id_fetch[model.model_id])
  38. def unschedule(self, model):
  39. """
  40. Unschedule a model
  41. :param model: model to unschedule
  42. """
  43. # Update the referece still in the heap
  44. self.id_fetch[model.model_id][2] = False
  45. # Remove the reference in our id_fetch
  46. self.id_fetch[model.model_id] = None
  47. def massReschedule(self, reschedule_set):
  48. """
  49. Reschedule all models provided.
  50. Equivalent to calling unschedule(model); schedule(model) on every element in the iterable.
  51. :param reschedule_set: iterable containing all models to reschedule
  52. """
  53. #NOTE rather dirty, though a lot faster for huge models
  54. #assert debug("Mass rescheduling")
  55. inf = float('inf')
  56. for model in reschedule_set:
  57. event = self.id_fetch[model.model_id]
  58. if event[2]:
  59. if model.timeNext == event[0]:
  60. continue
  61. self.invalids += 1
  62. event[2] = False
  63. self.id_fetch[model.model_id] = [model.timeNext, model.model_id, True, model]
  64. heappush(self.heap, self.id_fetch[model.model_id])
  65. #assert debug("Optimizing heap")
  66. if self.invalids >= self.maxInvalids:
  67. #assert info("Heap compaction in progress")
  68. self.heap = [i for i in self.heap if i[2]]
  69. heapify(self.heap)
  70. self.invalids = 0
  71. #assert info("Heap compaction complete")
  72. def readFirst(self):
  73. """
  74. Returns the time of the first model that has to transition
  75. :returns: timestamp of the first model
  76. """
  77. #assert debug("Reading first element from heap")
  78. self.cleanFirst()
  79. return self.heap[0][0]
  80. def cleanFirst(self):
  81. """
  82. Clean up the invalid elements in front of the list
  83. """
  84. #assert debug("Cleaning list")
  85. try:
  86. while not self.heap[0][2]:
  87. heappop(self.heap)
  88. self.invalids -= 1
  89. except IndexError:
  90. # Nothing left, so it as clean as can be
  91. #assert debug("None in list")
  92. pass
  93. def getImminent(self, time):
  94. """
  95. Returns a list of all models that transition at the provided time, with a specified epsilon deviation allowed.
  96. :param time: timestamp to check for models
  97. .. warning:: For efficiency, this method only checks the **first** elements, so trying to invoke this function with a timestamp higher than the value provided with the *readFirst* method, will **always** return an empty set.
  98. """
  99. #assert debug("Asking all imminent models")
  100. immChildren = []
  101. t, age = time
  102. try:
  103. # Age must be exactly the same
  104. first = self.heap[0]
  105. while (abs(first[0][0] - t) < self.epsilon) and (first[0][1] == age):
  106. # Check if the found event is actually still active
  107. if(first[2]):
  108. # Active, so event is imminent
  109. immChildren.append(first[3])
  110. first[2] = False
  111. else:
  112. # Wasn't active, but we will have to pop this to get the next
  113. # So we can lower the number of invalids
  114. self.invalids -= 1
  115. # Advance the while loop
  116. heappop(self.heap)
  117. first = self.heap[0]
  118. except IndexError:
  119. pass
  120. return immChildren