def __init__(self, problem, node_limit=None, time_limit=5, queue_limit=None, track_bound=True, queue_strategy="depth"): # we import from pybnb.solver here to avoid a # circular import reference from pybnb.solver import Solver self._problem = problem self._node_limit = node_limit self._time_limit = time_limit self._queue_limit = queue_limit self._track_bound = track_bound self._queue_strategy = queue_strategy self._solver = Solver(comm=None) self._log = logging.Logger(None, level=logging.WARNING) self._convergence_checker = None self._best_objective = None self._best_node = None self._current_node = None self._results = None self._queue = None super(NestedSolver, self).__init__()
def test_bad_dispatcher_rank(comm): with pytest.raises(ValueError): Solver(comm=comm, dispatcher_rank=-1) with pytest.raises(ValueError): Solver(comm=comm, dispatcher_rank=comm.size) with pytest.raises(ValueError): Solver(comm=comm, dispatcher_rank=comm.size - 1.1) Solver(comm=comm, dispatcher_rank=comm.size - 1)
def test_bad_dispatcher_rank(self): with pytest.raises(ValueError): Solver(comm=None, dispatcher_rank=-1) with pytest.raises(ValueError): Solver(comm=None, dispatcher_rank=1) with pytest.raises(ValueError): Solver(comm=None, dispatcher_rank=1.1) Solver(comm=None, dispatcher_rank=0) Solver(comm=None)
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 _execute_single_test(problem, baseline, solver=None, comm=None, **kwds): if solver is None: solver = Solver(comm=comm) else: assert comm is None if solver.comm is not None: if solver.comm.rank == 0: pass elif solver.comm.rank == 1: pass elif solver.comm.rank == 3: pass orig = pybnb.node.Node() problem.save_state(orig) results = solver.solve(problem, **kwds) current = pybnb.node.Node() problem.save_state(current) assert len(current.state) == len(orig.state) for i in range(len(current.state)): assert current.state[i] == orig.state[i] assert len(vars(results)) > 0 assert len(vars(results)) == len(vars(baseline)) for name in sorted(sorted(list(vars(results).keys()))): if getattr(baseline, name) is _ignore_value_: continue if (name == 'nodes') and \ (solver.comm is not None) and \ (solver.comm.size > 2): pass else: assert getattr(results, name) == getattr(baseline, name), \ ("value for '"+str(name)+"' ("+ str(getattr(results, name))+") does " "not match baseline ("+ str(getattr(baseline, name))+")") if solver.is_dispatcher: q = solver.save_dispatcher_queue() assert len(q) == 2 assert q.next_tree_id >= 0 assert len(q.nodes) == solver._disp.queue.size()
def test_no_mpi(self): b = Solver(comm=None) assert b.comm is None assert b.worker_comm is None assert b.is_worker == True assert b.is_dispatcher == True assert b.worker_count == 1 b._reset_local_solve_stats() stats = b.collect_worker_statistics() assert len(stats) == 13 assert stats['rank'] == [0] assert stats['wall_time'] == [0] assert stats['queue_time'] == [0] assert stats['queue_call_count'] == [0] assert stats['objective_time'] == [0] assert stats['objective_call_count'] == [0] assert stats['bound_time'] == [0] assert stats['bound_call_count'] == [0] assert stats['branch_time'] == [0] assert stats['branch_call_count'] == [0] assert stats['load_state_time'] == [0] assert stats['load_state_call_count'] == [0] assert stats['explored_nodes_count'] == [0] out = \ """Number of Workers: 1 Load Imbalance: 0.00% Average Worker Timing: - queue: 0.00% [avg time: 0.0 s , count: 0] - load_state: 0.00% [avg time: 0.0 s , count: 0] - bound: 0.00% [avg time: 0.0 s , count: 0] - objective: 0.00% [avg time: 0.0 s , count: 0] - branch: 0.00% [avg time: 0.0 s , count: 0] - other: 0.00% [avg time: 0.0 s , count: 0] """ tmp = StringIO() summarize_worker_statistics(stats, stream=tmp) assert tmp.getvalue() == out
def _execute_tests(comm, problem, baseline, **kwds): assert 'log_interval_second' not in kwds assert 'log' not in kwds _execute_single_test(problem, baseline, comm=comm, **kwds) kwds['log_interval_seconds'] = 0.0 _execute_single_test(problem, baseline, comm=comm, **kwds) kwds['log_interval_seconds'] = 100.0 _execute_single_test(problem, baseline, comm=comm, **kwds) kwds['log_interval_seconds'] = 0.0 kwds['log'] = None _execute_single_test(problem, baseline, comm=comm, **kwds) kwds['log_interval_seconds'] = 100.0 _execute_single_test(problem, baseline, comm=comm, **kwds) solver = Solver(comm=comm) _execute_single_test(problem, baseline, solver=solver, **kwds) _execute_single_test(problem, baseline, solver=solver, **kwds) kwds['log'] = get_simple_logger(level="WARNING") _execute_single_test(problem, baseline, solver=solver, **kwds)
def _test_delayed_unbounded_min(comm): solver = Solver(comm=comm) baseline = SolverResults() baseline.solution_status = "unbounded" baseline.termination_condition = "no_nodes" baseline.objective = -inf baseline.bound = -inf baseline.nodes = _ignore_value_ baseline.wall_time = _ignore_value_ problem = delayed_unbounded_min.DelayedUnboundedMin() _execute_single_test(problem, baseline, solver=solver) for queue_strategy in sorted(pybnb.QueueStrategy): if queue_strategy == "custom": continue _execute_single_test(problem, baseline, solver=solver, queue_strategy=queue_strategy)
def _test_root_infeasible_min(comm): solver = Solver(comm=comm) baseline = SolverResults() baseline.solution_status = "infeasible" baseline.termination_condition = "optimality" baseline.objective = inf baseline.bound = inf baseline.nodes = 1 baseline.wall_time = _ignore_value_ problem = root_infeasible_min.RootInfeasibleMin() _execute_single_test(problem, baseline, solver=solver) for queue_strategy in sorted(pybnb.QueueStrategy): if queue_strategy == "custom": continue _execute_single_test(problem, baseline, solver=solver, queue_strategy=queue_strategy)
def test_solver_comm(comm): solver = Solver(comm=comm, dispatcher_rank=0) if comm.size > 1: if comm.rank == 0: assert solver.is_dispatcher assert not solver.is_worker assert solver.comm is comm assert solver.worker_comm is None else: assert not solver.is_dispatcher assert solver.is_worker assert solver.comm is comm assert solver.worker_comm is not None else: assert solver.is_dispatcher assert solver.is_worker assert solver.comm is comm assert solver.worker_comm is comm
def _logging_check(comm): opt = Solver(comm=comm) p = DummyProblem() 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() assert root.tree_id is None Node._insert_tree_id(root._data, 0) initialize_queue = DispatcherQueueData(nodes=[root], next_tree_id=1) out = StringIO() formatter = logging.Formatter("[%(levelname)s] %(message)s") opt._disp.initialize( p.infeasible_objective(), initialize_queue, "bound", ConvergenceChecker(p.sense()), None, None, get_simple_logger(console=True, stream=out, level=logging.DEBUG, formatter=formatter), 0.0, True) opt._disp.log_debug("0: debug") opt._disp.log_info("0: info") opt._disp.log_warning("0: warning") opt._disp.log_error("0: error") 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: opt._disp.log_debug(str(comm.rank) + ": debug") opt._disp.log_info(str(comm.rank) + ": info") opt._disp.log_warning(str(comm.rank) + ": warning") opt._disp.log_error(str(comm.rank) + ": error") 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()[8:])) == \ _get_logging_baseline(comm.size if comm is not None else 1)
def _test_infeasible_max(comm): solver = None if comm is not None: solver = Solver(comm=comm) baseline = SolverResults() baseline.solution_status = "infeasible" baseline.termination_condition = "optimality" baseline.objective = -inf baseline.bound = -inf baseline.nodes = 255 baseline.wall_time = _ignore_value_ problem = infeasible_max.InfeasibleMax() if comm is None: _execute_tests(comm, problem, baseline) else: _execute_single_test(problem, baseline, solver=solver) baseline = SolverResults() baseline.solution_status = "unknown" baseline.termination_condition = "no_nodes" baseline.objective = -inf baseline.bound = -16 baseline.nodes = 31 baseline.wall_time = _ignore_value_ problem = infeasible_max.InfeasibleMax(branching_abstol=0.1) if comm is None: _execute_tests(comm, problem, baseline) else: _execute_single_test(problem, baseline, solver=solver) baseline = SolverResults() baseline.solution_status = "unknown" baseline.termination_condition = "objective_limit" baseline.objective = -inf baseline.bound = -16 baseline.nodes = 31 baseline.wall_time = _ignore_value_ problem = infeasible_max.InfeasibleMax(branching_abstol=0.1) if comm is None: _execute_tests(comm, problem, baseline, bound_stop=-15) else: _execute_single_test(problem, baseline, solver=solver, bound_stop=-15) baseline = SolverResults() baseline.solution_status = "feasible" baseline.termination_condition = "objective_limit" baseline.objective = -20 baseline.bound = -16 baseline.absolute_gap = 4 baseline.relative_gap = 0.2 baseline.nodes = 31 baseline.wall_time = _ignore_value_ problem = infeasible_max.InfeasibleMax(branching_abstol=0.1, fixed_objective=-20) if comm is None: _execute_tests(comm, problem, baseline, bound_stop=-15) else: _execute_single_test(problem, baseline, solver=solver, bound_stop=-15) baseline = SolverResults() baseline.solution_status = "unknown" baseline.termination_condition = "node_limit" baseline.objective = -inf baseline.bound = -16 baseline.nodes = 31 baseline.wall_time = _ignore_value_ problem = infeasible_max.InfeasibleMax() if comm is None: _execute_tests(comm, problem, baseline, node_limit=31) elif comm.size <= 2: # skip for larger comm sizes # as the node_limit can lead to # a number of different outcomes _execute_single_test(problem, baseline, solver=solver, node_limit=31) baseline = SolverResults() baseline.solution_status = "feasible" baseline.termination_condition = "node_limit" baseline.objective = -17 baseline.bound = -16 baseline.absolute_gap = 1.0 baseline.relative_gap = 1.0 / 17 baseline.nodes = 31 baseline.wall_time = _ignore_value_ problem = infeasible_max.InfeasibleMax(fixed_objective=-17) if comm is None: _execute_tests(comm, problem, baseline, node_limit=31) elif comm.size <= 2: # skip for larger comm sizes # as the node_limit can lead to # a number of different outcomes _execute_single_test(problem, baseline, solver=solver, node_limit=31) baseline = SolverResults() baseline.solution_status = "unknown" baseline.termination_condition = "time_limit" baseline.objective = -inf baseline.bound = inf baseline.nodes = 0 baseline.wall_time = _ignore_value_ problem = infeasible_max.InfeasibleMax() if comm is None: _execute_tests(comm, problem, baseline, time_limit=0) else: _execute_single_test(problem, baseline, solver=solver, time_limit=0) if solver is None: solver = Solver(comm=comm) baseline = SolverResults() baseline.solution_status = "infeasible" baseline.termination_condition = "optimality" baseline.objective = -inf baseline.bound = -inf baseline.nodes = 255 baseline.wall_time = _ignore_value_ problem = infeasible_max.InfeasibleMax() for queue_strategy in sorted(pybnb.QueueStrategy): if queue_strategy == "custom": continue _execute_single_test(problem, baseline, solver=solver, queue_strategy=queue_strategy)
def _test_zero_objective_min(comm): solver = Solver(comm=comm) baseline = SolverResults() baseline.solution_status = "optimal" baseline.termination_condition = "optimality" baseline.objective = 0.0 baseline.bound = -0.0078125 baseline.absolute_gap = 0.0078125 baseline.relative_gap = 0.0078125 baseline.nodes = 255 baseline.wall_time = _ignore_value_ problem = zero_objective_min.ZeroObjectiveMin() _execute_single_test(problem, baseline, solver=solver, relative_gap=0.01, absolute_gap=0.01) _execute_single_test(problem, baseline, solver=solver, relative_gap=0.0, absolute_gap=0.01) _execute_single_test(problem, baseline, solver=solver, relative_gap=0.01, absolute_gap=0.0) baseline = SolverResults() baseline.solution_status = "optimal" baseline.termination_condition = "optimality" baseline.objective = 0.0 baseline.bound = -0.0009765625 baseline.absolute_gap = 0.0009765625 baseline.relative_gap = 0.0009765625 baseline.nodes = 2047 baseline.wall_time = _ignore_value_ problem = zero_objective_min.ZeroObjectiveMin() _execute_single_test(problem, baseline, solver=solver, relative_gap=0.001, absolute_gap=0.001) _execute_single_test(problem, baseline, solver=solver, relative_gap=0.0, absolute_gap=0.001) _execute_single_test(problem, baseline, solver=solver, relative_gap=0.001, absolute_gap=0.0) baseline = SolverResults() baseline.solution_status = "feasible" baseline.termination_condition = "no_nodes" baseline.objective = 0.0 baseline.bound = -0.0009765625 baseline.absolute_gap = 0.0009765625 baseline.relative_gap = 0.0009765625 baseline.nodes = 2047 baseline.wall_time = _ignore_value_ problem = zero_objective_min.ZeroObjectiveMin() _execute_single_test(problem, baseline, solver=solver, relative_gap=0.0001, absolute_gap=0.0001) _execute_single_test(problem, baseline, solver=solver, relative_gap=0.0, absolute_gap=0.0001) _execute_single_test(problem, baseline, solver=solver, relative_gap=0.0001, absolute_gap=0.0) baseline = SolverResults() baseline.solution_status = "optimal" baseline.termination_condition = "optimality" baseline.objective = 0.0 baseline.bound = -0.0078125 baseline.absolute_gap = 0.0078125 baseline.relative_gap = 0.0078125 baseline.nodes = None baseline.wall_time = _ignore_value_ problem = zero_objective_min.ZeroObjectiveMin() for queue_strategy in sorted(pybnb.QueueStrategy): if queue_strategy == "custom": continue if queue_strategy == "depth": baseline.nodes = 2033 elif queue_strategy == "random": baseline.nodes = _ignore_value_ else: baseline.nodes = 255 _execute_single_test(problem, baseline, solver=solver, relative_gap=0.01, absolute_gap=0.01, queue_strategy=queue_strategy)
class NestedSolver(_ProblemWithSolveInfoCollection): """A class for creating a nested branch-and-bound solve strategy. An instance of this class (wrapped around a standard problem) can be passed to the solver as the problem argument. Parameters ---------- problem : :class:`pybnb.Problem <pybnb.problem.Problem>` An object defining a branch-and-bound problem. node_limit : int, optional The same as the standard solver option, but applied to the nested solver to limit the number of nodes to explore when processing a work item. (default: None) time_limit : float, optional The same as the standard solver option, but applied to the nested solver to limit the amount of time spent processing a work item. (default: 5) queue_limit : int, optional The same as the standard solver option, but applied to the nested solver to limit the size of the queue. (default: None) track_bound : bool, optional The same as the standard solver option, but applied to the nested solver control bound tracking. (default: True) queue_strategy : :class:`QueueStrategy <pybnb.common.QueueStrategy>` or tuple The same as the standard solver option, but applied to the nested solver to control the queue strategy used when processing a work item. (default: 'depth') """ def __init__(self, problem, node_limit=None, time_limit=5, queue_limit=None, track_bound=True, queue_strategy="depth"): # we import from pybnb.solver here to avoid a # circular import reference from pybnb.solver import Solver self._problem = problem self._node_limit = node_limit self._time_limit = time_limit self._queue_limit = queue_limit self._track_bound = track_bound self._queue_strategy = queue_strategy self._solver = Solver(comm=None) self._log = logging.Logger(None, level=logging.WARNING) self._convergence_checker = None self._best_objective = None self._best_node = None self._current_node = None self._results = None self._queue = None super(NestedSolver, self).__init__() def _initialize(self, dispatcher, best_objective, disable_objective_call): assert best_objective is not None self._best_objective = best_objective self._disable_objective_call = disable_objective_call self._log.addHandler(_RedirectHandler(dispatcher)) def _solve(self): bound_stop = None if not math.isinf(self._best_objective): bound_stop = self._best_objective # shallow copy root = copy.copy(self._current_node) init_queue = DispatcherQueueData(nodes=[root], worst_terminal_bound=None, sense=self._convergence_checker.sense) self._results = self._solver.solve( self._problem, best_objective=self._best_objective, best_node=self._best_node, bound_stop=bound_stop, initialize_queue=init_queue, log=self._log, absolute_gap=\ self._convergence_checker.absolute_gap, relative_gap=\ self._convergence_checker.relative_gap, scale_function=\ self._convergence_checker.scale_function, queue_tolerance=\ self._convergence_checker.queue_tolerance, branch_tolerance=\ self._convergence_checker.branch_tolerance, comparison_tolerance=\ self._convergence_checker.comparison_tolerance, objective_stop=\ self._convergence_checker.objective_stop, disable_objective_call=self._disable_objective_call, node_limit=self._node_limit, time_limit=self._time_limit, queue_limit=self._queue_limit, track_bound=self._track_bound, queue_strategy=self._queue_strategy, disable_signal_handlers=True) self._queue = self._solver.save_dispatcher_queue() self._solve_info.add_from(self._solver._global_solve_info) # # Ducktype a partial Problem interface # def objective(self): #pragma:nocover """The solver does not call this method when it sees the problem implements a nested solve.""" raise NotImplementedError() def bound(self): #pragma:nocover """The solver does not call this method when it sees the problem implements a nested solve.""" raise NotImplementedError() def branch(self): #pragma:nocover """The solver does not call this method when it sees the problem implements a nested solve.""" raise NotImplementedError() def sense(self): """Calls the sense() method on the user-provided problem.""" return self._problem.sense() def save_state(self, node): """Calls the save_state() method on the user-provided problem.""" self._problem.save_state(node) def load_state(self, node): """Calls the load_state() method on the user-provided problem and prepares for a nested solve.""" self._problem.load_state(node) self._results = None self._children = None self._current_node = node self._solve_found_best_node = False def notify_solve_begins(self, comm, worker_comm, convergence_checker): """Calls the notify_solve_begins() method on the user-provided problem and prepares for a solve.""" self._best_objective = None self._best_node = None self._convergence_checker = convergence_checker self._current_node = None self._results = None self._queue = None self._problem.notify_solve_begins(comm, worker_comm, convergence_checker) def notify_new_best_node(self, node, current): """Calls the notify_new_best_node() method on the user-provided problem and stores the best node for use in the next nested solve.""" self._best_objective = self._convergence_checker.\ best_objective(self._best_objective, node.objective) self._best_node = node self._problem.notify_new_best_node(node, current) def notify_solve_finished(self, comm, worker_comm, results): """Calls the notify_solve_finished() method on the user-provided problem and does some final cleanup.""" while len(self._log.handlers) > 0: self._log.removeHandler(self._log.handlers[0]) self._problem.notify_solve_finished(comm, worker_comm, results)
def _test_initialize_queue(comm): solver = Solver(comm=comm) # no initial queue for sense in (minimize, maximize): problem = DummyProblem(sense) results = solver.solve(problem, queue_limit=0) assert results.solution_status == "unknown" assert results.termination_condition == "queue_limit" assert results.objective == (inf if (sense == minimize) else -inf) assert results.bound == (-inf if (sense == minimize) else inf) assert results.absolute_gap is None assert results.relative_gap is None assert results.nodes == 0 assert results.wall_time is not None assert results.best_node is None assert problem._notify_new_best_node_call_count == 0 assert solver._local_solve_info.explored_nodes_count == 0 problem = DummyProblem(sense) results = solver.solve(problem) assert results.solution_status == "optimal" assert results.termination_condition == "optimality" assert results.objective == 0 assert results.bound == 0 assert results.absolute_gap == 0 assert results.relative_gap == 0 assert results.nodes == 1 assert results.wall_time is not None assert results.best_node is not None assert results.best_node.objective == results.objective assert results.best_node.tree_depth == 0 if solver.is_worker: assert problem._notify_new_best_node_call_count == 1 assert problem._notify_new_best_node_args[0] is results.best_node if solver._local_solve_info.explored_nodes_count == 1: assert problem._notify_new_best_node_args[1] else: assert solver._local_solve_info.explored_nodes_count == 0 assert not problem._notify_new_best_node_args[1] else: assert problem._notify_new_best_node_call_count == 0 problem = DummyProblem(sense) results = solver.solve( problem, best_objective=(1 if (sense == minimize) else -1) ) assert results.solution_status == "optimal" assert results.termination_condition == "optimality" assert results.objective == 0 assert results.bound == 0 assert results.absolute_gap == 0 assert results.relative_gap == 0 assert results.nodes == 1 assert results.wall_time is not None assert results.best_node is not None assert results.best_node.objective == results.objective assert results.best_node.tree_depth == 0 if solver.is_worker: assert problem._notify_new_best_node_call_count == 1 assert problem._notify_new_best_node_args[0] is results.best_node if solver._local_solve_info.explored_nodes_count == 1: assert problem._notify_new_best_node_args[1] else: assert solver._local_solve_info.explored_nodes_count == 0 assert not problem._notify_new_best_node_args[1] else: assert problem._notify_new_best_node_call_count == 0 problem = DummyProblem(sense) results = solver.solve( problem, best_objective=(1 if (sense == minimize) else -1), disable_objective_call=True, ) assert results.solution_status == "feasible" assert results.termination_condition == "queue_empty" assert results.objective == (1 if (sense == minimize) else -1) assert results.bound == 0 assert results.absolute_gap == 1 assert results.relative_gap == 1 assert results.nodes == 1 assert results.wall_time is not None assert results.best_node is None assert problem._notify_new_best_node_call_count == 0 best_node_ = Node() best_node_.objective = 1 if (sense == minimize) else -1 best_node_._uuid = "abcd" problem = DummyProblem(sense) results = solver.solve( problem, best_node=best_node_, disable_objective_call=True ) assert results.solution_status == "feasible" assert results.termination_condition == "queue_empty" assert results.objective == best_node_.objective assert results.bound == 0 assert results.absolute_gap == 1 assert results.relative_gap == 1 assert results.nodes == 1 assert results.best_node._uuid == best_node_._uuid if (comm is None) or (comm.size == 1): assert results.best_node is best_node_ assert results.best_node.objective == results.objective if solver.is_worker: assert problem._notify_new_best_node_call_count == 1 assert problem._notify_new_best_node_args[0] is results.best_node if solver._local_solve_info.explored_nodes_count == 1: assert not problem._notify_new_best_node_args[1] else: assert solver._local_solve_info.explored_nodes_count == 0 assert not problem._notify_new_best_node_args[1] else: assert problem._notify_new_best_node_call_count == 0 best_node_ = Node() best_node_.objective = 1 if (sense == minimize) else -1 best_node_._uuid = "abcd" problem = DummyProblem(sense) results = solver.solve(problem, best_node=best_node_) assert results.solution_status == "optimal" assert results.termination_condition == "optimality" assert results.objective == 0 assert results.bound == 0 assert results.absolute_gap == 0 assert results.relative_gap == 0 assert results.nodes == 1 assert results.wall_time is not None assert results.best_node._uuid != best_node_._uuid if (comm is None) or (comm.size == 1): assert results.best_node is not best_node_ assert results.best_node.objective == results.objective assert results.best_node.tree_depth == 0 if solver.is_worker: assert problem._notify_new_best_node_call_count >= 1 assert problem._notify_new_best_node_args[0] is results.best_node if problem._notify_new_best_node_call_count == 2: assert problem._notify_new_best_node_args[1] else: assert problem._notify_new_best_node_args[0] else: assert problem._notify_new_best_node_call_count == 0 # empty initial queue queue = DispatcherQueueData(nodes=[], worst_terminal_bound=None, sense=minimize) for sense in (minimize, maximize): queue.sense = sense problem = DummyProblem(sense) results = solver.solve(problem, initialize_queue=queue) assert results.solution_status == "unknown" assert results.termination_condition == "queue_empty" assert results.objective == (inf if (sense == minimize) else -inf) assert results.bound == (-inf if (sense == minimize) else inf) assert results.absolute_gap is None assert results.relative_gap is None assert results.nodes == 0 assert results.wall_time is not None assert results.best_node is None assert problem._notify_new_best_node_call_count == 0 problem = DummyProblem(sense) results = solver.solve(problem, initialize_queue=queue, best_objective=0) assert results.solution_status == "feasible" assert results.termination_condition == "queue_empty" assert results.objective == 0 assert results.bound == (-inf if (sense == minimize) else inf) assert results.absolute_gap == inf assert results.relative_gap == inf assert results.nodes == 0 assert results.wall_time is not None assert results.best_node is None assert problem._notify_new_best_node_call_count == 0 problem = DummyProblem(sense) results = solver.solve( problem, initialize_queue=queue, best_objective=0, disable_objective_call=True, ) assert results.solution_status == "feasible" assert results.termination_condition == "queue_empty" assert results.objective == 0 assert results.bound == (-inf if (sense == minimize) else inf) assert results.absolute_gap == inf assert results.relative_gap == inf assert results.nodes == 0 assert results.wall_time is not None assert results.best_node is None assert problem._notify_new_best_node_call_count == 0 best_node_ = Node() best_node_.objective = 1 if (sense == minimize) else -1 best_node_._uuid = "abcd" problem = DummyProblem(sense) results = solver.solve( problem, initialize_queue=queue, best_objective=0, best_node=best_node_ ) assert results.solution_status == "feasible" assert results.termination_condition == "queue_empty" assert results.objective == 0 assert results.bound == (-inf if (sense == minimize) else inf) assert results.absolute_gap == inf assert results.relative_gap == inf assert results.nodes == 0 assert results.wall_time is not None assert results.best_node._uuid == best_node_._uuid if (comm is None) or (comm.size == 1): assert results.best_node is best_node_ if solver.is_worker: assert problem._notify_new_best_node_call_count == 1 assert problem._notify_new_best_node_args[0] is results.best_node assert solver._local_solve_info.explored_nodes_count == 0 assert not problem._notify_new_best_node_args[1] else: assert problem._notify_new_best_node_call_count == 0 best_node_ = Node() best_node_.objective = 1 if (sense == minimize) else -1 best_node_._uuid = "abcd" problem = DummyProblem(sense) results = solver.solve( problem, initialize_queue=queue, best_objective=(2 if (sense == minimize) else -2), best_node=best_node_, ) assert results.solution_status == "feasible" assert results.termination_condition == "queue_empty" assert results.objective == (1 if (sense == minimize) else -1) assert results.bound == (-inf if (sense == minimize) else inf) assert results.absolute_gap == inf assert results.relative_gap == inf assert results.nodes == 0 assert results.wall_time is not None assert results.best_node._uuid == best_node_._uuid if (comm is None) or (comm.size == 1): assert results.best_node is best_node_ if solver.is_worker: assert problem._notify_new_best_node_call_count == 1 assert problem._notify_new_best_node_args[0] is results.best_node assert solver._local_solve_info.explored_nodes_count == 0 assert not problem._notify_new_best_node_args[1] else: assert problem._notify_new_best_node_call_count == 0 # non-empty initial queue root = Node() root._uuid = "abcd" root.tree_depth = 0 root.objective = 0 root.bound = 0 queue = DispatcherQueueData(nodes=[root], worst_terminal_bound=None, sense=minimize) orig_objective = queue.nodes[0].objective for sense in (minimize, maximize): queue.sense = sense queue.nodes[0].objective = orig_objective problem = DummyProblem(sense) results = solver.solve(problem, initialize_queue=queue) assert results.solution_status == "optimal" assert results.termination_condition == "optimality" assert results.objective == 0 assert results.bound == 0 assert results.absolute_gap == 0 assert results.relative_gap == 0 assert results.nodes == 1 assert results.wall_time is not None assert results.best_node._uuid == root._uuid if (comm is None) or (comm.size == 1): assert results.best_node is root if solver.is_worker: assert problem._notify_new_best_node_call_count == 1 assert problem._notify_new_best_node_args[0] is results.best_node if solver._local_solve_info.explored_nodes_count == 1: assert problem._notify_new_best_node_args[1] else: assert solver._local_solve_info.explored_nodes_count == 0 assert not problem._notify_new_best_node_args[1] else: assert problem._notify_new_best_node_call_count == 0 queue.nodes[0].objective = orig_objective problem = DummyProblem(sense) results = solver.solve( problem, initialize_queue=queue, best_objective=(1 if (sense == minimize) else -1), ) assert results.solution_status == "optimal" assert results.termination_condition == "optimality" assert results.objective == 0 assert results.bound == 0 assert results.absolute_gap == 0 assert results.relative_gap == 0 assert results.nodes == 1 assert results.wall_time is not None assert results.best_node._uuid == root._uuid if (comm is None) or (comm.size == 1): assert results.best_node is root if solver.is_worker: assert problem._notify_new_best_node_call_count == 1 assert problem._notify_new_best_node_args[0] is results.best_node if solver._local_solve_info.explored_nodes_count == 1: assert problem._notify_new_best_node_args[1] else: assert solver._local_solve_info.explored_nodes_count == 0 assert not problem._notify_new_best_node_args[1] else: assert problem._notify_new_best_node_call_count == 0 queue.nodes[0].objective = inf if (sense == minimize) else -inf problem = DummyProblem(sense) results = solver.solve( problem, initialize_queue=queue, best_objective=(1 if (sense == minimize) else -1), ) assert results.solution_status == "optimal" assert results.termination_condition == "optimality" assert results.objective == 0 assert results.bound == 0 assert results.absolute_gap == 0 assert results.relative_gap == 0 assert results.nodes == 1 assert results.wall_time is not None assert results.best_node._uuid == root._uuid if (comm is None) or (comm.size == 1): assert results.best_node is root if solver.is_worker: assert problem._notify_new_best_node_call_count == 1 assert problem._notify_new_best_node_args[0] is results.best_node if solver._local_solve_info.explored_nodes_count == 1: assert problem._notify_new_best_node_args[1] else: assert solver._local_solve_info.explored_nodes_count == 0 assert not problem._notify_new_best_node_args[1] else: assert problem._notify_new_best_node_call_count == 0 queue.nodes[0].objective = inf if (sense == minimize) else -inf problem = DummyProblem(sense) results = solver.solve( problem, initialize_queue=queue, best_objective=(1 if (sense == minimize) else -1), disable_objective_call=True, ) assert results.solution_status == "feasible" assert results.termination_condition == "queue_empty" assert results.objective == (1 if (sense == minimize) else -1) assert results.bound == 0 assert results.absolute_gap == 1 assert results.relative_gap == 1 assert results.nodes == 1 assert results.wall_time is not None assert results.best_node is None assert problem._notify_new_best_node_call_count == 0 queue.nodes[0].objective = orig_objective
def test_solver_nocomm(): solver = Solver(comm=None) assert solver.is_worker assert solver.is_dispatcher assert solver.comm is None assert solver.worker_comm is None