def put_get(self, item): if self._queue.size() > 0: cnt_next, tmp_ = self._queue.next() assert type(cnt_next) is int if self._queue.requires_priority: if not Node._has_queue_priority(item): raise ValueError("A node queue priority is required") priority = Node._extract_queue_priority(item) cnt, item_ = self._queue.put_get(item, priority) else: cnt, item_ = self._queue.put_get(item) if item_ is not item: assert item_ is tmp_ bound = Node._extract_bound(item) assert not math.isnan(bound) bound_ = Node._extract_bound(item_) assert not math.isnan(bound_) if self._sense == maximize: self._sorted_by_bound.add((-bound, cnt, item)) self._sorted_by_bound.remove((-bound_, cnt_next, item_)) else: self._sorted_by_bound.add((bound, cnt, item)) self._sorted_by_bound.remove((bound_, cnt_next, item_)) else: if self._queue.requires_priority: if not Node._has_queue_priority(item): raise ValueError("A node queue priority is required") priority = Node._extract_queue_priority(item) cnt, item_ = self._queue.put_get(item, priority) else: cnt, item_ = self._queue.put_get(item) return cnt, item_
def test_usage_maximize(self): q = WorstBoundFirstPriorityQueue(maximize) assert q.size() == 0 assert q.bound() is None assert len(list(q.items())) == 0 assert q.get() is None items = [] for i in range(1, 11): node = Node(size=0) node.bound = -i assert node.queue_priority is None cnt_ = q.put(node._data) assert cnt_ == i - 1 assert node.queue_priority == node.bound items.append(node._data) assert q.size() == i assert q.bound() == -1 _check_items(q, items) assert q.size() == 10 assert q.bound() == -1 removed = q.filter(lambda data: \ Node._extract_bound(data) <= -5) assert q.size() == 6 assert len(removed) == 4 for data in removed: assert Node._extract_bound(data) > -5 assert q.bound() == -5 for i in range(5, 11): node = Node(data_=q.get()) assert node.bound == -i assert node.queue_priority == node.bound if i != 10: assert q.bound() == -i - 1 else: assert q.bound() is None assert q.size() == 0 node = Node(size=0) node.bound = 1 assert node.queue_priority is None cnt_, data = q.put_get(node._data) assert cnt_ == 10 assert data is node._data assert node.queue_priority == 1 node.bound = 2 cnt_ = q.put(node._data) assert node.queue_priority == 2 assert cnt_ == 11 node2 = Node(size=0) node2.bound = 3 assert node2.queue_priority is None cnt_, data = q.put_get(node2._data) assert cnt_ == 12 assert data is node2._data assert node2.queue_priority == 3 node2.bound = 1 cnt_, data = q.put_get(node2._data) assert node2.queue_priority == 1 assert cnt_ == 13 assert data is node._data assert q.size() == 1
def _get_work_to_send(self, dest): node_data = self._get_work_item() bound = Node._extract_bound(node_data) self.last_known_bound[dest] = bound self.external_bounds.add(bound) self.has_work.add(dest) return node_data
def put_get(self, item): bound = Node._extract_bound(item) if self._sense == minimize: priority = -bound else: priority = bound Node._insert_queue_priority(item, priority) return self._queue.put_get(item, priority)
def _get_gap(self, item): objective = Node._extract_objective(item) bound = Node._extract_bound(item) if self._sense == minimize: gap = objective - bound else: gap = bound - objective assert not math.isnan(gap) return gap
def filter(self, func): removed = [] for cnt, item in self._queue.filter(func, include_counters=True): removed.append(item) bound = Node._extract_bound(item) if self._sense == maximize: self._sorted_by_bound.remove((-bound, cnt, item)) else: self._sorted_by_bound.remove((bound, cnt, item)) return removed
def get(self): if self._queue.size() > 0: cnt, tmp_ = self._queue.next() assert type(cnt) is int item = self._queue.get() assert tmp_ is item bound = Node._extract_bound(item) if self._sense == maximize: self._sorted_by_bound.remove((-bound, cnt, item)) else: self._sorted_by_bound.remove((bound, cnt, item)) return item else: return None
def put(self, item): bound = Node._extract_bound(item) assert not math.isnan(bound) if self._queue.requires_priority: if not Node._has_queue_priority(item): raise ValueError("A node queue priority is required") priority = Node._extract_queue_priority(item) cnt = self._queue.put(item, priority) else: cnt = self._queue.put(item) if self._sense == maximize: self._sorted_by_bound.add((-bound, cnt, item)) else: self._sorted_by_bound.add((bound, cnt, item)) return cnt
def _check_update_best_objective(self, objective): updated = False if self.converger.objective_improved(objective, self.best_objective): updated = True if self.journalist is not None: self.journalist.new_objective(report=self.log_new_incumbent) self.best_objective = objective eligible_for_queue_ = self.converger.eligible_for_queue extract_bound_ = Node._extract_bound removed = self.queue.filter(lambda node_data_: eligible_for_queue_( extract_bound_(node_data_), objective)) for node_data in removed: self._check_update_worst_terminal_bound( Node._extract_bound(node_data)) return updated
def _add_work_to_queue(self, node_data, set_tree_id=True): if set_tree_id: assert not Node._has_tree_id(node_data) Node._insert_tree_id(node_data, self.next_tree_id) self.next_tree_id += 1 else: assert Node._has_tree_id(node_data) assert Node._extract_tree_id(node_data) < self.next_tree_id bound = Node._extract_bound(node_data) if self.converger.eligible_for_queue(bound, self.best_objective): self.queue.put(node_data) return True else: self._check_update_worst_terminal_bound(bound) return False
def test_usage_minimize(self): q = CustomPriorityQueue(minimize) assert q.size() == 0 assert q.bound() is None assert len(list(q.items())) == 0 assert q.get() is None items = [] for i in range(1, 11): node = Node(size=0) node.bound = i node.queue_priority = -i assert q.put(node._data) == i - 1 assert node.queue_priority == -i items.append(node._data) assert q.size() == i assert q.bound() == 1 _check_items(q, items) assert q.size() == 10 assert q.bound() == 1 removed = q.filter(lambda data: \ Node._extract_bound(data) >= 5) assert q.size() == 6 assert len(removed) == 4 for data in removed: assert Node._extract_bound(data) < 5 assert q.bound() == 5 for i in range(5, 11): node = Node(data_=q.get()) assert node.bound == i assert node.queue_priority == -i if i != 10: assert q.bound() == i + 1 else: assert q.bound() is None assert q.size() == 0 node = Node(size=0) node.bound = 0 node.queue_priority = 1 cnt_, data = q.put_get(node._data) assert cnt_ == 10 assert data is node._data assert node.queue_priority == 1 assert q.bound() is None node.queue_priority = 2 cnt_ = q.put(node._data) assert node.queue_priority == 2 assert cnt_ == 11 assert q.bound() == 0 node2 = Node(size=0) node2.bound = 1 node2.queue_priority = 3 cnt_, data = q.put_get(node2._data) assert cnt_ == 12 assert data is node2._data assert node2.queue_priority == 3 assert q.bound() == 0 node2.queue_priority = 1 cnt_, data = q.put_get(node2._data) assert node2.queue_priority == 1 assert cnt_ == 13 assert data is node._data assert q.size() == 1 assert q.bound() == 1
def bound(self): try: return Node._extract_bound(self._sorted_by_bound[0][2]) except IndexError: return None
def bound(self): try: return Node._extract_bound(self._queue.next()[1]) except IndexError: return None
def update(self, best_objective, previous_bound, solve_info, node_data_list): """Update local worker information. Parameters ---------- best_objective : float The current best objective value known to the worker. previous_bound : float The updated bound computed for the last node that was processed by the worker. solve_info : :class:`_SolveInfo` The most up-to-date worker solve information. node_data_list : list A list of node data arrays to add to the queue. Returns ------- solve_finished : bool Indicates if the dispatcher has terminated the solve. new_objective : float The best objective value known to the dispatcher. data : ``array.array`` or None If solve_finished is false, a data array representing a new node for the worker to process. Otherwise, a tuple containing the global bound, the termination condition string, and the number of explored nodes. """ assert self.initialized self._check_update_best_objective(best_objective) self.solve_info.data[:] = solve_info.data self.external_bound = None self.active_nodes = 0 if len(node_data_list): for node_data in node_data_list: self._add_work_to_queue(node_data, set_tree_id=True) else: if not self.first_update: self._check_update_worst_terminal_bound(previous_bound) self.first_update = False last_global_bound = self.last_global_bound self._check_convergence() if (self.queue.size() == 0) and \ (self.termination_condition is None): self.termination_condition = TerminationCondition.no_nodes if self.termination_condition is None: node_data = self._get_work_item() self.active_nodes = 1 self.external_bound = Node._extract_bound(node_data) if self.journalist is not None: force = (last_global_bound == \ self.converger.unbounded_objective) and \ (last_global_bound != \ self.last_global_bound) self.journalist.tic(force=force) return (False, self.best_objective, node_data) else: if self.journalist is not None: self.journalist.tic(force=True) self.journalist.log_info(self.journalist._lines) self.initialized = False return (True, self.best_objective, (self._get_current_bound(), self.termination_condition, self._get_final_solve_info()))