Source code for pybnb.solver_results

"""
Branch-and-bound solver results object.

Copyright by Gabriel A. Hackebeil (gabe.hackebeil@gmail.com).
"""

# recognized pytest-doctestplus plugin,
# not the standard doctest
__doctest_requires__ = {'SolverResults.write': ['yaml']}

import sys
import base64

from pybnb.common import (SolutionStatus,
                          TerminationCondition)
from pybnb.misc import (time_format,
                        as_stream)
from pybnb.node import dumps

import six

[docs]class SolverResults(object): """Stores the results of a branch-and-bound solve. Attributes ---------- solution_status : string The solution status will be set to one of the strings documented by the :class:`SolutionStatus <pybnb.common.SolutionStatus>` enum. termination_condition : string The solve termination condition, as determined by the dispatcher, will be set to one of the strings documented by the :class:`TerminationCondition <pybnb.common.TerminationCondition>` enum. objective : float The best objective found. bound : float The global optimality bound. absolute_gap : float or None The absolute gap between the objective and bound. This will only be set when the solution status sf "optimal" or "feasible"; otherwise, it will be None. relative_gap : float or None The relative gap between the objective and bound. This will only be set when the solution status sf "optimal" or "feasible"; otherwise, it will be None. nodes : int The total number of nodes processes by all workers. wall_time : float The process-local wall time (seconds). This is the only value on the results object that varies between processes. best_node : :class:`Node <pybnb.node.Node>` The node with the best objective obtained during the solve. Note that if the best_objective solver option was used, the best_node on the results object may have an objective that is worse than the objective stored on the results (or may be None). """ def __init__(self): self.solution_status = None self.termination_condition = None self.objective = None self.bound = None self.absolute_gap = None self.relative_gap = None self.nodes = None self.wall_time = None self.best_node = None
[docs] def pprint(self, stream=sys.stdout): """Prints a nicely formatted representation of the results. Parameters ---------- stream : file-like object or string, optional A file-like object or a filename where results should be written to. (default: ``sys.stdout``) """ with as_stream(stream) as stream: stream.write("solver results:\n") self.write(stream, prefix=" - ", pretty=True)
[docs] def write(self, stream, prefix="", pretty=False): """Writes results in YAML format to a stream or file. Changing the parameter values from their defaults may result in the output becoming non-compatible with the YAML format. Parameters ---------- stream : file-like object or string A file-like object or a filename where results should be written to. prefix : string, optional A string to use as a prefix for each line that is written. (default: '') pretty : bool, optional Indicates whether or not certain recognized attributes should be formatted for more human-readable output. (default: False) Example ------- >>> import six >>> import pybnb >>> results = pybnb.SolverResults() >>> results.best_node = pybnb.Node() >>> results.best_node.objective = 123 >>> out = six.StringIO() >>> # the best_node is serialized >>> results.write(out) >>> del results >>> import yaml >>> results_dict = yaml.safe_load(out.getvalue()) >>> # de-serialize the best_node >>> best_node = pybnb.node.loads(results_dict['best_node']) >>> assert best_node.objective == 123 """ with as_stream(stream) as stream: attrs = vars(self) names = sorted(list(attrs.keys())) first = ('solution_status', 'termination_condition', 'objective', 'bound', 'absolute_gap', 'relative_gap', 'nodes', 'wall_time', 'best_node') for cnt, name in enumerate(first): if not hasattr(self, name): continue names.remove(name) val = getattr(self, name) if val is not None: if name in ("solution_status", "termination_condition"): if type(val) in (SolutionStatus, TerminationCondition): val = val.value elif pretty: if name == 'wall_time': val = time_format(val, digits=2) elif name in ('objective', 'bound', 'absolute_gap', 'relative_gap'): val = "%.7g" % (val) elif name == "best_node": if val.objective is not None: val = ("Node(objective=%.7g)" % (val.objective)) else: val = "Node(objective=None)" else: if name == "best_node": val = dumps(val) if hasattr(base64, 'encodebytes'): val = base64.encodebytes(val).\ decode("ascii") else: val = base64.encodestring(val).\ decode("ascii") val = ('\n '.join( val.splitlines())) val = ("!!binary |\n %s" % (val)) else: val_ = "%r" % (val) if type(val) is float: if val_ == 'inf': val_ = '.inf' elif val_ == '-inf': val_ = "-.inf" elif val_ == 'nan': val_ = ".nan" val = val_ del val_ if pretty or (val is not None): stream.write(prefix+'%s: %s\n' % (name, val)) else: assert val is None stream.write(prefix+'%s: null\n' % (name)) for name in names: val = getattr(self, name) if pretty: stream.write(prefix+'%s: %r\n' % (name, val)) else: if val is None: stream.write(prefix+'%s: null\n' % (name)) else: val_ = "%r" % (val) if type(val) is float: if val_ == 'inf': val_ = '.inf' elif val_ == '-inf': val_ = "-.inf" elif val_ == 'nan': val_ = ".nan" val = val_ del val_ stream.write(prefix+'%s: %s\n' % (name, val))
def __str__(self): """Represents the results as a string.""" tmp = six.StringIO() self.pprint(stream=tmp) return tmp.getvalue()