Beispiel #1
0
 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__()
Beispiel #2
0
 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)
Beispiel #3
0
 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)
Beispiel #4
0
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)
Beispiel #5
0
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()
Beispiel #6
0
    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
Beispiel #7
0
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)
Beispiel #8
0
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)
Beispiel #9
0
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)
Beispiel #10
0
 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
Beispiel #11
0
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)
Beispiel #12
0
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)
Beispiel #13
0
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)
Beispiel #14
0
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)
Beispiel #15
0
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
Beispiel #16
0
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