Source code for pyomo.core.kernel.piecewise_library.util

#  ___________________________________________________________________________
#  Pyomo: Python Optimization Modeling Objects
#  Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC
#  Under the terms of Contract DE-NA0003525 with National Technology and
#  Engineering Solutions of Sandia, LLC, the U.S. Government retains certain
#  rights in this software.
#  This software is distributed under the 3-clause BSD License.
#  ___________________________________________________________________________

import operator
import itertools

from pyomo.common.dependencies import (
    numpy, numpy_available, scipy, scipy_available

[docs]class PiecewiseValidationError(Exception): """An exception raised when validation of piecewise linear functions fail."""
[docs]def is_constant(vals): """Checks if a list of points is constant""" if len(vals) <= 1: return True it = iter(vals) next(it) op = operator.eq return all(itertools.starmap(op, zip(it,vals)))
[docs]def is_nondecreasing(vals): """Checks if a list of points is nondecreasing""" if len(vals) <= 1: return True it = iter(vals) next(it) op = return all(itertools.starmap(op, zip(it,vals)))
[docs]def is_nonincreasing(vals): """Checks if a list of points is nonincreasing""" if len(vals) <= 1: return True it = iter(vals) next(it) op = operator.le return all(itertools.starmap(op, zip(it,vals)))
[docs]def is_positive_power_of_two(x): """Checks if a number is a nonzero and positive power of 2""" if (x <= 0): return False else: return ( (x & (x - 1)) == 0 )
[docs]def log2floor(n): """Computes the exact value of floor(log2(n)) without using floating point calculations. Input argument must be a positive integer.""" assert n > 0 return n.bit_length() - 1
[docs]def generate_gray_code(nbits): """Generates a Gray code of nbits as list of lists""" bitset = [0 for i in range(nbits)] # important that we copy bitset each time graycode = [list(bitset)] for i in range(2,(1<<nbits)+1): if i%2: for j in range(-1,-nbits,-1): if bitset[j]: bitset[j-1]=bitset[j-1]^1 break else: bitset[-1]=bitset[-1]^1 # important that we copy bitset each time graycode.append(list(bitset)) return graycode
[docs]def characterize_function(breakpoints, values): """ Characterizes a piecewise linear function described by a list of breakpoints and function values. Args: breakpoints (list): The list of breakpoints of the piecewise linear function. It is assumed that the list of breakpoints is in non-decreasing order. values (list): The values of the piecewise linear function corresponding to the breakpoints. Returns: (int, list): a function characterization code and \ the list of slopes. .. note:: The function characterization codes are * 1: affine * 2: convex * 3: concave * 4: step * 5: other If the function has step points, some of the slopes may be :const:`None`. """ if not is_nondecreasing(breakpoints): raise ValueError( "The list of breakpoints must be nondecreasing") step = False slopes = [] for i in range(1, len(breakpoints)): if breakpoints[i] != breakpoints[i-1]: slope = float(values[i] - values[i-1]) / \ (breakpoints[i] - breakpoints[i-1]) else: slope = None step = True slopes.append(slope) if step: return characterize_function.step, slopes elif is_constant(slopes): return characterize_function.affine, slopes elif is_nondecreasing(slopes): return characterize_function.convex, slopes elif is_nonincreasing(slopes): return characterize_function.concave, slopes else: return characterize_function.other, slopes
characterize_function.affine = 1 characterize_function.convex = 2 characterize_function.concave = 3 characterize_function.step = 4 characterize_function.other = 5
[docs]def generate_delaunay(variables, num=10, **kwds): """ Generate a Delaunay triangulation of the D-dimensional bounded variable domain given a list of D variables. Requires numpy and scipy. Args: variables: A list of variables, each having a finite upper and lower bound. num (int): The number of grid points to generate for each variable (default=10). **kwds: All additional keywords are passed to the scipy.spatial.Delaunay constructor. Returns: A scipy.spatial.Delaunay object. """ linegrids = [] for v in variables: if v.has_lb() and v.has_ub(): linegrids.append(numpy.linspace(, v.ub, num)) else: raise ValueError( "Variable %s does not have a " "finite lower and upper bound.") # generates a meshgrid and then flattens and transposes # the meshgrid into an (npoints, D) shaped array of # coordinates points = numpy.vstack(numpy.meshgrid(*linegrids)).\ reshape(len(variables),-1).T return scipy.spatial.Delaunay(points, **kwds)