class GD_PQ_Bucketing(object): def __init__(self, bucket_bounds, name = None): if name: self.name = name else: self.name = self.__class__.__name__ self.bucket_bounds = bucket_bounds self.bucket_costs = [float(a + b)/2 for a,b in zip([0] + bucket_bounds[:-1], bucket_bounds)] self.bucket_frac = [(0,0) for _ in bucket_bounds] self.bucket_evictions = [0 for _ in self.bucket_bounds] self.bucket_misses = [0 for _ in self.bucket_bounds] self.nodes = SortedCollection(key=attrgetter("priority")) self.H = 0 self.time = 0 self.error_numer = 0 self.error_denom = 1 def update_bucket(self, bucket_ix, new_cost): # average of current contents bucket_frac = self.bucket_frac[bucket_ix] new_frac = (bucket_frac[0] + new_cost, bucket_frac[1] + 1) self.bucket_frac[bucket_ix] = new_frac self.bucket_costs[bucket_ix] = float(new_frac[0])/new_frac[1] return self.bucket_costs[bucket_ix] def touch(self, node): self.nodes.remove(node) b_cost = self.bucket_costs[node.bucket] node.cost = b_cost node.priority = (self.H + b_cost, self.time) self.time += 1 self.error_numer += abs(b_cost - node.real_c) self.error_denom += 1 self.nodes.insert(node) def add_node(self, value, cost): bucket_ix = bisect_left(self.bucket_bounds, cost) b_cost = self.update_bucket(bucket_ix, cost) new_node = PQNode(value, b_cost) new_node.priority = (self.H + b_cost, self.time) new_node.real_c = cost new_node.bucket = bucket_ix self.bucket_misses[bucket_ix] += cost self.time += 1 self.nodes.insert(new_node) self.error_numer += abs(b_cost - cost) self.error_denom += 1 return new_node def evict_node(self): to_evict = self.nodes[0] del self.nodes[0] self.H = to_evict.cost # update the bucket average bucket_ix = to_evict.bucket bucket_frac = self.bucket_frac[bucket_ix] new_frac = (bucket_frac[0] - to_evict.real_c, bucket_frac[1] - 1) self.bucket_frac[bucket_ix] = new_frac if(new_frac[1] == 0): self.bucket_costs[bucket_ix] = 0 else: self.bucket_costs[bucket_ix] = float(new_frac[0])/new_frac[1] self.bucket_evictions[to_evict.bucket] += to_evict.real_c return to_evict
class GD_PQ_Bucketing_Old(object): def __init__(self, bucket_bounds, name = None, **kwargs): if name: self.name = name else: self.name = self.__class__.__name__ self.bucket_bounds = bucket_bounds self.bucket_costs = [float(a + b)/2 for a,b in zip([0] + bucket_bounds[:-1], bucket_bounds)] self.bucket_frac = [(0,0) for _ in bucket_bounds] self.nodes = SortedCollection(key=self.objective_f) self.H = 0 self.time = 0 def objective_f(self, node): node_L, bucket_ix = node.cost return (self.bucket_costs[bucket_ix] + node_L, node.priority) # such an absurd objf. def update_ordering(self): # self.nodes.key = self.objective_f # will resort the collection. pass def update_bucket(self, bucket_ix, new_cost): # default:: just set bucket to newest. # self.bucket_costs[bucket_ix] = float(new_cost) # do a moving window? bucket_frac = self.bucket_frac[bucket_ix] new_frac = (bucket_frac[0] + new_cost, bucket_frac[1] + 1) self.bucket_frac[bucket_ix] = new_frac self.bucket_costs[bucket_ix] = float(new_frac[0])/new_frac[1] return self.bucket_costs[bucket_ix] def touch(self, node): self.nodes.remove(node) node.cost = (self.H, node.cost[1]) node.priority = self.time self.time += 1 self.nodes.insert(node) def add_node(self, value, cost): bucket_ix = bisect_left(self.bucket_bounds, cost) self.update_bucket(bucket_ix, cost) self.update_ordering() new_node = PQNode(value, (self.H, bucket_ix)) new_node.priority = self.time new_node.real_c = cost self.time += 1 self.nodes.insert(new_node) return new_node def evict_node(self): to_evict = self.nodes[0] del self.nodes[0] self.H = self.objective_f(to_evict)[0] # update the bucket average bucket_ix = to_evict.cost[1] bucket_frac = self.bucket_frac[bucket_ix] new_frac = (bucket_frac[0] - to_evict.real_c, bucket_frac[1] - 1) self.bucket_frac[bucket_ix] = new_frac if(new_frac[1] == 0): self.bucket_costs[bucket_ix] = 0 else: self.bucket_costs[bucket_ix] = float(new_frac[0])/new_frac[1] self.update_ordering() return to_evict