def test_missing_queue_priority(self): q = CustomPriorityQueue(sense=minimize, track_bound=True) node = Node() node.tree_depth = 0 node.bound = 0 assert node.queue_priority is None with pytest.raises(ValueError): q.put(node) node.queue_priority = 1 q.put(node) child = _new_child(node) assert child.queue_priority is None with pytest.raises(ValueError): q.put(child)
def test_usage_maximize(self): q = CustomPriorityQueue(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 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(10, 4, -1): node = Node(data_=q.get()) assert node.bound == -i assert node.queue_priority == i if i != 5: assert q.bound() == -5 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 test_missing_queue_priority(self): q = CustomPriorityQueue(minimize) node = Node(size=0) node.bound = 0 assert node.queue_priority is None with pytest.raises(ValueError): q.put(node._data) with pytest.raises(ValueError): q.put_get(node._data) node.queue_priority = 1 q.put(node._data) child = node.new_child() assert child.queue_priority is None with pytest.raises(ValueError): q.put(child._data) with pytest.raises(ValueError): q.put_get(child._data)
def test_usage_maximize(self): q = CustomPriorityQueue(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 node.queue_priority = i assert q.put(node) == i - 1 assert node.queue_priority == i 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(10, 4, -1): node = q.get() assert node.bound == -i assert node.queue_priority == i if i != 5: assert q.bound() == -5 else: assert q.bound() is None assert q.size() == 0 node = Node() node.bound = 0 node.queue_priority = 1 cnt_ = q.put(node) node_ = q.get() assert cnt_ == 10 assert node_ is node assert node.queue_priority == 1 assert q.bound() is None node.queue_priority = 2 cnt_ = q.put(node) assert node.queue_priority == 2 assert cnt_ == 11 assert q.bound() == 0 node2 = Node() node2.bound = 1 node2.queue_priority = 3 cnt_ = q.put(node2) node_ = q.get() assert cnt_ == 12 assert node_ is node2 assert node2.queue_priority == 3 assert q.bound() == 0 node2.queue_priority = 1 cnt_ = q.put(node2) node_ = q.get() assert node2.queue_priority == 1 assert cnt_ == 13 assert node_ is node assert q.size() == 1 assert q.bound() == 1 # no bound tracking q = CustomPriorityQueue(sense=maximize, track_bound=False) 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 node.queue_priority = i assert q.put(node) == i - 1 assert node.queue_priority == i items.append(node) assert q.size() == i assert q.bound() == inf _check_items(q, items) assert q.size() == 10 assert q.bound() == inf 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() == inf for i in range(10, 4, -1): node = q.get() assert node.bound == -i assert node.queue_priority == i if i != 5: assert q.bound() == inf else: assert q.bound() is None assert q.size() == 0
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)