Source code for pybnb.pyomo.misc

"""
Miscellaneous utilities used for development.

Copyright by Gabriel A. Hackebeil (gabe.hackebeil@gmail.com).
"""
import math
import array
import collections
import hashlib

import pybnb

import six
from six.moves import xrange as range

import pyomo.kernel as pmo
if getattr(pmo,'version_info',(0,)*3) < (5,4,3):   #pragma:nocover
    raise ImportError(
        "Pyomo 5.4.3 or later is not available")

[docs]def hash_joblist(jobs): """Create a hash of a Python list by casting each entry to a string.""" x = hashlib.sha1() for entry in jobs: x.update(str(entry).encode()) return x.hexdigest()
[docs]def add_tmp_component(model, name, obj): """Add a temporary component to a model, adjusting the name as needed to make sure it is unique.""" while hasattr(model, name): name = "."+name+"." setattr(model, name, obj) return name
[docs]def mpi_partition(comm, items, root=0): """A generator that partitions the list of items across processes in the communicator. If the communicator size is greater than 1, the root process iterates over no items, but rather serves them dynamically after receiving requests from workers. This function assumes each process has an identical copy of the items list. Therefore, items in the list are not transferred (only indices).""" assert root >= 0 N = len(items) if N > 0: if (comm is None) or \ (comm.size == 1): assert root == 0 for x in items: yield x else: import mpi4py.MPI # it would be pretty easy to refactor this # code to avoid this limitation assert N <= mpi4py.MPI.COMM_WORLD.Get_attr( mpi4py.MPI.TAG_UB) _null = [array.array('b',[]),mpi4py.MPI.CHAR] last_tag = {} if comm.rank == root: i = 0 requests = [] for dest in range(comm.size): if dest == root: continue last_tag[dest] = i requests.append(comm.Isend(_null, dest, tag=i)) i += 1 status = mpi4py.MPI.Status() while i < N: comm.Recv(_null, status=status) last_tag[status.Get_source()] = i requests.append(comm.Isend(_null, status.Get_source(), tag=i)) i += 1 for dest in last_tag: if last_tag[dest] < N: requests.append(comm.Isend(_null, dest, tag=N)) requests.append(comm.Irecv(_null, dest)) mpi4py.MPI.Request.Waitall(requests) else: status = mpi4py.MPI.Status() comm.Recv(_null, source=root, status=status) if status.Get_tag() >= N: comm.Send(_null,root) else: while status.Get_tag() < N: yield items[status.Get_tag()] comm.Sendrecv(_null, root, recvbuf=_null, source=root, status=status)
[docs]def correct_integer_lb(lb, integer_tolerance): """Converts a lower bound for an integer optimization variable to an integer equal to `ceil(ub)`, taking care not to move a non-integer bound away from an integer point already within a given tolerance.""" assert 0 <= integer_tolerance < 0.5 if lb-math.floor(lb) > integer_tolerance: return int(math.ceil(lb)) else: return int(math.floor(lb))
[docs]def correct_integer_ub(ub, integer_tolerance): """Converts an upper bound for an integer optimization variable to an integer equal to `floor(ub)`, taking care not to move a non-integer bound away from an integer point already within a given tolerance.""" assert 0 <= integer_tolerance < 0.5 if math.ceil(ub)-ub > integer_tolerance: return int(math.floor(ub)) else: return int(math.ceil(ub))
[docs]def create_optimality_bound(problem, pyomo_objective, best_objective_value): """Returns a constraint that bounds an objective function with a known best value. That is, the constraint will require the objective function to be better than the given value.""" optbound = pmo.constraint(body=pyomo_objective) if problem.sense() == pybnb.minimize: assert pyomo_objective.sense == pmo.minimize optbound.ub = best_objective_value else: assert problem.sense() == pybnb.maximize assert pyomo_objective.sense == pmo.maximize optbound.lb = best_objective_value return optbound
[docs]def generate_cids(model, prefix=(), **kwds): """Generate forward and reverse mappings between model components and deterministic, unique identifiers that are safe to serialize or use as dictionary keys.""" object_to_cid = pmo.ComponentMap() cid_to_object = collections.OrderedDict() if hasattr(pmo, 'preorder_traversal'): #pragma:nocover fn = lambda *args, **kwds: pmo.preorder_traversal(model, *args, **kwds) else: #pragma:nocover fn = model.preorder_traversal try: fn(return_key=True) except TypeError: traversal = fn(**kwds) obj_ = six.next(traversal) assert obj_ is model object_to_cid[model] = prefix cid_to_object[prefix] = model for obj in traversal: parent = obj.parent key = obj.storage_key cid_ = object_to_cid[obj] = object_to_cid[parent]+(key,) cid_to_object[cid_] = obj else: #pragma:nocover traversal = fn(return_key=True, **kwds) obj_ = six.next(traversal)[1] assert obj_ is model object_to_cid[model] = prefix cid_to_object[prefix] = model for key, obj in traversal: parent = obj.parent cid_ = object_to_cid[obj] = object_to_cid[parent]+(key,) cid_to_object[cid_] = obj return object_to_cid, cid_to_object