def listen(self, root=0): """Listen for requests to run range reduction. All processes within the communicator, except for the root process, should call this method. Parameters ---------- root : int The rank of the process acting as the root. The root process should not call this function. """ assert self._comm.size > 1 assert self._comm.rank != root orig = Node() self.save_state(orig) node = Node() try: data = array.array('d', [0]) * 3 self._comm.Bcast([data, mpi4py.MPI.DOUBLE], root=root) again = bool(data[0]) self._best_objective = float(data[1]) while again: node.state = self._comm.bcast(node.state, root=root) self.load_state(node) self._tighten_bounds() self._comm.Bcast([data, mpi4py.MPI.DOUBLE], root=root) again = bool(data[0]) self._best_objective = float(data[1]) finally: self.load_state(orig)
def _logging_redirect_check(comm): opt = Solver(comm=comm) p = DummyProblem() log = logging.Logger(None, level=logging.WARNING) log.addHandler(_RedirectHandler(opt._disp)) if opt.is_dispatcher: assert (comm is None) or (comm.rank == 0) root = Node() p.save_state(root) root.objective = p.infeasible_objective() root.bound = p.unbounded_objective() initialize_queue = DispatcherQueueData(nodes=[root], worst_terminal_bound=None, sense=p.sense()) out = StringIO() formatter = logging.Formatter("[%(levelname)s] %(message)s") opt._disp.initialize( p.infeasible_objective(), None, initialize_queue, "bound", ConvergenceChecker(p.sense()), None, None, None, True, get_simple_logger(console=True, stream=out, level=logging.DEBUG, formatter=formatter), 0.0, True, ) log.debug("0: debug") log.info("0: info") log.warning("0: warning") log.error("0: error") log.critical("0: critical") if (comm is not None) and (comm.size > 1): opt._disp.serve() else: assert comm is not None if comm.size > 1: for i in range(1, comm.size): if comm.rank == i: log.debug(str(comm.rank) + ": debug") log.info(str(comm.rank) + ": info") log.warning(str(comm.rank) + ": warning") log.error(str(comm.rank) + ": error") log.critical(str(comm.rank) + ": critical") opt.worker_comm.Barrier() if opt.worker_comm.rank == 0: opt._disp.stop_listen() if comm is not None: comm.Barrier() if opt.is_dispatcher: assert ("\n".join( out.getvalue().splitlines()[7:])) == _get_logging_baseline( comm.size if comm is not None else 1)
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 _new_child(node): child = Node() child.objective = node.objective child.bound = node.bound child.tree_depth = node.tree_depth + 1 assert child.queue_priority is None assert child.state is None return child
def put_get(self, item): objective = Node._extract_objective(item) if self._sense == minimize: priority = -objective else: priority = objective Node._insert_queue_priority(item, priority) return super(BestObjectiveFirstPriorityQueue, self).put_get(item)
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 test_bad_best_options(self): node = Node() node.objective = None with pytest.raises(ValueError): solve(DummyProblem(), comm=None, best_node=node) node.objective = nan with pytest.raises(ValueError): solve(DummyProblem(), comm=None, best_node=node) node.objective = 0 solve(DummyProblem(), comm=None, best_node=node)
def test_overwrites_queue_priority(self): q = BestObjectiveFirstPriorityQueue(sense=minimize, track_bound=True) node = Node() node.tree_depth = 0 node.bound = -1 assert node.queue_priority is None node.objective = 1 assert q.put(node) == 0 assert node.objective == 1 assert node.queue_priority == -1 child = _new_child(node) assert child.objective == 1 child.objective = 0 assert child.queue_priority is None cnt = q.put(child) node_ = q.get() assert child.queue_priority == 0 assert cnt == 1 assert node_ is child child.objective = 2 cnt = q.put(child) node_ = q.get() assert child.queue_priority == -2 assert cnt == 2 assert node_ is node assert q.bound() == -1 q = BestObjectiveFirstPriorityQueue(sense=maximize, track_bound=True) node = Node() node.tree_depth = 0 node.bound = 3 assert node.queue_priority is None node.objective = 1 assert q.put(node) == 0 assert node.objective == 1 assert node.queue_priority == 1 child = _new_child(node) assert child.objective == 1 child.objective = 2 assert child.queue_priority is None cnt = q.put(child) node_ = q.get() assert child.queue_priority == 2 assert cnt == 1 assert node_ is child child.objective = 0 cnt = q.put(child) node_ = q.get() assert child.queue_priority == 0 assert cnt == 2 assert node_ is node assert q.bound() == 3
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_overwrites_queue_priority(self): q = FIFOQueue(minimize) node = Node(size=0) node.bound = 0 assert node.queue_priority is None assert q.put(node._data) == 0 assert node.queue_priority == 0 child = node.new_child() assert child.queue_priority is None assert q.put(child._data) == 1 assert child.queue_priority == -1 l1 = Node(size=0) l1.bound = 1 l2 = l1.new_child() l3 = l2.new_child() q = FIFOQueue(minimize) cnt, data = q.put_get(l2._data) assert cnt == 0 assert data is l2._data cnt = q.put(l2._data) assert cnt == 1 cnt, data_ = q.put_get(l3._data) assert cnt == 2 assert data_ is l2._data cnt, data_ = q.put_get(l1._data) assert cnt == 3 assert data_ is l3._data assert q.bound() == 1
def test_overwrites_queue_priority(self): q = LIFOQueue(sense=minimize, track_bound=True) node = Node() node.tree_depth = 0 node.bound = 0 assert node.queue_priority is None assert q.put(node) == 0 assert node.queue_priority == 0 child = _new_child(node) assert child.queue_priority is None assert q.put(child) == 1 assert child.queue_priority == 1 l1 = Node() l1.tree_depth = 0 l1.bound = 1 l2 = _new_child(l1) l3 = _new_child(l2) q = LIFOQueue(sense=minimize, track_bound=True) cnt = q.put(l2) node_ = q.get() assert cnt == 0 assert node_ is l2 cnt = q.put(l2) assert q.bound() == 1 assert cnt == 1 cnt = q.put(l3) node_ = q.get() assert cnt == 2 assert node_ is l3 node_ = q.get() assert node_ is l2 assert q.bound() is None
def test_overwrites_queue_priority(self): q = RandomPriorityQueue(minimize) node = Node(size=0) node.bound = 0 assert node.queue_priority is None assert q.put(node._data) == 0 assert node.queue_priority is not None assert 0 <= node.queue_priority <= 1 child = node.new_child() assert child.queue_priority is None assert q.put(child._data) == 1 assert child.queue_priority is not None assert 0 <= child.queue_priority <= 1 l1 = Node(size=0) l1.bound = 1 l2 = l1.new_child() l3 = l2.new_child() q = RandomPriorityQueue(minimize) assert l2.queue_priority is None cnt, data = q.put_get(l2._data) assert data is l2._data assert l2.queue_priority is not None assert 0 <= l2.queue_priority <= 1 assert cnt == 0 cnt = q.put(l2._data) assert cnt == 1 assert l3.queue_priority is None cnt, data_ = q.put_get(l3._data) assert cnt == 2 assert l3.queue_priority is not None assert 0 <= l3.queue_priority <= 1 assert data_ is max([l2, l3], key=lambda x_: x_.queue_priority)._data
def test_overwrites_queue_priority(self): q = DepthFirstPriorityQueue(minimize) node = Node(size=0) node.bound = 0 assert node.queue_priority is None assert q.put(node._data) == 0 assert node.tree_depth == 0 assert node.queue_priority == 0 child = node.new_child() assert child.tree_depth == 1 assert child.queue_priority is None assert q.put(child._data) == 1 assert child.queue_priority == child.tree_depth l1 = Node(size=0) l1.bound = 1 l2 = l1.new_child() l3 = l2.new_child() q = DepthFirstPriorityQueue(minimize) q.put(l2._data) cnt, data_ = q.put_get(l3._data) assert cnt == 1 assert data_ is l3._data cnt, data_ = q.put_get(l2._data) assert cnt == 2 assert data_ is l2._data assert q.bound() == 1
def test_str(self): node = Node() node.objective = -1 node.bound = -2 node.tree_depth = 3 node.queue_priority = (1, 2, 3) node.state = "a" assert (str(node) == """\ Node(objective=-1, bound=-2, tree_depth=3)""")
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 _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 test_init(self): node = Node() assert node.objective is None assert node.bound is None assert node.tree_depth is None assert node.queue_priority is None assert node.state is None
def test_str(self): node = Node() node.objective = -1 node.bound = -2 node.tree_depth = 3 node.queue_priority = (1,2,3) node.state = 'a' assert str(node) == \ """\
def test_overwrites_queue_priority(self): q = BestObjectiveFirstPriorityQueue(minimize) node = Node(size=0) node.bound = -1 assert node.queue_priority is None node.objective = 1 assert q.put(node._data) == 0 assert node.objective == 1 assert node.queue_priority == -1 child = node.new_child() assert child.objective == 1 child.objective = 0 assert child.queue_priority is None cnt, data_ = q.put_get(child._data) assert child.queue_priority == 0 assert cnt == 1 assert data_ is child._data child.objective = 2 cnt, data_ = q.put_get(child._data) assert child.queue_priority == -2 assert cnt == 2 assert data_ is node._data assert q.bound() == -1 q = BestObjectiveFirstPriorityQueue(maximize) node = Node(size=0) node.bound = 3 assert node.queue_priority is None node.objective = 1 assert q.put(node._data) == 0 assert node.objective == 1 assert node.queue_priority == 1 child = node.new_child() assert child.objective == 1 child.objective = 2 assert child.queue_priority is None cnt, data_ = q.put_get(child._data) assert child.queue_priority == 2 assert cnt == 1 assert data_ is child._data child.objective = 0 cnt, data_ = q.put_get(child._data) assert child.queue_priority == 0 assert cnt == 2 assert data_ is node._data assert q.bound() == 3
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_initialize_queue(self): node_limit = None time_limit = None log = get_simple_logger() log_interval_seconds = inf log_new_incumbent = True convergence_checker = ConvergenceChecker(minimize) root = Node(size=0) Node._insert_tree_id(root._data, 0) root.bound = convergence_checker.unbounded_objective root.objective = convergence_checker.infeasible_objective queue = DispatcherQueueData(nodes=[root], next_tree_id=1) disp = DispatcherLocal() disp.initialize(0, queue, 'bound', convergence_checker, node_limit, time_limit, log, log_interval_seconds, log_new_incumbent) assert disp.best_objective == 0 disp.initialize(1, queue, 'bound', convergence_checker, node_limit, time_limit, log, log_interval_seconds, log_new_incumbent) assert disp.best_objective == 1 root.objective = -1 disp.initialize(1, queue, 'bound', convergence_checker, node_limit, time_limit, log, log_interval_seconds, log_new_incumbent) assert disp.best_objective == -1
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 test_tree_id(self): node = Node() assert node.tree_id is None assert node.parent_tree_id is None Node._insert_tree_id(node._data, 1) assert node.tree_id == 1 assert node.parent_tree_id is None Node._insert_parent_tree_id(node._data, 2) assert node.tree_id == 1 assert node.parent_tree_id == 2 Node._clear_tree_id(node._data) assert node.tree_id is None assert node.parent_tree_id == 2 Node._clear_parent_tree_id(node._data) assert node.tree_id is None assert node.parent_tree_id is None
def bound(self): # tell the listeners to start bounds tightening node = Node() self.save_state(node) continue_loop = True while continue_loop: if (self._comm is not None) and (self._comm.size > 1): self._notify_continue_listen(node) continue_loop = self.range_reduction_process_bounds(*self._tighten_bounds()) self.save_state(node) return self.problem.bound()
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 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 test_state_update(self): node = Node(size=3) node.state[0] = 1.1 node.state[1] = 0.0 node.state[2] = 0.0 assert node.state[0] == 1.1 assert node.state[1] == 0 assert node.state[2] == 0 node.state[1:3] = [-1.0, 5.3] assert node.state[0] == 1.1 assert node.state[1] == -1.0 assert node.state[2] == 5.3 state = node.state node.resize(4) with pytest.raises(ValueError): state[0] = 1
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 save_dispatcher_queue(self): """Saves the current dispatcher queue. The result can be used to re-initialize a solve. Returns ------- queue_data : :class:`pybnb.dispatcher.DispatcherQueueData` An object storing information that can be used to re-initialize the dispatcher queue to its current state. """ return DispatcherQueueData(nodes=[ Node(data_=numpy.array(data, dtype=float)) for data in self.queue.items() ], next_tree_id=self.next_tree_id)
def test_new_child(self): node = Node() assert node.objective is None assert node.bound is None assert node.tree_depth is None assert node.queue_priority is None assert node.state is None node.tree_depth = 0 node = node.new_child() assert node.objective is None assert node.bound is None assert node.tree_depth == 1 assert node.queue_priority is None assert node.state is None node.objective = 1 node.bound = -1 node.queue_priority = 5 node.state = "a" node = node.new_child() assert node.objective == 1 assert node.bound == -1 assert node.tree_depth == 2 assert node.queue_priority is None assert node.state is None