def __init__(self, comm): assert comm.size > 1 import mpi4py.MPI assert mpi4py.MPI.Is_initialized() super(DispatcherDistributed, self).__init__() self.clock = mpi4py.MPI.Wtime self.comm = comm # send rank of dispatcher to all workers self.dispatcher_rank = DispatcherProxy._init(self.comm, ProcessType.dispatcher) assert self.dispatcher_rank == self.comm.rank self.worker_ranks = [ i for i in range(self.comm.size) if i != self.comm.rank ] self.needs_work_queue = \ collections.deque([], len(self.worker_ranks)) self._solve_info_by_source = \ {i: _SolveInfo() for i in self.worker_ranks} self.last_known_bound = dict() self.external_bounds = SortedList() self.first_update = \ {_r: True for _r in self.worker_ranks} self.has_work = set() self._send_requests = None self.explored_nodes_count = 0
def __init__(self): super(DispatcherLocal, self).__init__() self.external_bound = None self.solve_info = _SolveInfo() self.first_update = None self.active_nodes = 0 self.clock = time.time
def update(self, best_objective, best_node, previous_bound, solve_info, node_list_): """A proxy to :func:`pybnb.dispatcher.Dispatcher.update`.""" if best_node is not None: best_node = _SerializedNode.to_slots(best_node) node_list = [] for node_ in node_list_: # make sure the user-defined queue_priority # can be safely marshaled assert (node_.queue_priority is None) or \ node_.queue_priority == marshal.loads( marshal.dumps(node_.queue_priority, config.MARSHAL_PROTOCOL_VERSION)) node_list.append(_SerializedNode.to_slots(node_)) data = marshal.dumps((best_objective, best_node, previous_bound, solve_info.data, node_list), config.MARSHAL_PROTOCOL_VERSION) self.comm.Send([data, mpi4py.MPI.BYTE], self.dispatcher_rank, tag=DispatcherAction.update) self.comm.Probe(status=self._status) assert not self._status.Get_error() tag = self._status.Get_tag() recv_size = self._status.Get_count(mpi4py.MPI.BYTE) data = bytearray(recv_size) recv_data(self.comm, self._status, datatype=mpi4py.MPI.BYTE, out=data) if tag == DispatcherResponse.nowork: if six.PY2: data_ = str(data) else: data_ = data (best_objective, best_node_slots, global_bound, termination_condition_int, solve_info_data) = marshal.loads(data_) best_node = None if best_node_slots is not None: best_node = _SerializedNode.restore_node(best_node_slots) solve_info = _SolveInfo() solve_info.data = array.array('d', solve_info_data) return (True, best_objective, best_node, (global_bound, _int_to_termination_condition[termination_condition_int], solve_info)) else: assert tag == DispatcherResponse.work if six.PY2: data_ = str(data) else: data_ = data (best_objective, best_node_slots, node_slots) = marshal.loads(data_) best_node = None if best_node_slots is not None: best_node = _SerializedNode.restore_node(best_node_slots) node = _SerializedNode.restore_node(node_slots) return False, best_objective, best_node, node
def test_add_from(self): info = _SolveInfo() info.explored_nodes_count = 1 info.total_queue_time = 2 info.queue_call_count = 3 info.total_objective_time = 4 info.objective_call_count = 5 info.total_bound_time = 6 info.bound_call_count = 7 info.total_branch_time = 8 info.branch_call_count = 9 info.total_load_state_time = 10 info.load_state_call_count = 11 info1 = _SolveInfo() info1.explored_nodes_count = -2 info1.total_queue_time = -3 info1.queue_call_count = -4 info1.total_objective_time = -5 info1.objective_call_count = -6 info1.total_bound_time = -7 info1.bound_call_count = -8 info1.total_branch_time = -9 info1.branch_call_count = -10 info1.total_load_state_time = -11 info1.load_state_call_count = -12 info.add_from(info1) assert info.explored_nodes_count == -1 assert info.total_queue_time == -1 assert info.queue_call_count == -1 assert info.total_objective_time == -1 assert info.objective_call_count == -1 assert info.total_bound_time == -1 assert info.bound_call_count == -1 assert info.total_branch_time == -1 assert info.branch_call_count == -1 assert info.total_load_state_time == -1 assert info.load_state_call_count == -1 with pytest.raises(TypeError): info.add_from(1)
def update(self, best_objective, previous_bound, solve_info, node_data_list): """A proxy to :func:`pybnb.dispatcher.Dispatcher.update`.""" size = 3 + _SolveInfo._data_size node_count = len(node_data_list) if node_count > 0: for node_data_ in node_data_list: size += 1 size += len(node_data_) if (self._update_buffer is None) or \ len(self._update_buffer) < size: self._update_buffer = numpy.empty(size, dtype=float) data = self._update_buffer data[0] = best_objective assert float(data[0]) == best_objective data[1] = previous_bound assert float(data[1]) == previous_bound data[2] = node_count assert data[2] == node_count assert int(data[2]) == int(node_count) data[3:(_SolveInfo._data_size)+3] = solve_info.data if node_count > 0: pos = _SolveInfo._data_size+3 for node_data in node_data_list: data[pos] = len(node_data) pos += 1 data[pos:pos+len(node_data)] = node_data pos += len(node_data) self.comm.Send([data,mpi4py.MPI.DOUBLE], self.dispatcher_rank, tag=DispatcherAction.update) self.comm.Probe(status=self._status) assert not self._status.Get_error() tag = self._status.Get_tag() if tag == DispatcherResponse.nowork: data = recv_data(self.comm, self._status) best_objective = float(data[0]) global_bound = float(data[1]) termination_condition = \ _int_to_termination_condition[int(data[2])] solve_info = _SolveInfo() solve_info.data[:] = data[3:] return (True, best_objective, (global_bound, termination_condition, solve_info)) else: assert tag == DispatcherResponse.work recv_size = self._status.Get_count(mpi4py.MPI.DOUBLE) if len(self._update_buffer) < recv_size: self._update_buffer = numpy.empty(recv_size, dtype=float) # Note that this function returns a node data # array that is a view on its own update # buffer. Thus, it assumes that the caller is no # longer using the node data view that was # returned when the next update is called # (because it will be corrupted). data = self._update_buffer[:recv_size] recv_data(self.comm, self._status, datatype=mpi4py.MPI.DOUBLE, out=data) best_objective = Node._extract_best_objective(data) return False, best_objective, data
def test_methods(self): info = _SolveInfo() assert info.explored_nodes_count == 0 assert info.total_queue_time == 0 assert info.queue_call_count == 0 assert info.total_objective_time == 0 assert info.objective_call_count == 0 assert info.total_bound_time == 0 assert info.bound_call_count == 0 assert info.total_branch_time == 0 assert info.branch_call_count == 0 assert info.total_load_state_time == 0 assert info.load_state_call_count == 0 info.explored_nodes_count += 1 assert info.explored_nodes_count == 1 assert info.total_queue_time == 0 assert info.queue_call_count == 0 assert info.total_objective_time == 0 assert info.objective_call_count == 0 assert info.total_bound_time == 0 assert info.bound_call_count == 0 assert info.total_branch_time == 0 assert info.branch_call_count == 0 assert info.total_load_state_time == 0 assert info.load_state_call_count == 0 info._increment_explored_nodes_stat(2) assert info.explored_nodes_count == 3 assert info.total_queue_time == 0 assert info.queue_call_count == 0 assert info.total_objective_time == 0 assert info.objective_call_count == 0 assert info.total_bound_time == 0 assert info.bound_call_count == 0 assert info.total_branch_time == 0 assert info.branch_call_count == 0 assert info.total_load_state_time == 0 assert info.load_state_call_count == 0 info.total_queue_time += 1.5 assert info.explored_nodes_count == 3 assert info.total_queue_time == 1.5 assert info.queue_call_count == 0 assert info.total_objective_time == 0 assert info.objective_call_count == 0 assert info.total_bound_time == 0 assert info.bound_call_count == 0 assert info.total_branch_time == 0 assert info.branch_call_count == 0 assert info.total_load_state_time == 0 assert info.load_state_call_count == 0 info.queue_call_count += 1 assert info.explored_nodes_count == 3 assert info.total_queue_time == 1.5 assert info.queue_call_count == 1 assert info.total_objective_time == 0 assert info.objective_call_count == 0 assert info.total_bound_time == 0 assert info.bound_call_count == 0 assert info.total_branch_time == 0 assert info.branch_call_count == 0 assert info.total_load_state_time == 0 assert info.load_state_call_count == 0 info._increment_queue_stat(2.0, 2) assert info.explored_nodes_count == 3 assert info.total_queue_time == 3.5 assert info.queue_call_count == 3 assert info.total_objective_time == 0 assert info.objective_call_count == 0 assert info.total_bound_time == 0 assert info.bound_call_count == 0 assert info.total_branch_time == 0 assert info.branch_call_count == 0 assert info.total_load_state_time == 0 assert info.load_state_call_count == 0 info.total_objective_time += 1.5 assert info.explored_nodes_count == 3 assert info.total_queue_time == 3.5 assert info.queue_call_count == 3 assert info.total_objective_time == 1.5 assert info.objective_call_count == 0 assert info.total_bound_time == 0 assert info.bound_call_count == 0 assert info.total_branch_time == 0 assert info.branch_call_count == 0 assert info.total_load_state_time == 0 assert info.load_state_call_count == 0 info.objective_call_count += 1 assert info.explored_nodes_count == 3 assert info.total_queue_time == 3.5 assert info.queue_call_count == 3 assert info.total_objective_time == 1.5 assert info.objective_call_count == 1 assert info.total_bound_time == 0 assert info.bound_call_count == 0 assert info.total_branch_time == 0 assert info.branch_call_count == 0 assert info.total_load_state_time == 0 assert info.load_state_call_count == 0 info._increment_objective_stat(2.0, 2) assert info.explored_nodes_count == 3 assert info.total_queue_time == 3.5 assert info.queue_call_count == 3 assert info.total_objective_time == 3.5 assert info.objective_call_count == 3 assert info.total_bound_time == 0 assert info.bound_call_count == 0 assert info.total_branch_time == 0 assert info.branch_call_count == 0 assert info.total_load_state_time == 0 assert info.load_state_call_count == 0 info.total_bound_time += 1.5 assert info.explored_nodes_count == 3 assert info.total_queue_time == 3.5 assert info.queue_call_count == 3 assert info.total_objective_time == 3.5 assert info.objective_call_count == 3 assert info.total_bound_time == 1.5 assert info.bound_call_count == 0 assert info.total_branch_time == 0 assert info.branch_call_count == 0 assert info.total_load_state_time == 0 assert info.load_state_call_count == 0 info.bound_call_count += 1 assert info.explored_nodes_count == 3 assert info.total_queue_time == 3.5 assert info.queue_call_count == 3 assert info.total_objective_time == 3.5 assert info.objective_call_count == 3 assert info.total_bound_time == 1.5 assert info.bound_call_count == 1 assert info.total_branch_time == 0 assert info.branch_call_count == 0 assert info.total_load_state_time == 0 assert info.load_state_call_count == 0 info._increment_bound_stat(2.0, 2) assert info.explored_nodes_count == 3 assert info.total_queue_time == 3.5 assert info.queue_call_count == 3 assert info.total_objective_time == 3.5 assert info.objective_call_count == 3 assert info.total_bound_time == 3.5 assert info.bound_call_count == 3 assert info.total_branch_time == 0 assert info.branch_call_count == 0 assert info.total_load_state_time == 0 assert info.load_state_call_count == 0 info.total_branch_time += 1.5 assert info.explored_nodes_count == 3 assert info.total_queue_time == 3.5 assert info.queue_call_count == 3 assert info.total_objective_time == 3.5 assert info.objective_call_count == 3 assert info.total_bound_time == 3.5 assert info.bound_call_count == 3 assert info.total_branch_time == 1.5 assert info.branch_call_count == 0 assert info.total_load_state_time == 0 assert info.load_state_call_count == 0 info.branch_call_count += 1 assert info.explored_nodes_count == 3 assert info.total_queue_time == 3.5 assert info.queue_call_count == 3 assert info.total_objective_time == 3.5 assert info.objective_call_count == 3 assert info.total_bound_time == 3.5 assert info.bound_call_count == 3 assert info.total_branch_time == 1.5 assert info.branch_call_count == 1 assert info.total_load_state_time == 0 assert info.load_state_call_count == 0 info._increment_branch_stat(2.0, 2) assert info.explored_nodes_count == 3 assert info.total_queue_time == 3.5 assert info.queue_call_count == 3 assert info.total_objective_time == 3.5 assert info.objective_call_count == 3 assert info.total_bound_time == 3.5 assert info.bound_call_count == 3 assert info.total_branch_time == 3.5 assert info.branch_call_count == 3 assert info.total_load_state_time == 0 assert info.load_state_call_count == 0 info.total_load_state_time += 1.5 assert info.explored_nodes_count == 3 assert info.total_queue_time == 3.5 assert info.queue_call_count == 3 assert info.total_objective_time == 3.5 assert info.objective_call_count == 3 assert info.total_bound_time == 3.5 assert info.bound_call_count == 3 assert info.total_branch_time == 3.5 assert info.branch_call_count == 3 assert info.total_load_state_time == 1.5 assert info.load_state_call_count == 0 info.load_state_call_count += 1 assert info.explored_nodes_count == 3 assert info.total_queue_time == 3.5 assert info.queue_call_count == 3 assert info.total_objective_time == 3.5 assert info.objective_call_count == 3 assert info.total_bound_time == 3.5 assert info.bound_call_count == 3 assert info.total_branch_time == 3.5 assert info.branch_call_count == 3 assert info.total_load_state_time == 1.5 assert info.load_state_call_count == 1 info._increment_load_state_stat(2.0, 2) assert info.explored_nodes_count == 3 assert info.total_queue_time == 3.5 assert info.queue_call_count == 3 assert info.total_objective_time == 3.5 assert info.objective_call_count == 3 assert info.total_bound_time == 3.5 assert info.bound_call_count == 3 assert info.total_branch_time == 3.5 assert info.branch_call_count == 3 assert info.total_load_state_time == 3.5 assert info.load_state_call_count == 3 info.reset() assert info.explored_nodes_count == 0 assert info.total_queue_time == 0 assert info.queue_call_count == 0 assert info.total_objective_time == 0 assert info.objective_call_count == 0 assert info.total_bound_time == 0 assert info.bound_call_count == 0 assert info.total_branch_time == 0 assert info.branch_call_count == 0 assert info.total_load_state_time == 0 assert info.load_state_call_count == 0
def test__SimpleSolveInfoCollector(self): class Junk(Problem): def __init__(self): self.d = {} self.d['sense'] = False self.d['objective'] = False self.d['bound'] = False self.d['save_state'] = False self.d['load_state'] = False self.d['branch'] = False self.d['notify_solve_begins'] = False self.d['notify_new_best_node'] = False self.d['notify_solve_finished'] = False def sense(self): self.d['sense'] = True return -1 def objective(self): time.sleep(0.01) self.d['objective'] = True return -2 def bound(self): time.sleep(0.01) self.d['bound'] = True return -3 def save_state(self, node): self.d['save_state'] = True def load_state(self, node): time.sleep(0.01) self.d['load_state'] = True def branch(self): time.sleep(0.01) self.d['branch'] = True return () def notify_solve_begins(self, comm, worker_comm, convergence_checker): self.d['notify_solve_begins'] = True def notify_new_best_node(self, node, current): self.d['notify_new_best_node'] = True def notify_solve_finished(self, comm, worker_comm, results): self.d['notify_solve_finished'] = True j = Junk() assert j.d['sense'] == False assert j.d['objective'] == False assert j.d['bound'] == False assert j.d['save_state'] == False assert j.d['load_state'] == False assert j.d['branch'] == False assert j.d['notify_solve_begins'] == False assert j.d['notify_new_best_node'] == False assert j.d['notify_solve_finished'] == False p = _SimpleSolveInfoCollector(j) p.set_clock(time.time) info = _SolveInfo() assert info.explored_nodes_count == 0 assert info.total_queue_time == 0 assert info.queue_call_count == 0 assert info.total_objective_time == 0 assert info.objective_call_count == 0 assert info.total_bound_time == 0 assert info.bound_call_count == 0 assert info.total_branch_time == 0 assert info.branch_call_count == 0 p.set_solve_info_object(info) assert j.d['sense'] == False assert j.d['objective'] == False assert j.d['bound'] == False assert j.d['save_state'] == False assert j.d['load_state'] == False assert j.d['branch'] == False assert j.d['notify_solve_begins'] == False assert j.d['notify_new_best_node'] == False assert j.d['notify_solve_finished'] == False assert info.explored_nodes_count == 0 assert info.total_queue_time == 0 assert info.queue_call_count == 0 assert info.total_objective_time == 0 assert info.objective_call_count == 0 assert info.total_bound_time == 0 assert info.bound_call_count == 0 assert info.total_branch_time == 0 assert info.branch_call_count == 0 assert info.total_load_state_time == 0 assert info.load_state_call_count == 0 p.sense() assert j.d['sense'] == True assert j.d['objective'] == False assert j.d['bound'] == False assert j.d['save_state'] == False assert j.d['load_state'] == False assert j.d['branch'] == False assert j.d['notify_solve_begins'] == False assert j.d['notify_new_best_node'] == False assert j.d['notify_solve_finished'] == False assert info.explored_nodes_count == 0 assert info.total_queue_time == 0 assert info.queue_call_count == 0 assert info.total_objective_time == 0 assert info.objective_call_count == 0 assert info.total_bound_time == 0 assert info.bound_call_count == 0 assert info.total_branch_time == 0 assert info.branch_call_count == 0 assert info.total_load_state_time == 0 assert info.load_state_call_count == 0 p.objective() assert j.d['sense'] == True assert j.d['objective'] == True assert j.d['bound'] == False assert j.d['save_state'] == False assert j.d['load_state'] == False assert j.d['branch'] == False assert j.d['notify_solve_begins'] == False assert j.d['notify_new_best_node'] == False assert j.d['notify_solve_finished'] == False assert info.explored_nodes_count == 0 assert info.total_queue_time == 0 assert info.queue_call_count == 0 assert info.total_objective_time > 0 assert info.objective_call_count == 1 assert info.total_bound_time == 0 assert info.bound_call_count == 0 assert info.total_branch_time == 0 assert info.branch_call_count == 0 assert info.total_load_state_time == 0 assert info.load_state_call_count == 0 p.bound() assert j.d['sense'] == True assert j.d['objective'] == True assert j.d['bound'] == True assert j.d['save_state'] == False assert j.d['load_state'] == False assert j.d['branch'] == False assert j.d['notify_solve_begins'] == False assert j.d['notify_new_best_node'] == False assert j.d['notify_solve_finished'] == False assert info.explored_nodes_count == 0 assert info.total_queue_time == 0 assert info.queue_call_count == 0 assert info.total_objective_time > 0 assert info.objective_call_count == 1 assert info.total_bound_time > 0 assert info.bound_call_count == 1 assert info.total_branch_time == 0 assert info.branch_call_count == 0 assert info.total_load_state_time == 0 assert info.load_state_call_count == 0 p.save_state(None) assert j.d['sense'] == True assert j.d['objective'] == True assert j.d['bound'] == True assert j.d['save_state'] == True assert j.d['load_state'] == False assert j.d['branch'] == False assert j.d['notify_solve_begins'] == False assert j.d['notify_new_best_node'] == False assert j.d['notify_solve_finished'] == False assert info.explored_nodes_count == 0 assert info.total_queue_time == 0 assert info.queue_call_count == 0 assert info.total_objective_time > 0 assert info.objective_call_count == 1 assert info.total_bound_time > 0 assert info.bound_call_count == 1 assert info.total_branch_time == 0 assert info.branch_call_count == 0 assert info.total_load_state_time == 0 assert info.load_state_call_count == 0 p.load_state(None) assert j.d['sense'] == True assert j.d['objective'] == True assert j.d['bound'] == True assert j.d['save_state'] == True assert j.d['load_state'] == True assert j.d['branch'] == False assert j.d['notify_solve_begins'] == False assert j.d['notify_new_best_node'] == False assert j.d['notify_solve_finished'] == False assert info.explored_nodes_count == 0 assert info.total_queue_time == 0 assert info.queue_call_count == 0 assert info.total_objective_time > 0 assert info.objective_call_count == 1 assert info.total_bound_time > 0 assert info.bound_call_count == 1 assert info.total_branch_time == 0 assert info.branch_call_count == 0 assert info.total_load_state_time > 0 assert info.load_state_call_count == 1 list(p.branch()) assert j.d['sense'] == True assert j.d['objective'] == True assert j.d['bound'] == True assert j.d['save_state'] == True assert j.d['load_state'] == True assert j.d['branch'] == True assert j.d['notify_solve_begins'] == False assert j.d['notify_new_best_node'] == False assert j.d['notify_solve_finished'] == False assert info.explored_nodes_count == 0 assert info.total_queue_time == 0 assert info.queue_call_count == 0 assert info.total_objective_time > 0 assert info.objective_call_count == 1 assert info.total_bound_time > 0 assert info.bound_call_count == 1 assert info.total_branch_time > 0 assert info.branch_call_count == 1 assert info.total_load_state_time > 0 assert info.load_state_call_count == 1 p.notify_solve_begins(None, None, None) assert j.d['sense'] == True assert j.d['objective'] == True assert j.d['bound'] == True assert j.d['save_state'] == True assert j.d['load_state'] == True assert j.d['branch'] == True assert j.d['notify_solve_begins'] == True assert j.d['notify_new_best_node'] == False assert j.d['notify_solve_finished'] == False assert info.explored_nodes_count == 0 assert info.total_queue_time == 0 assert info.queue_call_count == 0 assert info.total_objective_time > 0 assert info.objective_call_count == 1 assert info.total_bound_time > 0 assert info.bound_call_count == 1 assert info.total_branch_time > 0 assert info.branch_call_count == 1 assert info.total_load_state_time > 0 assert info.load_state_call_count == 1 p.notify_new_best_node(None, None) assert j.d['sense'] == True assert j.d['objective'] == True assert j.d['bound'] == True assert j.d['save_state'] == True assert j.d['load_state'] == True assert j.d['branch'] == True assert j.d['notify_solve_begins'] == True assert j.d['notify_new_best_node'] == True assert j.d['notify_solve_finished'] == False assert info.explored_nodes_count == 0 assert info.total_queue_time == 0 assert info.queue_call_count == 0 assert info.total_objective_time > 0 assert info.objective_call_count == 1 assert info.total_bound_time > 0 assert info.bound_call_count == 1 assert info.total_branch_time > 0 assert info.branch_call_count == 1 assert info.total_load_state_time > 0 assert info.load_state_call_count == 1 p.notify_solve_finished(None, None, None) assert j.d['sense'] == True assert j.d['objective'] == True assert j.d['bound'] == True assert j.d['save_state'] == True assert j.d['load_state'] == True assert j.d['branch'] == True assert j.d['notify_solve_begins'] == True assert j.d['notify_new_best_node'] == True assert j.d['notify_solve_finished'] == True assert info.explored_nodes_count == 0 assert info.total_queue_time == 0 assert info.queue_call_count == 0 assert info.total_objective_time > 0 assert info.objective_call_count == 1 assert info.total_bound_time > 0 assert info.bound_call_count == 1 assert info.total_branch_time > 0 assert info.branch_call_count == 1 assert info.total_load_state_time > 0 assert info.load_state_call_count == 1
def _get_final_solve_info(self): solve_info = _SolveInfo() for worker_solve_info in self._solve_info_by_source.values(): solve_info.add_from(worker_solve_info) return solve_info
def _get_final_solve_info(self): """Get the final solve information""" solve_info = _SolveInfo() solve_info.data[:] = self.solve_info.data return solve_info
def serve(self): """Start listening for distributed branch-and-bound commands and map them to commands in the local dispatcher interface.""" def rebuild_update_requests(size): update_requests = {} update_data = bytearray(size) for i in self.worker_ranks: update_requests[i] = self.comm.Recv_init( update_data, source=i, tag=DispatcherAction.update ) return update_requests, update_data update_requests = None solve_info_ = _SolveInfo() data = None msg = Message(self.comm) while 1: msg.probe() tag = msg.tag source = msg.source if tag == DispatcherAction.update: size = msg.status.Get_count(datatype=mpi4py.MPI.BYTE) if (data is None) or (len(data) < size): update_requests, data = rebuild_update_requests(size) req = update_requests[msg.status.Get_source()] req.Start() req.Wait() if six.PY2: data_ = str(data) else: data_ = data ( best_objective, best_node, terminal_bound, solve_info_data, node_list, ) = marshal.loads(data_) solve_info_.data = array.array("d", solve_info_data) if best_node is not None: best_node = _SerializedNode(best_node) node_list = [_SerializedNode(state) for state in node_list] ret = self.update( best_objective, best_node, terminal_bound, solve_info_, node_list, source, ) stop = ret[0] if stop: best_node = ret[2] if best_node is not None: best_node = _SerializedNode.restore_node(best_node.slots) return ( ret[1], # best_objective best_node, ret[3][0], # global_bound ret[3][1], # termination_condition ret[3][2], ) # global_solve_info elif tag == DispatcherAction.log_info: msg.recv(mpi4py.MPI.CHAR) self.log_info(msg.data) elif tag == DispatcherAction.log_warning: msg.recv(mpi4py.MPI.CHAR) self.log_warning(msg.data) elif tag == DispatcherAction.log_debug: msg.recv(mpi4py.MPI.CHAR) self.log_debug(msg.data) elif tag == DispatcherAction.log_error: msg.recv(mpi4py.MPI.CHAR) self.log_error(msg.data) elif tag == DispatcherAction.log_critical: msg.recv(mpi4py.MPI.CHAR) self.log_critical(msg.data) elif tag == DispatcherAction.stop_listen: msg.recv() assert msg.data is None return (None, None, None, None, None) else: # pragma:nocover raise RuntimeError( "Dispatcher received invalid " "message tag '%s' from rank '%s'" % (tag, source) )
def serve(self): """Start listening for distributed branch-and-bound commands and map them to commands in the local dispatcher interface.""" def rebuild_update_requests(size): update_requests = {} # Note: The code below relies on the fact that # this is an array.array type and _not_ a # numpy.array type. It issumes a copy of # the data is made when a slice is # created. update_data = array.array('d', [0]) * size for i in self.worker_ranks: update_requests[i] = self.comm.Recv_init( update_data, source=i, tag=DispatcherAction.update) return update_requests, update_data update_requests = None data = None solve_info_ = _SolveInfo() msg = Message(self.comm) while (1): msg.probe() tag = msg.tag source = msg.source if tag == DispatcherAction.update: size = msg.status.Get_count(datatype=mpi4py.MPI.DOUBLE) if (data is None) or \ (len(data) < size): update_requests, data = \ rebuild_update_requests(size) req = update_requests[msg.status.Get_source()] req.Start() req.Wait() best_objective = float(data[0]) previous_bound = float(data[1]) assert int(data[2]) == data[2] nodes_receiving_count = int(data[2]) solve_info_.data[:] = data[3:(_SolveInfo._data_size + 3)] if nodes_receiving_count > 0: pos = 3 + _SolveInfo._data_size node_data_list = [] for i in range(nodes_receiving_count): assert int(data[pos]) == data[pos] data_size = int(data[pos]) pos += 1 node_data_list.append(data[pos:pos + data_size]) pos += data_size else: node_data_list = () ret = self.update(best_objective, previous_bound, solve_info_, node_data_list, source) stop = ret[0] if stop: return ( ret[1], # best_objective ret[2][0], # global_bound ret[2][1], # termination_condition ret[2][2]) # global_solve_info elif tag == DispatcherAction.log_info: msg.recv(mpi4py.MPI.CHAR) self.log_info(msg.data) elif tag == DispatcherAction.log_warning: msg.recv(mpi4py.MPI.CHAR) self.log_warning(msg.data) elif tag == DispatcherAction.log_debug: msg.recv(mpi4py.MPI.CHAR) self.log_debug(msg.data) elif tag == DispatcherAction.log_error: msg.recv(mpi4py.MPI.CHAR) self.log_error(msg.data) elif tag == DispatcherAction.stop_listen: msg.recv() assert msg.data is None return (None, None, None, None) else: #pragma:nocover raise RuntimeError("Dispatcher received invalid " "message tag '%s' from rank '%s'" % (tag, source))
def __init__(self, comm=_notset, dispatcher_rank=0): mpi = True if comm is None: mpi = False self._comm = None self._worker_flag = None self._dispatcher_flag = None self._disp = None self._time = None if mpi: import mpi4py.MPI assert mpi4py.MPI.Is_initialized() assert comm is not None if comm is _notset: comm = mpi4py.MPI.COMM_WORLD if (int(dispatcher_rank) != dispatcher_rank) or \ (dispatcher_rank < 0) or \ (dispatcher_rank >= comm.size): raise ValueError("The 'dispatcher_rank' keyword " "has been set to %s, which is not " "an available rank given the " "size of the MPI communicator (%d)." % (dispatcher_rank, comm.size)) self._comm = comm if comm.size > 1: dispatcher_rank = int(dispatcher_rank) if comm.rank == dispatcher_rank: self._disp = DispatcherDistributed(comm) self._worker_flag = False self._dispatcher_flag = True else: self._disp = DispatcherProxy(comm) self._worker_flag = True self._dispatcher_flag = False else: self._disp = DispatcherLocal() self._worker_flag = True self._dispatcher_flag = True self._time = mpi4py.MPI.Wtime else: if dispatcher_rank != 0: raise ValueError( "MPI functionality has been disabled but " "the 'dispatcher_rank' keyword is set to " "something other than 0.") assert self._comm is None self._disp = DispatcherLocal() self._worker_flag = True self._dispatcher_flag = True self._time = time.time assert self._worker_flag in (True, False) assert self._dispatcher_flag in (True, False) assert self._disp is not None assert self._time is not None self._solve_start = None self._wall_time = 0.0 self._best_objective = None self._local_solve_info = _SolveInfo() self._global_solve_info = None