def test_overwrites_queue_priority(self): q = WorstBoundFirstPriorityQueue(minimize) node = Node(size=0) node.bound = 1 assert node.queue_priority is None q.put(node._data) assert node.queue_priority == -1 q = WorstBoundFirstPriorityQueue(maximize) node = Node(size=0) node.bound = 1 assert node.queue_priority is None q.put(node._data) assert node.queue_priority == 1
def test_overwrites_queue_priority(self): q = WorstBoundFirstPriorityQueue(sense=minimize, track_bound=True) node = Node() node.bound = 1 assert node.queue_priority is None q.put(node) assert node.queue_priority == -1 q = WorstBoundFirstPriorityQueue(sense=maximize, track_bound=True) node = Node() node.bound = 1 assert node.queue_priority is None q.put(node) assert node.queue_priority == 1
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 test_usage_maximize(self): q = WorstBoundFirstPriorityQueue(sense=maximize, track_bound=True) 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() node.bound = -i assert node.queue_priority is None cnt_ = q.put(node) assert cnt_ == i - 1 assert node.queue_priority == node.bound items.append(node) assert q.size() == i assert q.bound() == -1 _check_items(q, items) assert q.size() == 10 assert q.bound() == -1 removed = q.filter(lambda n_: n_.bound <= -5) assert q.size() == 6 assert len(removed) == 4 for node_ in removed: assert node_.bound > -5 assert q.bound() == -5 for i in range(5, 11): node = 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() node.bound = 1 assert node.queue_priority is None cnt_ = q.put(node) node_ = q.get() assert cnt_ == 10 assert node_ is node assert node.queue_priority == 1 node.bound = 2 cnt_ = q.put(node) assert node.queue_priority == 2 assert cnt_ == 11 node2 = Node() node2.bound = 3 assert node2.queue_priority is None cnt_ = q.put(node2) node_ = q.get() assert cnt_ == 12 assert node_ is node2 assert node2.queue_priority == 3 node2.bound = 1 cnt_ = q.put(node2) node_ = q.get() assert node2.queue_priority == 1 assert cnt_ == 13 assert node_ is node assert q.size() == 1
def initialize(self, best_objective, initialize_queue, queue_strategy, converger, node_limit, time_limit, log, log_interval_seconds, log_new_incumbent): """Initialize the dispatcher for a new solve. Parameters ---------- best_objective : float The assumed best objective to start with. initialize_queue : :class:`pybnb.dispatcher.DispatcherQueueData` The initial queue. queue_strategy : :class:`QueueStrategy <pybnb.common.QueueStrategy>` Sets the strategy for prioritizing nodes in the central dispatcher queue. See the :class:`QueueStrategy <pybnb.common.QueueStrategy>` enum for the list of acceptable values. converger : :class:`pybnb.convergence_checker.ConvergenceChecker` The branch-and-bound convergence checker object. node_limit : int or None An integer representing the maximum number of nodes to processes before beginning to terminate the solve. If None, no node limit will be enforced. time_limit : float or None The maximum amount of time to spend processing nodes before beginning to terminate the solve. If None, no time limit will be enforced. log : ``logging.Logger`` A log object where solver output should be sent. log_interval_seconds : float The approximate maximum time (in seconds) between solver log updates. More time may pass between log updates if no updates have been received from any workers, and less time may pass if a new incumbent is found. log_new_incumbent : bool Controls whether updates to the best objective are logged immediately (overriding the log interval). Setting this to false can be useful when frequent updates to the incumbent are expected and the additional logging slows down the dispatcher. """ assert (node_limit is None) or \ ((node_limit > 0) and \ (node_limit == int(node_limit))) assert (time_limit is None) or \ (time_limit >= 0) self.start_time = self.clock() self.initialized = True self.log_new_incumbent = log_new_incumbent self.termination_condition = None self.converger = converger self.last_global_bound = self.converger.unbounded_objective self.best_objective = best_objective if queue_strategy == "bound": self.queue = WorstBoundFirstPriorityQueue(self.converger.sense) elif queue_strategy == "custom": self.queue = CustomPriorityQueue(self.converger.sense) elif queue_strategy == "objective": self.queue = BestObjectiveFirstPriorityQueue(self.converger.sense) elif queue_strategy == "fifo": self.queue = FIFOQueue(self.converger.sense) elif queue_strategy == "breadth": self.queue = BreadthFirstPriorityQueue(self.converger.sense) elif queue_strategy == "depth": self.queue = DepthFirstPriorityQueue(self.converger.sense) elif queue_strategy == "local_gap": self.queue = LocalGapPriorityQueue(self.converger.sense) elif queue_strategy == "random": self.queue = RandomPriorityQueue(self.converger.sense) else: raise ValueError("'queue_strategy' must be one of: %s" % (str([v.value for v in QueueStrategy]))) self.node_limit = None if node_limit is not None: self.node_limit = int(node_limit) self.time_limit = None if time_limit is not None: self.time_limit = float(time_limit) self.served_nodes_count = 0 self.worst_terminal_bound = None self.next_tree_id = initialize_queue.next_tree_id self.journalist = None if (log is not None) and (not log.disabled): self.journalist = StatusPrinter( self, log, log_interval_seconds=log_interval_seconds) if len(initialize_queue.nodes): self._check_update_best_objective( self.converger.best_objective( node.objective for node in initialize_queue.nodes)) for node in initialize_queue.nodes: assert node.tree_id is not None self._add_work_to_queue(node._data, set_tree_id=False)