From b3548442a5807d8b8ca7e8087b8346a320aa8074 Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Wed, 26 Jul 2023 20:28:24 -0600 Subject: [PATCH 01/61] initial commit for EDI --- .gitignore | 5 +- pyomo/contrib/edi/README.md | 101 +++ pyomo/contrib/edi/__init__.py | 26 + pyomo/contrib/edi/blackBoxFunctionModel.py | 816 +++++++++++++++++++++ pyomo/contrib/edi/formulation.py | 305 ++++++++ 5 files changed, 1252 insertions(+), 1 deletion(-) create mode 100644 pyomo/contrib/edi/README.md create mode 100644 pyomo/contrib/edi/__init__.py create mode 100644 pyomo/contrib/edi/blackBoxFunctionModel.py create mode 100644 pyomo/contrib/edi/formulation.py diff --git a/.gitignore b/.gitignore index 09069552990..638dc70d13e 100644 --- a/.gitignore +++ b/.gitignore @@ -24,4 +24,7 @@ gurobi.log # Jupyterhub/Jupyterlab checkpoints .ipynb_checkpoints -cplex.log \ No newline at end of file +cplex.log + +# Mac tracking files +*.DS_Store* diff --git a/pyomo/contrib/edi/README.md b/pyomo/contrib/edi/README.md new file mode 100644 index 00000000000..ce1f9598505 --- /dev/null +++ b/pyomo/contrib/edi/README.md @@ -0,0 +1,101 @@ +# Engineering Design Interface + +The Pyomo Engineering Design Interface (EDI) is a lightweight wrapper on the Pyomo language that is targeted at composing engineering design optimization problems. The language and interface have been designed to mimic many of the features found in [GPkit](https://github.com/convexengineering/gpkit) and [CVXPY](https://github.com/cvxpy/cvxpy) while also providing a simple, clean interface for black-box analysis codes that are common in engineering design applications. + +## Installation + +EDI follows the standard installation process for Pyomo extensions: +``` +pip install pyomo +pyomo download-extensions +pyomo build-extensions +``` + +## Usage + +The core object in EDI is the `Formulation` object, which inherits from the `pyomo.environ.ConcreteModel`. Essentally, a `Formulation` is a Pyomo `Model` with some extra stuff, but can be treated exactly as if it were a Pyomo `Model`. However, an EDI `Formulation` has some additional features that can help simplify model construction. + +Below is a simple example to get started, but additional resources can be found in the [examples](https://github.com/Pyomo/pyomo/tree/main/pyomo/contrib/edi/examples) folder or in the EDI [documentation](https://pyomo.readthedocs.io/en/stable/contributed_packages/edi/index.html) + +``` +# ================= +# Import Statements +# ================= +import numpy as np +import pyomo.environ as pyo +from pyomo.environ import units +from pyomo.edi import Formulation, RuntimeConstraint, BlackBoxFunctionModel, bbVariable + +# =================== +# Declare Formulation +# =================== +f = Formulation() + +# ================= +# Declare Variables +# ================= +x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') +y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') +z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The unit circle output') + +# ================= +# Declare Constants +# ================= +c = f.Constant(name = 'c', value = 1.0, units = '', description = 'A constant c', size = 2) + +# ===================== +# Declare the Objective +# ===================== +f.Objective( + c[0]*x + c[1]*y +) + +# =================== +# Declare a Black Box +# =================== +class UnitCircle(BlackBoxFunctionModel): + def __init__(self): # The initalization function + + # Initalize the black box model + super(UnitCircle, self).__init__() + + # A brief description of the model + self.description = 'This model evaluates the function: y = exp(x)' + + # Declare the black box model inputs + self.inputs.append(bbVariable(name = 'x', size = 0, units = 'ft' , description = 'The x variable')) + self.inputs.append(bbVariable(name = 'y', size = 0, units = 'ft' , description = 'The y variable')) + + # Declare the black box model outputs + self.outputs.append(bbVariable('z', None, 'ft**2', 'Resultant of the unit circle evaluation')) + + # Declare the maximum available derivative + self.availableDerivative = 2 + + # Post-initalization setup + self.post_init_setup(len(self.inputs)) + + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float + y = pyo.value(units.convert(y,self.inputs[0].units)) # Converts to correct units then casts to float + + z = x**2 + y**2 # Compute z + dzdx = 2*x # Compute dz/dx + dzdy = 2*y # Compute dz/dy + + z *= units.ft**2 + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + +# ======================= +# Declare the Constraints +# ======================= +f.ConstraintList( + [ + RuntimeConstraint(z, '==', [x,y], UnitCircle() ) , + z <= 1*units.m**2 + ] +) +``` \ No newline at end of file diff --git a/pyomo/contrib/edi/__init__.py b/pyomo/contrib/edi/__init__.py new file mode 100644 index 00000000000..bce95c9b6ae --- /dev/null +++ b/pyomo/contrib/edi/__init__.py @@ -0,0 +1,26 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright (c) 2008-2022 +# 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. +# +# Development of this module was conducted as part of the Institute for +# the Design of Advanced Energy Systems (IDAES) with support through the +# Simulation-Based Engineering, Crosscutting Research Program within the +# U.S. Department of Energy’s Office of Fossil Energy and Carbon Management. +# +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + +# # Recommended just to build all of the appropriate things +# import pyomo.environ + +# # Import the relevant classes from Formulation +# from formulation import Formulation, RuntimeConstraint + +# # Import the black box modeling tools +# from blackBoxFunctionModel import BlackBoxFunctionModel +# from blackBoxFunctionModel import BlackBoxFunctionModel_Variable as bbVariable \ No newline at end of file diff --git a/pyomo/contrib/edi/blackBoxFunctionModel.py b/pyomo/contrib/edi/blackBoxFunctionModel.py new file mode 100644 index 00000000000..3550d5d2ea8 --- /dev/null +++ b/pyomo/contrib/edi/blackBoxFunctionModel.py @@ -0,0 +1,816 @@ +import copy +import numpy as np +import pyomo +import pyomo.environ as pyo +from pyomo.environ import units as pyomo_units +import scipy.sparse as sps +from pyomo.contrib.pynumero.interfaces.external_grey_box import ExternalGreyBoxModel, ExternalGreyBoxBlock + + +class TypeCheckedList(list): + def __init__(self, checkItem, itemList = None): + super(TypeCheckedList, self).__init__() + self.checkItem = checkItem + + if itemList is not None: + if isinstance(itemList, list) or isinstance(itemList, tuple): + for itm in itemList: + self.append(itm) + else: + raise ValueError('Input to itemList is not iterable') + + def __setitem__(self, key, val): + if isinstance(val, self.checkItem): + super(TypeCheckedList, self).__setitem__(key, val) + else: + raise ValueError('Input must be an instance of the defined type') + + def __setslice__(self, i, j, sequence): + performSet = True + for val in sequence: + if not isinstance(val, self.checkItem): + performSet = False + break + + if performSet: + super(TypeCheckedList, self).__setslice__(i,j,sequence) + else: + raise ValueError('All values in the input must be an instance of the defined type') + + def append(self, val): + if isinstance(val, self.checkItem): + super(TypeCheckedList, self).append(val) + else: + raise ValueError('Input must be an instance of the defined type') + + +class BlackBoxFunctionModel_Variable(object): + def __init__(self, name, size, units, description = ''): + # Order matters + self.name = name + self.units = units + self.size = size + self.description = description +# ===================================================================================================================== +# The printing function +# ===================================================================================================================== + def __repr__(self): + return self.name +# ===================================================================================================================== +# Define the name +# ===================================================================================================================== + @property + def name(self): + return self._name + @name.setter + def name(self,val): + if isinstance(val, str): + self._name = val + else: + raise ValueError('Invalid name. Must be a string.') +# ===================================================================================================================== +# Define the units +# ===================================================================================================================== + @property + def units(self): + return self._units + @units.setter + def units(self, val): + # set dimensionless if a null string is passed in + if isinstance(val, str): + if val in ['-', '', ' ']: + val = 'dimensionless' + + if isinstance(val, str): + self._units = pyomo_units.__getattr__(val) + elif isinstance(val, pyomo.core.base.units_container._PyomoUnit): + self._units = val + else: + raise ValueError('Invalid units. Must be a string compatible with pint or a unit instance.') +# ===================================================================================================================== +# Define the size +# ===================================================================================================================== + @property + def size(self): + return self._size + @size.setter + def size(self, val): + invalid = False + if isinstance(val,(list, tuple)): + sizeTemp = [] + for x in val: + if isinstance(x, str): + # is a vector of unknown length, should be 'inf', but any string accepted + x = -1 + # pass + elif not isinstance(x,int): + raise ValueError('Invalid size. Must be an integer or list/tuple of integers') + if x == 1: + raise ValueError('A value of 1 is not valid for defining size. Use fewer dimensions.') + sizeTemp.append(x) + self._size = val + else: + if val is None: + self._size = None + elif isinstance(val, str): + # is a 1D vector of unknown length, should be 'inf', but any string accepted + self._size = -1 + # pass + elif isinstance(val, int): + if val == 1: + raise ValueError('A value of 1 is not valid for defining size. Use 0 to indicate a scalar value.') + else: + self._size = val + else: + raise ValueError('Invalid size. Must be an integer or list/tuple of integers') +# ===================================================================================================================== +# Define the description +# ===================================================================================================================== + @property + def description(self): + return self._description + @description.setter + def description(self, val): + if isinstance(val,str): + self._description = val + else: + raise ValueError('Invalid description. Must be a string.') + +BBVariable = BlackBoxFunctionModel_Variable + +errorString = 'This function is calling to the base Model class and has not been defined.' + +class BlackBoxFunctionModel(ExternalGreyBoxModel): +# --------------------------------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------------------------------- + def __init__(self): + super(BlackBoxFunctionModel, self).__init__() + + # List of the inputs and outputs + self.inputs = TypeCheckedList(BlackBoxFunctionModel_Variable, []) + self.outputs = TypeCheckedList(BlackBoxFunctionModel_Variable, []) + + self.inputVariables_optimization = None + self.outputVariables_optimization = None + + # A simple description of the model + self.description = None + + # Defines the order of derivative available in the black box + self.availableDerivative = 0 + + self._cache = None + self._NunwrappedOutputs = None + self._NunwrappedInputs = None + + def setOptimizationVariables(self, inputVariables_optimization, outputVariables_optimization): + self.inputVariables_optimization = inputVariables_optimization + self.outputVariables_optimization = outputVariables_optimization + +# --------------------------------------------------------------------------------------------------------------------- +# pyomo things +# --------------------------------------------------------------------------------------------------------------------- + def input_names(self): + inputs_unwrapped = [] + for ivar in self.inputVariables_optimization: + if isinstance(ivar, pyomo.core.base.var.ScalarVar): + inputs_unwrapped.append(ivar) + elif isinstance(ivar, pyomo.core.base.var.IndexedVar): + validIndicies = list(ivar.index_set().data()) + for vi in validIndicies: + inputs_unwrapped.append(ivar[vi]) + else: + print(type(ivar)) + raise ValueError("Invalid type for input variable") + + return [ip.__str__() for ip in inputs_unwrapped] + + + def output_names(self): + outputs_unwrapped = [] + for ovar in self.outputVariables_optimization: + if isinstance(ovar, pyomo.core.base.var.ScalarVar): + outputs_unwrapped.append(ovar) + elif isinstance(ovar, pyomo.core.base.var.IndexedVar): + validIndicies = list(ovar.index_set().data()) + for vi in validIndicies: + outputs_unwrapped.append(ovar[vi]) + else: + raise ValueError("Invalid type for output variable") + + return [op.__str__() for op in outputs_unwrapped] + + def set_input_values(self, input_values): + self._input_values = input_values + self._cache = None + + def evaluate_outputs(self): + self.fillCache() + opts = self._cache['pyomo_outputs'] + return opts + + + def evaluate_jacobian_outputs(self): + self.fillCache() + jac = self._cache['pyomo_jacobian'] + return jac + + def post_init_setup(self, N_inputs, defaultVal = 0.0): + self._input_values = np.ones(N_inputs) * defaultVal + + + def fillCache(self): + if self._cache is None: + self._cache = {} + + raw_inputs = self._input_values + bb_inputs = [] + + ptr = 0 + + for i in range(0,len(self.inputVariables_optimization)): + optimizationInput = self.inputVariables_optimization[i] + ipt = self.inputs[i] + + shape = [len(idx) for idx in optimizationInput.index_set().subsets()] + localShape = ipt.size + + optimizationUnits = self.inputVariables_optimization[i].get_units() + localUnits = ipt.units + + if isinstance(optimizationInput, pyomo.core.base.var.IndexedVar): + value = np.zeros(shape) + for vix in list(optimizationInput.index_set().data()): + value[vix] = raw_inputs[ptr] + ptr += 1 + value *= optimizationUnits + self.sizeCheck(localShape, value) + value_correctedUnits = pyomo_units.convert(value, localUnits) + bb_inputs.append(value_correctedUnits) + + elif isinstance(optimizationInput, pyomo.core.base.var.ScalarVar): + value = raw_inputs[ptr] * optimizationUnits + ptr += 1 + self.sizeCheck(localShape, value) + value_correctedUnits = pyomo_units.convert(value, localUnits) + bb_inputs.append(value_correctedUnits) + + else: + raise ValueError("Invalid input variable type") + + bbo = self.BlackBox(*bb_inputs) + + self._cache['raw'] = bbo + self._cache['raw_value'] = bbo[0] + self._cache['raw_jacobian'] = bbo[1] + + outputVector = [] + if not isinstance(bbo[0], (list,tuple)): + valueList = [bbo[0]] + jacobianList = [bbo[1]] + else: + valueList = bbo[0] + jacobianList = bbo[1] + for i in range(0,len(valueList)): + optimizationOutput = self.outputVariables_optimization[i] + opt = self.outputs[i] + + modelOutputUnits = opt.units + ouptutOptimizationUnits = optimizationOutput.get_units() + vl = valueList[i] + if isinstance(vl, pyomo.core.expr.numvalue.NumericNDArray): + validIndexList = optimizationOutput.index_set().data() + for j in range(0,len(validIndexList)): + vi = validIndexList[j] + corrected_value = pyo.value(pyomo_units.convert(vl[vi], ouptutOptimizationUnits)) # now unitless in correct units + outputVector.append(corrected_value) + + elif isinstance(vl, (pyomo.core.expr.numeric_expr.NPV_ProductExpression, + pyomo.core.base.units_container._PyomoUnit)): + corrected_value = pyo.value(pyomo_units.convert(vl, ouptutOptimizationUnits)) # now unitless in correct units + outputVector.append(corrected_value) + + else: + raise ValueError("Invalid output variable type") + + self._cache['pyomo_outputs'] = outputVector + + outputJacobian = np.zeros([self._NunwrappedOutputs, self._NunwrappedInputs]) + ptr_row = 0 + ptr_col = 0 + + for i in range(0,len(jacobianList)): + oopt = self.outputVariables_optimization[i] + lopt = self.outputs[i] + oounits = oopt.get_units() + lounits = lopt.units + # oshape = [len(idx) for idx in oopt.index_set().subsets()] + for j in range(0,len(self.inputs)): + oipt = self.inputVariables_optimization[j] + lipt = self.inputs[j] + oiunits = oipt.get_units() + liunits = lipt.units + # ishape = [len(idx) for idx in oipt.index_set().subsets()] + + jacobianValue_raw = jacobianList[i][j] + + if isinstance(jacobianValue_raw, (pyomo.core.expr.numeric_expr.NPV_ProductExpression, + pyomo.core.base.units_container._PyomoUnit)): + corrected_value = pyo.value(pyomo_units.convert(jacobianValue_raw, oounits/oiunits)) # now unitless in correct units + outputJacobian[ptr_row,ptr_col] = corrected_value + ptr_col += 1 + ptr_row_step = 1 + + elif isinstance(jacobianValue_raw,pyomo.core.expr.numvalue.NumericNDArray): + jshape = jacobianValue_raw.shape + + if isinstance(oopt, pyomo.core.base.var.ScalarVar): + oshape = 0 + elif isinstance(oopt, pyomo.core.base.var.IndexedVar): + oshape = [len(idx) for idx in oopt.index_set().subsets()] + else: + raise ValueError("Invalid type for output variable") + + if isinstance(oipt, pyomo.core.base.var.ScalarVar): + ishape = 0 + elif isinstance(oipt, pyomo.core.base.var.IndexedVar): + ishape = [len(idx) for idx in oipt.index_set().subsets()] + else: + raise ValueError("Invalid type for input variable") + + if oshape == 0: + validIndicies = list(oipt.index_set().data()) + for vi in validIndicies: + corrected_value = pyo.value(pyomo_units.convert(jacobianValue_raw[vi], oounits/oiunits)) # now unitless in correct units + outputJacobian[ptr_row,ptr_col] = corrected_value + ptr_col += 1 + ptr_row_step = 1 + + elif ishape == 0: + ptr_row_cache = ptr_row + validIndicies = list(oopt.index_set().data()) + for vi in validIndicies: + corrected_value = pyo.value(pyomo_units.convert(jacobianValue_raw[vi], oounits/oiunits)) # now unitless in correct units + outputJacobian[ptr_row,ptr_col] = corrected_value + ptr_row += 1 + ptr_row = ptr_row_cache + ptr_row_step = len(validIndicies) + + # elif ishape == 0 and oshape == 0: # Handled by the scalar case above + + else: + # both are dimensioned vectors + #oshape, ishape, jshape + ptr_row_cache = ptr_row + validIndicies_o = list(oopt.index_set().data()) + validIndicies_i = list(oipt.index_set().data()) + + for vio in validIndicies_o: + for vii in validIndicies_i: + corrected_value = pyo.value(pyomo_units.convert(jacobianValue_raw[vio + vii], oounits/oiunits)) # now unitless in correct units + outputJacobian[ptr_row,ptr_col] = corrected_value + ptr_col += 1 + ptr_row += 1 + ptr_row = ptr_row_cache + ptr_row_step = len(validIndicies_o) + + else: + raise ValueError("Invalid jacobian type") + + ptr_row += ptr_row_step + + self._cache['pyomo_jacobian'] = sps.coo_matrix(outputJacobian) + +# --------------------------------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------------------------------- + # These models must be defined in each individual model, just placeholders here + def BlackBox(*args, **kwargs): + raise AttributeError(errorString) + +# --------------------------------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------------------------------- + def parseInputs(*args, **kwargs): + args = list(args) # convert tuple to list + self = args.pop(0) # pop off the self argument + + inputNames = [self.inputs[i].name for i in range(0,len(self.inputs))] + + # ------------------------------ + # ------------------------------ + if len(args) + len(kwargs.values()) == 1: + if len(args) == 1: + inputData = args[0] + if len(kwargs.values()) == 1: + inputData = list(kwargs.values())[0] + + if len(inputNames) == 1: + try: + rs = self.sanitizeInputs(inputData) + return [dict(zip(inputNames,[rs]))], -self.availableDerivative-1, {} # one input being passed in + except: + pass #otherwise, proceed + + if isinstance(inputData, (list,tuple)): + dataRuns = [] + for idc in inputData: + if isinstance(idc, dict): + sips = self.sanitizeInputs(**idc) + if len(inputNames) == 1: + sips = [sips] + runDictS = dict(zip(inputNames,sips)) + dataRuns.append(runDictS) # the BlackBox([{'x1':x1, 'x2':x2},{'x1':x1, 'x2':x2},...]) case + elif isinstance(idc,(list,tuple)): + if len(idc) == len(inputNames): + sips = self.sanitizeInputs(*idc) + if len(inputNames) == 1: + sips = [sips] + runDictS = dict(zip(inputNames,sips)) + dataRuns.append(runDictS) # the BlackBox([ [x1, x2], [x1, x2],...]) case + else: + raise ValueError('Entry in input data list has improper length') + else: + raise ValueError("Invalid data type in the input list. Note that BlackBox([x1,x2,y]) must be passed in as BlackBox([[x1,x2,y]]) or "+ + "BlackBox(*[x1,x2,y]) or BlackBox({'x1':x1,'x2':x2,'y':y}) or simply BlackBox(x1, x2, y) to avoid processing singularities. "+ + "Best practice is BlackBox({'x1':x1,'x2':x2,'y':y})") + return dataRuns, self.availableDerivative, {} + + elif isinstance(inputData, dict): + if set(list(inputData.keys())) == set(inputNames): + try: + inputLengths = [len(inputData[kw]) for kw in inputData.keys()] + except: + sips = self.sanitizeInputs(**inputData) + if len(inputNames) == 1: + sips = [sips] + return [dict(zip(inputNames,sips))], -self.availableDerivative-1, {} # the BlackBox(*{'x1':x1, 'x2':x2}) case, somewhat likely + + if not all([inputLengths[i] == inputLengths[0] for i in range(0,len(inputLengths))]): + sips = self.sanitizeInputs(**inputData) + return [dict(zip(inputNames,sips))], -self.availableDerivative-1, {} # the BlackBox(*{'x1':x1, 'x2':x2}) case where vectors for x1... are passed in (likely to fail on previous line) + else: + try: + sips = self.sanitizeInputs(**inputData) + return [dict(zip(inputNames,sips))], -self.availableDerivative-1, {} # the BlackBox(*{'x1':x1, 'x2':x2}) case where vectors all inputs have same length intentionally (likely to fail on previous line) + except: + dataRuns = [] + for i in range(0,inputLengths[0]): + runDict = {} + for ky,vl in inputData.items(): + runDict[ky] = vl[i] + sips = self.sanitizeInputs(**runDict) + if len(inputNames) == 1: + sips = [sips] + runDictS = dict(zip(inputNames,sips)) + dataRuns.append(runDictS) + return dataRuns, self.availableDerivative, {} # the BlackBox({'x1':x1_vec, 'x2':x2_vec}) case, most likely + else: + raise ValueError('Keywords did not match the exptected list') + else: + raise ValueError('Got unexpected data type %s'%(str(type(inputData)))) + # ------------------------------ + # ------------------------------ + else: + if any([ list(kwargs.keys())[i] in inputNames for i in range(0,len(list(kwargs.keys()))) ]): + # some of the inputs are defined in the kwargs + if len(args) >= len(inputNames): + raise ValueError('A keyword input is defining an input, but there are too many unkeyed arguments for this to occour. Check the inputs.') + else: + if len(args) != 0: + availableKeywords = inputNames[-len(args):] + else: + availableKeywords = inputNames + + valList = args + [None]*(len(inputNames)-len(args)) + for ky in availableKeywords: + ix = inputNames.index(ky) + valList[ix] = kwargs[ky] + + if any([valList[i]==None for i in range(0,len(valList))]): + raise ValueError('Kewords did not properly fill in the remaining arguments. Check the inputs.') + + sips = self.sanitizeInputs(*valList) + if len(inputNames) == 1: + sips = [sips] + + remainingKwargs = copy.deepcopy(kwargs) + for nm in inputNames: + del remainingKwargs[nm] + + return [dict(zip(inputNames,sips))], -self.availableDerivative-1, remainingKwargs # Mix of args and kwargs define inputs + else: + # all of the inputs are in the args + try: + sips = self.sanitizeInputs(*args[0:len(inputNames)]) + if len(inputNames) == 1: + sips = [sips] + + remainingKwargs = copy.deepcopy(kwargs) + remainingKwargs['remainingArgs'] = args[len(inputNames):] + return [dict(zip(inputNames,sips))], -self.availableDerivative-1, remainingKwargs # all inputs are in args + except: + runCases, returnMode, extra_singleInput = self.parseInputs(args[0]) + remainingKwargs = copy.deepcopy(kwargs) + remainingKwargs['remainingArgs'] = args[len(inputNames):] + return runCases, returnMode, remainingKwargs + +# --------------------------------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------------------------------- + def sizeCheck(self, size, ipval_correctUnits): + if size is not None: + szVal = ipval_correctUnits + if isinstance(szVal, (pyomo.core.expr.numeric_expr.NPV_ProductExpression, + pyomo.core.base.units_container._PyomoUnit)): + if size != 0 and size != 1 : + raise ValueError('Size of %s did not match the expected size %s (ie: Scalar)'%(name, str(size))) + elif isinstance(szVal, pyomo.core.expr.numvalue.NumericNDArray): + shp = szVal.shape + if isinstance(size,(int,float)): + size = [size] + # else: + if len(shp) != len(size): + raise ValueError('Shapes/Sizes of %s does not match the expected %s'%(str(shp),str(size))) + for j in range(0,len(shp)): + if size[j] != -1: # was declared of flexible length + if size[j] != shp[j]: + raise ValueError('Shapes/Sizes of %s does not match the expected %s'%(str(shp),str(size))) + else: + raise ValueError('Invalid type detected when checking size (Should never display)') + +# --------------------------------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------------------------------- + def sanitizeInputs(self, *args, **kwargs): + nameList = [self.inputs[i].name for i in range(0,len(self.inputs))] + if len(args) + len(kwargs.values()) > len(nameList): + raise ValueError('Too many inputs') + if len(args) + len(kwargs.values()) < len(nameList): + raise ValueError('Not enough inputs') + inputDict = {} + for i in range(0,len(args)): + rg = args[i] + inputDict[nameList[i]] = rg + + for ky, vl in kwargs.items(): + if ky in nameList: + inputDict[ky] = vl + else: + raise ValueError('Unexpected input keyword argument %s in the inputs'%(ky)) + + opts = [] + + for i in range(0,len(nameList)): + name = nameList[i] + nameCheck = self.inputs[i].name + unts = self.inputs[i].units + size = self.inputs[i].size + + if name != nameCheck: + raise RuntimeError('Something went wrong and values are not consistent. Check your defined inputs.') + + ipval = inputDict[name] + + if unts is not None: + try: + ipval_correctUnits = pyomo_units.convert(ipval, unts)#ipval.to(unts) + except: + raise ValueError('Could not convert %s of %s to %s'%(name, str(ipval),str(unts))) + else: + ipval_correctUnits = ipval + + if not isinstance(ipval_correctUnits, (pyomo.core.expr.numeric_expr.NPV_ProductExpression, + pyomo.core.expr.numvalue.NumericNDArray, + pyomo.core.base.units_container._PyomoUnit)): + ipval_correctUnits *= pyomo_units.dimensionless + + self.sizeCheck(size, ipval_correctUnits) + + opts.append(ipval_correctUnits) + if len(opts) == 1: + opts = opts[0] + + return opts + +# --------------------------------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------------------------------- + def checkOutputs(self, *args, **kwargs): + nameList = [self.outputs[i].name for i in range(0,len(self.outputs))] + if len(args) + len(kwargs.values()) > len(nameList): + raise ValueError('Too many inputs') + if len(args) + len(kwargs.values()) < len(nameList): + raise ValueError('Not enough inputs') + inputDict = {} + for i in range(0,len(args)): + rg = args[i] + inputDict[nameList[i]] = rg + + for ky, vl in kwargs.items(): + if ky in nameList: + inputDict[ky] = vl + else: + raise ValueError('Unexpected input keyword argument %s in the inputs'%(ky)) + + opts = [] + + for i in range(0,len(nameList)): + name = nameList[i] + nameCheck = self.outputs[i].name + unts = self.outputs[i].units + size = self.outputs[i].size + + if name != nameCheck: + raise RuntimeError('Something went wrong and values are not consistent. Check your defined inputs.') + + ipval = inputDict[name] + + if unts is not None: + try: + ipval_correctUnits = pyomo_units.convert(ipval, unts) + except: + raise ValueError('Could not convert %s of %s to %s'%(name, str(ipval),str(unts))) + else: + ipval_correctUnits = ipval + + if not isinstance(ipval_correctUnits, (pyomo.core.expr.numeric_expr.NPV_ProductExpression, + pyomo.core.expr.numvalue.NumericNDArray, + pyomo.core.base.units_container._PyomoUnit)): + ipval_correctUnits *= pyomo_units.dimensionless + + self.sizeCheck(size, ipval_correctUnits) + + opts.append(ipval_correctUnits) + if len(opts) == 1: + opts = opts[0] + + return opts + +# --------------------------------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------------------------------- + def getSummary(self, whitespace = 6): + pstr = '\n' + pstr += 'Model Description\n' + pstr += '=================\n' + descr_str = self.description.__repr__() + pstr += descr_str[1:-1] + '\n\n' + + longestName = 0 + longestUnits = 0 + longestSize = 0 + for ipt in self.inputs: + nml = len(ipt.name) + if nml > longestName: + longestName = nml + if ipt.units is None: + unts = 'None' + else: + unts = ipt.units.__str__()#_repr_html_() + # unts = unts.replace('','^') + # unts = unts.replace('','') + # unts = unts.replace('\[', '[') + # unts = unts.replace('\]', ']') + unl = len(unts) + if unl > longestUnits: + longestUnits = unl + if ipt.size is None: + lsz = 4 + else: + if type(ipt.size) == list: + lsz = len(ipt.size.__repr__()) + else: + lsz = len(str(ipt.size)) + if lsz > longestSize: + longestSize = lsz + namespace = max([4, longestName]) + whitespace + unitspace = max([5, longestUnits]) + whitespace + sizespace = max([4, longestSize]) + whitespace + fulllength = namespace+unitspace+sizespace+11 + pstr += 'Inputs\n' + pstr += '='*fulllength + pstr += '\n' + pstr += 'Name'.ljust(namespace) + pstr += 'Units'.ljust(unitspace) + pstr += 'Size'.ljust(sizespace) + pstr += 'Description' + pstr += '\n' + pstr += '-'*(namespace - whitespace) + pstr += ' '*whitespace + pstr += '-'*(unitspace - whitespace) + pstr += ' '*whitespace + pstr += '-'*(sizespace - whitespace) + pstr += ' '*whitespace + pstr += '-----------' + pstr += '\n' + for ipt in self.inputs: + pstr += ipt.name.ljust(namespace) + if ipt.units is None: + unts = 'None' + else: + unts = ipt.units.__str__()#_repr_html_() + # unts = unts.replace('','^') + # unts = unts.replace('','') + # unts = unts.replace('\[', '[') + # unts = unts.replace('\]', ']') + pstr += unts.ljust(unitspace) + if ipt.size is None: + lnstr = 'None' + else: + lnstr = '%s'%(ipt.size.__repr__()) + pstr += lnstr.ljust(sizespace) + pstr += ipt.description + pstr += '\n' + pstr += '\n' + + longestName = 0 + longestUnits = 0 + longestSize = 0 + for opt in self.outputs: + nml = len(opt.name) + if nml > longestName: + longestName = nml + if opt.units is None: + unts = 'None' + else: + unts = opt.units.__str__()#_repr_html_() + # unts = unts.replace('','^') + # unts = unts.replace('','') + # unts = unts.replace('\[', '[') + # unts = unts.replace('\]', ']') + unl = len(unts) + if unl > longestUnits: + longestUnits = unl + if opt.size is None: + lsz = 4 + else: + if type(opt.size) == list: + lsz = len(opt.size.__repr__()) + else: + lsz = len(str(opt.size)) + if lsz > longestSize: + longestSize = lsz + namespace = max([4, longestName]) + whitespace + unitspace = max([5, longestUnits]) + whitespace + sizespace = max([4, longestSize]) + whitespace + fulllength = namespace+unitspace+sizespace+11 + pstr += 'Outputs\n' + pstr += '='*fulllength + pstr += '\n' + pstr += 'Name'.ljust(namespace) + pstr += 'Units'.ljust(unitspace) + pstr += 'Size'.ljust(sizespace) + pstr += 'Description' + pstr += '\n' + pstr += '-'*(namespace - whitespace) + pstr += ' '*whitespace + pstr += '-'*(unitspace - whitespace) + pstr += ' '*whitespace + pstr += '-'*(sizespace - whitespace) + pstr += ' '*whitespace + pstr += '-----------' + pstr += '\n' + for opt in self.outputs: + pstr += opt.name.ljust(namespace) + if opt.units is None: + unts = 'None' + else: + unts = opt.units.__str__()#_repr_html_() + # unts = unts.replace('','^') + # unts = unts.replace('','') + # unts = unts.replace('\[', '[') + # unts = unts.replace('\]', ']') + pstr += unts.ljust(unitspace) + if opt.size is None: + lnstr = 'None' + else: + lnstr = '%s'%(opt.size.__repr__()) + pstr += lnstr.ljust(sizespace) + pstr += opt.description + pstr += '\n' + pstr += '\n' + + return pstr + +# --------------------------------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------------------------------- + + @property + def summary(self): + return self.getSummary() + +# --------------------------------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------------------------------- + def __repr__(self): + pstr = 'AnalysisModel( [' + for i in range(0,len(self.outputs)): + pstr += self.outputs[i].name + pstr += ',' + pstr = pstr[0:-1] + pstr += ']' + pstr += ' == ' + pstr += 'f(' + for ipt in self.inputs: + pstr += ipt.name + pstr += ', ' + pstr = pstr[0:-2] + pstr += ')' + pstr += ' , ' + pstr = pstr[0:-2] + pstr += '])' + return pstr diff --git a/pyomo/contrib/edi/formulation.py b/pyomo/contrib/edi/formulation.py new file mode 100644 index 00000000000..d0e996779ba --- /dev/null +++ b/pyomo/contrib/edi/formulation.py @@ -0,0 +1,305 @@ +import pyomo +import pyomo.environ as pyo +from pyomo.util.check_units import assert_units_consistent +from pyomo.environ import ConcreteModel +from pyomo.environ import Var, Param, Objective, Constraint, Set +from pyomo.environ import maximize, minimize +from pyomo.environ import units as pyomo_units +from pyomo.contrib.pynumero.interfaces.external_grey_box import ExternalGreyBoxModel, ExternalGreyBoxBlock +from pyomo.environ import ( + Reals, + PositiveReals, + NonPositiveReals, + NegativeReals, + NonNegativeReals, + Integers, + PositiveIntegers, + NonPositiveIntegers, + NegativeIntegers, + NonNegativeIntegers, + Boolean, + Binary, + Any, + AnyWithNone, + EmptySet, + UnitInterval, + PercentFraction, + RealInterval, + IntegerInterval, +) + +domainList = [ + Reals, + PositiveReals, + NonPositiveReals, + NegativeReals, + NonNegativeReals, + Integers, + PositiveIntegers, + NonPositiveIntegers, + NegativeIntegers, + NonNegativeIntegers, + Boolean, + Binary, + Any, + AnyWithNone, + EmptySet, + UnitInterval, + PercentFraction, + RealInterval, + IntegerInterval, +] + +def decodeUnits(u_val): + if isinstance(u_val,str): + if u_val in ['','-','None',' ','dimensionless']: + return pyomo_units.__getattr__('dimensionless') + else: + return pyomo_units.__getattr__(u_val) + else: + return u_val + +class RuntimeConstraint(object): + def __init__(self, outputs, operators, inputs, black_box): + + inputs_raw = inputs + outputs_raw = outputs + operators_raw = operators + + if isinstance(inputs_raw, (pyomo.core.base.var.IndexedVar,pyomo.core.base.var.ScalarVar)): + inputs_raw = [inputs_raw] + elif isinstance(inputs_raw, (list, tuple)): + inputs_raw = list(inputs_raw) + else: + raise ValueError("Invalid type for input variables") + + if isinstance(outputs_raw, (pyomo.core.base.var.IndexedVar,pyomo.core.base.var.ScalarVar)): + outputs_raw = [outputs_raw] + elif isinstance(outputs_raw, (list, tuple)): + outputs_raw = list(outputs_raw) + else: + raise ValueError("Invalid type for output variables") + + if isinstance(operators_raw, (list, tuple)): + operators_raw = list(outputs_raw) + elif isinstance(operators_raw,str): + if operators_raw in ["==",">=","<="]: + operators_raw = [operators_raw] + else: + raise ValueError("Invalid operator") + else: + raise ValueError("Invalid type for operators") + + + self.outputs = outputs_raw + self.operators = operators_raw + self.inputs = inputs_raw + self.black_box = black_box + + self.black_box.setOptimizationVariables(inputs_raw, outputs_raw) + +class Formulation(ConcreteModel): + def __init__(self): + super(Formulation, self).__init__() + # self._variable_counter = 1 + # self._constant_counter = 1 + self._objective_counter = 0 + self._constraint_counter = 0 + + self._variable_keys = [] + self._constant_keys = [] + self._objective_keys = [] + self._runtimeObjective_keys = [] + self._objective_keys = [] + self._runtimeConstraint_keys = [] + self._constraint_keys = [] + self._allConstraint_keys = [] + + def Variable(self, name, guess, units, description='', size = None, bounds=None, domain=None): + + if domain is None: + domain = Reals + else: + if domain not in domainList: + raise RuntimeError("Invalid domain") + + if size is not None: + if isinstance(size,(list, tuple)): + for i in range(0,len(size)): + if not isinstance(size[i],int): + raise ValueError('Invalid size. Must be an integer or list/tuple of integers') + if size[i] == 1: + raise ValueError('A value of 1 is not valid for defining size. Use fewer dimensions.') + if i == 0: + st = Set(initialize=list(range(0,size[i]))) + else: + st *= Set(initialize=list(range(0,size[i]))) + st.construct() + self.add_component(name, Var(st, name=name, initialize=guess, domain=domain, doc=description, units=decodeUnits(units))) + else: + if isinstance(size, int): + if size == 1 or size == 0: + self.add_component(name, Var(name=name, initialize=guess, domain=domain, doc=description, units=decodeUnits(units))) + else: + st = Set(initialize=list(range(0,size))) + st.construct() + self.add_component(name, Var(st, name=name, initialize=guess, domain=domain, doc=description, units=decodeUnits(units))) + else: + raise ValueError('Invalid size. Must be an integer or list/tuple of integers') + else: + self.add_component(name, Var(name=name, initialize=guess, domain=domain, doc=description, units=decodeUnits(units))) + self.__dict__[name].construct() + self._variable_keys.append(name) + return self.__dict__[name] + + def Constant(self, name, value, units, description, size=None, within=None): + if within is None: + within = Reals + else: + if within not in domainList: + raise RuntimeError("Invalid within") + + if size is not None: + if isinstance(size,(list, tuple)): + for i in range(0,len(size)): + if not isinstance(size[i],int): + raise ValueError('Invalid size. Must be an integer or list/tuple of integers') + if size[i] == 1: + raise ValueError('A value of 1 is not valid for defining size. Use fewer dimensions.') + if i == 0: + st = Set(initialize=list(range(0,size[i]))) + else: + st *= Set(initialize=list(range(0,size[i]))) + st.construct() + self.add_component(name, Param(st, name=name, initialize=value, within=within, doc=description, units=decodeUnits(units), mutable=True)) + else: + if isinstance(size, int): + if size == 1 or size == 0: + self.add_component(name, Param(name=name, initialize=value, within=within, doc=description, units=decodeUnits(units), mutable=True)) + else: + st = Set(initialize=list(range(0,size))) + st.construct() + self.add_component(name, Param(st, name=name, initialize=value, within=within, doc=description, units=decodeUnits(units), mutable=True)) + else: + raise ValueError('Invalid size. Must be an integer or list/tuple of integers') + else: + self.add_component(name, Param(name=name, initialize=value, within=within, doc=description, units=decodeUnits(units), mutable=True)) + + self.__dict__[name].construct() + self._constant_keys.append(name) + return self.__dict__[name] + + def Objective(self, expr, sense=minimize): + self._objective_counter += 1 + self.add_component('objective_'+str(self._objective_counter) , Objective(expr=expr,sense=sense)) + self._objective_keys.append('objective_'+str(self._objective_counter)) + self.__dict__['objective_'+str(self._objective_counter)].construct() + + # def RuntimeObjective(self): + # pass + + def Constraint(self, expr): + # Should use + # N = [1,2,3] + # a = {1:1, 2:3.1, 3:4.5} + # b = {1:1, 2:2.9, 3:3.1} + # model.y = pyo.Var(N, within=pyo.NonNegativeReals, initialize=0.0) + # def CoverConstr_rule(model, i): return a[i] * model.y[i] >= b[i] + # model.CoverConstr = pyo.Constraint(N, rule=CoverConstr_rule) + # to handle things like "x(a set Variable) <= 20" + self._constraint_counter += 1 + conName = 'constraint_'+str(self._constraint_counter) + self.add_component(conName, Constraint(expr=expr)) + self._constraint_keys.append(conName) + self._allConstraint_keys.append(conName) + self.__dict__[conName].construct() + + + def RuntimeConstraint(self, rcCon): + self._constraint_counter += 1 + conName = 'constraint_'+str(self._constraint_counter) + self._runtimeConstraint_keys.append(conName) + self._allConstraint_keys.append(conName) + + self.add_component(conName, ExternalGreyBoxBlock() ) + self.__dict__[conName].construct() + + # TODO: Need to include operators after Michael fixes things + + inputs_raw = rcCon.inputs + outputs_raw = rcCon.outputs + operators_raw = rcCon.operators + + outputs_raw_length = len(outputs_raw) + operators_raw_length = len(operators_raw) + + outputs_unwrapped = [] + for ovar in outputs_raw: + if isinstance(ovar, pyomo.core.base.var.ScalarVar): + outputs_unwrapped.append(ovar) + elif isinstance(ovar, pyomo.core.base.var.IndexedVar): + validIndicies = list(ovar.index_set().data()) + for vi in validIndicies: + outputs_unwrapped.append(ovar[vi]) + else: + raise ValueError("Invalid type for output variable") + + inputs_unwrapped = [] + for ivar in inputs_raw: + if isinstance(ivar, pyomo.core.base.var.ScalarVar): + inputs_unwrapped.append(ivar) + elif isinstance(ivar, pyomo.core.base.var.IndexedVar): + validIndicies = list(ivar.index_set().data()) + for vi in validIndicies: + inputs_unwrapped.append(ivar[vi]) + else: + raise ValueError("Invalid type for input variable") + + rcCon.black_box._NunwrappedOutputs = len(outputs_unwrapped) + rcCon.black_box._NunwrappedInputs = len(inputs_unwrapped) + + # TODO: Need to unwrap operators + + + self.__dict__[conName].set_external_model(rcCon.black_box, inputs=inputs_unwrapped, outputs=outputs_unwrapped)#,operators=operators_unwrapped) + + def ConstraintList(self, conList): + for i in range(0,len(conList)): + # self._constraint_counter += 1 + con = conList[i] + if isinstance(con, RuntimeConstraint): + self.RuntimeConstraint(con) + else: + self.Constraint(con) + + def get_variables(self): + return [self.__dict__[nm] for nm in self.__dict__.keys() if nm in self._variable_keys] + + def get_constants(self): + return [self.__dict__[nm] for nm in self.__dict__.keys() if nm in self._constant_keys] + + def get_objectives(self): + return [self.__dict__[nm] for nm in self.__dict__.keys() if nm in self._objective_keys] + + def get_constraints(self): + return [self.__dict__[nm] for nm in self.__dict__.keys() if nm in self._allConstraint_keys] + + def get_explicitConstraints(self): + return [self.__dict__[nm] for nm in self.__dict__.keys() if nm in self._constraint_keys] + + def get_runtimeConstraints(self): + return [self.__dict__[nm] for nm in self.__dict__.keys() if nm in self._runtimeConstraint_keys] + + def solve(self): + + # objectives = + for i in range(1,self._objective_counter+1): + assert_units_consistent(self.__dict__['objective_'+str(i)]) + + for i in range(1,self._constraint_counter+1): + if not isinstance(self.__dict__['constraint_'+str(i)],pyomo.contrib.pynumero.interfaces.external_grey_box.ExternalGreyBoxBlock): + assert_units_consistent(self.__dict__['constraint_'+str(i)]) + + + self.pprint() + From 3377110134af8f4909e7b5d0e9028c7b6583801d Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Wed, 26 Jul 2023 20:44:16 -0600 Subject: [PATCH 02/61] adding coloring to the readme --- pyomo/contrib/edi/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyomo/contrib/edi/README.md b/pyomo/contrib/edi/README.md index ce1f9598505..2f6e34f02f6 100644 --- a/pyomo/contrib/edi/README.md +++ b/pyomo/contrib/edi/README.md @@ -17,7 +17,7 @@ The core object in EDI is the `Formulation` object, which inherits from the `py Below is a simple example to get started, but additional resources can be found in the [examples](https://github.com/Pyomo/pyomo/tree/main/pyomo/contrib/edi/examples) folder or in the EDI [documentation](https://pyomo.readthedocs.io/en/stable/contributed_packages/edi/index.html) -``` +```python # ================= # Import Statements # ================= From 604b0a1a0bbecb947012b7c02bc220a2ce3dd6bd Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Wed, 26 Jul 2023 20:50:27 -0600 Subject: [PATCH 03/61] correcting the readme example --- pyomo/contrib/edi/README.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/pyomo/contrib/edi/README.md b/pyomo/contrib/edi/README.md index 2f6e34f02f6..0b77f0cfca8 100644 --- a/pyomo/contrib/edi/README.md +++ b/pyomo/contrib/edi/README.md @@ -23,8 +23,9 @@ Below is a simple example to get started, but additional resources can be found # ================= import numpy as np import pyomo.environ as pyo -from pyomo.environ import units -from pyomo.edi import Formulation, RuntimeConstraint, BlackBoxFunctionModel, bbVariable +from pyomo.environ import PositiveReals, units +from pyomo.contrib.edi.formulation import Formulation, RuntimeConstraint +from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel, BBVariable # =================== # Declare Formulation @@ -32,7 +33,7 @@ from pyomo.edi import Formulation, RuntimeConstraint, BlackBoxFunctionModel, bbV f = Formulation() # ================= -# Declare Variables +# Declare Variables\ # ================= x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') @@ -63,11 +64,11 @@ class UnitCircle(BlackBoxFunctionModel): self.description = 'This model evaluates the function: y = exp(x)' # Declare the black box model inputs - self.inputs.append(bbVariable(name = 'x', size = 0, units = 'ft' , description = 'The x variable')) - self.inputs.append(bbVariable(name = 'y', size = 0, units = 'ft' , description = 'The y variable')) + self.inputs.append(BBVariable(name = 'x', size = 0, units = 'ft' , description = 'The x variable')) + self.inputs.append(BBVariable(name = 'y', size = 0, units = 'ft' , description = 'The y variable')) # Declare the black box model outputs - self.outputs.append(bbVariable('z', None, 'ft**2', 'Resultant of the unit circle evaluation')) + self.outputs.append(BBVariable('z', None, 'ft**2', 'Resultant of the unit circle evaluation')) # Declare the maximum available derivative self.availableDerivative = 2 From eed941f7c10603437591c15753b5d0ae1c603efc Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Wed, 26 Jul 2023 20:51:27 -0600 Subject: [PATCH 04/61] fixing readme again --- pyomo/contrib/edi/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyomo/contrib/edi/README.md b/pyomo/contrib/edi/README.md index 0b77f0cfca8..be62c0bbe28 100644 --- a/pyomo/contrib/edi/README.md +++ b/pyomo/contrib/edi/README.md @@ -23,7 +23,7 @@ Below is a simple example to get started, but additional resources can be found # ================= import numpy as np import pyomo.environ as pyo -from pyomo.environ import PositiveReals, units +from pyomo.environ import units from pyomo.contrib.edi.formulation import Formulation, RuntimeConstraint from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel, BBVariable From f5a3232c101d6e11e1bc3343cef800daa26d556f Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Wed, 26 Jul 2023 20:52:13 -0600 Subject: [PATCH 05/61] fixing readme again again --- pyomo/contrib/edi/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyomo/contrib/edi/README.md b/pyomo/contrib/edi/README.md index be62c0bbe28..d7521ae3131 100644 --- a/pyomo/contrib/edi/README.md +++ b/pyomo/contrib/edi/README.md @@ -33,7 +33,7 @@ from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel, BBVar f = Formulation() # ================= -# Declare Variables\ +# Declare Variables # ================= x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') From d2696b6da6e13925df3d8e2e70fe0e3c8bc8750d Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Wed, 26 Jul 2023 20:53:53 -0600 Subject: [PATCH 06/61] fixing readme again again again --- pyomo/contrib/edi/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyomo/contrib/edi/README.md b/pyomo/contrib/edi/README.md index d7521ae3131..19d8fc2b123 100644 --- a/pyomo/contrib/edi/README.md +++ b/pyomo/contrib/edi/README.md @@ -61,7 +61,7 @@ class UnitCircle(BlackBoxFunctionModel): super(UnitCircle, self).__init__() # A brief description of the model - self.description = 'This model evaluates the function: y = exp(x)' + self.description = 'This model evaluates the function: z = x**2 + y**2' # Declare the black box model inputs self.inputs.append(BBVariable(name = 'x', size = 0, units = 'ft' , description = 'The x variable')) From eba2990830dc670918a47f87752301d4773784b2 Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Wed, 26 Jul 2023 20:55:15 -0600 Subject: [PATCH 07/61] updating readme, I really should read... --- pyomo/contrib/edi/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyomo/contrib/edi/README.md b/pyomo/contrib/edi/README.md index 19d8fc2b123..3bccb69df99 100644 --- a/pyomo/contrib/edi/README.md +++ b/pyomo/contrib/edi/README.md @@ -68,7 +68,7 @@ class UnitCircle(BlackBoxFunctionModel): self.inputs.append(BBVariable(name = 'y', size = 0, units = 'ft' , description = 'The y variable')) # Declare the black box model outputs - self.outputs.append(BBVariable('z', None, 'ft**2', 'Resultant of the unit circle evaluation')) + self.outputs.append(BBVariable(name = 'z', size = 0, units = 'ft**2', description = 'Resultant of the unit circle evaluation')) # Declare the maximum available derivative self.availableDerivative = 2 From f625ef21ddb7e98ed04290ea22513bd101922fc4 Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Thu, 27 Jul 2023 10:46:58 -0600 Subject: [PATCH 08/61] updating some legal stuff --- pyomo/contrib/edi/README.md | 20 +++++++++++++++++++- pyomo/contrib/edi/__init__.py | 2 +- pyomo/contrib/edi/blackBoxFunctionModel.py | 17 +++++++++++++++++ pyomo/contrib/edi/formulation.py | 17 +++++++++++++++++ 4 files changed, 54 insertions(+), 2 deletions(-) diff --git a/pyomo/contrib/edi/README.md b/pyomo/contrib/edi/README.md index 3bccb69df99..09b0776dc3b 100644 --- a/pyomo/contrib/edi/README.md +++ b/pyomo/contrib/edi/README.md @@ -99,4 +99,22 @@ f.ConstraintList( z <= 1*units.m**2 ] ) -``` \ No newline at end of file +``` + + + +## Acknowledgement + +Pyomo: Python Optimization Modeling Objects +Copyright (c) 2008-2023 +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. + +Development of this module was conducted as part of the Institute for +the Design of Advanced Energy Systems (IDAES) with support through the +Simulation-Based Engineering, Crosscutting Research Program within the +U.S. Department of Energy’s Office of Fossil Energy and Carbon Management. + +This software is distributed under the 3-clause BSD License. \ No newline at end of file diff --git a/pyomo/contrib/edi/__init__.py b/pyomo/contrib/edi/__init__.py index bce95c9b6ae..f4b757ba3e6 100644 --- a/pyomo/contrib/edi/__init__.py +++ b/pyomo/contrib/edi/__init__.py @@ -1,7 +1,7 @@ # ___________________________________________________________________________ # # Pyomo: Python Optimization Modeling Objects -# Copyright (c) 2008-2022 +# Copyright (c) 2008-2023 # 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 diff --git a/pyomo/contrib/edi/blackBoxFunctionModel.py b/pyomo/contrib/edi/blackBoxFunctionModel.py index 3550d5d2ea8..3c6f8f1ef14 100644 --- a/pyomo/contrib/edi/blackBoxFunctionModel.py +++ b/pyomo/contrib/edi/blackBoxFunctionModel.py @@ -1,3 +1,20 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright (c) 2008-2023 +# 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. +# +# Development of this module was conducted as part of the Institute for +# the Design of Advanced Energy Systems (IDAES) with support through the +# Simulation-Based Engineering, Crosscutting Research Program within the +# U.S. Department of Energy’s Office of Fossil Energy and Carbon Management. +# +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + import copy import numpy as np import pyomo diff --git a/pyomo/contrib/edi/formulation.py b/pyomo/contrib/edi/formulation.py index d0e996779ba..7c9771d9b48 100644 --- a/pyomo/contrib/edi/formulation.py +++ b/pyomo/contrib/edi/formulation.py @@ -1,3 +1,20 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright (c) 2008-2023 +# 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. +# +# Development of this module was conducted as part of the Institute for +# the Design of Advanced Energy Systems (IDAES) with support through the +# Simulation-Based Engineering, Crosscutting Research Program within the +# U.S. Department of Energy’s Office of Fossil Energy and Carbon Management. +# +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + import pyomo import pyomo.environ as pyo from pyomo.util.check_units import assert_units_consistent From b8dd90e79115bc755382bd724988ca422c78b848 Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Thu, 27 Jul 2023 10:49:10 -0600 Subject: [PATCH 09/61] fixing the readme --- pyomo/contrib/edi/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyomo/contrib/edi/README.md b/pyomo/contrib/edi/README.md index 09b0776dc3b..4541f8126c7 100644 --- a/pyomo/contrib/edi/README.md +++ b/pyomo/contrib/edi/README.md @@ -105,9 +105,9 @@ f.ConstraintList( ## Acknowledgement -Pyomo: Python Optimization Modeling Objects -Copyright (c) 2008-2023 -National Technology and Engineering Solutions of Sandia, LLC +Pyomo: Python Optimization Modeling Objects +Copyright (c) 2008-2023 +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. From 4603e40c67d953dc0c67df22af8eb508c10ad7f4 Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Thu, 27 Jul 2023 13:35:20 -0600 Subject: [PATCH 10/61] adding Michaels help: --- pyomo/contrib/edi/README.md | 10 +- pyomo/contrib/edi/__init__.py | 14 +-- pyomo/contrib/edi/examples/readme_example.py | 80 +++++++++++++ pyomo/contrib/edi/formulation.py | 4 +- pyomo/contrib/edi/tests/test_formulation.py | 118 +++++++++++++++++++ 5 files changed, 209 insertions(+), 17 deletions(-) create mode 100644 pyomo/contrib/edi/examples/readme_example.py create mode 100644 pyomo/contrib/edi/tests/test_formulation.py diff --git a/pyomo/contrib/edi/README.md b/pyomo/contrib/edi/README.md index 4541f8126c7..6f783c99fb5 100644 --- a/pyomo/contrib/edi/README.md +++ b/pyomo/contrib/edi/README.md @@ -4,11 +4,9 @@ The Pyomo Engineering Design Interface (EDI) is a lightweight wrapper on the Pyo ## Installation -EDI follows the standard installation process for Pyomo extensions: +EDI is a part of the standard installation process for Pyomo: ``` pip install pyomo -pyomo download-extensions -pyomo build-extensions ``` ## Usage @@ -21,11 +19,9 @@ Below is a simple example to get started, but additional resources can be found # ================= # Import Statements # ================= -import numpy as np import pyomo.environ as pyo from pyomo.environ import units -from pyomo.contrib.edi.formulation import Formulation, RuntimeConstraint -from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel, BBVariable +from pyomo.contrib.edi import Formulation, RuntimeConstraint, BlackBoxFunctionModel, BBVariable # =================== # Declare Formulation @@ -101,8 +97,6 @@ f.ConstraintList( ) ``` - - ## Acknowledgement Pyomo: Python Optimization Modeling Objects diff --git a/pyomo/contrib/edi/__init__.py b/pyomo/contrib/edi/__init__.py index f4b757ba3e6..5ed60c7e6ac 100644 --- a/pyomo/contrib/edi/__init__.py +++ b/pyomo/contrib/edi/__init__.py @@ -15,12 +15,12 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -# # Recommended just to build all of the appropriate things -# import pyomo.environ +# Recommended just to build all of the appropriate things +import pyomo.environ -# # Import the relevant classes from Formulation -# from formulation import Formulation, RuntimeConstraint +# Import the relevant classes from Formulation +from formulation import Formulation, RuntimeConstraint -# # Import the black box modeling tools -# from blackBoxFunctionModel import BlackBoxFunctionModel -# from blackBoxFunctionModel import BlackBoxFunctionModel_Variable as bbVariable \ No newline at end of file +# Import the black box modeling tools +from blackBoxFunctionModel import BlackBoxFunctionModel +from blackBoxFunctionModel import BlackBoxFunctionModel_Variable as BBVariable \ No newline at end of file diff --git a/pyomo/contrib/edi/examples/readme_example.py b/pyomo/contrib/edi/examples/readme_example.py new file mode 100644 index 00000000000..53a3be300a5 --- /dev/null +++ b/pyomo/contrib/edi/examples/readme_example.py @@ -0,0 +1,80 @@ +# ================= +# Import Statements +# ================= +import pyomo.environ as pyo +from pyomo.environ import units +from pyomo.contrib.edi.formulation import Formulation, RuntimeConstraint +from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel, BBVariable + +# =================== +# Declare Formulation +# =================== +f = Formulation() + +# ================= +# Declare Variables +# ================= +x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') +y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') +z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The unit circle output') + +# ================= +# Declare Constants +# ================= +c = f.Constant(name = 'c', value = 1.0, units = '', description = 'A constant c', size = 2) + +# ===================== +# Declare the Objective +# ===================== +f.Objective( + c[0]*x + c[1]*y +) + +# =================== +# Declare a Black Box +# =================== +class UnitCircle(BlackBoxFunctionModel): + def __init__(self): # The initalization function + + # Initalize the black box model + super(UnitCircle, self).__init__() + + # A brief description of the model + self.description = 'This model evaluates the function: z = x**2 + y**2' + + # Declare the black box model inputs + self.inputs.append(BBVariable(name = 'x', size = 0, units = 'ft' , description = 'The x variable')) + self.inputs.append(BBVariable(name = 'y', size = 0, units = 'ft' , description = 'The y variable')) + + # Declare the black box model outputs + self.outputs.append(BBVariable(name = 'z', size = 0, units = 'ft**2', description = 'Resultant of the unit circle evaluation')) + + # Declare the maximum available derivative + self.availableDerivative = 2 + + # Post-initalization setup + self.post_init_setup(len(self.inputs)) + + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float + y = pyo.value(units.convert(y,self.inputs[0].units)) # Converts to correct units then casts to float + + z = x**2 + y**2 # Compute z + dzdx = 2*x # Compute dz/dx + dzdy = 2*y # Compute dz/dy + + z *= units.ft**2 + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + +# ======================= +# Declare the Constraints +# ======================= +f.ConstraintList( + [ + RuntimeConstraint(z, '==', [x,y], UnitCircle() ) , + z <= 1*units.m**2 + ] +) \ No newline at end of file diff --git a/pyomo/contrib/edi/formulation.py b/pyomo/contrib/edi/formulation.py index 7c9771d9b48..3604c29c1b2 100644 --- a/pyomo/contrib/edi/formulation.py +++ b/pyomo/contrib/edi/formulation.py @@ -145,8 +145,8 @@ def Variable(self, name, guess, units, description='', size = None, bounds=None, for i in range(0,len(size)): if not isinstance(size[i],int): raise ValueError('Invalid size. Must be an integer or list/tuple of integers') - if size[i] == 1: - raise ValueError('A value of 1 is not valid for defining size. Use fewer dimensions.') + if size[i] == 1 or size[i] == 0 : + raise ValueError('A value of 0 or 1 is not valid for defining size. Use fewer dimensions.') if i == 0: st = Set(initialize=list(range(0,size[i]))) else: diff --git a/pyomo/contrib/edi/tests/test_formulation.py b/pyomo/contrib/edi/tests/test_formulation.py new file mode 100644 index 00000000000..cde8909a21c --- /dev/null +++ b/pyomo/contrib/edi/tests/test_formulation.py @@ -0,0 +1,118 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright (c) 2008-2023 +# 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. +# +# Development of this module was conducted as part of the Institute for +# the Design of Advanced Energy Systems (IDAES) with support through the +# Simulation-Based Engineering, Crosscutting Research Program within the +# U.S. Department of Energy’s Office of Fossil Energy and Carbon Management. +# +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + +import pyomo.common.unittest as unittest +import pyomo.environ as pyo +# from pyomo.opt import check_optimal_termination +from pyomo.common.dependencies import attempt_import + +# np, numpy_available = attempt_import( +# 'numpy', 'edi requires numpy' +# ) +# scipy, scipy_available = attempt_import( +# 'scipy', 'inverse_reduced_hessian requires scipy' +# ) + +# if not (numpy_available and scipy_available): +# if not numpy_available: +# raise unittest.SkipTest( +# 'edi.formulation tests require numpy' +# ) + +class TestEDIFormulation(unittest.TestCase): + def test_edi_formulation_init(self): + "Tests that a formulation initializes to the correct type and has proper data" + from pyomo.environ import ConcreteModel + from pyomo.contrib.edi.formulation import Formulation + f = Formulation() + + self.assertIsInstance(f, Formulation) + self.assertIsInstance(f, ConcreteModel) + + self.assertEqual(f._objective_counter , 0 ) + self.assertEqual(f._constraint_counter , 0 ) + self.assertEqual(f._variable_keys , [] ) + self.assertEqual(f._constant_keys , [] ) + self.assertEqual(f._objective_keys , [] ) + self.assertEqual(f._runtimeObjective_keys , [] ) + self.assertEqual(f._objective_keys , [] ) + self.assertEqual(f._runtimeConstraint_keys , [] ) + self.assertEqual(f._constraint_keys , [] ) + self.assertEqual(f._allConstraint_keys , [] ) + + + def test_edi_formulation_variable(self): + "Tests the variable constructor in edi.formulation" + from pyomo.contrib.edi.formulation import Formulation + from pyomo.environ import Reals, PositiveReals + + f = Formulation() + + x1 = f.Variable(name = 'x1', guess = 1.0, units = 'm' , description = 'The x variable', size = None, bounds=None, domain=None ) + self.assertRaises(RuntimeError,f.Variable,*('x1', 1.0, 'm')) + x2 = f.Variable('x2', 1.0, 'm') + x3 = f.Variable('x3', 1.0, 'm' , 'The x variable', None, None, None ) + x4 = f.Variable(name = 'x4', guess = 1.0, units = 'm' , description = 'The x variable', size = None, bounds=None, domain=PositiveReals ) + self.assertRaises(RuntimeError, f.Variable, **{'name':'x5', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': None, 'bounds':None, 'domain':"error" }) + + x6 = f.Variable(name = 'x6', guess = 1.0, units = 'm' , description = 'The x variable', size = 0, bounds=None, domain=None ) + x7 = f.Variable(name = 'x7', guess = 1.0, units = 'm' , description = 'The x variable', size = 5, bounds=None, domain=None ) + self.assertRaises(ValueError, f.Variable, **{'name':'x8', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': 'error', 'bounds':None, 'domain':None }) + + x9 = f.Variable(name = 'x9', guess = 1.0, units = 'm' , description = 'The x variable', size = [2,2], bounds=None, domain=None ) + self.assertRaises(ValueError, f.Variable, **{'name':'x10', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': ['2','2'], 'bounds':None, 'domain':None }) + self.assertRaises(ValueError, f.Variable, **{'name':'x11', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': [2,1], 'bounds':None, 'domain':None }) + + + + + +# def Variable(self, name, guess, units, description='', size = None, bounds=None, domain=None): +# def Constant(self, name, value, units, description, size=None, within=None): +# def Objective(self, expr, sense=minimize): +# def RuntimeObjective(self): +# def Constraint(self, expr): +# def CoverConstr_rule(model, i): return a[i] * model.y[i] >= b[i] +# def RuntimeConstraint(self, rcCon): +# def ConstraintList(self, conList): +# def get_variables(self): +# def get_constants(self): +# def get_objectives(self): +# def get_constraints(self): +# def get_explicitConstraints(self): +# def get_runtimeConstraints(self): +# def solve(self): + +if __name__ == '__main__': + unittest.main() + + + + + + + + + + + + + + + + + From 39ea9603f171e75cd00a5294d870774cb74c5a3b Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Thu, 27 Jul 2023 17:16:52 -0600 Subject: [PATCH 11/61] working on unit tests --- pyomo/contrib/edi/__init__.py | 6 +- pyomo/contrib/edi/formulation.py | 30 +++-- pyomo/contrib/edi/tests/test_formulation.py | 124 +++++++++++++++++--- 3 files changed, 129 insertions(+), 31 deletions(-) diff --git a/pyomo/contrib/edi/__init__.py b/pyomo/contrib/edi/__init__.py index 5ed60c7e6ac..0f562e0a4da 100644 --- a/pyomo/contrib/edi/__init__.py +++ b/pyomo/contrib/edi/__init__.py @@ -19,8 +19,8 @@ import pyomo.environ # Import the relevant classes from Formulation -from formulation import Formulation, RuntimeConstraint +from pyomo.contrib.edi.formulation import Formulation, RuntimeConstraint # Import the black box modeling tools -from blackBoxFunctionModel import BlackBoxFunctionModel -from blackBoxFunctionModel import BlackBoxFunctionModel_Variable as BBVariable \ No newline at end of file +from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel +from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable as BBVariable \ No newline at end of file diff --git a/pyomo/contrib/edi/formulation.py b/pyomo/contrib/edi/formulation.py index 3604c29c1b2..38fc8a7d904 100644 --- a/pyomo/contrib/edi/formulation.py +++ b/pyomo/contrib/edi/formulation.py @@ -140,6 +140,19 @@ def Variable(self, name, guess, units, description='', size = None, bounds=None, if domain not in domainList: raise RuntimeError("Invalid domain") + if bounds is not None: + if not isinstance(bounds,(list,tuple)): + raise ValueError('The keyword bounds must be a 2 length list or tuple of floats') + if len(bounds)!=2: + raise ValueError('The keyword bounds must be a 2 length list or tuple of floats') + if not isinstance(bounds[0], (float, int)): + raise ValueError('The keyword bounds must be a 2 length list or tuple of floats') + if not isinstance(bounds[1], (float, int)): + raise ValueError('The keyword bounds must be a 2 length list or tuple of floats') + if bounds[0] > bounds[1]: + raise ValueError("Lower bound is higher than upper bound") + + if size is not None: if isinstance(size,(list, tuple)): for i in range(0,len(size)): @@ -152,24 +165,24 @@ def Variable(self, name, guess, units, description='', size = None, bounds=None, else: st *= Set(initialize=list(range(0,size[i]))) st.construct() - self.add_component(name, Var(st, name=name, initialize=guess, domain=domain, doc=description, units=decodeUnits(units))) + self.add_component(name, Var(st, name=name, initialize=guess, domain=domain, bounds=bounds, doc=description, units=decodeUnits(units))) else: if isinstance(size, int): if size == 1 or size == 0: - self.add_component(name, Var(name=name, initialize=guess, domain=domain, doc=description, units=decodeUnits(units))) + self.add_component(name, Var(name=name, initialize=guess, domain=domain, bounds=bounds, doc=description, units=decodeUnits(units))) else: st = Set(initialize=list(range(0,size))) st.construct() - self.add_component(name, Var(st, name=name, initialize=guess, domain=domain, doc=description, units=decodeUnits(units))) + self.add_component(name, Var(st, name=name, initialize=guess, domain=domain, bounds=bounds, doc=description, units=decodeUnits(units))) else: raise ValueError('Invalid size. Must be an integer or list/tuple of integers') else: - self.add_component(name, Var(name=name, initialize=guess, domain=domain, doc=description, units=decodeUnits(units))) + self.add_component(name, Var(name=name, initialize=guess, domain=domain, bounds=bounds, doc=description, units=decodeUnits(units))) self.__dict__[name].construct() self._variable_keys.append(name) return self.__dict__[name] - def Constant(self, name, value, units, description, size=None, within=None): + def Constant(self, name, value, units, description='', size=None, within=None): if within is None: within = Reals else: @@ -306,10 +319,8 @@ def get_explicitConstraints(self): def get_runtimeConstraints(self): return [self.__dict__[nm] for nm in self.__dict__.keys() if nm in self._runtimeConstraint_keys] - - def solve(self): - # objectives = + def check_units(self): for i in range(1,self._objective_counter+1): assert_units_consistent(self.__dict__['objective_'+str(i)]) @@ -317,6 +328,5 @@ def solve(self): if not isinstance(self.__dict__['constraint_'+str(i)],pyomo.contrib.pynumero.interfaces.external_grey_box.ExternalGreyBoxBlock): assert_units_consistent(self.__dict__['constraint_'+str(i)]) - - self.pprint() + diff --git a/pyomo/contrib/edi/tests/test_formulation.py b/pyomo/contrib/edi/tests/test_formulation.py index cde8909a21c..2844bc4a83e 100644 --- a/pyomo/contrib/edi/tests/test_formulation.py +++ b/pyomo/contrib/edi/tests/test_formulation.py @@ -37,7 +37,7 @@ class TestEDIFormulation(unittest.TestCase): def test_edi_formulation_init(self): "Tests that a formulation initializes to the correct type and has proper data" from pyomo.environ import ConcreteModel - from pyomo.contrib.edi.formulation import Formulation + from pyomo.contrib.edi import Formulation f = Formulation() self.assertIsInstance(f, Formulation) @@ -57,7 +57,8 @@ def test_edi_formulation_init(self): def test_edi_formulation_variable(self): "Tests the variable constructor in edi.formulation" - from pyomo.contrib.edi.formulation import Formulation + import pyomo + from pyomo.contrib.edi import Formulation from pyomo.environ import Reals, PositiveReals f = Formulation() @@ -72,30 +73,117 @@ def test_edi_formulation_variable(self): x6 = f.Variable(name = 'x6', guess = 1.0, units = 'm' , description = 'The x variable', size = 0, bounds=None, domain=None ) x7 = f.Variable(name = 'x7', guess = 1.0, units = 'm' , description = 'The x variable', size = 5, bounds=None, domain=None ) self.assertRaises(ValueError, f.Variable, **{'name':'x8', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': 'error', 'bounds':None, 'domain':None }) - x9 = f.Variable(name = 'x9', guess = 1.0, units = 'm' , description = 'The x variable', size = [2,2], bounds=None, domain=None ) self.assertRaises(ValueError, f.Variable, **{'name':'x10', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': ['2','2'], 'bounds':None, 'domain':None }) self.assertRaises(ValueError, f.Variable, **{'name':'x11', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': [2,1], 'bounds':None, 'domain':None }) + x12 = f.Variable(name = 'x12', guess = 1.0, units = 'm' , description = 'The x variable', size = None, bounds=[-10,10], domain=None ) + self.assertRaises(ValueError, f.Variable, **{'name':'x13', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': None, 'bounds':[10,-10], 'domain':None }) + self.assertRaises(ValueError, f.Variable, **{'name':'x14', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': None, 'bounds':["-10","10"], 'domain':None }) + self.assertRaises(ValueError, f.Variable, **{'name':'x15', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': None, 'bounds':[1,2,3], 'domain':None }) + self.assertRaises(ValueError, f.Variable, **{'name':'x16', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': None, 'bounds':"error", 'domain':None }) + self.assertRaises(ValueError, f.Variable, **{'name':'x17', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': None, 'bounds':[0,"10"], 'domain':None }) + + + def test_edi_formulation_constant(self): + "Tests the constant constructor in edi.formulation" + from pyomo.contrib.edi import Formulation + from pyomo.environ import Reals, PositiveReals + + f = Formulation() + + c1 = f.Constant(name = 'c1', value = 1.0, units = 'm', description = 'A constant c', size = None, within = None) + self.assertRaises(RuntimeError,f.Constant,*('c1', 1.0, 'm')) + c2 = f.Constant('c2', 1.0, 'm') + c3 = f.Constant('c3', 1.0, 'm', 'A constant c', None, None) + c4 = f.Constant(name = 'c4', value = 1.0, units = 'm' , description = 'A constant c', size = None, within=PositiveReals ) + self.assertRaises(RuntimeError, f.Constant, **{'name':'c5', 'value':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': None, 'within':"error" }) + + c6 = f.Constant(name = 'c6', value = 1.0, units = 'm' , description = 'A constant c', size = 0, within=None ) + c7 = f.Constant(name = 'c7', value = 1.0, units = 'm' , description = 'A constant c', size = 5, within=None ) + self.assertRaises(ValueError, f.Constant, **{'name':'c8', 'value':1.0 , 'units' :'m' , 'description': 'A constant c', 'size': 'error', 'within':None }) + c9 = f.Constant(name = 'c9', value = 1.0, units = 'm' , description = 'A constant c', size = [2,2], within=None ) + self.assertRaises(ValueError, f.Constant, **{'name':'c10', 'value':1.0 , 'units' :'m' , 'description': 'A constant c', 'size': ['2','2'], 'within':None }) + self.assertRaises(ValueError, f.Constant, **{'name':'c11', 'value':1.0 , 'units' :'m' , 'description': 'A constant c', 'size': [2,1], 'within':None }) + + + def test_edi_formulation_objective(self): + "Tests the objective constructor in edi.formulation" + import pyomo.environ as pyo + from pyomo.environ import units + from pyomo.contrib.edi import Formulation + + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + f.Objective( x + y ) + + def test_edi_formulation_runtimeobjective(self): + "Tests the runtime objective constructor in edi.formulation" + # TODO: not currently implemented, see: https://github.com/codykarcher/pyomo/issues/5 + pass + + def test_edi_formulation_constraint(self): + "Tests the constraint constructor in edi.formulation" + pass + + + def test_edi_formulation_runtimeconstraint(self): + "Tests the runtime constraint constructor in edi.formulation" + pass + + + def test_edi_formulation_constraintlist(self): + "Tests the constraint list constructor in edi.formulation" + pass + + def test_edi_formulation_getvariables(self): + "Tests the get_variables function in edi.formulation" + import pyomo.environ as pyo + from pyomo.environ import units + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + + vrs = f.get_variables() + self.assertListEqual(vrs, [x,y]) + + def test_edi_formulation_getconstants(self): + "Tests the get_constants function in edi.formulation" + import pyomo.environ as pyo + from pyomo.environ import units + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + + c1 = f.Constant(name = 'c1', value = 1.0, units = 'm', description = 'A constant c1', size = None, within = None) + c2 = f.Constant(name = 'c2', value = 1.0, units = 'm', description = 'A constant c2', size = None, within = None) + + csts = f.get_constants() + self.assertListEqual(csts, [c1,c2]) + + def test_edi_formulation_getobjectives(self): + "Tests the get_objectives function in edi.formulation" + pass + + def test_edi_formulation_getconstraints(self): + "Tests the get_constraints function in edi.formulation" + pass + def test_edi_formulation_getexplicitconstraints(self): + "Tests the get_explicitConstraints function in edi.formulation" + pass + def test_edi_formulation_getruntimeconstraints(self): + "Tests the get_runtimeConstraints function in edi.formulation" + pass + def test_edi_formulation_checkunits(self): + "Tests the check_units function in edi.formulation" + pass -# def Variable(self, name, guess, units, description='', size = None, bounds=None, domain=None): -# def Constant(self, name, value, units, description, size=None, within=None): -# def Objective(self, expr, sense=minimize): -# def RuntimeObjective(self): -# def Constraint(self, expr): -# def CoverConstr_rule(model, i): return a[i] * model.y[i] >= b[i] -# def RuntimeConstraint(self, rcCon): -# def ConstraintList(self, conList): -# def get_variables(self): -# def get_constants(self): -# def get_objectives(self): -# def get_constraints(self): -# def get_explicitConstraints(self): -# def get_runtimeConstraints(self): -# def solve(self): if __name__ == '__main__': unittest.main() From fe2821f1063c74e7053141fe388ff61de0d168b2 Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Thu, 27 Jul 2023 17:22:29 -0600 Subject: [PATCH 12/61] minor fixes --- pyomo/contrib/edi/README.md | 2 +- pyomo/contrib/edi/examples/readme_example.py | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/pyomo/contrib/edi/README.md b/pyomo/contrib/edi/README.md index 6f783c99fb5..fa45c5cb0ac 100644 --- a/pyomo/contrib/edi/README.md +++ b/pyomo/contrib/edi/README.md @@ -74,7 +74,7 @@ class UnitCircle(BlackBoxFunctionModel): def BlackBox(self, x, y): # The actual function that does things x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float - y = pyo.value(units.convert(y,self.inputs[0].units)) # Converts to correct units then casts to float + y = pyo.value(units.convert(y,self.inputs[1].units)) # Converts to correct units then casts to float z = x**2 + y**2 # Compute z dzdx = 2*x # Compute dz/dx diff --git a/pyomo/contrib/edi/examples/readme_example.py b/pyomo/contrib/edi/examples/readme_example.py index 53a3be300a5..8190e45e9ec 100644 --- a/pyomo/contrib/edi/examples/readme_example.py +++ b/pyomo/contrib/edi/examples/readme_example.py @@ -3,8 +3,7 @@ # ================= import pyomo.environ as pyo from pyomo.environ import units -from pyomo.contrib.edi.formulation import Formulation, RuntimeConstraint -from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel, BBVariable +from pyomo.contrib.edi import Formulation, RuntimeConstraint, BlackBoxFunctionModel, BBVariable # =================== # Declare Formulation @@ -57,7 +56,7 @@ def __init__(self): # The initalization function def BlackBox(self, x, y): # The actual function that does things x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float - y = pyo.value(units.convert(y,self.inputs[0].units)) # Converts to correct units then casts to float + y = pyo.value(units.convert(y,self.inputs[1].units)) # Converts to correct units then casts to float z = x**2 + y**2 # Compute z dzdx = 2*x # Compute dz/dx From 550fb70c4f9cf829e66244e00659327171e29f8a Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Thu, 27 Jul 2023 22:46:34 -0600 Subject: [PATCH 13/61] still writing tests --- pyomo/contrib/edi/blackBoxFunctionModel.py | 4 +- pyomo/contrib/edi/formulation.py | 1 - pyomo/contrib/edi/tests/test_formulation.py | 130 ++++++++++++++++++-- 3 files changed, 121 insertions(+), 14 deletions(-) diff --git a/pyomo/contrib/edi/blackBoxFunctionModel.py b/pyomo/contrib/edi/blackBoxFunctionModel.py index 3c6f8f1ef14..6d8325a5455 100644 --- a/pyomo/contrib/edi/blackBoxFunctionModel.py +++ b/pyomo/contrib/edi/blackBoxFunctionModel.py @@ -153,9 +153,7 @@ def description(self, val): else: raise ValueError('Invalid description. Must be a string.') -BBVariable = BlackBoxFunctionModel_Variable - -errorString = 'This function is calling to the base Model class and has not been defined.' +errorString = 'This function is calling to the base class and has not been defined.' class BlackBoxFunctionModel(ExternalGreyBoxModel): # --------------------------------------------------------------------------------------------------------------------- diff --git a/pyomo/contrib/edi/formulation.py b/pyomo/contrib/edi/formulation.py index 38fc8a7d904..9c09e774bb3 100644 --- a/pyomo/contrib/edi/formulation.py +++ b/pyomo/contrib/edi/formulation.py @@ -295,7 +295,6 @@ def RuntimeConstraint(self, rcCon): def ConstraintList(self, conList): for i in range(0,len(conList)): - # self._constraint_counter += 1 con = conList[i] if isinstance(con, RuntimeConstraint): self.RuntimeConstraint(con) diff --git a/pyomo/contrib/edi/tests/test_formulation.py b/pyomo/contrib/edi/tests/test_formulation.py index 2844bc4a83e..e03a534b514 100644 --- a/pyomo/contrib/edi/tests/test_formulation.py +++ b/pyomo/contrib/edi/tests/test_formulation.py @@ -166,23 +166,133 @@ def test_edi_formulation_getconstants(self): def test_edi_formulation_getobjectives(self): "Tests the get_objectives function in edi.formulation" - pass + import pyomo.environ as pyo + from pyomo.environ import units + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + f.Objective(x+y) + objList = f.get_objectives() + self.assertTrue(len(objList)==1) + # not really sure how to check this, so I wont def test_edi_formulation_getconstraints(self): - "Tests the get_constraints function in edi.formulation" - pass + "Tests the get_constraints, get_explicitConstraints, and get_runtimeConstraints functions in edi.formulation" + # ================= + # Import Statements + # ================= + import pyomo.environ as pyo + from pyomo.environ import units + from pyomo.contrib.edi import Formulation, RuntimeConstraint, BlackBoxFunctionModel, BBVariable - def test_edi_formulation_getexplicitconstraints(self): - "Tests the get_explicitConstraints function in edi.formulation" - pass + # =================== + # Declare Formulation + # =================== + f = Formulation() - def test_edi_formulation_getruntimeconstraints(self): - "Tests the get_runtimeConstraints function in edi.formulation" - pass + # ================= + # Declare Variables + # ================= + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The unit circle output') + + # ================= + # Declare Constants + # ================= + c = f.Constant(name = 'c', value = 1.0, units = '', description = 'A constant c', size = 2) + + # ===================== + # Declare the Objective + # ===================== + f.Objective( + c[0]*x + c[1]*y + ) + + # =================== + # Declare a Black Box + # =================== + class UnitCircle(BlackBoxFunctionModel): + def __init__(self): # The initalization function + + # Initalize the black box model + super(UnitCircle, self).__init__() + + # A brief description of the model + self.description = 'This model evaluates the function: z = x**2 + y**2' + + # Declare the black box model inputs + self.inputs.append(BBVariable(name = 'x', size = 0, units = 'ft' , description = 'The x variable')) + self.inputs.append(BBVariable(name = 'y', size = 0, units = 'ft' , description = 'The y variable')) + + # Declare the black box model outputs + self.outputs.append(BBVariable(name = 'z', size = 0, units = 'ft**2', description = 'Resultant of the unit circle evaluation')) + + # Declare the maximum available derivative + self.availableDerivative = 2 + + # Post-initalization setup + self.post_init_setup(len(self.inputs)) + + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float + y = pyo.value(units.convert(y,self.inputs[1].units)) # Converts to correct units then casts to float + + z = x**2 + y**2 # Compute z + dzdx = 2*x # Compute dz/dx + dzdy = 2*y # Compute dz/dy + + z *= units.ft**2 + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + + # ======================= + # Declare the Constraints + # ======================= + f.ConstraintList( + [ + RuntimeConstraint(z, '==', [x,y], UnitCircle() ) , + z <= 1*units.m**2 + ] + ) + + cl = f.get_constraints() + ecl = f.get_explicitConstraints() + rcl = f.get_runtimeConstraints() + + self.assertTrue(len(cl)==2) + self.assertTrue(len(ecl)==1) + self.assertTrue(len(rcl)==1) def test_edi_formulation_checkunits(self): "Tests the check_units function in edi.formulation" - pass + import pyomo + import pyomo.environ as pyo + from pyomo.environ import units + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + + f.Objective(x+y) + f.Constraint(x+y <= 1.0*units.m) + f.check_units() + + f.Constraint(2.0*x+y <= 1.0) + self.assertRaises(pyomo.core.base.units_container.UnitsError, f.check_units, *()) + + + f2 = Formulation() + u = f2.Variable(name = 'u', guess = 1.0, units = 'm' , description = 'The u variable') + v = f2.Variable(name = 'v', guess = 1.0, units = 'kg' , description = 'The v variable') + f2.Objective(u+v) + self.assertRaises(pyomo.core.base.units_container.UnitsError, f2.check_units, *()) + + + if __name__ == '__main__': From f3d306fb7292e25ba9d792b721d7b83b48085b0d Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Sun, 30 Jul 2023 20:51:08 -0600 Subject: [PATCH 14/61] working on documentation --- .../edi/blackboxconstraints.rst | 11 +++ .../edi/blackboxobjectives.rst | 6 ++ .../contributed_packages/edi/constants.rst | 2 + .../contributed_packages/edi/constraints.rst | 17 ++++ .../contributed_packages/edi/examples.rst | 13 +++ .../contributed_packages/edi/formulation.rst | 92 +++++++++++++++++++ .../contributed_packages/edi/index.rst | 36 ++++++++ .../contributed_packages/edi/objectives.rst | 2 + .../contributed_packages/edi/quickstart.rst | 19 ++++ .../contributed_packages/edi/variables.rst | 2 + doc/OnlineDocs/contributed_packages/index.rst | 1 + pyomo/contrib/edi/README.md | 2 +- pyomo/contrib/edi/examples/__init__.py | 0 pyomo/contrib/edi/examples/readme_example.py | 27 ++++-- pyomo/contrib/edi/tests/test_examples.py | 71 ++++++++++++++ pyomo/contrib/edi/tests/test_formulation.py | 86 ++++++++++++++++- 16 files changed, 374 insertions(+), 13 deletions(-) create mode 100644 doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst create mode 100644 doc/OnlineDocs/contributed_packages/edi/blackboxobjectives.rst create mode 100644 doc/OnlineDocs/contributed_packages/edi/constants.rst create mode 100644 doc/OnlineDocs/contributed_packages/edi/constraints.rst create mode 100644 doc/OnlineDocs/contributed_packages/edi/examples.rst create mode 100644 doc/OnlineDocs/contributed_packages/edi/formulation.rst create mode 100644 doc/OnlineDocs/contributed_packages/edi/index.rst create mode 100644 doc/OnlineDocs/contributed_packages/edi/objectives.rst create mode 100644 doc/OnlineDocs/contributed_packages/edi/quickstart.rst create mode 100644 doc/OnlineDocs/contributed_packages/edi/variables.rst create mode 100644 pyomo/contrib/edi/examples/__init__.py create mode 100644 pyomo/contrib/edi/tests/test_examples.py diff --git a/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst b/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst new file mode 100644 index 00000000000..cbc426f1548 --- /dev/null +++ b/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst @@ -0,0 +1,11 @@ +Black-Box Constraints +===================== + + + + + +Known Issues +------------ + +* Runtime constraints must output to a variable, numbers and constants are not permitted (see `this issue `_) diff --git a/doc/OnlineDocs/contributed_packages/edi/blackboxobjectives.rst b/doc/OnlineDocs/contributed_packages/edi/blackboxobjectives.rst new file mode 100644 index 00000000000..6ea6f6bacfc --- /dev/null +++ b/doc/OnlineDocs/contributed_packages/edi/blackboxobjectives.rst @@ -0,0 +1,6 @@ +Black-Box Objectives +==================== + +Black-box objectives are not currently supported and are awaiting an update to pyomo (see `this issue `_). + +Please contact the developers if you are looking for this functionality diff --git a/doc/OnlineDocs/contributed_packages/edi/constants.rst b/doc/OnlineDocs/contributed_packages/edi/constants.rst new file mode 100644 index 00000000000..0b076926ce7 --- /dev/null +++ b/doc/OnlineDocs/contributed_packages/edi/constants.rst @@ -0,0 +1,2 @@ +Constants +========= diff --git a/doc/OnlineDocs/contributed_packages/edi/constraints.rst b/doc/OnlineDocs/contributed_packages/edi/constraints.rst new file mode 100644 index 00000000000..a95a5faa60a --- /dev/null +++ b/doc/OnlineDocs/contributed_packages/edi/constraints.rst @@ -0,0 +1,17 @@ +Constraints +=========== + + + + + + + + + +Known Issues +------------ + +* Currently only equality constraints are supported, pending an update to pyomo (see `this issue `_) +* Indexed variables must be broken up using either indicies or a pyomo rule (see `this issue `_) +* Units that are inconsistent, but not the same (ie, meters and feet) will flag as invalid when checking units (see `this issue `_) diff --git a/doc/OnlineDocs/contributed_packages/edi/examples.rst b/doc/OnlineDocs/contributed_packages/edi/examples.rst new file mode 100644 index 00000000000..4110c31f232 --- /dev/null +++ b/doc/OnlineDocs/contributed_packages/edi/examples.rst @@ -0,0 +1,13 @@ +Examples +======== + +Using Multi-Dimensional Variables +--------------------------------- + + +Advanced Black-Box Modeling +--------------------------- + + +A Geometric Program for Aircraft Design +--------------------------------------- diff --git a/doc/OnlineDocs/contributed_packages/edi/formulation.rst b/doc/OnlineDocs/contributed_packages/edi/formulation.rst new file mode 100644 index 00000000000..6b78c208f35 --- /dev/null +++ b/doc/OnlineDocs/contributed_packages/edi/formulation.rst @@ -0,0 +1,92 @@ +Formulation +=========== + +The core object in EDI is called a *Formulation*. For experienced pyomo users, a Formulation inherits from a pyomo *ConcreteModel*, and can therefore be treated exactly as a typical *ConcreteModel* with a few additional features. + +Each modeling element (ex: Variable, Constant, Objective, and Constraint) has a constructor that is used to create the corresponding element in the *Formulation* instance. In addition, there are a number of helper functions that collect and return model elements or perform supporting actions. + +Construction +------------ +A *Formulation* is constructed as follows: + +.. code-block:: python + :linenos: + + from pyomo.contrib.edi import Formulation + f = Formulation() + +Standard practice is to construct a formulation to namespace variable ``f``, but any valid python name can be used. Standard pyomo practice would be to construct this to ``model`` or ``m``. + + +Declaring Variables +------------------- + +See the :doc:`Variables <./variables>` Documentation + + +Declaring Constants +------------------- + +See the :doc:`Constants <./constants>` Documentation + + + +Declaring Objectives +-------------------- + +See the :doc:`Objectives <./objectives>` Documentation + + +Declaring Constraints +--------------------- + +See the :doc:`Constraints <./constraints>` Documentation + + + +Declaring Black-Box Constraints +------------------------------- + +See the :doc:`Black-Box Constraints <./blackboxconstraints>` Documentation + + +Support Functions +----------------- + +.. |br| raw:: html + +
+ + +``f.get_variables()`` |br| +Returns a list variables that have been defined in the formulation in declaration order. Will only return variables defined via EDI. + +``f.get_constants()`` |br| +Returns a list of constants that have been defined in the formulation in declaration order. Will only return constants/parameters defined via EDI. + +``f.get_objectives()`` |br| +Returns a list of objectives that have been defined in the formulation in declaration order. Will only return objectives defined via EDI. + +``f.get_constraints()`` |br| +Returns a list of constraints that have been defined in the formulation in declaration order. This command returns a list that includes both explicit and runtime (black-box) constraints, but only constraints that have been defined via EDI. + +``f.get_explicitConstraints()`` |br| +Returns a list of *explicit* constraints that have been defined in the formulation in declaration order. This command returns a list that includes *only* the explicit constraints and *not* the runtime (black-box) constraints. Only includes constraints that have been defined via EDI. + +``f.get_runtimeConstraints()`` |br| +Returns a list of *runtime* (ie. black-box) constraints that have been defined in the formulation in declaration order. This command returns a list that includes *only* the runtime constraints and *not* the explicit constraints. Only includes constraints that have been defined via EDI. + +``f.check_units()`` |br| +Checks the units of each objective and constraint for consistency. Will only check objectives and constraints defined via EDI. + + + + + + + + + + + + diff --git a/doc/OnlineDocs/contributed_packages/edi/index.rst b/doc/OnlineDocs/contributed_packages/edi/index.rst new file mode 100644 index 00000000000..9a39cb04f41 --- /dev/null +++ b/doc/OnlineDocs/contributed_packages/edi/index.rst @@ -0,0 +1,36 @@ +Engineering Design Interface (EDI) +================================== + +The Pyomo Engineering Design Interface (EDI) is a lightweight wrapper on the Pyomo language that is targeted at composing engineering design optimization problems. The language and interface have been designed to mimic many of the features found in `GPkit `_ and `CVXPY `_ while also providing a simple, clean interface for black-box analysis codes that are common in engineering design applications. + + +Installation +------------ + +EDI installs as a part of the standard pyomo install: + +:: + + pip install pyomo + +User's Guide +------------ + +.. toctree:: + :maxdepth: 1 + + quickstart.rst + formulation.rst + variables.rst + constants.rst + objectives.rst + blackboxobjectives.rst + constraints.rst + blackboxconstraints.rst + examples.rst + + +Developers +---------- + +The pyomo EDI interface is developed and maintained by `Cody Karcher `_ \ No newline at end of file diff --git a/doc/OnlineDocs/contributed_packages/edi/objectives.rst b/doc/OnlineDocs/contributed_packages/edi/objectives.rst new file mode 100644 index 00000000000..5f919b2f296 --- /dev/null +++ b/doc/OnlineDocs/contributed_packages/edi/objectives.rst @@ -0,0 +1,2 @@ +Objectives +========== diff --git a/doc/OnlineDocs/contributed_packages/edi/quickstart.rst b/doc/OnlineDocs/contributed_packages/edi/quickstart.rst new file mode 100644 index 00000000000..b24ce8f8290 --- /dev/null +++ b/doc/OnlineDocs/contributed_packages/edi/quickstart.rst @@ -0,0 +1,19 @@ +Quickstart +========== + +Below is a simple example written using EDI that should help get new users off the ground. For simple problems this example case is fine to build on, but for more advanced techniques (ex: using multi-dimensional variables or advanced black-box usage) see the additional documentation. + + +The example shown here minimizes a linear objective function subject to the interior area of the unit circle: + +.. math:: + \begin{align*} + & \underset{x,y,z}{\text{minimize}} + & & x+y \\ + & \text{subject to} + & & z = x^2 + y^2\\ + &&& z \leq 1.0 \text{ [m$^2$]} + \end{align*} + +.. literalinclude:: ../../../../pyomo/contrib/edi/examples/readme_example.py + :language: python \ No newline at end of file diff --git a/doc/OnlineDocs/contributed_packages/edi/variables.rst b/doc/OnlineDocs/contributed_packages/edi/variables.rst new file mode 100644 index 00000000000..a5ab1a8ff97 --- /dev/null +++ b/doc/OnlineDocs/contributed_packages/edi/variables.rst @@ -0,0 +1,2 @@ +Variables +========= diff --git a/doc/OnlineDocs/contributed_packages/index.rst b/doc/OnlineDocs/contributed_packages/index.rst index f893753780e..0278e2caa36 100644 --- a/doc/OnlineDocs/contributed_packages/index.rst +++ b/doc/OnlineDocs/contributed_packages/index.rst @@ -16,6 +16,7 @@ Contributed packages distributed with Pyomo: :maxdepth: 1 community.rst + edi/index.rst doe/doe.rst gdpopt.rst iis.rst diff --git a/pyomo/contrib/edi/README.md b/pyomo/contrib/edi/README.md index fa45c5cb0ac..b2a688c75f5 100644 --- a/pyomo/contrib/edi/README.md +++ b/pyomo/contrib/edi/README.md @@ -67,7 +67,7 @@ class UnitCircle(BlackBoxFunctionModel): self.outputs.append(BBVariable(name = 'z', size = 0, units = 'ft**2', description = 'Resultant of the unit circle evaluation')) # Declare the maximum available derivative - self.availableDerivative = 2 + self.availableDerivative = 1 # Post-initalization setup self.post_init_setup(len(self.inputs)) diff --git a/pyomo/contrib/edi/examples/__init__.py b/pyomo/contrib/edi/examples/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/pyomo/contrib/edi/examples/readme_example.py b/pyomo/contrib/edi/examples/readme_example.py index 8190e45e9ec..6fb2be524ae 100644 --- a/pyomo/contrib/edi/examples/readme_example.py +++ b/pyomo/contrib/edi/examples/readme_example.py @@ -3,7 +3,8 @@ # ================= import pyomo.environ as pyo from pyomo.environ import units -from pyomo.contrib.edi import Formulation, RuntimeConstraint, BlackBoxFunctionModel, BBVariable +from pyomo.contrib.edi import Formulation, RuntimeConstraint +from pyomo.contrib.edi import BlackBoxFunctionModel, BBVariable # =================== # Declare Formulation @@ -15,7 +16,7 @@ # ================= x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') -z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The unit circle output') +z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'Model output') # ================= # Declare Constants @@ -42,21 +43,31 @@ def __init__(self): # The initalization function self.description = 'This model evaluates the function: z = x**2 + y**2' # Declare the black box model inputs - self.inputs.append(BBVariable(name = 'x', size = 0, units = 'ft' , description = 'The x variable')) - self.inputs.append(BBVariable(name = 'y', size = 0, units = 'ft' , description = 'The y variable')) + self.inputs.append(BBVariable(name = 'x', + size = 0, + units = 'ft' , + description = 'The x variable') ) + self.inputs.append(BBVariable(name = 'y', + size = 0, + units = 'ft' , + description = 'The y variable') ) # Declare the black box model outputs - self.outputs.append(BBVariable(name = 'z', size = 0, units = 'ft**2', description = 'Resultant of the unit circle evaluation')) + self.outputs.append(BBVariable(name = 'z', + size = 0, + units = 'ft**2', + description = 'Resultant of the unit circle') ) # Declare the maximum available derivative - self.availableDerivative = 2 + self.availableDerivative = 1 # Post-initalization setup self.post_init_setup(len(self.inputs)) def BlackBox(self, x, y): # The actual function that does things - x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float - y = pyo.value(units.convert(y,self.inputs[1].units)) # Converts to correct units then casts to float + # Converts to correct units then casts to float + x = pyo.value(units.convert(x,self.inputs[0].units)) + y = pyo.value(units.convert(y,self.inputs[1].units)) z = x**2 + y**2 # Compute z dzdx = 2*x # Compute dz/dx diff --git a/pyomo/contrib/edi/tests/test_examples.py b/pyomo/contrib/edi/tests/test_examples.py new file mode 100644 index 00000000000..0910e8e49e2 --- /dev/null +++ b/pyomo/contrib/edi/tests/test_examples.py @@ -0,0 +1,71 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright (c) 2008-2023 +# 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. +# +# Development of this module was conducted as part of the Institute for +# the Design of Advanced Energy Systems (IDAES) with support through the +# Simulation-Based Engineering, Crosscutting Research Program within the +# U.S. Department of Energy’s Office of Fossil Energy and Carbon Management. +# +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + +import pyomo.common.unittest as unittest +import pyomo.environ as pyo +from pyomo.common.dependencies import attempt_import + +import importlib + + + +testIndex = 0 +class TestEDIExamples(unittest.TestCase): + pass + +def createTest(filename): + def testFunction(self): + importName = filename[0:-3] + # filename = ".."+filename + try: + importlib.import_module("pyomo.contrib.edi.examples."+importName) + except: + self.fail("This example is failing: %s"%(filename)) + return testFunction + + + +pythonFileList = [ + "readme_example.py" +] + +for filename in pythonFileList: + testName = 'test_DocumentationExample_%d'%(testIndex) + testIndex += 1 + testFunction = createTest(filename) + setattr(TestEDIExamples, testName, testFunction) + + +if __name__ == '__main__': + unittest.main() + + + + + + + + + + + + + + + + + diff --git a/pyomo/contrib/edi/tests/test_formulation.py b/pyomo/contrib/edi/tests/test_formulation.py index e03a534b514..5632042c8e5 100644 --- a/pyomo/contrib/edi/tests/test_formulation.py +++ b/pyomo/contrib/edi/tests/test_formulation.py @@ -125,17 +125,95 @@ def test_edi_formulation_runtimeobjective(self): def test_edi_formulation_constraint(self): "Tests the constraint constructor in edi.formulation" - pass + import pyomo.environ as pyo + from pyomo.environ import units + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + f.Objective(x+y) + f.Constraint(x+y <= 1.0*units.m) def test_edi_formulation_runtimeconstraint(self): "Tests the runtime constraint constructor in edi.formulation" - pass + import pyomo.environ as pyo + from pyomo.environ import units + from pyomo.contrib.edi import Formulation, RuntimeConstraint, BlackBoxFunctionModel, BBVariable + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The unit circle output') + c = f.Constant(name = 'c', value = 1.0, units = '', description = 'A constant c', size = 2) + f.Objective( x + y ) + + class UnitCircle(BlackBoxFunctionModel): + def __init__(self): + super(UnitCircle, self).__init__() + self.description = 'This model evaluates the function: z = x**2 + y**2' + self.inputs.append(BBVariable(name = 'x', size = 0, units = 'ft' , description = 'The x variable')) + self.inputs.append(BBVariable(name = 'y', size = 0, units = 'ft' , description = 'The y variable')) + self.outputs.append(BBVariable(name = 'z', size = 0, units = 'ft**2', description = 'Resultant of the unit circle evaluation')) + self.availableDerivative = 1 + self.post_init_setup(len(self.inputs)) + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float + y = pyo.value(units.convert(y,self.inputs[1].units)) # Converts to correct units then casts to float + z = x**2 + y**2 # Compute z + dzdx = 2*x # Compute dz/dx + dzdy = 2*y # Compute dz/dy + z *= units.ft**2 + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + + f.RuntimeConstraint( RuntimeConstraint(z, '==', [x,y], UnitCircle() ) ) + f.Constraint( z <= 1*units.m**2 ) def test_edi_formulation_constraintlist(self): "Tests the constraint list constructor in edi.formulation" - pass + import pyomo.environ as pyo + from pyomo.environ import units + from pyomo.contrib.edi import Formulation, RuntimeConstraint, BlackBoxFunctionModel, BBVariable + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The unit circle output') + c = f.Constant(name = 'c', value = 1.0, units = '', description = 'A constant c', size = 2) + f.Objective( x + y ) + + class UnitCircle(BlackBoxFunctionModel): + def __init__(self): + super(UnitCircle, self).__init__() + self.description = 'This model evaluates the function: z = x**2 + y**2' + self.inputs.append(BBVariable(name = 'x', size = 0, units = 'ft' , description = 'The x variable')) + self.inputs.append(BBVariable(name = 'y', size = 0, units = 'ft' , description = 'The y variable')) + self.outputs.append(BBVariable(name = 'z', size = 0, units = 'ft**2', description = 'Resultant of the unit circle evaluation')) + self.availableDerivative = 1 + self.post_init_setup(len(self.inputs)) + + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float + y = pyo.value(units.convert(y,self.inputs[1].units)) # Converts to correct units then casts to float + z = x**2 + y**2 # Compute z + dzdx = 2*x # Compute dz/dx + dzdy = 2*y # Compute dz/dy + z *= units.ft**2 + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + + f.ConstraintList( + [ + RuntimeConstraint(z, '==', [x,y], UnitCircle() ) , + z <= 1*units.m**2 + ] + ) + + cl = f.get_constraints() + + self.assertTrue(len(cl)==2) def test_edi_formulation_getvariables(self): "Tests the get_variables function in edi.formulation" @@ -230,7 +308,7 @@ def __init__(self): # The initalization function self.outputs.append(BBVariable(name = 'z', size = 0, units = 'ft**2', description = 'Resultant of the unit circle evaluation')) # Declare the maximum available derivative - self.availableDerivative = 2 + self.availableDerivative = 1 # Post-initalization setup self.post_init_setup(len(self.inputs)) From ab02abab15d5ae92b9b06be70dce0db08ff5392a Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Sun, 30 Jul 2023 23:57:01 -0600 Subject: [PATCH 15/61] still documenting --- .../contributed_packages/edi/formulation.rst | 103 ++++++++++++++++++ pyomo/contrib/edi/formulation.py | 8 -- 2 files changed, 103 insertions(+), 8 deletions(-) diff --git a/doc/OnlineDocs/contributed_packages/edi/formulation.rst b/doc/OnlineDocs/contributed_packages/edi/formulation.rst index 6b78c208f35..1cc23154475 100644 --- a/doc/OnlineDocs/contributed_packages/edi/formulation.rst +++ b/doc/OnlineDocs/contributed_packages/edi/formulation.rst @@ -23,12 +23,30 @@ Declaring Variables See the :doc:`Variables <./variables>` Documentation +Variables are declared using the ``f.Variable()`` function. This function creates an instance of ``pyomo.environ.Var`` and adds it to the ``edi.contrib.Formulation``. The function returns an instance of a ``pyomo.environ.Var`` that can be used in later construction. + +.. code-block:: python + :linenos: + + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = '') + + Declaring Constants ------------------- See the :doc:`Constants <./constants>` Documentation +Constants (referred to in base pyomo as parameters or ``Params``) are declared using the ``f.Constant()`` function. This function creates an instance of a ``pyomo.environ.Param`` and adds it to the ``edi.contrib.Formulation``. This function also returns an instance of ``pyomo.environ.Param`` that can be used in later construction. + +.. code-block:: python + :linenos: + + from pyomo.contrib.edi import Formulation + f = Formulation() + c = f.Constant(name = 'c', value = 1.0, units = '') Declaring Objectives @@ -36,12 +54,97 @@ Declaring Objectives See the :doc:`Objectives <./objectives>` Documentation +Objectives are declared using the ``f.Objective()`` function. This function creates an instance of a ``pyomo.environ.Objective`` and adds it to the ``edi.contrib.Formulation``. Multiple objectives can be declared, but interpretation of multiple objectives will depend on the solver. The returned values of the ``f.Variable()`` and ``f.Constant()`` declarations can be used to construct the objective. Black-box (ie, Runtime) objectives are not supported at this time, but are planned in a future update. + +.. code-block:: python + :linenos: + + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = '') + y = f.Variable(name = 'y', guess = 1.0, units = '') + c = f.Constant(name = 'c', value = 1.0, units = '') + f.Objective( c*x + y ) + + +By default, objectives are minimized, but can be switched to a maximize using the ``sense`` keyword from pyomo: + +.. code-block:: python + :linenos: + + from pyomo.contrib.edi import Formulation + from pyomo.environ import maximize, minimize + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = '') + y = f.Variable(name = 'y', guess = 1.0, units = '') + c = f.Constant(name = 'c', value = 1.0, units = '') + f.Objective( c*x + y , sense = maximize) + + +Note: Future version will allow a string to be passed into ``sense`` (see `this issue `_) + + Declaring Constraints --------------------- See the :doc:`Constraints <./constraints>` Documentation +Constraints can be declared in two ways. First is using the standard ``f.Constraint()`` constructor. This function creates an instance of ``pyomo.environ.Constraint`` and adds it to the ``edi.contrib.Formulation``. The operators ``<=``, ``>=``, and ``==`` are used as constraint constructors. + +.. code-block:: python + :linenos: + + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = '') + y = f.Variable(name = 'y', guess = 1.0, units = '') + c = f.Constant(name = 'c', value = 1.0, units = '') + f.Objective( c*x + y ) + f.Constraint( x**2 + y**2 <= 1.0 ) + f.Constraint( x >= 0) + f.Constraint( y <= 0) + +Constraints can also be declared using the ``f.ConstraintList()`` function. This function takes in a list of constraints and allows for multiple constraints to be declared in one go: + +.. code-block:: python + :linenos: + + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = '') + y = f.Variable(name = 'y', guess = 1.0, units = '') + c = f.Constant(name = 'c', value = 1.0, units = '') + f.Objective( c*x + y ) + f.ConstraintList( + [ + x**2 + y**2 <= 1.0 , + x >= 0, + y <= 0, + ] + ) + +The constraint list can also be declared a priori and passed in to the ``f.ConstraintList()`` function, which may be beneficial for complex models: + +.. code-block:: python + :linenos: + + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = '') + y = f.Variable(name = 'y', guess = 1.0, units = '') + c = f.Constant(name = 'c', value = 1.0, units = '') + f.Objective( c*x + y ) + + constraintList = [ + x**2 + y**2 <= 1.0 , + x >= 0, + y <= 0, + ] + + f.ConstraintList( constraintList ) + +We recommend that most users should be using the ``f.ConstraintList()`` function, with the ``f.Constraint()`` function being reserved for under-the-hood usage. Declaring Black-Box Constraints diff --git a/pyomo/contrib/edi/formulation.py b/pyomo/contrib/edi/formulation.py index 9c09e774bb3..c08948382ad 100644 --- a/pyomo/contrib/edi/formulation.py +++ b/pyomo/contrib/edi/formulation.py @@ -229,14 +229,6 @@ def Objective(self, expr, sense=minimize): # pass def Constraint(self, expr): - # Should use - # N = [1,2,3] - # a = {1:1, 2:3.1, 3:4.5} - # b = {1:1, 2:2.9, 3:3.1} - # model.y = pyo.Var(N, within=pyo.NonNegativeReals, initialize=0.0) - # def CoverConstr_rule(model, i): return a[i] * model.y[i] >= b[i] - # model.CoverConstr = pyo.Constraint(N, rule=CoverConstr_rule) - # to handle things like "x(a set Variable) <= 20" self._constraint_counter += 1 conName = 'constraint_'+str(self._constraint_counter) self.add_component(conName, Constraint(expr=expr)) From 4e3f1d164aa1f5144a20d40ae75089c3f903aa14 Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Mon, 31 Jul 2023 17:20:52 -0600 Subject: [PATCH 16/61] formulation unit testing finished --- pyomo/contrib/edi/README.md | 6 +- pyomo/contrib/edi/__init__.py | 2 +- pyomo/contrib/edi/blackBoxFunctionModel.py | 53 +++-- pyomo/contrib/edi/examples/readme_example.py | 9 +- pyomo/contrib/edi/formulation.py | 102 +++++---- pyomo/contrib/edi/tests/test_formulation.py | 207 ++++++++++++++++++- 6 files changed, 285 insertions(+), 94 deletions(-) diff --git a/pyomo/contrib/edi/README.md b/pyomo/contrib/edi/README.md index b2a688c75f5..6366d782271 100644 --- a/pyomo/contrib/edi/README.md +++ b/pyomo/contrib/edi/README.md @@ -21,7 +21,7 @@ Below is a simple example to get started, but additional resources can be found # ================= import pyomo.environ as pyo from pyomo.environ import units -from pyomo.contrib.edi import Formulation, RuntimeConstraint, BlackBoxFunctionModel, BBVariable +from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel, BBVariable # =================== # Declare Formulation @@ -70,7 +70,7 @@ class UnitCircle(BlackBoxFunctionModel): self.availableDerivative = 1 # Post-initalization setup - self.post_init_setup(len(self.inputs)) + self.post_init_setup() def BlackBox(self, x, y): # The actual function that does things x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float @@ -91,7 +91,7 @@ class UnitCircle(BlackBoxFunctionModel): # ======================= f.ConstraintList( [ - RuntimeConstraint(z, '==', [x,y], UnitCircle() ) , + [ z, '==', [x,y], UnitCircle() ] , z <= 1*units.m**2 ] ) diff --git a/pyomo/contrib/edi/__init__.py b/pyomo/contrib/edi/__init__.py index 0f562e0a4da..fec8a6c7ab3 100644 --- a/pyomo/contrib/edi/__init__.py +++ b/pyomo/contrib/edi/__init__.py @@ -19,7 +19,7 @@ import pyomo.environ # Import the relevant classes from Formulation -from pyomo.contrib.edi.formulation import Formulation, RuntimeConstraint +from pyomo.contrib.edi.formulation import Formulation # Import the black box modeling tools from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel diff --git a/pyomo/contrib/edi/blackBoxFunctionModel.py b/pyomo/contrib/edi/blackBoxFunctionModel.py index 6d8325a5455..48c83da517a 100644 --- a/pyomo/contrib/edi/blackBoxFunctionModel.py +++ b/pyomo/contrib/edi/blackBoxFunctionModel.py @@ -230,8 +230,8 @@ def evaluate_jacobian_outputs(self): jac = self._cache['pyomo_jacobian'] return jac - def post_init_setup(self, N_inputs, defaultVal = 0.0): - self._input_values = np.ones(N_inputs) * defaultVal + def post_init_setup(self, defaultVal = 1.0): + self._input_values = np.ones(self._NunwrappedInputs) * defaultVal def fillCache(self): @@ -256,12 +256,12 @@ def fillCache(self): if isinstance(optimizationInput, pyomo.core.base.var.IndexedVar): value = np.zeros(shape) for vix in list(optimizationInput.index_set().data()): - value[vix] = raw_inputs[ptr] + raw_val = float(raw_inputs[ptr]) * optimizationUnits + raw_val_correctedUnits = pyomo_units.convert(raw_val, localUnits) + value[vix] = pyo.value(raw_val_correctedUnits) ptr += 1 - value *= optimizationUnits - self.sizeCheck(localShape, value) - value_correctedUnits = pyomo_units.convert(value, localUnits) - bb_inputs.append(value_correctedUnits) + self.sizeCheck(localShape, value*localUnits) + bb_inputs.append(value*localUnits) elif isinstance(optimizationInput, pyomo.core.base.var.ScalarVar): value = raw_inputs[ptr] * optimizationUnits @@ -293,7 +293,7 @@ def fillCache(self): modelOutputUnits = opt.units ouptutOptimizationUnits = optimizationOutput.get_units() vl = valueList[i] - if isinstance(vl, pyomo.core.expr.numvalue.NumericNDArray): + if isinstance(vl, pyomo.core.expr.numeric_expr.NumericNDArray): validIndexList = optimizationOutput.index_set().data() for j in range(0,len(validIndexList)): vi = validIndexList[j] @@ -310,7 +310,7 @@ def fillCache(self): self._cache['pyomo_outputs'] = outputVector - outputJacobian = np.zeros([self._NunwrappedOutputs, self._NunwrappedInputs]) + outputJacobian = np.ones([self._NunwrappedOutputs, self._NunwrappedInputs]) * -1 ptr_row = 0 ptr_col = 0 @@ -320,6 +320,7 @@ def fillCache(self): oounits = oopt.get_units() lounits = lopt.units # oshape = [len(idx) for idx in oopt.index_set().subsets()] + ptr_col = 0 for j in range(0,len(self.inputs)): oipt = self.inputVariables_optimization[j] lipt = self.inputs[j] @@ -336,7 +337,7 @@ def fillCache(self): ptr_col += 1 ptr_row_step = 1 - elif isinstance(jacobianValue_raw,pyomo.core.expr.numvalue.NumericNDArray): + elif isinstance(jacobianValue_raw,pyomo.core.expr.numeric_expr.NumericNDArray): jshape = jacobianValue_raw.shape if isinstance(oopt, pyomo.core.base.var.ScalarVar): @@ -377,14 +378,20 @@ def fillCache(self): # both are dimensioned vectors #oshape, ishape, jshape ptr_row_cache = ptr_row + ptr_col_cache = ptr_col validIndicies_o = list(oopt.index_set().data()) validIndicies_i = list(oipt.index_set().data()) for vio in validIndicies_o: + if isinstance(vio, (float,int)): + vio = (vio,) for vii in validIndicies_i: + if isinstance(vii, (float,int)): + vii = (vii,) corrected_value = pyo.value(pyomo_units.convert(jacobianValue_raw[vio + vii], oounits/oiunits)) # now unitless in correct units outputJacobian[ptr_row,ptr_col] = corrected_value ptr_col += 1 + ptr_col = ptr_col_cache ptr_row += 1 ptr_row = ptr_row_cache ptr_row_step = len(validIndicies_o) @@ -537,7 +544,7 @@ def sizeCheck(self, size, ipval_correctUnits): pyomo.core.base.units_container._PyomoUnit)): if size != 0 and size != 1 : raise ValueError('Size of %s did not match the expected size %s (ie: Scalar)'%(name, str(size))) - elif isinstance(szVal, pyomo.core.expr.numvalue.NumericNDArray): + elif isinstance(szVal, pyomo.core.expr.numeric_expr.NumericNDArray): shp = szVal.shape if isinstance(size,(int,float)): size = [size] @@ -584,17 +591,25 @@ def sanitizeInputs(self, *args, **kwargs): ipval = inputDict[name] if unts is not None: - try: - ipval_correctUnits = pyomo_units.convert(ipval, unts)#ipval.to(unts) - except: - raise ValueError('Could not convert %s of %s to %s'%(name, str(ipval),str(unts))) + if isinstance(ipval, pyomo.core.expr.numeric_expr.NumericNDArray): + for ii in range(0,len(ipval)): + try: + ipval[ii] = pyomo_units.convert(ipval[ii], unts)#ipval.to(unts) + except: + raise ValueError('Could not convert %s of %s to %s'%(name, str(ipval),str(unts))) + ipval_correctUnits = ipval + else: + try: + ipval_correctUnits = pyomo_units.convert(ipval, unts)#ipval.to(unts) + except: + raise ValueError('Could not convert %s of %s to %s'%(name, str(ipval),str(unts))) else: ipval_correctUnits = ipval if not isinstance(ipval_correctUnits, (pyomo.core.expr.numeric_expr.NPV_ProductExpression, - pyomo.core.expr.numvalue.NumericNDArray, + pyomo.core.expr.numeric_expr.NumericNDArray, pyomo.core.base.units_container._PyomoUnit)): - ipval_correctUnits *= pyomo_units.dimensionless + ipval_correctUnits = ipval_correctUnits * pyomo_units.dimensionless self.sizeCheck(size, ipval_correctUnits) @@ -645,9 +660,9 @@ def checkOutputs(self, *args, **kwargs): ipval_correctUnits = ipval if not isinstance(ipval_correctUnits, (pyomo.core.expr.numeric_expr.NPV_ProductExpression, - pyomo.core.expr.numvalue.NumericNDArray, + pyomo.core.expr.numeric_expr.NumericNDArray, pyomo.core.base.units_container._PyomoUnit)): - ipval_correctUnits *= pyomo_units.dimensionless + ipval_correctUnits = ipval_correctUnits * pyomo_units.dimensionless self.sizeCheck(size, ipval_correctUnits) diff --git a/pyomo/contrib/edi/examples/readme_example.py b/pyomo/contrib/edi/examples/readme_example.py index 6fb2be524ae..5727d50e609 100644 --- a/pyomo/contrib/edi/examples/readme_example.py +++ b/pyomo/contrib/edi/examples/readme_example.py @@ -3,7 +3,7 @@ # ================= import pyomo.environ as pyo from pyomo.environ import units -from pyomo.contrib.edi import Formulation, RuntimeConstraint +from pyomo.contrib.edi import Formulation from pyomo.contrib.edi import BlackBoxFunctionModel, BBVariable # =================== @@ -60,10 +60,7 @@ def __init__(self): # The initalization function # Declare the maximum available derivative self.availableDerivative = 1 - - # Post-initalization setup - self.post_init_setup(len(self.inputs)) - + def BlackBox(self, x, y): # The actual function that does things # Converts to correct units then casts to float x = pyo.value(units.convert(x,self.inputs[0].units)) @@ -84,7 +81,7 @@ def BlackBox(self, x, y): # The actual function that does things # ======================= f.ConstraintList( [ - RuntimeConstraint(z, '==', [x,y], UnitCircle() ) , + [ z, '==', [x,y], UnitCircle() ] , z <= 1*units.m**2 ] ) \ No newline at end of file diff --git a/pyomo/contrib/edi/formulation.py b/pyomo/contrib/edi/formulation.py index c08948382ad..a7e300bacbe 100644 --- a/pyomo/contrib/edi/formulation.py +++ b/pyomo/contrib/edi/formulation.py @@ -75,45 +75,6 @@ def decodeUnits(u_val): return pyomo_units.__getattr__(u_val) else: return u_val - -class RuntimeConstraint(object): - def __init__(self, outputs, operators, inputs, black_box): - - inputs_raw = inputs - outputs_raw = outputs - operators_raw = operators - - if isinstance(inputs_raw, (pyomo.core.base.var.IndexedVar,pyomo.core.base.var.ScalarVar)): - inputs_raw = [inputs_raw] - elif isinstance(inputs_raw, (list, tuple)): - inputs_raw = list(inputs_raw) - else: - raise ValueError("Invalid type for input variables") - - if isinstance(outputs_raw, (pyomo.core.base.var.IndexedVar,pyomo.core.base.var.ScalarVar)): - outputs_raw = [outputs_raw] - elif isinstance(outputs_raw, (list, tuple)): - outputs_raw = list(outputs_raw) - else: - raise ValueError("Invalid type for output variables") - - if isinstance(operators_raw, (list, tuple)): - operators_raw = list(outputs_raw) - elif isinstance(operators_raw,str): - if operators_raw in ["==",">=","<="]: - operators_raw = [operators_raw] - else: - raise ValueError("Invalid operator") - else: - raise ValueError("Invalid type for operators") - - - self.outputs = outputs_raw - self.operators = operators_raw - self.inputs = inputs_raw - self.black_box = black_box - - self.black_box.setOptimizationVariables(inputs_raw, outputs_raw) class Formulation(ConcreteModel): def __init__(self): @@ -236,8 +197,8 @@ def Constraint(self, expr): self._allConstraint_keys.append(conName) self.__dict__[conName].construct() - - def RuntimeConstraint(self, rcCon): + + def RuntimeConstraint(self, outputs, operators, inputs, black_box): self._constraint_counter += 1 conName = 'constraint_'+str(self._constraint_counter) self._runtimeConstraint_keys.append(conName) @@ -248,9 +209,39 @@ def RuntimeConstraint(self, rcCon): # TODO: Need to include operators after Michael fixes things - inputs_raw = rcCon.inputs - outputs_raw = rcCon.outputs - operators_raw = rcCon.operators + inputs_raw = inputs + outputs_raw = outputs + operators_raw = operators + + if isinstance(inputs_raw, (pyomo.core.base.var.IndexedVar,pyomo.core.base.var.ScalarVar)): + inputs_raw = [inputs_raw] + elif isinstance(inputs_raw, (list, tuple)): + inputs_raw = list(inputs_raw) + else: + raise ValueError("Invalid type for input variables") + + if isinstance(outputs_raw, (pyomo.core.base.var.IndexedVar,pyomo.core.base.var.ScalarVar)): + outputs_raw = [outputs_raw] + elif isinstance(outputs_raw, (list, tuple)): + outputs_raw = list(outputs_raw) + else: + raise ValueError("Invalid type for output variables") + for lst in [outputs_raw, inputs_raw]: + for vr in lst: + if not isinstance(vr, (pyomo.core.base.var.IndexedVar,pyomo.core.base.var.ScalarVar)): + raise ValueError("Invalid type when checking inputs and outputs") + + if isinstance(operators_raw, (list, tuple)): + operators_raw = list(operators_raw) + elif isinstance(operators_raw,str): + operators_raw = [operators_raw] + else: + raise ValueError("Invalid type for operators") + for opr in operators_raw: + if opr not in ["==",">=","<="]: + raise ValueError("Invalid operator") + + black_box.setOptimizationVariables(inputs_raw, outputs_raw) outputs_raw_length = len(outputs_raw) operators_raw_length = len(operators_raw) @@ -259,37 +250,36 @@ def RuntimeConstraint(self, rcCon): for ovar in outputs_raw: if isinstance(ovar, pyomo.core.base.var.ScalarVar): outputs_unwrapped.append(ovar) - elif isinstance(ovar, pyomo.core.base.var.IndexedVar): + else: # isinstance(ovar, pyomo.core.base.var.IndexedVar), validated above validIndicies = list(ovar.index_set().data()) for vi in validIndicies: outputs_unwrapped.append(ovar[vi]) - else: - raise ValueError("Invalid type for output variable") inputs_unwrapped = [] for ivar in inputs_raw: if isinstance(ivar, pyomo.core.base.var.ScalarVar): inputs_unwrapped.append(ivar) - elif isinstance(ivar, pyomo.core.base.var.IndexedVar): + else: # isinstance(ivar, pyomo.core.base.var.IndexedVar), validated above validIndicies = list(ivar.index_set().data()) for vi in validIndicies: - inputs_unwrapped.append(ivar[vi]) - else: - raise ValueError("Invalid type for input variable") + inputs_unwrapped.append(ivar[vi]) - rcCon.black_box._NunwrappedOutputs = len(outputs_unwrapped) - rcCon.black_box._NunwrappedInputs = len(inputs_unwrapped) + black_box._NunwrappedOutputs = len(outputs_unwrapped) + black_box._NunwrappedInputs = len(inputs_unwrapped) + black_box.post_init_setup() # TODO: Need to unwrap operators - self.__dict__[conName].set_external_model(rcCon.black_box, inputs=inputs_unwrapped, outputs=outputs_unwrapped)#,operators=operators_unwrapped) + self.__dict__[conName].set_external_model(black_box, inputs=inputs_unwrapped, outputs=outputs_unwrapped)#,operators=operators_unwrapped) def ConstraintList(self, conList): for i in range(0,len(conList)): con = conList[i] - if isinstance(con, RuntimeConstraint): - self.RuntimeConstraint(con) + if isinstance(con, (tuple,list)): + self.RuntimeConstraint(*con) + elif isinstance(con, dict): + self.RuntimeConstraint(**con) else: self.Constraint(con) diff --git a/pyomo/contrib/edi/tests/test_formulation.py b/pyomo/contrib/edi/tests/test_formulation.py index 5632042c8e5..95b927e16e7 100644 --- a/pyomo/contrib/edi/tests/test_formulation.py +++ b/pyomo/contrib/edi/tests/test_formulation.py @@ -84,6 +84,9 @@ def test_edi_formulation_variable(self): self.assertRaises(ValueError, f.Variable, **{'name':'x16', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': None, 'bounds':"error", 'domain':None }) self.assertRaises(ValueError, f.Variable, **{'name':'x17', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': None, 'bounds':[0,"10"], 'domain':None }) + # verifies alternate unit construction + x18 = f.Variable('x18', 1.0, pyo.units.m) + self.assertRaises(AttributeError, f.Variable, *('x19', 1.0, 'strng')) def test_edi_formulation_constant(self): "Tests the constant constructor in edi.formulation" @@ -135,18 +138,17 @@ def test_edi_formulation_constraint(self): f.Constraint(x+y <= 1.0*units.m) - def test_edi_formulation_runtimeconstraint(self): + def test_edi_formulation_runtimeconstraint_tuple(self): "Tests the runtime constraint constructor in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units - from pyomo.contrib.edi import Formulation, RuntimeConstraint, BlackBoxFunctionModel, BBVariable + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel, BBVariable f = Formulation() x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The unit circle output') c = f.Constant(name = 'c', value = 1.0, units = '', description = 'A constant c', size = 2) f.Objective( x + y ) - class UnitCircle(BlackBoxFunctionModel): def __init__(self): super(UnitCircle, self).__init__() @@ -156,7 +158,40 @@ def __init__(self): self.outputs.append(BBVariable(name = 'z', size = 0, units = 'ft**2', description = 'Resultant of the unit circle evaluation')) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float + y = pyo.value(units.convert(y,self.inputs[1].units)) # Converts to correct units then casts to float + z = x**2 + y**2 # Compute z + dzdx = 2*x # Compute dz/dx + dzdy = 2*y # Compute dz/dy + z *= units.ft**2 + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + f.Constraint( z <= 1*units.m**2 ) + + f.RuntimeConstraint( *( z, '==', [x,y], UnitCircle() ) ) + def test_edi_formulation_runtimeconstraint_list(self): + "Tests the runtime constraint constructor in edi.formulation" + import pyomo.environ as pyo + from pyomo.environ import units + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel, BBVariable + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The unit circle output') + c = f.Constant(name = 'c', value = 1.0, units = '', description = 'A constant c', size = 2) + f.Objective( x + y ) + class UnitCircle(BlackBoxFunctionModel): + def __init__(self): + super(UnitCircle, self).__init__() + self.description = 'This model evaluates the function: z = x**2 + y**2' + self.inputs.append(BBVariable(name = 'x', size = 0, units = 'ft' , description = 'The x variable')) + self.inputs.append(BBVariable(name = 'y', size = 0, units = 'ft' , description = 'The y variable')) + self.outputs.append(BBVariable(name = 'z', size = 0, units = 'ft**2', description = 'Resultant of the unit circle evaluation')) + self.availableDerivative = 1 + self.post_init_setup(len(self.inputs)) def BlackBox(self, x, y): # The actual function that does things x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float y = pyo.value(units.convert(y,self.inputs[1].units)) # Converts to correct units then casts to float @@ -167,15 +202,51 @@ def BlackBox(self, x, y): # The actual function that does things dzdx *= units.ft # units.ft**2 / units.ft dzdy *= units.ft # units.ft**2 / units.ft return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + f.Constraint( z <= 1*units.m**2 ) + + f.RuntimeConstraint( *[ [z], ['=='], [x,y], UnitCircle() ] ) - f.RuntimeConstraint( RuntimeConstraint(z, '==', [x,y], UnitCircle() ) ) + + def test_edi_formulation_runtimeconstraint_dict(self): + "Tests the runtime constraint constructor in edi.formulation" + import pyomo.environ as pyo + from pyomo.environ import units + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel, BBVariable + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The unit circle output') + c = f.Constant(name = 'c', value = 1.0, units = '', description = 'A constant c', size = 2) + f.Objective( x + y ) + class UnitCircle(BlackBoxFunctionModel): + def __init__(self): + super(UnitCircle, self).__init__() + self.description = 'This model evaluates the function: z = x**2 + y**2' + self.inputs.append(BBVariable(name = 'x', size = 0, units = 'ft' , description = 'The x variable')) + self.inputs.append(BBVariable(name = 'y', size = 0, units = 'ft' , description = 'The y variable')) + self.outputs.append(BBVariable(name = 'z', size = 0, units = 'ft**2', description = 'Resultant of the unit circle evaluation')) + self.availableDerivative = 1 + self.post_init_setup(len(self.inputs)) + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float + y = pyo.value(units.convert(y,self.inputs[1].units)) # Converts to correct units then casts to float + z = x**2 + y**2 # Compute z + dzdx = 2*x # Compute dz/dx + dzdy = 2*y # Compute dz/dy + z *= units.ft**2 + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... f.Constraint( z <= 1*units.m**2 ) + + f.RuntimeConstraint( **{ 'outputs':z, 'operators':'==', 'inputs':[x,y], 'black_box':UnitCircle() } ) + - def test_edi_formulation_constraintlist(self): + def test_edi_formulation_constraintlist_1(self): "Tests the constraint list constructor in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units - from pyomo.contrib.edi import Formulation, RuntimeConstraint, BlackBoxFunctionModel, BBVariable + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel, BBVariable f = Formulation() x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') @@ -206,7 +277,7 @@ def BlackBox(self, x, y): # The actual function that does things f.ConstraintList( [ - RuntimeConstraint(z, '==', [x,y], UnitCircle() ) , + (z, '==', [x,y], UnitCircle() ) , z <= 1*units.m**2 ] ) @@ -215,6 +286,124 @@ def BlackBox(self, x, y): # The actual function that does things self.assertTrue(len(cl)==2) + + def test_edi_formulation_constraintlist_2(self): + "Tests the constraint list constructor in edi.formulation" + import pyomo.environ as pyo + from pyomo.environ import units + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel, BBVariable + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm**2' , description = 'The y variable') + f.Objective( y ) + + class Parabola(BlackBoxFunctionModel): + def __init__(self): + super(Parabola, self).__init__() + self.description = 'This model evaluates the function: y = x**2' + self.inputs.append(BBVariable(name = 'x', size = 0, units = 'ft' , description = 'The x variable')) + self.outputs.append(BBVariable(name = 'y', size = 0, units = 'ft**2' , description = 'The y variable')) + self.availableDerivative = 1 + self.post_init_setup(len(self.inputs)) + + def BlackBox(self, x): # The actual function that does things + x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float + y = x**2 # Compute y + dydx = 2*x # Compute dy/dx + y *= units.ft**2 + dydx *= units.ft # units.ft**2 / units.ft + return y, [dydx] # return z, grad(z), hess(z)... + + f.ConstraintList( + [ + {'outputs':y, 'operators':'==', 'inputs':x, 'black_box':Parabola() }, + ] + ) + + cl = f.get_constraints() + + self.assertTrue(len(cl)==1) + + + def test_edi_formulation_constraintlist_3(self): + "Tests the constraint list constructor in edi.formulation" + import pyomo.environ as pyo + from pyomo.environ import units + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel, BBVariable + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable', size=3) + y = f.Variable(name = 'y', guess = 1.0, units = 'm**2' , description = 'The y variable', size=3) + f.Objective( y[0] + y[1] + y[2] ) + class Parabola(BlackBoxFunctionModel): + def __init__(self): + super(Parabola, self).__init__() + self.description = 'This model evaluates the function: y = x**2' + self.inputs.append(BBVariable(name = 'x', size = 3, units = 'ft' , description = 'The x variable')) + self.outputs.append(BBVariable(name = 'y', size = 3, units = 'ft**2' , description = 'The y variable')) + self.availableDerivative = 1 + self.post_init_setup(len(self.inputs)) + + def BlackBox(*args, **kwargs):# The actual function that does things + args = list(args) + self = args.pop(0) + runCases, returnMode, remainingKwargs = self.parseInputs(*args, **kwargs) + + x = self.sanitizeInputs(runCases[0]['x']) + x = np.array([pyo.value(xval) for xval in x], dtype=np.float64) + + y = x**2 # Compute y + dydx = 2*x # Compute dy/dx + + y = y * units.ft**2 + dydx = np.diag(dydx) + dydx = dydx * units.ft # units.ft**2 / units.ft + + return y, [dydx] # return z, grad(z), hess(z)... + f.ConstraintList([ {'outputs':y, 'operators':'==', 'inputs':x, 'black_box':Parabola() },]) + cl = f.get_constraints() + self.assertTrue(len(cl)==1) + + + def test_edi_formulation_runtimeconstraint_exceptions(self): + "Tests the runtime constraint constructor in edi.formulation" + import pyomo.environ as pyo + from pyomo.environ import units + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel, BBVariable + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The unit circle output') + c = f.Constant(name = 'c', value = 1.0, units = '', description = 'A constant c', size = 2) + f.Objective( x + y ) + class UnitCircle(BlackBoxFunctionModel): + def __init__(self): + super(UnitCircle, self).__init__() + self.description = 'This model evaluates the function: z = x**2 + y**2' + self.inputs.append(BBVariable(name = 'x', size = 0, units = 'ft' , description = 'The x variable')) + self.inputs.append(BBVariable(name = 'y', size = 0, units = 'ft' , description = 'The y variable')) + self.outputs.append(BBVariable(name = 'z', size = 0, units = 'ft**2', description = 'Resultant of the unit circle evaluation')) + self.availableDerivative = 1 + self.post_init_setup(len(self.inputs)) + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float + y = pyo.value(units.convert(y,self.inputs[1].units)) # Converts to correct units then casts to float + z = x**2 + y**2 # Compute z + dzdx = 2*x # Compute dz/dx + dzdy = 2*y # Compute dz/dy + z *= units.ft**2 + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + f.Constraint( z <= 1*units.m**2 ) + + # flaggs the input or as bad before assigning the incorrect black box + self.assertRaises(ValueError, f.RuntimeConstraint, *(z, '==', 1.0 , UnitCircle() )) + self.assertRaises(ValueError, f.RuntimeConstraint, *(1.0, '==', [x,y] , UnitCircle() )) + + self.assertRaises(ValueError, f.RuntimeConstraint, *(z, '==', [1.0,y], UnitCircle() )) + self.assertRaises(ValueError, f.RuntimeConstraint, *(z, 1.0 , [x ,y], UnitCircle() )) + self.assertRaises(ValueError, f.RuntimeConstraint, *(z, '=', [x ,y], UnitCircle() )) + def test_edi_formulation_getvariables(self): "Tests the get_variables function in edi.formulation" import pyomo.environ as pyo @@ -262,7 +451,7 @@ def test_edi_formulation_getconstraints(self): # ================= import pyomo.environ as pyo from pyomo.environ import units - from pyomo.contrib.edi import Formulation, RuntimeConstraint, BlackBoxFunctionModel, BBVariable + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel, BBVariable # =================== # Declare Formulation @@ -332,7 +521,7 @@ def BlackBox(self, x, y): # The actual function that does things # ======================= f.ConstraintList( [ - RuntimeConstraint(z, '==', [x,y], UnitCircle() ) , + (z, '==', [x,y], UnitCircle() ) , z <= 1*units.m**2 ] ) From d62b0112fc55218397a01e623e6eceef159a036f Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Mon, 31 Jul 2023 22:20:36 -0600 Subject: [PATCH 17/61] variable docs done i think --- .../edi/blackboxconstraints.rst | 4 +- .../edi/blackboxobjectives.rst | 4 +- .../contributed_packages/edi/formulation.rst | 196 +++++++++++++++++- .../contributed_packages/edi/variables.rst | 174 ++++++++++++++++ 4 files changed, 367 insertions(+), 11 deletions(-) diff --git a/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst b/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst index cbc426f1548..09633d9cb62 100644 --- a/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst +++ b/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst @@ -1,5 +1,5 @@ -Black-Box Constraints -===================== +Runtime (Black-Box) Constraints +=============================== diff --git a/doc/OnlineDocs/contributed_packages/edi/blackboxobjectives.rst b/doc/OnlineDocs/contributed_packages/edi/blackboxobjectives.rst index 6ea6f6bacfc..b6e04cdd001 100644 --- a/doc/OnlineDocs/contributed_packages/edi/blackboxobjectives.rst +++ b/doc/OnlineDocs/contributed_packages/edi/blackboxobjectives.rst @@ -1,5 +1,5 @@ -Black-Box Objectives -==================== +Runtime (Black-Box) Objectives +============================== Black-box objectives are not currently supported and are awaiting an update to pyomo (see `this issue `_). diff --git a/doc/OnlineDocs/contributed_packages/edi/formulation.rst b/doc/OnlineDocs/contributed_packages/edi/formulation.rst index 1cc23154475..4145df9e00c 100644 --- a/doc/OnlineDocs/contributed_packages/edi/formulation.rst +++ b/doc/OnlineDocs/contributed_packages/edi/formulation.rst @@ -1,6 +1,11 @@ Formulation =========== +.. |br| raw:: html + +
+ + The core object in EDI is called a *Formulation*. For experienced pyomo users, a Formulation inherits from a pyomo *ConcreteModel*, and can therefore be treated exactly as a typical *ConcreteModel* with a few additional features. Each modeling element (ex: Variable, Constant, Objective, and Constraint) has a constructor that is used to create the corresponding element in the *Formulation* instance. In addition, there are a number of helper functions that collect and return model elements or perform supporting actions. @@ -147,19 +152,196 @@ The constraint list can also be declared a priori and passed in to the ``f.Const We recommend that most users should be using the ``f.ConstraintList()`` function, with the ``f.Constraint()`` function being reserved for under-the-hood usage. -Declaring Black-Box Constraints -------------------------------- +Declaring Runtime (Black-Box) Constraints +----------------------------------------- -See the :doc:`Black-Box Constraints <./blackboxconstraints>` Documentation +See the :doc:`Runtime (Black-Box) Constraints <./blackboxconstraints>` Documentation +One of the main features of EDI is the streamlined implementation of Black-Box Constraints. A *Black-Box* is defined as a routine that performs hidden computation not visible EDI, pyomo, or more generally the optimization algorithm. However, it is **not** assumed that black-boxes are unable to return gradient information. A black-box in this context may be capable of returning arbitrary derivative information. -Support Functions ------------------ +Black-box constraints are considered to be a sub-class of a more general class of constraints called *Runtime Constraints*, that is constraints that are not actually constructed until the optimization routine is actually running. In most cases, Runtime Constraints are approximated as linear by the solver, and therefore a Runtime Constraint is expected to provide function evaluations and gradient information. -.. |br| raw:: html +The use of Runtime constraints requires a black box model that is discussed in detail in the dedicated documentation (see :doc:`here <./blackboxconstraints>`), but for the purposes of demonstrating the constructors, a simple black box example will appear in all of the code snippets below. -
+Runtime Constraints are declared one of two ways, just as regular constraints. The ``f.RuntimeConstraint()`` constructor is available: + +.. code-block:: python + :linenos: + + import pyomo.environ as pyo + from pyomo.environ import units + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel, BBVariable + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'Output var') + f.Objective( x + y ) + class UnitCircle(BlackBoxFunctionModel): + def __init__(self): + super(UnitCircle, self).__init__() + self.description = 'This model evaluates the function: z = x**2 + y**2' + self.inputs.append(BBVariable( name = 'x', + size = 0, + units = 'ft' , + description = 'The x variable' ) ) + self.inputs.append(BBVariable( name = 'y', + size = 0, + units = 'ft' , + description = 'The y variable' ) ) + self.outputs.append(BBVariable( name = 'z', + size = 0, + units = 'ft**2', + description = 'Output variable' ) ) + self.availableDerivative = 1 + self.post_init_setup(len(self.inputs)) + def BlackBox(self, x, y): # The actual function that does things + # Converts to correct units then casts to float + x = pyo.value(units.convert(x,self.inputs[0].units)) + y = pyo.value(units.convert(y,self.inputs[1].units)) + z = x**2 + y**2 # Compute z + dzdx = 2*x # Compute dz/dx + dzdy = 2*y # Compute dz/dy + z *= units.ft**2 + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + f.Constraint( z <= 1*units.m**2 ) + + f.RuntimeConstraint( z, '==', [x,y], UnitCircle() ) + + +The ``f.RuntimeConstraint()`` constructor takes in the following inputs: + +.. py:function:: f.RuntimeConstraint(outputs, operators, inputs, black_box) + + Declares a runtime constraint in a pyomo.edi.formulation + + :param outputs: The outputs of the black box function + :type outputs: pyomo.environ.Var or list or tuple + :param operators: The operators that are used to construct constraints. Currently, only equality constraints are supported and will be the default no matter what is passed in here (see `this issue `_) + :type operators: str or list or tuple + :param inputs: The inputs to the black box function + :type inputs: pyomo.environ.Var or list or tuple + :param black_box: The object that stores the black-box function. See the :doc:`black box constraint documentation <./blackboxconstraints>` for details on constructing this object + :type black_box: pyomo.contrib.edi.BlackBoxFunctionModel + + +The following are alternative construction methods that may be of use: + +.. code-block:: python + + f.RuntimeConstraint( *( z, '==', [x,y], UnitCircle() ) ) + + +.. code-block:: python + + f.RuntimeConstraint( *[ z, '==', [x,y], UnitCircle() ] ) + +.. code-block:: python + + f.RuntimeConstraint( **{ 'outputs' : z, + 'operators' : '==', + 'inputs' : [x,y], + 'black_box' : UnitCircle() } ) +.. code-block:: python + + f.RuntimeConstraint( *( [z], ['=='], [x,y], UnitCircle() ) ) + +However, more commonly we expect users to construct Runtime Constraints as a part of a ``f.ConstraintList()`` declaration. Simply include a list, tuple, or dict as a part of the ConstraintList as follows: + +.. code-block:: python + :linenos: + + import pyomo.environ as pyo + from pyomo.environ import units + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel, BBVariable + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'Output var') + f.Objective( x + y ) + class UnitCircle(BlackBoxFunctionModel): + def __init__(self): + super(UnitCircle, self).__init__() + self.description = 'This model evaluates the function: z = x**2 + y**2' + self.inputs.append(BBVariable( name = 'x', + size = 0, + units = 'ft' , + description = 'The x variable' ) ) + self.inputs.append(BBVariable( name = 'y', + size = 0, + units = 'ft' , + description = 'The y variable' ) ) + self.outputs.append(BBVariable( name = 'z', + size = 0, + units = 'ft**2', + description = 'Output variable' ) ) + self.availableDerivative = 1 + self.post_init_setup(len(self.inputs)) + def BlackBox(self, x, y): # The actual function that does things + # Converts to correct units then casts to float + x = pyo.value(units.convert(x,self.inputs[0].units)) + y = pyo.value(units.convert(y,self.inputs[1].units)) + z = x**2 + y**2 # Compute z + dzdx = 2*x # Compute dz/dx + dzdy = 2*y # Compute dz/dy + z *= units.ft**2 + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + + + f.ConstraintList( + [ + z <= 1*units.m**2 ) , + [ z, '==', [x,y], UnitCircle() ] , + ] + ) + +Any of the alternative declarations above are valid to pass into the ``f.ConstraintList()`` constructor, for example: + +.. code-block:: python + + f.ConstraintList( + [ + z <= 1*units.m**2 ) , + ( z, '==', [x,y], UnitCircle() ) , + ] + ) + +.. code-block:: python + + f.ConstraintList( + [ + z <= 1*units.m**2 ) , + [ z, '==', [x,y], UnitCircle() ] , + ] + ) + +.. code-block:: python + + f.ConstraintList( + [ + z <= 1*units.m**2 ) , + { 'outputs' : z, + 'operators' : '==', + 'inputs' : [x,y], + 'black_box' : UnitCircle() } , + ] + ) + +.. code-block:: python + + f.ConstraintList( + [ + z <= 1*units.m**2 ) , + ( [z], ['=='], [x,y], UnitCircle() ) , + ] + ) + +Support Functions +----------------- ``f.get_variables()`` |br| Returns a list variables that have been defined in the formulation in declaration order. Will only return variables defined via EDI. diff --git a/doc/OnlineDocs/contributed_packages/edi/variables.rst b/doc/OnlineDocs/contributed_packages/edi/variables.rst index a5ab1a8ff97..abdb172ed6f 100644 --- a/doc/OnlineDocs/contributed_packages/edi/variables.rst +++ b/doc/OnlineDocs/contributed_packages/edi/variables.rst @@ -1,2 +1,176 @@ Variables ========= + +Overview +-------- +Variables are the mathematical representation of individual decisions being considered by the optimizer. In engineering design, variables are often representations of geometric parameters and operating conditions. + +Using the EDI package, variables can be defined as both scalar (``pyomo.core.base.var.ScalarVar``) and vector/matrix/tensor (``pyomo.core.base.var.IndexedVar``), and can exist in many mathematical spaces (All Real, Integers, etc). + +The Variable constructor is a very thin wrapper, and so experienced pyomo users will not see any significant differences from base pyomo. + + +Construction +------------ + +Variables are constructed by 1) creating an instance of a new variable in a EDI Formulation and 2) passing out this newly constructed variable to be used in objective and constraint construction. + +.. py:function:: f.Variable(name, guess, units, description='', size=None, bounds=None, domain=None) + + Declares a variable in a pyomo.edi.formulation + + :param name: The name of the variable for the purposes of tracking in the formulation. Commonly, this will be the same as the variable name in local namespace. + :type name: str + :param guess: The initial guess of the variable. For scalar variables, this should be a valid float or int for the specified domain. For vector variables, this will most often also be a single float or int, but a dictionary of index-value pairs is also accepted as in accordance with base pyomo. Numpy arrays will be supported in a future release (see `this issue `_) + :type guess: float or int or dict + :param units: The units of the variable. Every entry in a vector variable must have the same units. Entries of '', ' ', '-', 'None', and 'dimensionless' all become units.dimensionless + :type units: str or pyomo.core.base.units_container._PyomoUnit + :param description: A description of the variable + :type description: str + :param size: The size (or shape) of the variable. Entries of 0, 1, and None all correspond to scalar variables. Other integers correspond to vector variables. Matrix and tensor variable are declared using lists of ints, ex: [10,10]. Matrix and tensor variables with a dimension of 1 (ie, [10,10,1]) will be rejected as the extra dimension holds no meaningful value. + :type size: int or list + :param bounds: The bounds on the variable. A list or tuple of two elements [lower_bound, upper_bound] where the two bounds are assumed to be either ints or floats. WARNING: User is currently responsible for ensuring the units are correct (see `this issue `_) + :type bounds: list or tuple + :param domain: The domain of the variable (ex: Reals, Integers, etc). Default of None constructs a variable in Reals. + :type domain: pyomo set + + :return: The variable that was declared in the formulation + :rtype: pyomo.core.base.var.ScalarVar or pyomo.core.base.var.IndexedVar + + +Relation to Pyomo Var +--------------------- + +The fields: name, guess, domain, and bounds are directly passed to the pyomo ``Var`` constructor, with some minor checking. The description field is passed to the doc field in the pyomo ``Var``. Units are passed directly with an additional check. + + +Examples +-------- + + +A standard declaration statement +++++++++++++++++++++++++++++++++ + +.. code-block:: python + :linenos: + + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + + +Shortest possible declaration ++++++++++++++++++++++++++++++ + +.. code-block:: python + :linenos: + + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Variable('x', 1.0, 'm' ) + + +A variable with bounds +++++++++++++++++++++++ + +.. code-block:: python + :linenos: + + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Variable( name = 'x', + guess = 1.0, + units = 'm' , + description = 'The x variable', + bounds = [-10,10] ) + + +An integer variable ++++++++++++++++++++ + +.. code-block:: python + :linenos: + + from pyomo.contrib.edi import Formulation + from pyomo.environ import Integers + f = Formulation() + x = f.Variable( name = 'x', + guess = 1.0, + units = 'm' , + description = 'The x variable', + domain = Integers ) + + +An alternative units definition ++++++++++++++++++++++++++++++++ + +.. code-block:: python + :linenos: + + from pyomo.environ import units + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = units.m , description = 'The x variable') + + +A vector variable ++++++++++++++++++ + +.. code-block:: python + :linenos: + + from pyomo.environ import units + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Variable( name = 'x', + guess = 1.0, + units = 'm' , + description = 'The x variable', + size = 5 ) + + +A matrix/tensor variable +++++++++++++++++++++++++ + +.. code-block:: python + :linenos: + + from pyomo.environ import units + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Variable( name = 'x', + guess = 1.0, + units = 'm' , + description = 'The x variable', + size = [10,2] ) + + +More complicated units definition ++++++++++++++++++++++++++++++++++ + +.. code-block:: python + :linenos: + + from pyomo.environ import units + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Variable( name = 'x', + guess = 1.0, + units = 'kg*m/s**2' , + description = 'The x variable' ) + + +Tips +---- + +* Declare variables in alphabetical order. Trust me. It's a pain at first, but it saves a huge amount of time down the road, especially for large models. +* Designate a section in your file for variable declarations, as is done in the :doc:`introductory example <./quickstart>` +* Align all of your variable declarations in a pretty, grid like fashion. Again, a pain at first, but a big time saver +* Use the keyword names during variable declarations. Takes extra space, but is a massive boost to readability and intrepretability +* Declare one variable on one single line with no breaks, no matter what style guides tell you. Again, this is a significant boost to readability +* Do not skimp out on the description field, it is extremely helpful + + + + + From 39a5211282b731905963901e80f4a302f909d93f Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Tue, 1 Aug 2023 13:03:04 -0600 Subject: [PATCH 18/61] fixing the unit tests --- .../contributed_packages/edi/constants.rst | 171 ++++++++++++++++++ .../contributed_packages/edi/formulation.rst | 8 +- .../contributed_packages/edi/quickstart.rst | 3 +- .../contributed_packages/edi/variables.rst | 5 +- pyomo/contrib/edi/examples/readme_example.py | 6 +- pyomo/contrib/edi/tests/test_examples.py | 12 +- 6 files changed, 192 insertions(+), 13 deletions(-) diff --git a/doc/OnlineDocs/contributed_packages/edi/constants.rst b/doc/OnlineDocs/contributed_packages/edi/constants.rst index 0b076926ce7..5b90c83249a 100644 --- a/doc/OnlineDocs/contributed_packages/edi/constants.rst +++ b/doc/OnlineDocs/contributed_packages/edi/constants.rst @@ -1,2 +1,173 @@ Constants ========= + +Overview +-------- +Constants are a key mechanism used to capture the relationship between variables. In engineering design, constants are often defined by physics or operational limits. + +The Constant constructor is a very thin wrapper on pyomo ``Param``, and so experienced pyomo users will not see any significant differences from base pyomo. + + +Construction +------------ + +Constants are constructed by 1) creating an instance of a new parameter in a EDI Formulation and 2) passing out this newly constructed parameter to be used in objective and constraint construction. + +.. py:function:: f.Constant(name, value, units, description='', size=None, within=None) + + Declares a constant in a pyomo.edi.formulation + + :param name: The name of the constant for the purposes of tracking in the formulation. Commonly, this will be the same as the constant name in local namespace. + :type name: str + :param value: The value of the constant. For scalar constants, this should be a valid float or int for the specified domain. For vector constants, this will most often also be a single float or int, but a dictionary of index-value pairs is also accepted as in accordance with base pyomo. Numpy arrays will be supported in a future release (see `this issue `_) + :type value: float or int or dict + :param units: The units of the constant. Every entry in a vector constant must have the same units. Entries of '', ' ', '-', 'None', and 'dimensionless' all become units.dimensionless + :type units: str or pyomo.core.base.units_container._PyomoUnit + :param description: A description of the constant + :type description: str + :param size: The size (or shape) of the constant. Entries of 0, 1, and None all correspond to scalar constants. Other integers correspond to vector constants. Matrix and tensor constants are declared using lists of ints, ex: [10,10]. Matrix and tensor constants with a dimension of 1 (ie, [10,10,1]) will be rejected as the extra dimension holds no meaningful value. + :type size: int or list + :param within: The domain of the constant (ex: Reals, Integers, etc). Default of None constructs a constant in Reals. This option should rarely be used. + :type within: pyomo set + + :return: The constant that was declared in the formulation + :rtype: pyomo.core.base.var.ScalarVar or pyomo.core.base.var.IndexedVar + + +Relation to Pyomo Param +----------------------- + +The fields: name, guess, domain, and bounds are directly passed to the pyomo ``Var`` constructor, with some minor checking. The description field is passed to the doc field in the pyomo ``Var``. Units are passed directly with an additional check. + + +Examples +-------- + + +A standard declaration statement +++++++++++++++++++++++++++++++++ + +.. code-block:: python + :linenos: + + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + + +Shortest possible declaration ++++++++++++++++++++++++++++++ + +.. code-block:: python + :linenos: + + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Variable('x', 1.0, 'm' ) + + +A variable with bounds +++++++++++++++++++++++ + +.. code-block:: python + :linenos: + + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Variable( name = 'x', + guess = 1.0, + units = 'm' , + description = 'The x variable', + bounds = [-10,10] ) + + +An integer variable ++++++++++++++++++++ + +.. code-block:: python + :linenos: + + from pyomo.contrib.edi import Formulation + from pyomo.environ import Integers + f = Formulation() + x = f.Variable( name = 'x', + guess = 1.0, + units = 'm' , + description = 'The x variable', + domain = Integers ) + + +An alternative units definition ++++++++++++++++++++++++++++++++ + +.. code-block:: python + :linenos: + + from pyomo.environ import units + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Variable( name = 'x', + guess = 1.0, + units = units.m , + description = 'The x variable' ) + + +A vector variable ++++++++++++++++++ + +.. code-block:: python + :linenos: + + from pyomo.environ import units + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Variable( name = 'x', + guess = 1.0, + units = 'm' , + description = 'The x variable', + size = 5 ) + + +A matrix/tensor variable +++++++++++++++++++++++++ + +.. code-block:: python + :linenos: + + from pyomo.environ import units + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Variable( name = 'x', + guess = 1.0, + units = 'm' , + description = 'The x variable', + size = [10,2] ) + + +More complicated units definition ++++++++++++++++++++++++++++++++++ + +.. code-block:: python + :linenos: + + from pyomo.environ import units + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Variable( name = 'x', + guess = 1.0, + units = 'kg*m/s**2' , + description = 'The x variable' ) + + +Tips +---- + +* Declare variables in alphabetical order. Trust me. It's a pain at first, but it saves a huge amount of time down the road, especially for large models. +* Designate a section in your file for variable declarations, as is done in the :doc:`introductory example <./quickstart>` +* Align all of your variable declarations in a pretty, grid like fashion. Again, a pain at first, but a big time saver +* Use the keyword names during variable declarations. Takes extra space, but is a massive boost to readability and intrepretability +* Declare one variable on one single line with no breaks, no matter what style guides tell you. Again, this is a significant boost to readability +* Do not skimp out on the description field, it is extremely helpful + + + diff --git a/doc/OnlineDocs/contributed_packages/edi/formulation.rst b/doc/OnlineDocs/contributed_packages/edi/formulation.rst index 4145df9e00c..8ab0ffaf94d 100644 --- a/doc/OnlineDocs/contributed_packages/edi/formulation.rst +++ b/doc/OnlineDocs/contributed_packages/edi/formulation.rst @@ -172,8 +172,8 @@ Runtime Constraints are declared one of two ways, just as regular constraints. from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel, BBVariable f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'y variable') z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'Output var') f.Objective( x + y ) class UnitCircle(BlackBoxFunctionModel): @@ -257,8 +257,8 @@ However, more commonly we expect users to construct Runtime Constraints as a par from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel, BBVariable f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'y variable') z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'Output var') f.Objective( x + y ) class UnitCircle(BlackBoxFunctionModel): diff --git a/doc/OnlineDocs/contributed_packages/edi/quickstart.rst b/doc/OnlineDocs/contributed_packages/edi/quickstart.rst index b24ce8f8290..62620638a2d 100644 --- a/doc/OnlineDocs/contributed_packages/edi/quickstart.rst +++ b/doc/OnlineDocs/contributed_packages/edi/quickstart.rst @@ -16,4 +16,5 @@ The example shown here minimizes a linear objective function subject to the inte \end{align*} .. literalinclude:: ../../../../pyomo/contrib/edi/examples/readme_example.py - :language: python \ No newline at end of file + :language: python + :linenos: \ No newline at end of file diff --git a/doc/OnlineDocs/contributed_packages/edi/variables.rst b/doc/OnlineDocs/contributed_packages/edi/variables.rst index abdb172ed6f..429ccb4b2d3 100644 --- a/doc/OnlineDocs/contributed_packages/edi/variables.rst +++ b/doc/OnlineDocs/contributed_packages/edi/variables.rst @@ -110,7 +110,10 @@ An alternative units definition from pyomo.environ import units from pyomo.contrib.edi import Formulation f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = units.m , description = 'The x variable') + x = f.Variable( name = 'x', + guess = 1.0, + units = units.m , + description = 'The x variable' ) A vector variable diff --git a/pyomo/contrib/edi/examples/readme_example.py b/pyomo/contrib/edi/examples/readme_example.py index 5727d50e609..8722721247f 100644 --- a/pyomo/contrib/edi/examples/readme_example.py +++ b/pyomo/contrib/edi/examples/readme_example.py @@ -21,7 +21,11 @@ # ================= # Declare Constants # ================= -c = f.Constant(name = 'c', value = 1.0, units = '', description = 'A constant c', size = 2) +c = f.Constant( name = 'c', + value = 1.0, + units = '', + description = 'A constant c', + size = 2 ) # ===================== # Declare the Objective diff --git a/pyomo/contrib/edi/tests/test_examples.py b/pyomo/contrib/edi/tests/test_examples.py index 0910e8e49e2..22ebd61a34f 100644 --- a/pyomo/contrib/edi/tests/test_examples.py +++ b/pyomo/contrib/edi/tests/test_examples.py @@ -24,18 +24,18 @@ testIndex = 0 -class TestEDIExamples(unittest.TestCase): +class EDIExamples(unittest.TestCase): pass -def createTest(filename): - def testFunction(self): +def create_new(filename): + def t_function(self): importName = filename[0:-3] # filename = ".."+filename try: importlib.import_module("pyomo.contrib.edi.examples."+importName) except: self.fail("This example is failing: %s"%(filename)) - return testFunction + return t_function @@ -46,8 +46,8 @@ def testFunction(self): for filename in pythonFileList: testName = 'test_DocumentationExample_%d'%(testIndex) testIndex += 1 - testFunction = createTest(filename) - setattr(TestEDIExamples, testName, testFunction) + t_Function = create_new(filename) + setattr(EDIExamples, testName, t_Function) if __name__ == '__main__': From 09064b475c1996342642c3890ca30dec1a9a99db Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Tue, 1 Aug 2023 13:42:06 -0600 Subject: [PATCH 19/61] updating with an init file --- .../contributed_packages/edi/constants.rst | 14 ++++++++------ .../contributed_packages/edi/variables.rst | 4 +++- pyomo/contrib/edi/tests/__init__.py | 0 3 files changed, 11 insertions(+), 7 deletions(-) create mode 100644 pyomo/contrib/edi/tests/__init__.py diff --git a/doc/OnlineDocs/contributed_packages/edi/constants.rst b/doc/OnlineDocs/contributed_packages/edi/constants.rst index 5b90c83249a..0b1f6b06dad 100644 --- a/doc/OnlineDocs/contributed_packages/edi/constants.rst +++ b/doc/OnlineDocs/contributed_packages/edi/constants.rst @@ -37,7 +37,9 @@ Constants are constructed by 1) creating an instance of a new parameter in a EDI Relation to Pyomo Param ----------------------- -The fields: name, guess, domain, and bounds are directly passed to the pyomo ``Var`` constructor, with some minor checking. The description field is passed to the doc field in the pyomo ``Var``. Units are passed directly with an additional check. +The fields: name and within, and bounds are directly passed to the pyomo ``Param`` constructor, with some minor checking. The value field is passed to the ``Param`` initalize field. The description field is passed to the doc field in the pyomo ``Param``. Units are passed directly with an additional check. All Constants set the pyomo ``Param`` mutable field to True. + +Non-scalar constants are constructed using pyomo ``Sets``. Sets are constructed to be integer sets that fill the entire interval from lower bound to upper bound, ie a vector constant of length 5 would create a pyomo ``Set`` with valid indices [0,1,2,3,4] with no skips. In this way, non-scalar constatants are slightly less flexible than general non-scalar pyomo ``Params``. Examples @@ -162,11 +164,11 @@ More complicated units definition Tips ---- -* Declare variables in alphabetical order. Trust me. It's a pain at first, but it saves a huge amount of time down the road, especially for large models. -* Designate a section in your file for variable declarations, as is done in the :doc:`introductory example <./quickstart>` -* Align all of your variable declarations in a pretty, grid like fashion. Again, a pain at first, but a big time saver -* Use the keyword names during variable declarations. Takes extra space, but is a massive boost to readability and intrepretability -* Declare one variable on one single line with no breaks, no matter what style guides tell you. Again, this is a significant boost to readability +* Declare constants in alphabetical order. Trust me. It's a pain at first, but it saves a huge amount of time down the road, especially for large models. +* Designate a section in your file for constant declarations, as is done in the :doc:`introductory example <./quickstart>` +* Align all of your constant declarations in a pretty, grid like fashion. Depending on preference, these may or may not line up with variable declarations (I usually do not bother with this) +* Use the keyword names during constant declarations. Takes extra space, but is a massive boost to readability and intrepretability +* Declare one constant on one single line with no breaks, no matter what style guides tell you. Again, this is a significant boost to readability * Do not skimp out on the description field, it is extremely helpful diff --git a/doc/OnlineDocs/contributed_packages/edi/variables.rst b/doc/OnlineDocs/contributed_packages/edi/variables.rst index 429ccb4b2d3..1b10fe26bbc 100644 --- a/doc/OnlineDocs/contributed_packages/edi/variables.rst +++ b/doc/OnlineDocs/contributed_packages/edi/variables.rst @@ -41,7 +41,9 @@ Variables are constructed by 1) creating an instance of a new variable in a EDI Relation to Pyomo Var --------------------- -The fields: name, guess, domain, and bounds are directly passed to the pyomo ``Var`` constructor, with some minor checking. The description field is passed to the doc field in the pyomo ``Var``. Units are passed directly with an additional check. +The fields: name, domain, and bounds are directly passed to the pyomo ``Var`` constructor, with some minor checking. The guess field is passed to initalize. The description field is passed to the doc field in the pyomo ``Var``. Units are passed directly with an additional check. + +Non-scalar variables are constructed using pyomo ``Sets``. Sets are constructed to be integer sets that fill the entire interval from lower bound to upper bound, ie a vector variable of length 5 would create a pyomo ``Set`` with valid indices [0,1,2,3,4] with no skips. In this way, non-scalar constatants are slightly less flexible than general non-scalar pyomo ``Params``. Examples diff --git a/pyomo/contrib/edi/tests/__init__.py b/pyomo/contrib/edi/tests/__init__.py new file mode 100644 index 00000000000..e69de29bb2d From df6336bbad9d24c42daca4a0780fd562daac50d1 Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Tue, 1 Aug 2023 15:54:48 -0600 Subject: [PATCH 20/61] trying a testing fix --- .../edi/additionaltips.rst | 19 ++ .../edi/advancedruntimeconstraints.rst | 2 + .../edi/blackboxconstraints.rst | 187 +++++++++++++++++- .../contributed_packages/edi/constants.rst | 75 ++----- .../contributed_packages/edi/constraints.rst | 128 +++++++++++- .../contributed_packages/edi/formulation.rst | 13 +- .../contributed_packages/edi/index.rst | 4 +- .../contributed_packages/edi/objectives.rst | 156 +++++++++++++++ .../contributed_packages/edi/quickstart.rst | 6 +- .../contributed_packages/edi/variables.rst | 10 +- pyomo/contrib/edi/tests/test_examples.py | 11 +- pyomo/contrib/edi/tests/test_formulation.py | 5 + 12 files changed, 532 insertions(+), 84 deletions(-) create mode 100644 doc/OnlineDocs/contributed_packages/edi/additionaltips.rst create mode 100644 doc/OnlineDocs/contributed_packages/edi/advancedruntimeconstraints.rst diff --git a/doc/OnlineDocs/contributed_packages/edi/additionaltips.rst b/doc/OnlineDocs/contributed_packages/edi/additionaltips.rst new file mode 100644 index 00000000000..e00088f4ad0 --- /dev/null +++ b/doc/OnlineDocs/contributed_packages/edi/additionaltips.rst @@ -0,0 +1,19 @@ +Additional Tips +--------------- + +* If you wish to build the documentation locally, use: + +:: + + cd /pyomo/doc/OnlineDocs + make html + +then open the file ```` + + +* Unit tests and coverage can be run locally using: + +:: + + cd /pyomo/pyomo/contrib/edi + pytest --cov-report term-missing --cov=pyomo.contrib.edi -v ./tests/ \ No newline at end of file diff --git a/doc/OnlineDocs/contributed_packages/edi/advancedruntimeconstraints.rst b/doc/OnlineDocs/contributed_packages/edi/advancedruntimeconstraints.rst new file mode 100644 index 00000000000..b29d5583b37 --- /dev/null +++ b/doc/OnlineDocs/contributed_packages/edi/advancedruntimeconstraints.rst @@ -0,0 +1,2 @@ +Advanced Runtime Constraints +============================ diff --git a/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst b/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst index 09633d9cb62..abe46db0f13 100644 --- a/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst +++ b/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst @@ -2,10 +2,195 @@ Runtime (Black-Box) Constraints =============================== +Overview +-------- +While some constraints are explicitly known and can be written directly into the optimization problem, it is common (particularly in engineering design) for some relationships to be too complex to be directly coded as a constraint. + +One of the main features of EDI is the streamlined implementation of Black-Box Constraints. A *Black-Box* is defined as a routine that performs hidden computation not visible EDI, pyomo, or more generally the optimization algorithm. However, it is **not** assumed that black-boxes are unable to return gradient information. A black-box in this context may be capable of returning arbitrary derivative information. + +Black-box constraints are considered to be a sub-class of a more general class of constraints called *Runtime Constraints*, that is constraints that are not actually constructed until the optimization routine is actually running. In most cases, Runtime Constraints are approximated as linear by the solver, and therefore a Runtime Constraint is expected to provide function evaluations and gradient information. + +The use of Runtime constraints requires a black box model that is discussed in detail in the dedicated documentation (see :doc:`here <./blackboxconstraints>`), but for the purposes of demonstrating the constructors, a simple black box example will appear in all of the code snippets below. + +Runtime Constraints are declared one of two ways, just as regular constraints. The ``f.RuntimeConstraint()`` constructor is available: + +.. code-block:: python + + import pyomo.environ as pyo + from pyomo.environ import units + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel, BBVariable + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'y variable') + z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'Output var') + f.Objective( x + y ) + class UnitCircle(BlackBoxFunctionModel): + def __init__(self): + super(UnitCircle, self).__init__() + self.description = 'This model evaluates the function: z = x**2 + y**2' + self.inputs.append(BBVariable( name = 'x', + size = 0, + units = 'ft' , + description = 'The x variable' ) ) + self.inputs.append(BBVariable( name = 'y', + size = 0, + units = 'ft' , + description = 'The y variable' ) ) + self.outputs.append(BBVariable( name = 'z', + size = 0, + units = 'ft**2', + description = 'Output variable' ) ) + self.availableDerivative = 1 + self.post_init_setup(len(self.inputs)) + def BlackBox(self, x, y): # The actual function that does things + # Converts to correct units then casts to float + x = pyo.value(units.convert(x,self.inputs[0].units)) + y = pyo.value(units.convert(y,self.inputs[1].units)) + z = x**2 + y**2 # Compute z + dzdx = 2*x # Compute dz/dx + dzdy = 2*y # Compute dz/dy + z *= units.ft**2 + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + f.Constraint( z <= 1*units.m**2 ) + + f.RuntimeConstraint( z, '==', [x,y], UnitCircle() ) + + +The ``f.RuntimeConstraint()`` constructor takes in the following inputs: + +.. py:function:: f.RuntimeConstraint(outputs, operators, inputs, black_box) + + Declares a runtime constraint in a pyomo.edi.formulation + + :param outputs: The outputs of the black box function + :type outputs: pyomo.environ.Var or list or tuple + :param operators: The operators that are used to construct constraints. Currently, only equality constraints are supported and will be the default no matter what is passed in here (see `this issue `__) + :type operators: str or list or tuple + :param inputs: The inputs to the black box function + :type inputs: pyomo.environ.Var or list or tuple + :param black_box: The object that stores the black-box function. See the :doc:`black box constraint documentation <./blackboxconstraints>` for details on constructing this object + :type black_box: pyomo.contrib.edi.BlackBoxFunctionModel + + +The following are alternative construction methods that may be of use: + +.. code-block:: python + + f.RuntimeConstraint( *( z, '==', [x,y], UnitCircle() ) ) + + +.. code-block:: python + + f.RuntimeConstraint( *[ z, '==', [x,y], UnitCircle() ] ) + +.. code-block:: python + + f.RuntimeConstraint( **{ 'outputs' : z, + 'operators' : '==', + 'inputs' : [x,y], + 'black_box' : UnitCircle() } ) + +.. code-block:: python + + f.RuntimeConstraint( *( [z], ['=='], [x,y], UnitCircle() ) ) + +However, more commonly we expect users to construct Runtime Constraints as a part of a ``f.ConstraintList()`` declaration. Simply include a list, tuple, or dict as a part of the ConstraintList as follows: + +.. code-block:: python + + import pyomo.environ as pyo + from pyomo.environ import units + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel, BBVariable + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'y variable') + z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'Output var') + f.Objective( x + y ) + class UnitCircle(BlackBoxFunctionModel): + def __init__(self): + super(UnitCircle, self).__init__() + self.description = 'This model evaluates the function: z = x**2 + y**2' + self.inputs.append(BBVariable( name = 'x', + size = 0, + units = 'ft' , + description = 'The x variable' ) ) + self.inputs.append(BBVariable( name = 'y', + size = 0, + units = 'ft' , + description = 'The y variable' ) ) + self.outputs.append(BBVariable( name = 'z', + size = 0, + units = 'ft**2', + description = 'Output variable' ) ) + self.availableDerivative = 1 + self.post_init_setup(len(self.inputs)) + def BlackBox(self, x, y): # The actual function that does things + # Converts to correct units then casts to float + x = pyo.value(units.convert(x,self.inputs[0].units)) + y = pyo.value(units.convert(y,self.inputs[1].units)) + z = x**2 + y**2 # Compute z + dzdx = 2*x # Compute dz/dx + dzdy = 2*y # Compute dz/dy + z *= units.ft**2 + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + + + f.ConstraintList( + [ + z <= 1*units.m**2 ) , + [ z, '==', [x,y], UnitCircle() ] , + ] + ) + +Any of the alternative declarations above are valid to pass into the ``f.ConstraintList()`` constructor, for example: + +.. code-block:: python + + f.ConstraintList( + [ + z <= 1*units.m**2 ) , + ( z, '==', [x,y], UnitCircle() ) , + ] + ) + +.. code-block:: python + + f.ConstraintList( + [ + z <= 1*units.m**2 ) , + [ z, '==', [x,y], UnitCircle() ] , + ] + ) + +.. code-block:: python + + f.ConstraintList( + [ + z <= 1*units.m**2 ) , + { 'outputs' : z, + 'operators' : '==', + 'inputs' : [x,y], + 'black_box' : UnitCircle() } , + ] + ) + +.. code-block:: python + + f.ConstraintList( + [ + z <= 1*units.m**2 ) , + ( [z], ['=='], [x,y], UnitCircle() ) , + ] + ) Known Issues ------------ -* Runtime constraints must output to a variable, numbers and constants are not permitted (see `this issue `_) +* Currently only equality constraints are supported, pending an update to pyomo (see `this issue `__) +* Runtime constraints must output to a variable, numbers and constants are not permitted (see `this issue `__) diff --git a/doc/OnlineDocs/contributed_packages/edi/constants.rst b/doc/OnlineDocs/contributed_packages/edi/constants.rst index 0b1f6b06dad..10c41aecb1e 100644 --- a/doc/OnlineDocs/contributed_packages/edi/constants.rst +++ b/doc/OnlineDocs/contributed_packages/edi/constants.rst @@ -19,7 +19,7 @@ Constants are constructed by 1) creating an instance of a new parameter in a EDI :param name: The name of the constant for the purposes of tracking in the formulation. Commonly, this will be the same as the constant name in local namespace. :type name: str - :param value: The value of the constant. For scalar constants, this should be a valid float or int for the specified domain. For vector constants, this will most often also be a single float or int, but a dictionary of index-value pairs is also accepted as in accordance with base pyomo. Numpy arrays will be supported in a future release (see `this issue `_) + :param value: The value of the constant. For scalar constants, this should be a valid float or int for the specified domain. For vector constants, this will most often also be a single float or int, but a dictionary of index-value pairs is also accepted as in accordance with base pyomo. Numpy arrays will be supported in a future release (see `this issue `__) :type value: float or int or dict :param units: The units of the constant. Every entry in a vector constant must have the same units. Entries of '', ' ', '-', 'None', and 'dimensionless' all become units.dimensionless :type units: str or pyomo.core.base.units_container._PyomoUnit @@ -31,7 +31,7 @@ Constants are constructed by 1) creating an instance of a new parameter in a EDI :type within: pyomo set :return: The constant that was declared in the formulation - :rtype: pyomo.core.base.var.ScalarVar or pyomo.core.base.var.IndexedVar + :rtype: pyomo.core.base.param.ScalarParam or pyomo.core.base.param.IndexedParam Relation to Pyomo Param @@ -39,7 +39,7 @@ Relation to Pyomo Param The fields: name and within, and bounds are directly passed to the pyomo ``Param`` constructor, with some minor checking. The value field is passed to the ``Param`` initalize field. The description field is passed to the doc field in the pyomo ``Param``. Units are passed directly with an additional check. All Constants set the pyomo ``Param`` mutable field to True. -Non-scalar constants are constructed using pyomo ``Sets``. Sets are constructed to be integer sets that fill the entire interval from lower bound to upper bound, ie a vector constant of length 5 would create a pyomo ``Set`` with valid indices [0,1,2,3,4] with no skips. In this way, non-scalar constatants are slightly less flexible than general non-scalar pyomo ``Params``. +Non-scalar constants are constructed using pyomo ``Sets``. Sets are constructed to be integer sets that fill the entire interval from lower bound to upper bound, ie a vector constant of length 5 would create a pyomo ``Set`` with valid indices [0,1,2,3,4] with no skips. In this way, non-scalar constatants are slightly less flexible than general non-scalar pyomo ``Param``. Examples @@ -50,99 +50,63 @@ A standard declaration statement ++++++++++++++++++++++++++++++++ .. code-block:: python - :linenos: from pyomo.contrib.edi import Formulation f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + x = f.Constant(name = 'c', value = 1.0, units = 'm' , description = 'A constant c') Shortest possible declaration +++++++++++++++++++++++++++++ .. code-block:: python - :linenos: from pyomo.contrib.edi import Formulation f = Formulation() - x = f.Variable('x', 1.0, 'm' ) - - -A variable with bounds -++++++++++++++++++++++ - -.. code-block:: python - :linenos: - - from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable( name = 'x', - guess = 1.0, - units = 'm' , - description = 'The x variable', - bounds = [-10,10] ) - - -An integer variable -+++++++++++++++++++ - -.. code-block:: python - :linenos: - - from pyomo.contrib.edi import Formulation - from pyomo.environ import Integers - f = Formulation() - x = f.Variable( name = 'x', - guess = 1.0, - units = 'm' , - description = 'The x variable', - domain = Integers ) + x = f.Constant('c', 1.0, 'm' ) An alternative units definition +++++++++++++++++++++++++++++++ .. code-block:: python - :linenos: from pyomo.environ import units from pyomo.contrib.edi import Formulation f = Formulation() - x = f.Variable( name = 'x', - guess = 1.0, + x = f.Constant( name = 'c', + value = 1.0, units = units.m , - description = 'The x variable' ) + description = 'A constant c' ) -A vector variable +A vector constant +++++++++++++++++ .. code-block:: python - :linenos: from pyomo.environ import units from pyomo.contrib.edi import Formulation f = Formulation() - x = f.Variable( name = 'x', - guess = 1.0, + x = f.Constant( name = 'c', + value = 1.0, units = 'm' , - description = 'The x variable', + description = 'A constant c', size = 5 ) -A matrix/tensor variable +A matrix/tensor constant ++++++++++++++++++++++++ .. code-block:: python - :linenos: from pyomo.environ import units from pyomo.contrib.edi import Formulation f = Formulation() - x = f.Variable( name = 'x', - guess = 1.0, + x = f.Constant( name = 'c', + value = 1.0, units = 'm' , - description = 'The x variable', + description = 'A constant c', size = [10,2] ) @@ -150,15 +114,14 @@ More complicated units definition +++++++++++++++++++++++++++++++++ .. code-block:: python - :linenos: from pyomo.environ import units from pyomo.contrib.edi import Formulation f = Formulation() - x = f.Variable( name = 'x', - guess = 1.0, + x = f.Constant( name = 'c', + value = 1.0, units = 'kg*m/s**2' , - description = 'The x variable' ) + description = 'A constant c' ) Tips diff --git a/doc/OnlineDocs/contributed_packages/edi/constraints.rst b/doc/OnlineDocs/contributed_packages/edi/constraints.rst index a95a5faa60a..91968bd9549 100644 --- a/doc/OnlineDocs/contributed_packages/edi/constraints.rst +++ b/doc/OnlineDocs/contributed_packages/edi/constraints.rst @@ -2,16 +2,138 @@ Constraints =========== +Overview +-------- +Constraints are the mathematical representation of rules that are imposed on your decisions/variables when minimizing or maximizing. In engineering design, constraints are often imposed by physics or operational limits. +The Constraint constructor is a very thin wrapper on pyomo ``Constraint``, and so experienced pyomo users will not see any significant differences from base pyomo. +Construction +------------ + +Constraints are constructed by creating an instance of a new constraint in a EDI Formulation + +.. py:function:: f.Constraint(expr) + + Declares a constraint in a pyomo.edi.formulation + + :param expr: The expression representing the constraint + :type expr: pyomo expression + + :return: None + :rtype: None + + +However, the expected use case is the ``f.ConstraintList()`` function: + +.. py:function:: f.ConstraintList(conList) + + Declares new constraints in a pyomo.edi.formulation from a list of inputs + + :param conList: The list of constraints to be generated. Entries will be pyomo expressions, or lists/tuples/dicts that are used to create RuntimeConstraints (see :doc:`here <./blackboxconstraints>`) + :type conList: list + + :return: None + :rtype: None + + +Relation to Pyomo Constraint +---------------------------- + +The EDI constraint constructor is essentially a direct pass through to base pyomo. Constraints will be added to the ``pyomo.ConcreteModel`` in increasing order with key ``constraint_###`` where the the index of the objective appears after the underscore. First constraint is labeled as ``constraint_1``, and constraint names are never padded with zeros. RuntimeConstraints also contribute to this counter. + + +Examples +-------- + +A standard declaration statement +++++++++++++++++++++++++++++++++ + +.. code-block:: python + + from pyomo.environ import units + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) + f.Objective( c*x + y ) + f.ConstraintList( + [ + x**2 + y**2 <= 1.0*units.m**2 , + x <= 0.75*units.m, + x >= y , + ] + ) + + +With the core constructor ++++++++++++++++++++++++++ + +.. code-block:: python + + from pyomo.environ import units + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) + f.Objective( c*x + y ) + f.Constraint( x**2 + y**2 <= 1.0*units.m**2 ) + f.Constraint( x <= 0.75*units.m ) + f.Constraint( x >= y ) + + +Using indexed variables and constants ++++++++++++++++++++++++++++++++++++++ + +.. code-block:: python + + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Variable( name = 'x', + guess = 1.0, + units = 'm' , + description = 'The x variable', + bounds = [0,100] , + size = 3 ) + y = f.Variable( name = 'y', + guess = 1.0, + units = 'm' , + description = 'The y variable', + size = [2,2]) + c = f.Constant( name = 'c', + value = 1.0, + units = '' , + description = 'A constant c' , + size = 3 ) + f.Objective( c[0]*x[0] + c[1]*x[1] + c[2]*x[2] + + y[0,0]**4 + + y[0,1]**4 + + y[1,0]**4 + + y[1,1]**4 ) # Default is minimize + f.ConstraintList( + [ + x[0]**2 + x[1]**2 + x[2]**2 <= 1.0*units.m, + y[0,0] >= 1.0*units.m, + y[0,1] >= 1.0*units.m, + y[1,0] >= 1.0*units.m, + y[1,1] >= 1.0*units.m, + x[0] >= y[0,0] , + ] + ) + + +Tips +---- +* For the typical user, constraints should always be constructed using the ``f.ConstraintList()`` function to produce a cleaner input file that is easier to modify Known Issues ------------ -* Currently only equality constraints are supported, pending an update to pyomo (see `this issue `_) -* Indexed variables must be broken up using either indicies or a pyomo rule (see `this issue `_) -* Units that are inconsistent, but not the same (ie, meters and feet) will flag as invalid when checking units (see `this issue `_) +* Indexed variables must be broken up using either indicies or a pyomo rule (see `this issue `__) +* Units that are inconsistent, but not the same (ie, meters and feet) will flag as invalid when checking units (see `this issue `__) diff --git a/doc/OnlineDocs/contributed_packages/edi/formulation.rst b/doc/OnlineDocs/contributed_packages/edi/formulation.rst index 8ab0ffaf94d..de10c4faa85 100644 --- a/doc/OnlineDocs/contributed_packages/edi/formulation.rst +++ b/doc/OnlineDocs/contributed_packages/edi/formulation.rst @@ -15,7 +15,6 @@ Construction A *Formulation* is constructed as follows: .. code-block:: python - :linenos: from pyomo.contrib.edi import Formulation f = Formulation() @@ -31,7 +30,6 @@ See the :doc:`Variables <./variables>` Documentation Variables are declared using the ``f.Variable()`` function. This function creates an instance of ``pyomo.environ.Var`` and adds it to the ``edi.contrib.Formulation``. The function returns an instance of a ``pyomo.environ.Var`` that can be used in later construction. .. code-block:: python - :linenos: from pyomo.contrib.edi import Formulation f = Formulation() @@ -47,7 +45,6 @@ See the :doc:`Constants <./constants>` Documentation Constants (referred to in base pyomo as parameters or ``Params``) are declared using the ``f.Constant()`` function. This function creates an instance of a ``pyomo.environ.Param`` and adds it to the ``edi.contrib.Formulation``. This function also returns an instance of ``pyomo.environ.Param`` that can be used in later construction. .. code-block:: python - :linenos: from pyomo.contrib.edi import Formulation f = Formulation() @@ -62,7 +59,6 @@ See the :doc:`Objectives <./objectives>` Documentation Objectives are declared using the ``f.Objective()`` function. This function creates an instance of a ``pyomo.environ.Objective`` and adds it to the ``edi.contrib.Formulation``. Multiple objectives can be declared, but interpretation of multiple objectives will depend on the solver. The returned values of the ``f.Variable()`` and ``f.Constant()`` declarations can be used to construct the objective. Black-box (ie, Runtime) objectives are not supported at this time, but are planned in a future update. .. code-block:: python - :linenos: from pyomo.contrib.edi import Formulation f = Formulation() @@ -75,7 +71,6 @@ Objectives are declared using the ``f.Objective()`` function. This function cre By default, objectives are minimized, but can be switched to a maximize using the ``sense`` keyword from pyomo: .. code-block:: python - :linenos: from pyomo.contrib.edi import Formulation from pyomo.environ import maximize, minimize @@ -98,7 +93,6 @@ See the :doc:`Constraints <./constraints>` Documentation Constraints can be declared in two ways. First is using the standard ``f.Constraint()`` constructor. This function creates an instance of ``pyomo.environ.Constraint`` and adds it to the ``edi.contrib.Formulation``. The operators ``<=``, ``>=``, and ``==`` are used as constraint constructors. .. code-block:: python - :linenos: from pyomo.contrib.edi import Formulation f = Formulation() @@ -113,7 +107,6 @@ Constraints can be declared in two ways. First is using the standard ``f.Constr Constraints can also be declared using the ``f.ConstraintList()`` function. This function takes in a list of constraints and allows for multiple constraints to be declared in one go: .. code-block:: python - :linenos: from pyomo.contrib.edi import Formulation f = Formulation() @@ -132,7 +125,6 @@ Constraints can also be declared using the ``f.ConstraintList()`` function. Thi The constraint list can also be declared a priori and passed in to the ``f.ConstraintList()`` function, which may be beneficial for complex models: .. code-block:: python - :linenos: from pyomo.contrib.edi import Formulation f = Formulation() @@ -166,7 +158,6 @@ The use of Runtime constraints requires a black box model that is discussed in d Runtime Constraints are declared one of two ways, just as regular constraints. The ``f.RuntimeConstraint()`` constructor is available: .. code-block:: python - :linenos: import pyomo.environ as pyo from pyomo.environ import units @@ -213,12 +204,13 @@ Runtime Constraints are declared one of two ways, just as regular constraints. The ``f.RuntimeConstraint()`` constructor takes in the following inputs: .. py:function:: f.RuntimeConstraint(outputs, operators, inputs, black_box) + :noindex: Declares a runtime constraint in a pyomo.edi.formulation :param outputs: The outputs of the black box function :type outputs: pyomo.environ.Var or list or tuple - :param operators: The operators that are used to construct constraints. Currently, only equality constraints are supported and will be the default no matter what is passed in here (see `this issue `_) + :param operators: The operators that are used to construct constraints. Currently, only equality constraints are supported and will be the default no matter what is passed in here (see `this issue `__) :type operators: str or list or tuple :param inputs: The inputs to the black box function :type inputs: pyomo.environ.Var or list or tuple @@ -251,7 +243,6 @@ The following are alternative construction methods that may be of use: However, more commonly we expect users to construct Runtime Constraints as a part of a ``f.ConstraintList()`` declaration. Simply include a list, tuple, or dict as a part of the ConstraintList as follows: .. code-block:: python - :linenos: import pyomo.environ as pyo from pyomo.environ import units diff --git a/doc/OnlineDocs/contributed_packages/edi/index.rst b/doc/OnlineDocs/contributed_packages/edi/index.rst index 9a39cb04f41..297b0d63a8c 100644 --- a/doc/OnlineDocs/contributed_packages/edi/index.rst +++ b/doc/OnlineDocs/contributed_packages/edi/index.rst @@ -27,10 +27,12 @@ User's Guide blackboxobjectives.rst constraints.rst blackboxconstraints.rst + advancedruntimeconstraints.rst examples.rst + additionaltips.rst Developers ---------- -The pyomo EDI interface is developed and maintained by `Cody Karcher `_ \ No newline at end of file +The pyomo EDI interface is developed and maintained by `Cody Karcher `_ diff --git a/doc/OnlineDocs/contributed_packages/edi/objectives.rst b/doc/OnlineDocs/contributed_packages/edi/objectives.rst index 5f919b2f296..a5d5f20a97e 100644 --- a/doc/OnlineDocs/contributed_packages/edi/objectives.rst +++ b/doc/OnlineDocs/contributed_packages/edi/objectives.rst @@ -1,2 +1,158 @@ Objectives ========== + +Overview +-------- + +Objectives are the mathematical representation of what you wish to minimize or maximize. In engineering design, objectives are often minimizing cost, material, or time, or alternatively maximizing profit or utility. + +The Objective constructor is a very thin wrapper on pyomo ``Objective``, and so experienced pyomo users will not see any significant differences from base pyomo. + + +Construction +------------ + +Objectives are constructed by creating an instance of a new objective in a EDI Formulation + +.. py:function:: f.Objective(expr, sense=minimize) + + Declares an objective in a pyomo.edi.formulation + + :param expr: The expression to be optimized + :type expr: pyomo expression + :param sense: The sense in which the objective should be optimized, either minimized or maximized. Can import ``minimize`` and ``maximize`` from ``pyomo.environ``, but minimize corresponds to an integer of 1 and maximize to an integer of -1. + :type sense: int + + :return: None + :rtype: None + + +Relation to Pyomo Objective +--------------------------- + +The EDI objective constructor is essentially a direct pass through to base pyomo. Objectives will be added to the ``pyomo.ConcreteModel`` in increasing order with key ``objective_###`` where the the index of the objective appears after the underscore. First objective is labeled as ``objective_1``, and objective names are never padded with zeros. + + +Examples +-------- + +A standard declaration statement +++++++++++++++++++++++++++++++++ + +.. code-block:: python + + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) + f.Objective( c*x + y ) # Default is minimize + + +With a non-linear objective ++++++++++++++++++++++++++++ + +.. code-block:: python + + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) + f.Objective( c*x**4 + y**4 ) # Default is minimize + + +Explicitly minimize ++++++++++++++++++++ + +.. code-block:: python + + from pyomo.contrib.edi import Formulation + from pyomo.environ import minimize, maximize + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) + f.Objective( c*x**4 + y**4 , sense = minimize) + + +Explicitly minimize using integer ++++++++++++++++++++++++++++++++++ + +.. code-block:: python + + from pyomo.contrib.edi import Formulation + from pyomo.environ import minimize, maximize + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) + f.Objective( c*x**4 + y**4 , sense = 1 ) # 1 corresponds to minimize + + +Maximizing +++++++++++ + +.. code-block:: python + + from pyomo.contrib.edi import Formulation + from pyomo.environ import minimize, maximize + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) + f.Objective( -c*x**4 - y**4 , sense = maximize) + + +Maximizing using integer +++++++++++++++++++++++++ + +.. code-block:: python + + from pyomo.contrib.edi import Formulation + from pyomo.environ import minimize, maximize + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) + f.Objective( -c*x**4 - y**4 , sense = -1 ) # -1 corresponds to maximize + + +Using indexed variables and constants ++++++++++++++++++++++++++++++++++++++ + +.. code-block:: python + + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Variable( name = 'x', + guess = 1.0, + units = 'm' , + description = 'The x variable', + bounds = [0,100] , + size = 3 ) + y = f.Variable( name = 'y', + guess = 1.0, + units = 'm' , + description = 'The y variable', + size = [2,2]) + c = f.Constant( name = 'c', + value = 1.0, + units = '' , + description = 'A constant c' , + size = 3 ) + f.Objective( c[0]*x[0] + c[1]*x[1] + c[2]*x[2] + + y[0,0]**4 + + y[0,1]**4 + + y[1,0]**4 + + y[1,1]**4 ) # Default is minimize + + + +Tips +---- + +* Objectives are a pretty natural place to break your file. Put at least one blank line above and below the objective constructor and use good sectioning to create a whitespace easily identifiable when scrolling quickly + + + diff --git a/doc/OnlineDocs/contributed_packages/edi/quickstart.rst b/doc/OnlineDocs/contributed_packages/edi/quickstart.rst index 62620638a2d..beef3924563 100644 --- a/doc/OnlineDocs/contributed_packages/edi/quickstart.rst +++ b/doc/OnlineDocs/contributed_packages/edi/quickstart.rst @@ -3,6 +3,9 @@ Quickstart Below is a simple example written using EDI that should help get new users off the ground. For simple problems this example case is fine to build on, but for more advanced techniques (ex: using multi-dimensional variables or advanced black-box usage) see the additional documentation. +.. note:: + + The files and examples that appear in this documentation have been adapted to conform to PEP-8 standards, however, we **strongly discourage** the adoption of PEP-8 in the course of regular EDI usage as it makes the code (particularly variable declarations, constant declarations, and constraint list declarations) extremely challenging to read and debug The example shown here minimizes a linear objective function subject to the interior area of the unit circle: @@ -17,4 +20,5 @@ The example shown here minimizes a linear objective function subject to the inte .. literalinclude:: ../../../../pyomo/contrib/edi/examples/readme_example.py :language: python - :linenos: \ No newline at end of file + + diff --git a/doc/OnlineDocs/contributed_packages/edi/variables.rst b/doc/OnlineDocs/contributed_packages/edi/variables.rst index 1b10fe26bbc..988928b873d 100644 --- a/doc/OnlineDocs/contributed_packages/edi/variables.rst +++ b/doc/OnlineDocs/contributed_packages/edi/variables.rst @@ -29,7 +29,7 @@ Variables are constructed by 1) creating an instance of a new variable in a EDI :type description: str :param size: The size (or shape) of the variable. Entries of 0, 1, and None all correspond to scalar variables. Other integers correspond to vector variables. Matrix and tensor variable are declared using lists of ints, ex: [10,10]. Matrix and tensor variables with a dimension of 1 (ie, [10,10,1]) will be rejected as the extra dimension holds no meaningful value. :type size: int or list - :param bounds: The bounds on the variable. A list or tuple of two elements [lower_bound, upper_bound] where the two bounds are assumed to be either ints or floats. WARNING: User is currently responsible for ensuring the units are correct (see `this issue `_) + :param bounds: The bounds on the variable. A list or tuple of two elements [lower_bound, upper_bound] where the two bounds are assumed to be either ints or floats. WARNING: User is currently responsible for ensuring the units are correct (see `this issue `__) :type bounds: list or tuple :param domain: The domain of the variable (ex: Reals, Integers, etc). Default of None constructs a variable in Reals. :type domain: pyomo set @@ -54,7 +54,6 @@ A standard declaration statement ++++++++++++++++++++++++++++++++ .. code-block:: python - :linenos: from pyomo.contrib.edi import Formulation f = Formulation() @@ -65,7 +64,6 @@ Shortest possible declaration +++++++++++++++++++++++++++++ .. code-block:: python - :linenos: from pyomo.contrib.edi import Formulation f = Formulation() @@ -76,7 +74,6 @@ A variable with bounds ++++++++++++++++++++++ .. code-block:: python - :linenos: from pyomo.contrib.edi import Formulation f = Formulation() @@ -91,7 +88,6 @@ An integer variable +++++++++++++++++++ .. code-block:: python - :linenos: from pyomo.contrib.edi import Formulation from pyomo.environ import Integers @@ -107,7 +103,6 @@ An alternative units definition +++++++++++++++++++++++++++++++ .. code-block:: python - :linenos: from pyomo.environ import units from pyomo.contrib.edi import Formulation @@ -122,7 +117,6 @@ A vector variable +++++++++++++++++ .. code-block:: python - :linenos: from pyomo.environ import units from pyomo.contrib.edi import Formulation @@ -138,7 +132,6 @@ A matrix/tensor variable ++++++++++++++++++++++++ .. code-block:: python - :linenos: from pyomo.environ import units from pyomo.contrib.edi import Formulation @@ -154,7 +147,6 @@ More complicated units definition +++++++++++++++++++++++++++++++++ .. code-block:: python - :linenos: from pyomo.environ import units from pyomo.contrib.edi import Formulation diff --git a/pyomo/contrib/edi/tests/test_examples.py b/pyomo/contrib/edi/tests/test_examples.py index 22ebd61a34f..fd25e2cb665 100644 --- a/pyomo/contrib/edi/tests/test_examples.py +++ b/pyomo/contrib/edi/tests/test_examples.py @@ -19,13 +19,19 @@ import pyomo.environ as pyo from pyomo.common.dependencies import attempt_import +from pyomo.core.base.units_container import ( + pint_available, +) + import importlib testIndex = 0 class EDIExamples(unittest.TestCase): - pass + def test_edi_example_placeholder(self): + "A placeholder" + pass def create_new(filename): def t_function(self): @@ -47,7 +53,8 @@ def t_function(self): testName = 'test_DocumentationExample_%d'%(testIndex) testIndex += 1 t_Function = create_new(filename) - setattr(EDIExamples, testName, t_Function) + if pint_available: + setattr(EDIExamples, testName, t_Function) if __name__ == '__main__': diff --git a/pyomo/contrib/edi/tests/test_formulation.py b/pyomo/contrib/edi/tests/test_formulation.py index 95b927e16e7..327d42be1df 100644 --- a/pyomo/contrib/edi/tests/test_formulation.py +++ b/pyomo/contrib/edi/tests/test_formulation.py @@ -20,6 +20,10 @@ # from pyomo.opt import check_optimal_termination from pyomo.common.dependencies import attempt_import +from pyomo.core.base.units_container import ( + pint_available, +) + # np, numpy_available = attempt_import( # 'numpy', 'edi requires numpy' # ) @@ -33,6 +37,7 @@ # 'edi.formulation tests require numpy' # ) +@unittest.skipIf(not pint_available, 'Testing units requires pint') class TestEDIFormulation(unittest.TestCase): def test_edi_formulation_init(self): "Tests that a formulation initializes to the correct type and has proper data" From b369e686a0dd157aeabd7542db5cf88c4ce6d500 Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Tue, 1 Aug 2023 19:26:45 -0600 Subject: [PATCH 21/61] working on runtime constraint docs --- .../edi/additionaltips.rst | 16 +- .../edi/advancedruntimeconstraints.rst | 9 + .../edi/blackboxconstraints.rst | 305 ++++++++++++++++-- .../contributed_packages/edi/formulation.rst | 54 ++-- .../contributed_packages/edi/index.rst | 10 +- pyomo/contrib/edi/README.md | 14 +- pyomo/contrib/edi/__init__.py | 4 +- pyomo/contrib/edi/blackBoxFunctionModel.py | 151 ++++++--- pyomo/contrib/edi/examples/readme_example.py | 29 +- 9 files changed, 463 insertions(+), 129 deletions(-) diff --git a/doc/OnlineDocs/contributed_packages/edi/additionaltips.rst b/doc/OnlineDocs/contributed_packages/edi/additionaltips.rst index e00088f4ad0..c3f4fcdbbd7 100644 --- a/doc/OnlineDocs/contributed_packages/edi/additionaltips.rst +++ b/doc/OnlineDocs/contributed_packages/edi/additionaltips.rst @@ -1,6 +1,17 @@ Additional Tips --------------- +* Developers may need to install the following additional packages: + +:: + + pip install pytest + pip install pytest-cov + pip install sphinx + pip install sphinx_rtd_theme + pip install sphinx_copybutton + + * If you wish to build the documentation locally, use: :: @@ -16,4 +27,7 @@ then open the file ```` :: cd /pyomo/pyomo/contrib/edi - pytest --cov-report term-missing --cov=pyomo.contrib.edi -v ./tests/ \ No newline at end of file + pytest --cov-report term-missing --cov=pyomo.contrib.edi -v ./tests/ + + + diff --git a/doc/OnlineDocs/contributed_packages/edi/advancedruntimeconstraints.rst b/doc/OnlineDocs/contributed_packages/edi/advancedruntimeconstraints.rst index b29d5583b37..ba67f173066 100644 --- a/doc/OnlineDocs/contributed_packages/edi/advancedruntimeconstraints.rst +++ b/doc/OnlineDocs/contributed_packages/edi/advancedruntimeconstraints.rst @@ -1,2 +1,11 @@ Advanced Runtime Constraints ============================ + +Automated unpacking for multi-use black-boxes +--------------------------------------------- + + + + +Cases of non-scalar inputs or outputs +------------------------------------- \ No newline at end of file diff --git a/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst b/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst index abe46db0f13..5bf051cb7cf 100644 --- a/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst +++ b/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst @@ -7,11 +7,227 @@ Overview While some constraints are explicitly known and can be written directly into the optimization problem, it is common (particularly in engineering design) for some relationships to be too complex to be directly coded as a constraint. -One of the main features of EDI is the streamlined implementation of Black-Box Constraints. A *Black-Box* is defined as a routine that performs hidden computation not visible EDI, pyomo, or more generally the optimization algorithm. However, it is **not** assumed that black-boxes are unable to return gradient information. A black-box in this context may be capable of returning arbitrary derivative information. +EDI refers to these types of constraints as ``RuntimeConstraints`` because they are not constructed until they are needed by the solver. A particular subset of Runtime Constraints of interest are Black-Box constraints, that is, constraints which call to an external routine. To the average pyomo and EDI user, ``RuntimeConstraints`` are for all intents and purposes Black-Box constraint, and the distinction is semantic. -Black-box constraints are considered to be a sub-class of a more general class of constraints called *Runtime Constraints*, that is constraints that are not actually constructed until the optimization routine is actually running. In most cases, Runtime Constraints are approximated as linear by the solver, and therefore a Runtime Constraint is expected to provide function evaluations and gradient information. +In other words, if you wish to code a black-box constraint using EDI, you will be using the Runtime Constraint constructor. -The use of Runtime constraints requires a black box model that is discussed in detail in the dedicated documentation (see :doc:`here <./blackboxconstraints>`), but for the purposes of demonstrating the constructors, a simple black box example will appear in all of the code snippets below. +In this context, a *Black-Box* is defined as a routine that performs hidden computation not visible EDI, pyomo, or more generally the optimization algorithm. However, it is **not** assumed that black-boxes are unable to return gradient information. A black-box in this context may be capable of returning arbitrary derivative information. + + +Construction +------------ + +Runtime constraints consist of two separate elements that need to be constructed. + + +Constructing a Black Box +++++++++++++++++++++++++ + +First, we need to create an object which is visible to pyomo/EDI that calls the black-box function. EDI calls this a ``BlackBoxFunctionModel``, and it is a base class that gets inherited into the objects you will create as a user. + +A simple example is shown below: + +.. code-block:: python + + import pyomo.environ as pyo + from pyomo.environ import units + from pyomo.contrib.edi import BlackBoxFunctionModel + + class Parabola(BlackBoxFunctionModel): + def __init__(self): + + # Call parent init + super().__init__() + + # A brief description of the model + self.description = 'This model evaluates the function: y = x**2' + + # Append the model inputs + self.inputs.append( + name='x', units='ft' , description='The x variable' + ) + + # Append the model outputs + self.outputs.append( + name='y', units='ft**2' , description='The y variable' + ) + + # Set the highest available derivative + # Should be 1 for most cases but defauts to 0 + self.availableDerivative = 1 + + def BlackBox(self, x): # The actual function that does things + + # Convert to correct units and cast to a float + x = pyo.value(units.convert(x,self.inputs['x'].units)) + + # Compute y + y = x**2 + + # Compute dy/dx + dydx = 2*x + + # Add the units to the output + y = y * self.inputs['y'].units + + # Add the units to the derivative for output + dydx = dydx * self.inputs['y'].units / self.inputs['x'].units + + # Return + return y, [dydx] # return z, grad(z), hess(z)... + + +The inheriting classes can have any valid python name (in this case ``Parabola``) and have two methods ``__init__()`` and ``BlackBox()``. + + +The init method +*************** + +The ``__init__()`` function sets up the model, and has 5 distinct steps. First, the parent class ``__init__()`` must be called: + +.. code-block:: python + + super().__init__() + +In general, this line can be used verbatim. + +Next, you must tell the model what its inputs are by appending them to the ``self.inputs`` attribute. These inputs exist entirely in the local namespace of the black-box model, and are **independent** of the namespace in the optimization model (ex, something called ``x`` in the optimization can be called ``y`` in the black-box model). Inputs must have a ``name`` and ``units``, and has optional arguments ``description``, and ``size``, all of which are defined the same way as EDI variables. (There are some advanced uses reserved for the advanced discussion). + +.. py:function:: self.inputs.append(name, units, description='', size=0) + + Appends a variable to a black box input list + + :param name: The name of the variable, any valid python string. **Does not** have to match the name in the optimization formulation + :type name: str + :param units: The units of the variable. Every entry in a vector variable must have the same units. Entries of '', ' ', '-', 'None', and 'dimensionless' all become units.dimensionless. The units **must** be convertable to the units used in the optimization formulation (ex, meters and feet), but **are not required** to be the same. Because of this, for example, a black-box can be written in imperial units, while an optimization formulation operates in metric. + :type units: str or pyomo.core.base.units_container._PyomoUnit + :param description: A description of the variable + :type description: str + :param size: The size (or shape) of the variable. Entries of 0, 1, and None all correspond to scalar variables. Other integers correspond to vector variables. Matrix and tensor variable are declared using lists of ints, ex: [10,10]. Matrix and tensor variables with a dimension of 1 (ie, [10,10,1]) will be rejected as the extra dimension holds no meaningful value. + :type size: int or list + + +Models with multiple inputs simply call the ``self.input.append()`` command multiple times: + +.. code-block:: python + + # Append the model inputs + self.inputs.append( name='x', units='ft' , description='The x variable' ) + self.inputs.append( name='y', units='ft' , description='The y variable' ) + +Input names must be unique, and an error is raised if a repeated name is attempted to be set. + + +Next, outputs must be added to the model. This is done identically to inputs, however the function is now ``self.outputs.append()`` + +.. py:function:: self.outputs.append(name, units, description='', size=0) + + Appends a variable to a black box output list + + :param name: The name of the variable, any valid python string. **Does not** have to match the name in the optimization formulation + :type name: str + :param units: The units of the variable. Every entry in a vector variable must have the same units. Entries of '', ' ', '-', 'None', and 'dimensionless' all become units.dimensionless. The units **must** be convertable to the units used in the optimization formulation (ex, meters and feet), but **are not required** to be the same. Because of this, for example, a black-box can be written in imperial units, while an optimization formulation operates in metric. + :type units: str or pyomo.core.base.units_container._PyomoUnit + :param description: A description of the variable + :type description: str + :param size: The size (or shape) of the variable. Entries of 0, 1, and None all correspond to scalar variables. Other integers correspond to vector variables. Matrix and tensor variable are declared using lists of ints, ex: [10,10]. Matrix and tensor variables with a dimension of 1 (ie, [10,10,1]) will be rejected as the extra dimension holds no meaningful value. + :type size: int or list + + +and similarly: + +.. code-block:: python + + # Append the model inputs + self.outputs.append( name='u', units='ft' , description='The u variable' ) + self.outputs.append( name='v', units='ft' , description='The v variable' ) + + +Finally, the highest available derivative must be set. For models being used in optimization, this will most often be ``1``, ie first derivative, gradient, or Jacobian information. + + +.. code-block:: python + + # Set the highest available derivative + # Should be 1 for most cases but defauts to 0 + self.availableDerivative = 1 + + +The BlackBox method +******************* + +The ``BlackBox`` is extremely flexible, but here we present standard usage for a beginner user. Advanced users should also check the :doc:`advanced <./advancedruntimeconstraints>` documentation for more flexible ways of building black-box models + +The ``BlackBox`` method assumes to take in the inputs as arguments in the order defined during the ``__init__()`` method. Note that the method assumes inputs **with units** and expects outputs **with units**. In general, the units on inputs and outputs need not be in any specific system, but should be convertible (ex, meters and feet) to whatever has been specified as the input units when defining in the ``__init__()`` function. + +Since the units cannot be assumed on input, the first step in any black box is to convert to the model units: + +.. code-block:: python + + from pyomo.environ import units + x = units.convert(x,self.inputs['x'].units) + + +And frequently, it is a good idea to cast these to a float value using ``pyomo.environ.value``: + +.. code-block:: python + + import pyomo.environ as pyo + from pyomo.environ import units + x = pyo.value(units.convert(x,self.inputs['x'].units)) + +The assumed units can now be added if desired, but this may cause a slowdown in performance. + +Operations can now be performed to compute the output and derivatives as desired. + +When preparing the outputs, note that all outputs must have units: + +.. code-block:: python + + # Add the units to the output + y = y * self.inputs['y'].units + + # Add the units to the derivative for output + dydx = dydx * self.inputs['y'].units / self.inputs['x'].units + + +The ``BlackBox`` method then outputs a tuple of length ``self.availableDerivative+1``. Entry [0] is the values specified during the ``__init__()``, entry [1] is first derivative information, and similar for higher order if available. + +.. code-block:: python + + # Return + return y, [dydx] # return z, grad(z), hess(z)... + + +The full unpacking is as follows: + +:: + + output[0] = + output[0][] = + + output[1] = + output[1][][] = + + +if there is one single scalar output, the unpacking may be simplified to the following (as shown in the above example) + +:: + + output[0] = + output[0][] = + + output[1] = + output[1][] = + + +See the :doc:`advanced <./advancedruntimeconstraints>` documentation for cases where the inputs and outputs are not scalar. + + +Including a Black-Box in an EDI Formulation ++++++++++++++++++++++++++++++++++++++++++++ + +This second construction step is covered in the :doc:`Formulation <./formulation>` documentation, but is repeated here for completion. Runtime Constraints are declared one of two ways, just as regular constraints. The ``f.RuntimeConstraint()`` constructor is available: @@ -19,7 +235,7 @@ Runtime Constraints are declared one of two ways, just as regular constraints. import pyomo.environ as pyo from pyomo.environ import units - from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel, BBVariable + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel f = Formulation() x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'x variable') y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'y variable') @@ -27,26 +243,23 @@ Runtime Constraints are declared one of two ways, just as regular constraints. f.Objective( x + y ) class UnitCircle(BlackBoxFunctionModel): def __init__(self): - super(UnitCircle, self).__init__() + super().__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append(BBVariable( name = 'x', - size = 0, - units = 'ft' , - description = 'The x variable' ) ) - self.inputs.append(BBVariable( name = 'y', - size = 0, - units = 'ft' , - description = 'The y variable' ) ) - self.outputs.append(BBVariable( name = 'z', - size = 0, - units = 'ft**2', - description = 'Output variable' ) ) + self.inputs.append( name = 'x', + units = 'ft' , + description = 'The x variable' ) + self.inputs.append( name = 'y', + units = 'ft' , + description = 'The y variable' ) + self.outputs.append name = 'z', + units = 'ft**2', + description = 'Output variable' ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) def BlackBox(self, x, y): # The actual function that does things # Converts to correct units then casts to float - x = pyo.value(units.convert(x,self.inputs[0].units)) - y = pyo.value(units.convert(y,self.inputs[1].units)) + x = pyo.value(units.convert(x,self.inputs['x'].units)) + y = pyo.value(units.convert(y,self.inputs['y'].units)) z = x**2 + y**2 # Compute z dzdx = 2*x # Compute dz/dx dzdy = 2*y # Compute dz/dy @@ -103,7 +316,7 @@ However, more commonly we expect users to construct Runtime Constraints as a par import pyomo.environ as pyo from pyomo.environ import units - from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel, BBVariable + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel f = Formulation() x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'x variable') y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'y variable') @@ -111,20 +324,17 @@ However, more commonly we expect users to construct Runtime Constraints as a par f.Objective( x + y ) class UnitCircle(BlackBoxFunctionModel): def __init__(self): - super(UnitCircle, self).__init__() + super().__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append(BBVariable( name = 'x', - size = 0, - units = 'ft' , - description = 'The x variable' ) ) - self.inputs.append(BBVariable( name = 'y', - size = 0, - units = 'ft' , - description = 'The y variable' ) ) - self.outputs.append(BBVariable( name = 'z', - size = 0, - units = 'ft**2', - description = 'Output variable' ) ) + self.inputs.append( name = 'x', + units = 'ft' , + description = 'The x variable' ) + self.inputs.append( name = 'y', + units = 'ft' , + description = 'The y variable' ) + self.outputs.append name = 'z', + units = 'ft**2', + description = 'Output variable' ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) def BlackBox(self, x, y): # The actual function that does things @@ -188,6 +398,35 @@ Any of the alternative declarations above are valid to pass into the ``f.Constra ] ) +Examples +-------- + +A standard declaration statement +++++++++++++++++++++++++++++++++ + +.. code-block:: python + + from pyomo.environ import units + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) + f.Objective( c*x + y ) + f.ConstraintList( + [ + x**2 + y**2 <= 1.0*units.m**2 , + x <= 0.75*units.m, + x >= y , + ] + ) + + +Tips +---- + +* + Known Issues ------------ diff --git a/doc/OnlineDocs/contributed_packages/edi/formulation.rst b/doc/OnlineDocs/contributed_packages/edi/formulation.rst index de10c4faa85..2513b1f86c1 100644 --- a/doc/OnlineDocs/contributed_packages/edi/formulation.rst +++ b/doc/OnlineDocs/contributed_packages/edi/formulation.rst @@ -161,7 +161,7 @@ Runtime Constraints are declared one of two ways, just as regular constraints. import pyomo.environ as pyo from pyomo.environ import units - from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel, BBVariable + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel f = Formulation() x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'x variable') y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'y variable') @@ -169,26 +169,23 @@ Runtime Constraints are declared one of two ways, just as regular constraints. f.Objective( x + y ) class UnitCircle(BlackBoxFunctionModel): def __init__(self): - super(UnitCircle, self).__init__() + super().__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append(BBVariable( name = 'x', - size = 0, - units = 'ft' , - description = 'The x variable' ) ) - self.inputs.append(BBVariable( name = 'y', - size = 0, - units = 'ft' , - description = 'The y variable' ) ) - self.outputs.append(BBVariable( name = 'z', - size = 0, - units = 'ft**2', - description = 'Output variable' ) ) + self.inputs.append( name = 'x', + units = 'ft' , + description = 'The x variable' ) + self.inputs.append( name = 'y', + units = 'ft' , + description = 'The y variable' ) + self.outputs.append( name = 'z', + units = 'ft**2', + description = 'Output variable' ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) def BlackBox(self, x, y): # The actual function that does things # Converts to correct units then casts to float - x = pyo.value(units.convert(x,self.inputs[0].units)) - y = pyo.value(units.convert(y,self.inputs[1].units)) + x = pyo.value(units.convert(x,self.inputs['x'].units)) + y = pyo.value(units.convert(y,self.inputs['y'].units)) z = x**2 + y**2 # Compute z dzdx = 2*x # Compute dz/dx dzdy = 2*y # Compute dz/dy @@ -246,7 +243,7 @@ However, more commonly we expect users to construct Runtime Constraints as a par import pyomo.environ as pyo from pyomo.environ import units - from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel, BBVariable + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel f = Formulation() x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'x variable') y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'y variable') @@ -254,20 +251,17 @@ However, more commonly we expect users to construct Runtime Constraints as a par f.Objective( x + y ) class UnitCircle(BlackBoxFunctionModel): def __init__(self): - super(UnitCircle, self).__init__() + super().__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append(BBVariable( name = 'x', - size = 0, - units = 'ft' , - description = 'The x variable' ) ) - self.inputs.append(BBVariable( name = 'y', - size = 0, - units = 'ft' , - description = 'The y variable' ) ) - self.outputs.append(BBVariable( name = 'z', - size = 0, - units = 'ft**2', - description = 'Output variable' ) ) + self.inputs.append( name = 'x', + units = 'ft' , + description = 'The x variable' ) + self.inputs.append( name = 'y', + units = 'ft' , + description = 'The y variable' ) + self.outputs.append( name = 'z', + units = 'ft**2', + description = 'Output variable' ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) def BlackBox(self, x, y): # The actual function that does things diff --git a/doc/OnlineDocs/contributed_packages/edi/index.rst b/doc/OnlineDocs/contributed_packages/edi/index.rst index 297b0d63a8c..3b9d974548a 100644 --- a/doc/OnlineDocs/contributed_packages/edi/index.rst +++ b/doc/OnlineDocs/contributed_packages/edi/index.rst @@ -13,11 +13,19 @@ EDI installs as a part of the standard pyomo install: pip install pyomo + +EDI also requires the pint package that is viewed as optional by pyomo: + +:: + + pip install pint + + User's Guide ------------ .. toctree:: - :maxdepth: 1 + :maxdepth: 4 quickstart.rst formulation.rst diff --git a/pyomo/contrib/edi/README.md b/pyomo/contrib/edi/README.md index 6366d782271..c43be14d513 100644 --- a/pyomo/contrib/edi/README.md +++ b/pyomo/contrib/edi/README.md @@ -21,7 +21,7 @@ Below is a simple example to get started, but additional resources can be found # ================= import pyomo.environ as pyo from pyomo.environ import units -from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel, BBVariable +from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel # =================== # Declare Formulation @@ -54,17 +54,17 @@ class UnitCircle(BlackBoxFunctionModel): def __init__(self): # The initalization function # Initalize the black box model - super(UnitCircle, self).__init__() + super().__init__() # A brief description of the model self.description = 'This model evaluates the function: z = x**2 + y**2' # Declare the black box model inputs - self.inputs.append(BBVariable(name = 'x', size = 0, units = 'ft' , description = 'The x variable')) - self.inputs.append(BBVariable(name = 'y', size = 0, units = 'ft' , description = 'The y variable')) + self.inputs.append(name = 'x', units = 'ft' , description = 'The x variable') + self.inputs.append(name = 'y', units = 'ft' , description = 'The y variable') # Declare the black box model outputs - self.outputs.append(BBVariable(name = 'z', size = 0, units = 'ft**2', description = 'Resultant of the unit circle evaluation')) + self.outputs.append(name = 'z', units = 'ft**2', description = 'Resultant of the unit circle evaluation') # Declare the maximum available derivative self.availableDerivative = 1 @@ -73,8 +73,8 @@ class UnitCircle(BlackBoxFunctionModel): self.post_init_setup() def BlackBox(self, x, y): # The actual function that does things - x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float - y = pyo.value(units.convert(y,self.inputs[1].units)) # Converts to correct units then casts to float + x = pyo.value(units.convert(x,self.inputs['x'].units)) # Converts to correct units then casts to float + y = pyo.value(units.convert(y,self.inputs['y'].units)) # Converts to correct units then casts to float z = x**2 + y**2 # Compute z dzdx = 2*x # Compute dz/dx diff --git a/pyomo/contrib/edi/__init__.py b/pyomo/contrib/edi/__init__.py index fec8a6c7ab3..cd2d5ff3063 100644 --- a/pyomo/contrib/edi/__init__.py +++ b/pyomo/contrib/edi/__init__.py @@ -23,4 +23,6 @@ # Import the black box modeling tools from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel -from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable as BBVariable \ No newline at end of file +from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable as BlackBoxVariable +from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable as BBVariable +from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable as BBV \ No newline at end of file diff --git a/pyomo/contrib/edi/blackBoxFunctionModel.py b/pyomo/contrib/edi/blackBoxFunctionModel.py index 48c83da517a..79887c0e01d 100644 --- a/pyomo/contrib/edi/blackBoxFunctionModel.py +++ b/pyomo/contrib/edi/blackBoxFunctionModel.py @@ -24,45 +24,8 @@ from pyomo.contrib.pynumero.interfaces.external_grey_box import ExternalGreyBoxModel, ExternalGreyBoxBlock -class TypeCheckedList(list): - def __init__(self, checkItem, itemList = None): - super(TypeCheckedList, self).__init__() - self.checkItem = checkItem - - if itemList is not None: - if isinstance(itemList, list) or isinstance(itemList, tuple): - for itm in itemList: - self.append(itm) - else: - raise ValueError('Input to itemList is not iterable') - - def __setitem__(self, key, val): - if isinstance(val, self.checkItem): - super(TypeCheckedList, self).__setitem__(key, val) - else: - raise ValueError('Input must be an instance of the defined type') - - def __setslice__(self, i, j, sequence): - performSet = True - for val in sequence: - if not isinstance(val, self.checkItem): - performSet = False - break - - if performSet: - super(TypeCheckedList, self).__setslice__(i,j,sequence) - else: - raise ValueError('All values in the input must be an instance of the defined type') - - def append(self, val): - if isinstance(val, self.checkItem): - super(TypeCheckedList, self).append(val) - else: - raise ValueError('Input must be an instance of the defined type') - - class BlackBoxFunctionModel_Variable(object): - def __init__(self, name, size, units, description = ''): + def __init__(self, name, units, description = '', size = 0): # Order matters self.name = name self.units = units @@ -153,6 +116,114 @@ def description(self, val): else: raise ValueError('Invalid description. Must be a string.') + + + + +class TypeCheckedList(list): + def __init__(self, checkItem, itemList = None): + super(TypeCheckedList, self).__init__() + self.checkItem = checkItem + + if itemList is not None: + if isinstance(itemList, list) or isinstance(itemList, tuple): + for itm in itemList: + self.append(itm) + else: + raise ValueError('Input to itemList is not iterable') + + def __setitem__(self, key, val): + if isinstance(val, self.checkItem): + super(TypeCheckedList, self).__setitem__(key, val) + else: + raise ValueError('Input must be an instance of the defined type') + + def __setslice__(self, i, j, sequence): + performSet = True + for val in sequence: + if not isinstance(val, self.checkItem): + performSet = False + break + + if performSet: + super(TypeCheckedList, self).__setslice__(i,j,sequence) + else: + raise ValueError('All values in the input must be an instance of the defined type') + + def append(self, val): + if isinstance(val, self.checkItem): + super(TypeCheckedList, self).append(val) + else: + raise ValueError('Input must be an instance of the defined type') + + +class BBList(TypeCheckedList): + def __init__(self): + super(BBList, self).__init__(BlackBoxFunctionModel_Variable,[]) + self._lookupDict = {} + self._counter = 0 + + def __getitem__(self, val): + if isinstance(val, int): + return super(BBList, self).__getitem__(val) + elif isinstance(val, str): + return super(BBList, self).__getitem__(self._lookupDict[val]) + else: + raise ValueError('Input must be an integer or a valid variable name') + + + + def append(*args, **kwargs): + args = list(args) + self = args.pop(0) + + if len(args) + len(kwargs.values()) == 1: + if len(args) == 1: + inputData = args[0] + if len(kwargs.values()) == 1: + inputData = list(kwargs.values())[0] + + if isinstance(inputData, self.checkItem): + if inputData.name in self._lookupDict.keys(): + raise ValueError("Key '%s' already exists in the input list"%(inputData.name)) + self._lookupDict[inputData.name] = self._counter + self._counter += 1 + super(BBList, self).append(inputData) + else: + if isinstance(inputData, str): + raise ValueError("Key '%s' not passed in to the black box variable constructor"%('units')) + else: + raise ValueError('Invalid (single) input type') + + elif len(args) + len(kwargs.values()) <= 4: + argKeys = ['name','units','description','size'] + ipd = dict(zip(argKeys[0:len(args)],args)) + for ky, vl in kwargs.items(): + if ky in ipd: + raise ValueError("Key '%s' declared after non-keyword arguments and is out of order"%(ky)) + else: + ipd[ky]=vl + + for ak in argKeys: + if ak not in ipd.keys(): + if ak == 'description': + ipd['description']='' + elif ak == 'size': + ipd['size'] = 0 + else: + raise ValueError("Key '%s' not passed in to the black box variable constructor"%(ak)) + + + if ipd['name'] in self._lookupDict.keys(): + raise ValueError("Key '%s' already exists in the input list"%(ipd['name'])) + self._lookupDict[ipd['name']] = self._counter + self._counter += 1 + super(BBList, self).append(BlackBoxFunctionModel_Variable(**ipd)) + + else: + raise ValueError('Too many inputs to a black box variable') + + errorString = 'This function is calling to the base class and has not been defined.' class BlackBoxFunctionModel(ExternalGreyBoxModel): @@ -162,8 +233,8 @@ def __init__(self): super(BlackBoxFunctionModel, self).__init__() # List of the inputs and outputs - self.inputs = TypeCheckedList(BlackBoxFunctionModel_Variable, []) - self.outputs = TypeCheckedList(BlackBoxFunctionModel_Variable, []) + self.inputs = BBList() + self.outputs = BBList() self.inputVariables_optimization = None self.outputVariables_optimization = None diff --git a/pyomo/contrib/edi/examples/readme_example.py b/pyomo/contrib/edi/examples/readme_example.py index 8722721247f..c6264677306 100644 --- a/pyomo/contrib/edi/examples/readme_example.py +++ b/pyomo/contrib/edi/examples/readme_example.py @@ -4,7 +4,7 @@ import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation -from pyomo.contrib.edi import BlackBoxFunctionModel, BBVariable +from pyomo.contrib.edi import BlackBoxFunctionModel # =================== # Declare Formulation @@ -41,34 +41,31 @@ class UnitCircle(BlackBoxFunctionModel): def __init__(self): # The initalization function # Initalize the black box model - super(UnitCircle, self).__init__() + super().__init__() # A brief description of the model self.description = 'This model evaluates the function: z = x**2 + y**2' # Declare the black box model inputs - self.inputs.append(BBVariable(name = 'x', - size = 0, - units = 'ft' , - description = 'The x variable') ) - self.inputs.append(BBVariable(name = 'y', - size = 0, - units = 'ft' , - description = 'The y variable') ) + self.inputs.append( name = 'x', + units = 'ft' , + description = 'The x variable') + self.inputs.append( name = 'y', + units = 'ft' , + description = 'The y variable') # Declare the black box model outputs - self.outputs.append(BBVariable(name = 'z', - size = 0, - units = 'ft**2', - description = 'Resultant of the unit circle') ) + self.outputs.append( name = 'z', + units = 'ft**2', + description = 'Resultant of the unit circle') # Declare the maximum available derivative self.availableDerivative = 1 def BlackBox(self, x, y): # The actual function that does things # Converts to correct units then casts to float - x = pyo.value(units.convert(x,self.inputs[0].units)) - y = pyo.value(units.convert(y,self.inputs[1].units)) + x = pyo.value(units.convert(x,self.inputs['x'].units)) + y = pyo.value(units.convert(y,self.inputs['y'].units)) z = x**2 + y**2 # Compute z dzdx = 2*x # Compute dz/dx From 3a7918b51cd967fc6df37de07ad2669601f470fc Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Wed, 2 Aug 2023 11:23:47 -0600 Subject: [PATCH 22/61] black box constraint docs good enough --- .../edi/blackboxconstraints.rst | 80 ++++++++++++++----- pyomo/contrib/edi/README.md | 5 ++ 2 files changed, 64 insertions(+), 21 deletions(-) diff --git a/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst b/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst index 5bf051cb7cf..62017933826 100644 --- a/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst +++ b/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst @@ -68,10 +68,10 @@ A simple example is shown below: dydx = 2*x # Add the units to the output - y = y * self.inputs['y'].units + y = y * self.outputs['y'].units # Add the units to the derivative for output - dydx = dydx * self.inputs['y'].units / self.inputs['x'].units + dydx = dydx * self.outputs['y'].units / self.inputs['x'].units # Return return y, [dydx] # return z, grad(z), hess(z)... @@ -185,10 +185,10 @@ When preparing the outputs, note that all outputs must have units: .. code-block:: python # Add the units to the output - y = y * self.inputs['y'].units + y = y * self.outputs['y'].units # Add the units to the derivative for output - dydx = dydx * self.inputs['y'].units / self.inputs['x'].units + dydx = dydx * self.outputs['y'].units / self.inputs['x'].units The ``BlackBox`` method then outputs a tuple of length ``self.availableDerivative+1``. Entry [0] is the values specified during the ``__init__()``, entry [1] is first derivative information, and similar for higher order if available. @@ -401,31 +401,68 @@ Any of the alternative declarations above are valid to pass into the ``f.Constra Examples -------- -A standard declaration statement -++++++++++++++++++++++++++++++++ +More examples are in the :doc:`advanced <./advancedruntimeconstraints>` documentation, and will be added over time. Feel free to reach out to the developers if you have questions regarding model development. + +A standard construction ++++++++++++++++++++++++ .. code-block:: python - from pyomo.environ import units - from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) - f.Objective( c*x + y ) - f.ConstraintList( - [ - x**2 + y**2 <= 1.0*units.m**2 , - x <= 0.75*units.m, - x >= y , - ] - ) + import pyomo.environ as pyo + from pyomo.environ import units + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The z variable') + + f.Objective( x + y ) + + class UnitCircle(BlackBoxFunctionModel): + def __init__(self): + super().__init__() + self.description = 'This model evaluates the function: z = x**2 + y**2' + self.inputs.append( name = 'x', + units = 'ft' , + description = 'The x variable' ) + self.inputs.append( name = 'y', + units = 'ft' , + description = 'The y variable' ) + self.outputs.append( name = 'z', + units = 'ft**2', + description = 'Resultant of the unit circle evaluation' ) + self.availableDerivative = 1 + self.post_init_setup(len(self.inputs)) + + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value(units.convert(x,self.inputs['x'].units)) + y = pyo.value(units.convert(y,self.inputs['y'].units)) + z = x**2 + y**2 + dzdx = 2*x + dzdy = 2*y + z = z * self.outputs['z'].units + dzdx = dzdx * self.outputs['z'].units / self.inputs['x'].units + dzdy = dzdy * self.outputs['z'].units / self.inputs['y'].units + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + + f.ConstraintList( + [ + (z, '==', [x,y], UnitCircle() ) , + z <= 1*units.m**2 + ] + ) Tips ---- -* +* Align input and ouptut declarations just as is recommended for optimization variable and constant declarations +* Delcare an input/output all on one line, no matter what the style guides say +* This interface is really designed for subject matter experts who are not python users to have a simple, easy path to include their tools into a python based optimization architecture. Try to let them build their own models as a means of fostering trust in the optimization tools +* Embrace units. They will save you so many times, it is well worth the minor additional overhead +* Pyomo units work sligtly diffenrently than pint (for those with pint experience), but those differences should be hidden from the model creator for the most part +* It is common to use this framework to call to a piece of software external to python +* See the :doc:`advanced <./advancedruntimeconstraints>` documentation for extra tips and tricks Known Issues @@ -433,3 +470,4 @@ Known Issues * Currently only equality constraints are supported, pending an update to pyomo (see `this issue `__) * Runtime constraints must output to a variable, numbers and constants are not permitted (see `this issue `__) +* This functionality is not well tested when returning derivatives higher than first order. Though it should work, exercise caution and reach out to the dev team if questions arise. diff --git a/pyomo/contrib/edi/README.md b/pyomo/contrib/edi/README.md index c43be14d513..cfa7e8d71f7 100644 --- a/pyomo/contrib/edi/README.md +++ b/pyomo/contrib/edi/README.md @@ -9,6 +9,11 @@ EDI is a part of the standard installation process for Pyomo: pip install pyomo ``` +EDI also requires the pint dependency that is optional in base Pyomo: +``` +pip install pint +``` + ## Usage The core object in EDI is the `Formulation` object, which inherits from the `pyomo.environ.ConcreteModel`. Essentally, a `Formulation` is a Pyomo `Model` with some extra stuff, but can be treated exactly as if it were a Pyomo `Model`. However, an EDI `Formulation` has some additional features that can help simplify model construction. From a1049a85f91743faa59295075b9276f038667fb3 Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Wed, 2 Aug 2023 13:19:47 -0600 Subject: [PATCH 23/61] docs done I think --- .../edi/advancedruntimeconstraints.rst | 185 +++++++++++++++++- .../edi/blackboxconstraints.rst | 1 - pyomo/contrib/edi/blackBoxFunctionModel.py | 6 +- 3 files changed, 187 insertions(+), 5 deletions(-) diff --git a/doc/OnlineDocs/contributed_packages/edi/advancedruntimeconstraints.rst b/doc/OnlineDocs/contributed_packages/edi/advancedruntimeconstraints.rst index ba67f173066..283ee7c85af 100644 --- a/doc/OnlineDocs/contributed_packages/edi/advancedruntimeconstraints.rst +++ b/doc/OnlineDocs/contributed_packages/edi/advancedruntimeconstraints.rst @@ -1,11 +1,194 @@ Advanced Runtime Constraints ============================ + Automated unpacking for multi-use black-boxes --------------------------------------------- +Coding a black-box model often represents a significant effort, and it is therefore desirable to have the black-box work in many situations. A common case is to have a black-box model with a scalar input variable perform vectorized operations, ie, take in a vector and return a vector. In the more general case, this can be thought of as passing in multiple run-cases as input to the black-box model. + +The ``parseInputs()`` method enables this batch-like capability in a variety of forms: + +.. py:function:: BlackBoxFunctionModel.parseInputs(*args, **kwargs) + + Parses the inputs to a black-box model into a list of run-cases + + :param args: index passed arguments + :type args: list or tuple + :param kwargs: keyword passed arguments + :type kwargs: dict + + :return: runcases + :rtype: list + :return: returnMode + :rtype: int + :return: extras + :rtype: list + + +The function definition is not particularly helpful, so let's dive in a bit. For the typical user, we recommend that the top of all ``BlackBox()`` methods appear as follows: + +.. code-block:: python + + def BlackBox(*args, **kwargs): + args = list(args) # convert tuple to list + self = args.pop(0) # pop off the self argument + runCases, returnMode, extras = self.parseInputs(*args, **kwargs) + + +Essentially, ``parseInputs()`` is a pre-processor that directly takes the inputs of the black-box model after popping off the default ``self`` argument that is passed to all python class methods. The ``parseInputs()`` method will check all of the inputs, ensure that size and units are correct, split into run cases as appropriate, and return a run-cases list that is ready to operate on. + +The ``runCases`` return (which can be named as any valid python name) is a list of dicts, where the keys to the dict are the names of the inputs declared in the ``__init__()`` method. Ex: ``runCases[0]['x']`` will give the ``'x'`` input (in units specified in the ``__init__()`` method) in the first run-case. + +The ``returnMode`` return (which can be named as any valid python name) is an integer that indicates how the return should be processed. If ``returnMode`` is passed as a ``kwarg``, then the passed in value will be cast to this output. If it is not passed in, then ``returnMode`` will be either ``-1*self.availableDerivative``, indicating that there is only a single run case, or ``self.availableDerivative`` which indicated multiple run cases. A negative value for ``returnMode`` indicates that there will be one less layer of indexing in the output. + +The ``extras`` return (which can be named as any valid python name) is a dict that will include all of the additional passed in values not expected by the black-box model. The extra ``kwargs`` will appear as normal, and extra ``args`` get put in a list in ``extras['remainingArgs']``. If you pass in a ``kwarg`` with key name ``'remainingArgs'``, it will be overwritten. The extras dict is the place to find options that may affect the code (ex: tolerances, run modes, etc) that are not expected inputs to the black-box model. + +Note that when using run case input, the output will always take the following unpacking structure: + +:: + + output[][0] = + output[][0][] = + + output[][1] = + output[][1][][] = + + +There is **not** a shortcut for single scalar output black boxes as is the case when not using run cases, the final index to output 0 must be included. + + + +An Example +++++++++++ + +There are many ways this functionality can be used, we provide an example here to get new users started + + +.. code-block:: python + + import numpy as np + import pyomo.environ as pyo + from pyomo.environ import units + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + + class SignomialTest(BlackBoxFunctionModel): + def __init__(self): + # Set up all the attributes by calling Model.__init__ + super().__init__() + + #Setup Inputs + self.inputs.append( 'x', '', 'Independent Variable') + + #Setup Outputs + self.outputs.append( 'y', '', 'Dependent Variable') + + #Simple model description + self.description = 'This model evaluates the function: max([-6*x-6, x**4-3*x**2])' + + self.availableDerivative = 1 + + #standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) + def BlackBox(*args, **kwargs): + args = list(args) # convert tuple to list + self = args.pop(0) # pop off the self argument + runCases, returnMode, extras = self.parseInputs(*args, **kwargs) + x = np.array([ pyo.value(runCases[i]['x']) for i in range(0,len(runCases)) ]) + + y = np.maximum(-6*x-6, x**4-3*x**2) + dydx = 4*x**3 - 6*x + ddy_ddx = 12*x**2 - 6 + gradientSwitch = -6*x-6 > x**4-3*x**2 + dydx[gradientSwitch] = -6 + ddy_ddx[gradientSwitch] = 0 + + y = [ self.checkOutputs(yval) for yval in y ] + dydx = [dydx[i] * units.dimensionless for i in range(0,len(dydx))] + + if returnMode < 0: + returnMode = -1*(returnMode + 1) + if returnMode == 0: + return y[0] + if returnMode == 1: + return y[0], dydx + else: + if returnMode == 0: + opt = [] + for i in range(0,len(y)): + opt.append([ y[i] ]) + return opt + if returnMode == 1: + opt = [] + for i in range(0,len(y)): + opt.append([ [y[i]], [ [[dydx[i]]] ] ]) + return opt + + s = SignomialTest() + ivals = [[x] for x in np.linspace(-2,2,11)] + + # How the black box may be called using EDI + bbo = s.BlackBox(**{'x':0.5}) + bbo = s.BlackBox({'x':0.5}) + bbo = s.BlackBox(**{'x':0.5, 'optn':True}) + # Additional options available with parseInputs + bbo = s.BlackBox(*[0.5], **{'optn1': True, 'optn2': False}) + bbo = s.BlackBox(*[0.5,True], **{'optn': False}) + bbo = s.BlackBox({'x':[x for x in np.linspace(-2,2,11)]}) + bbo = s.BlackBox([{'x':x} for x in np.linspace(-2,2,11)]) + bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)]) + bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)], True, optn=False) + bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)], optn1=True, optn2=False) + + + +Check outputs +------------- + +There is a ``checkOutputs()`` method that can be used but is not currently fully operational. Contact the developers if you desire this functionality, but the following the practices described in this documentation should render the need for this moot. Cases of non-scalar inputs or outputs -------------------------------------- \ No newline at end of file +------------------------------------- + +Indexing can get somewhat complicated when inputs and outputs are not scalars. Users should be warned this feature is supported, but not well tested, so please contact the developers if you encounter any unusual behavior. + +The following unpacking remains the same: + +:: + + output[0] = + output[0][] = + + output[1] = + output[1][][] = + +However, for outputs, the result will be an array with dimensions equal to the size of the output. For Jacobians, it breaks down as the following: + +:: + + jacobian_list_entry[(output_dim_1_ix, output_dim_2_ix, ..., input_dim_1_ix, input_dim_2_ix, ...)] = + +For example, with an output that is ``NxN`` and an input that is also ``NxN`` + +:: + + output[1][][][(0,0,1,1)] + +is the derivative of ``output[0,0]`` with respect to ``input[1,1]`` + + + +Tips +---- + +* A model summary can be printed by calling ``print(model_instance.summary)`` + + + + + + + + + diff --git a/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst b/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst index 62017933826..f5c872e5e61 100644 --- a/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst +++ b/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst @@ -215,7 +215,6 @@ if there is one single scalar output, the unpacking may be simplified to the fol :: output[0] = - output[0][] = output[1] = output[1][] = diff --git a/pyomo/contrib/edi/blackBoxFunctionModel.py b/pyomo/contrib/edi/blackBoxFunctionModel.py index 79887c0e01d..6d73ad423d9 100644 --- a/pyomo/contrib/edi/blackBoxFunctionModel.py +++ b/pyomo/contrib/edi/blackBoxFunctionModel.py @@ -695,9 +695,9 @@ def sanitizeInputs(self, *args, **kwargs): def checkOutputs(self, *args, **kwargs): nameList = [self.outputs[i].name for i in range(0,len(self.outputs))] if len(args) + len(kwargs.values()) > len(nameList): - raise ValueError('Too many inputs') + raise ValueError('Too many outputs') if len(args) + len(kwargs.values()) < len(nameList): - raise ValueError('Not enough inputs') + raise ValueError('Not enough outputs') inputDict = {} for i in range(0,len(args)): rg = args[i] @@ -707,7 +707,7 @@ def checkOutputs(self, *args, **kwargs): if ky in nameList: inputDict[ky] = vl else: - raise ValueError('Unexpected input keyword argument %s in the inputs'%(ky)) + raise ValueError('Unexpected output keyword argument %s in the outputs'%(ky)) opts = [] From 816a02257470822ae9932ac4858321e04080c513 Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Thu, 3 Aug 2023 00:39:55 -0600 Subject: [PATCH 24/61] more testing --- .../edi/blackboxconstraints.rst | 4 +- .../contributed_packages/edi/formulation.rst | 2 +- pyomo/contrib/edi/blackBoxFunctionModel.py | 41 +- pyomo/contrib/edi/tests/test_blackbox.py | 448 ++++++++++++++++++ pyomo/contrib/edi/tests/test_formulation.py | 60 +-- 5 files changed, 497 insertions(+), 58 deletions(-) create mode 100644 pyomo/contrib/edi/tests/test_blackbox.py diff --git a/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst b/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst index f5c872e5e61..22cad842ef8 100644 --- a/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst +++ b/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst @@ -331,7 +331,7 @@ However, more commonly we expect users to construct Runtime Constraints as a par self.inputs.append( name = 'y', units = 'ft' , description = 'The y variable' ) - self.outputs.append name = 'z', + self.outputs.append(name = 'z', units = 'ft**2', description = 'Output variable' ) self.availableDerivative = 1 @@ -351,7 +351,7 @@ However, more commonly we expect users to construct Runtime Constraints as a par f.ConstraintList( [ - z <= 1*units.m**2 ) , + z <= 1*units.m**2 , [ z, '==', [x,y], UnitCircle() ] , ] ) diff --git a/doc/OnlineDocs/contributed_packages/edi/formulation.rst b/doc/OnlineDocs/contributed_packages/edi/formulation.rst index 2513b1f86c1..5aa2dc93e48 100644 --- a/doc/OnlineDocs/contributed_packages/edi/formulation.rst +++ b/doc/OnlineDocs/contributed_packages/edi/formulation.rst @@ -279,7 +279,7 @@ However, more commonly we expect users to construct Runtime Constraints as a par f.ConstraintList( [ - z <= 1*units.m**2 ) , + z <= 1*units.m**2 , [ z, '==', [x,y], UnitCircle() ] , ] ) diff --git a/pyomo/contrib/edi/blackBoxFunctionModel.py b/pyomo/contrib/edi/blackBoxFunctionModel.py index 6d73ad423d9..b97dbfe2c82 100644 --- a/pyomo/contrib/edi/blackBoxFunctionModel.py +++ b/pyomo/contrib/edi/blackBoxFunctionModel.py @@ -117,9 +117,6 @@ def description(self, val): raise ValueError('Invalid description. Must be a string.') - - - class TypeCheckedList(list): def __init__(self, checkItem, itemList = None): super(TypeCheckedList, self).__init__() @@ -135,21 +132,15 @@ def __init__(self, checkItem, itemList = None): def __setitem__(self, key, val): if isinstance(val, self.checkItem): super(TypeCheckedList, self).__setitem__(key, val) + elif isinstance(val, (tuple,list)): + cks = [isinstance(vl,self.checkItem) for vl in val] + if sum(cks) == len(cks): + super(TypeCheckedList, self).__setitem__(key, val) + else: + raise ValueError('Input must be an instance of the defined type') else: raise ValueError('Input must be an instance of the defined type') - def __setslice__(self, i, j, sequence): - performSet = True - for val in sequence: - if not isinstance(val, self.checkItem): - performSet = False - break - - if performSet: - super(TypeCheckedList, self).__setslice__(i,j,sequence) - else: - raise ValueError('All values in the input must be an instance of the defined type') - def append(self, val): if isinstance(val, self.checkItem): super(TypeCheckedList, self).append(val) @@ -266,7 +257,6 @@ def input_names(self): for vi in validIndicies: inputs_unwrapped.append(ivar[vi]) else: - print(type(ivar)) raise ValueError("Invalid type for input variable") return [ip.__str__() for ip in inputs_unwrapped] @@ -295,7 +285,6 @@ def evaluate_outputs(self): opts = self._cache['pyomo_outputs'] return opts - def evaluate_jacobian_outputs(self): self.fillCache() jac = self._cache['pyomo_jacobian'] @@ -316,13 +305,17 @@ def fillCache(self): for i in range(0,len(self.inputVariables_optimization)): optimizationInput = self.inputVariables_optimization[i] - ipt = self.inputs[i] + if isinstance(optimizationInput, (pyomo.core.base.var.IndexedVar, pyomo.core.base.var.ScalarVar)): + # optimizationInput = self.inputVariables_optimization[i] + ipt = self.inputs[i] - shape = [len(idx) for idx in optimizationInput.index_set().subsets()] - localShape = ipt.size + shape = [len(idx) for idx in optimizationInput.index_set().subsets()] + localShape = ipt.size - optimizationUnits = self.inputVariables_optimization[i].get_units() - localUnits = ipt.units + optimizationUnits = self.inputVariables_optimization[i].get_units() + localUnits = ipt.units + else: + raise ValueError("Invalid input variable type") if isinstance(optimizationInput, pyomo.core.base.var.IndexedVar): value = np.zeros(shape) @@ -334,15 +327,13 @@ def fillCache(self): self.sizeCheck(localShape, value*localUnits) bb_inputs.append(value*localUnits) - elif isinstance(optimizationInput, pyomo.core.base.var.ScalarVar): + else: # isinstance(optimizationInput, pyomo.core.base.var.ScalarVar):, just checked this value = raw_inputs[ptr] * optimizationUnits ptr += 1 self.sizeCheck(localShape, value) value_correctedUnits = pyomo_units.convert(value, localUnits) bb_inputs.append(value_correctedUnits) - else: - raise ValueError("Invalid input variable type") bbo = self.BlackBox(*bb_inputs) diff --git a/pyomo/contrib/edi/tests/test_blackbox.py b/pyomo/contrib/edi/tests/test_blackbox.py new file mode 100644 index 00000000000..951720fafab --- /dev/null +++ b/pyomo/contrib/edi/tests/test_blackbox.py @@ -0,0 +1,448 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright (c) 2008-2023 +# 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. +# +# Development of this module was conducted as part of the Institute for +# the Design of Advanced Energy Systems (IDAES) with support through the +# Simulation-Based Engineering, Crosscutting Research Program within the +# U.S. Department of Energy’s Office of Fossil Energy and Carbon Management. +# +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + +import pyomo.common.unittest as unittest +import pyomo.environ as pyo +from pyomo.common.dependencies import attempt_import + +from pyomo.core.base.units_container import ( + pint_available, +) + +np, numpy_available = attempt_import( + 'numpy', 'edi requires numpy' +) +# scipy, scipy_available = attempt_import( +# 'scipy', 'inverse_reduced_hessian requires scipy' +# ) + +# if not (numpy_available and scipy_available): +if not numpy_available: + raise unittest.SkipTest( + 'edi.formulation tests require numpy' + ) + +@unittest.skipIf(not pint_available, 'Testing units requires pint') +class TestEDIBlackBox(unittest.TestCase): + def test_edi_blackbox_variable(self): + "Tests the black box variable class" + from pyomo.environ import units + from pyomo.contrib.edi import Formulation + from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + + x = BlackBoxFunctionModel_Variable('x','') + x_print = x.__repr__() + x_name = x.name + x_units = x.units + x_size = x.size + x_desc = x.description + self.assertRaises(ValueError,x.__init__,*(1.0, '')) + + x.__init__('x', units.dimensionless) + + self.assertRaises(ValueError,x.__init__,*('x', 1.0)) + + x.__init__('x', units.dimensionless, '', 'flex') + x.__init__('x', units.dimensionless, '', ['flex',2]) + x.__init__('x', units.dimensionless, '', 2) + x.__init__('x', units.dimensionless, '', [2,2]) + + self.assertRaises(ValueError,x.__init__,*('x', '','',[[],2])) + self.assertRaises(ValueError,x.__init__,*('x', '','',[2,1])) + + x.__init__('x', units.dimensionless, '', None) + self.assertRaises(ValueError,x.__init__,*('x', '','', 1 )) + self.assertRaises(ValueError,x.__init__,*('x', '','', {} )) + self.assertRaises(ValueError,x.__init__,*('x', '', 1.0 )) + + + + + def test_edi_blackbox_tcl(self): + "Tests the black box type checked list class" + from pyomo.environ import units + from pyomo.contrib.edi import Formulation + from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + + tcl = TypeCheckedList(int, [1,2,3]) + tcl[1] = 1 + tcl[0:2] = [1,2] + + self.assertRaises(ValueError,tcl.__init__,*( int, 1 )) + self.assertRaises(ValueError,tcl.__setitem__,*( 1, 3.333 )) + self.assertRaises(ValueError,tcl.__setitem__,*( 1, [1, 2.222] )) + self.assertRaises(ValueError,tcl.append,*( 2.222 ,)) + + + + def test_edi_blackbox_bbl(self): + "Tests the black box BBList class" + from pyomo.environ import units + from pyomo.contrib.edi import Formulation + from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + + bbl = BBList() + bbl.append('x','') + bbl.append('y','') + bbl.append(BlackBoxFunctionModel_Variable('z','')) + bbl.append(var = BlackBoxFunctionModel_Variable('u','')) + self.assertRaises(ValueError,bbl.append,*( BlackBoxFunctionModel_Variable('u',''),)) + self.assertRaises(ValueError,bbl.append,*( 'badvar',)) + self.assertRaises(ValueError,bbl.append,*( 2.222,)) + + self.assertRaises(ValueError,bbl.append,*( 'bv','',''),**{'units':'m'}) + self.assertRaises(ValueError,bbl.append,**{'units':'m','description':'hi'}) + self.assertRaises(ValueError,bbl.append,**{'name':'x', 'units':''}) + self.assertRaises(ValueError,bbl.append,*( 'bv','','',0,'extra')) + + xv = bbl['x'] + xv2 = bbl[0] + self.assertRaises(ValueError,bbl.__getitem__,*( 2.22, )) + + + def test_edi_blackbox_someexceptions(self): + "Tests some of the exceptions in the black box model class" + import numpy as np + from pyomo.environ import units + from pyomo.contrib.edi import Formulation + from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + + bb = BlackBoxFunctionModel() + bb.inputVariables_optimization = [1,2,3] + # bb.set_input_values(np.array([1,2,3])) + self.assertRaises(ValueError,bb.input_names,*( )) + # self.assertRaises(ValueError,bb.fillCache,*( )) + + + bb = BlackBoxFunctionModel() + bb.outputVariables_optimization = [1,2,3] + self.assertRaises(ValueError,bb.output_names,*( )) + + + def test_edi_blackbox_etc_1(self): + "Tests a black box assertion issue" + from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + bbfm = BlackBoxFunctionModel() + self.assertRaises(AttributeError,bbfm.BlackBox,()) + + + def test_edi_blackbox_etc_2(self): + "Tests a black box assertion issue" + import numpy as np + from pyomo.environ import units + import pyomo.environ as pyo + from pyomo.contrib.edi import Formulation + from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'y variable') + z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'Output var') + f.Objective( x + y ) + class UnitCircle(BlackBoxFunctionModel): + def __init__(self): + super().__init__() + self.description = 'This model evaluates the function: z = x**2 + y**2' + self.inputs.append( name = 'x', + units = 'ft' , + description = 'The x variable' ) + self.inputs.append( name = 'y', + units = 'ft' , + description = 'The y variable' ) + self.outputs.append(name = 'z', + units = 'ft**2', + description = 'Output variable' ) + self.availableDerivative = 1 + self.post_init_setup(len(self.inputs)) + def BlackBox(self, x, y): # The actual function that does things + # Converts to correct units then casts to float + x = pyo.value(units.convert(x,self.inputs[0].units)) + y = pyo.value(units.convert(y,self.inputs[1].units)) + z = x**2 + y**2 # Compute z + dzdx = 2*x # Compute dz/dx + dzdy = 2*y # Compute dz/dy + z *= units.ft**2 + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + + + f.ConstraintList( + [ + z <= 1*units.m**2 , + [ z, '==', [x,y], UnitCircle() ] , + ] + ) + + f.__dict__['constraint_2'].get_external_model().set_input_values(np.array([2.0, 2.0])) + f.__dict__['constraint_2'].get_external_model().inputVariables_optimization = [1,2] + # f.__dict__['constraint_2'].get_external_model().fillCache() + self.assertRaises(ValueError,f.__dict__['constraint_2'].get_external_model().fillCache,*()) + + + def test_edi_blackbox_etc_3(self): + "Tests a black box assertion issue" + import numpy as np + from pyomo.environ import units + import pyomo.environ as pyo + from pyomo.contrib.edi import Formulation + from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'y variable') + z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'Output var') + f.Objective( x + y ) + class UnitCircle(BlackBoxFunctionModel): + def __init__(self): + super().__init__() + self.description = 'This model evaluates the function: z = x**2 + y**2' + self.inputs.append( name = 'x', + units = 'ft' , + description = 'The x variable' ) + self.inputs.append( name = 'y', + units = 'ft' , + description = 'The y variable' ) + self.outputs.append(name = 'z', + units = 'ft**2', + description = 'Output variable' ) + self.availableDerivative = 1 + self.post_init_setup(len(self.inputs)) + def BlackBox(self, x, y): # The actual function that does things + # Converts to correct units then casts to float + x = pyo.value(units.convert(x,self.inputs[0].units)) + y = pyo.value(units.convert(y,self.inputs[1].units)) + z = x**2 + y**2 # Compute z + dzdx = 2*x # Compute dz/dx + dzdy = 2*y # Compute dz/dy + z *= units.ft**2 + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return ['err'], [[dzdx, dzdy]] # return z, grad(z), hess(z)... + + + f.ConstraintList( + [ + z <= 1*units.m**2 , + [ z, '==', [x,y], UnitCircle() ] , + ] + ) + + self.assertRaises(ValueError,f.__dict__['constraint_2'].get_external_model().fillCache,*()) + + def test_edi_blackbox_example_1(self): + "Tests a black box example construction" + from pyomo.environ import units + import pyomo.environ as pyo + from pyomo.contrib.edi import Formulation + from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'y variable') + z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'Output var') + f.Objective( x + y ) + class UnitCircle(BlackBoxFunctionModel): + def __init__(self): + super().__init__() + self.description = 'This model evaluates the function: z = x**2 + y**2' + self.inputs.append( name = 'x', + units = 'ft' , + description = 'The x variable' ) + self.inputs.append( name = 'y', + units = 'ft' , + description = 'The y variable' ) + self.outputs.append(name = 'z', + units = 'ft**2', + description = 'Output variable' ) + self.availableDerivative = 1 + self.post_init_setup(len(self.inputs)) + def BlackBox(self, x, y): # The actual function that does things + # Converts to correct units then casts to float + x = pyo.value(units.convert(x,self.inputs[0].units)) + y = pyo.value(units.convert(y,self.inputs[1].units)) + z = x**2 + y**2 # Compute z + dzdx = 2*x # Compute dz/dx + dzdy = 2*y # Compute dz/dy + z *= units.ft**2 + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + + + f.ConstraintList( + [ + z <= 1*units.m**2 , + [ z, '==', [x,y], UnitCircle() ] , + ] + ) + + f.__dict__['constraint_2'].get_external_model().set_input_values(np.array([2.0, 2.0])) + opt = f.__dict__['constraint_2'].get_external_model().evaluate_outputs() + jac = f.__dict__['constraint_2'].get_external_model().evaluate_jacobian_outputs().todense() + + self.assertAlmostEqual(opt[0],8) + self.assertAlmostEqual(jac[0,0],4) + self.assertAlmostEqual(jac[0,1],4) + + sm = f.__dict__['constraint_2'].get_external_model().summary + e_print = f.__dict__['constraint_2'].get_external_model().__repr__() + + + + + def test_edi_blackbox_example_2(self): + "Tests a black box example construction" + import pyomo.environ as pyo + from pyomo.environ import units + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable', size=3) + y = f.Variable(name = 'y', guess = 1.0, units = 'm**2' , description = 'The y variable', size=3) + f.Objective( y[0] + y[1] + y[2] ) + class Parabola(BlackBoxFunctionModel): + def __init__(self): + super(Parabola, self).__init__() + self.description = 'This model evaluates the function: y = x**2' + self.inputs.append(name = 'x', size = 3, units = 'ft' , description = 'The x variable') + self.outputs.append(name = 'y', size = 3, units = 'ft**2' , description = 'The y variable') + self.availableDerivative = 1 + self.post_init_setup(len(self.inputs)) + + def BlackBox(*args, **kwargs):# The actual function that does things + args = list(args) + self = args.pop(0) + runCases, returnMode, remainingKwargs = self.parseInputs(*args, **kwargs) + + x = self.sanitizeInputs(runCases[0]['x']) + x = np.array([pyo.value(xval) for xval in x], dtype=np.float64) + + y = x**2 # Compute y + dydx = 2*x # Compute dy/dx + + y = y * units.ft**2 + dydx = np.diag(dydx) + dydx = dydx * units.ft # units.ft**2 / units.ft + + return y, [dydx] # return z, grad(z), hess(z)... + f.ConstraintList([ {'outputs':y, 'operators':'==', 'inputs':x, 'black_box':Parabola() },]) + + f.__dict__['constraint_1'].get_external_model().set_input_values(np.ones(3)*2) + opt = f.__dict__['constraint_1'].get_external_model().evaluate_outputs() + jac = f.__dict__['constraint_1'].get_external_model().evaluate_jacobian_outputs().todense() + + self.assertAlmostEqual(opt[0],4) + self.assertAlmostEqual(opt[1],4) + self.assertAlmostEqual(opt[2],4) + self.assertAlmostEqual(jac[0,0],4) + self.assertAlmostEqual(jac[0,1],0) + self.assertAlmostEqual(jac[0,2],0) + self.assertAlmostEqual(jac[0,1],0) + self.assertAlmostEqual(jac[1,1],4) + self.assertAlmostEqual(jac[2,1],0) + self.assertAlmostEqual(jac[2,0],0) + self.assertAlmostEqual(jac[2,1],0) + self.assertAlmostEqual(jac[2,2],4) + + sm = f.__dict__['constraint_1'].get_external_model().summary + e_print = f.__dict__['constraint_1'].get_external_model().__repr__() + + + + def test_edi_blackbox_todo2(self): + "TODO2" + import numpy as np + import pyomo.environ as pyo + from pyomo.environ import units + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + + class SignomialTest(BlackBoxFunctionModel): + def __init__(self): + # Set up all the attributes by calling Model.__init__ + super().__init__() + + #Setup Inputs + self.inputs.append( 'x', '', 'Independent Variable') + + #Setup Outputs + self.outputs.append( 'y', '', 'Dependent Variable') + + #Simple model description + self.description = 'This model evaluates the function: max([-6*x-6, x**4-3*x**2])' + + self.availableDerivative = 1 + + #standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) + def BlackBox(*args, **kwargs): + args = list(args) # convert tuple to list + self = args.pop(0) # pop off the self argument + runCases, returnMode, extras = self.parseInputs(*args, **kwargs) + x = np.array([ pyo.value(runCases[i]['x']) for i in range(0,len(runCases)) ]) + + y = np.maximum(-6*x-6, x**4-3*x**2) + dydx = 4*x**3 - 6*x + ddy_ddx = 12*x**2 - 6 + gradientSwitch = -6*x-6 > x**4-3*x**2 + dydx[gradientSwitch] = -6 + ddy_ddx[gradientSwitch] = 0 + + y = [ self.checkOutputs(yval) for yval in y ] + dydx = [dydx[i] * units.dimensionless for i in range(0,len(dydx))] + + if returnMode < 0: + returnMode = -1*(returnMode + 1) + if returnMode == 0: + return y[0] + if returnMode == 1: + return y[0], dydx + else: + if returnMode == 0: + opt = [] + for i in range(0,len(y)): + opt.append([ y[i] ]) + return opt + if returnMode == 1: + opt = [] + for i in range(0,len(y)): + opt.append([ [y[i]], [ [[dydx[i]]] ] ]) + return opt + + s = SignomialTest() + ivals = [[x] for x in np.linspace(-2,2,11)] + + # How the black box may be called using EDI + bbo = s.BlackBox(**{'x':0.5}) + bbo = s.BlackBox({'x':0.5}) + bbo = s.BlackBox(**{'x':0.5, 'optn':True}) + + # Additional options available with parseInputs + bbo = s.BlackBox(*[0.5], **{'optn1': True, 'optn2': False}) + bbo = s.BlackBox(*[0.5,True], **{'optn': False}) + bbo = s.BlackBox({'x':[x for x in np.linspace(-2,2,11)]}) + bbo = s.BlackBox([{'x':x} for x in np.linspace(-2,2,11)]) + bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)]) + bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)], True, optn=False) + bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)], optn1=True, optn2=False) + + + # def test_edi_formulation_variable(self): + # "Tests the variable constructor in edi.formulation" + + + +if __name__ == '__main__': + unittest.main() diff --git a/pyomo/contrib/edi/tests/test_formulation.py b/pyomo/contrib/edi/tests/test_formulation.py index 327d42be1df..3d6db035da2 100644 --- a/pyomo/contrib/edi/tests/test_formulation.py +++ b/pyomo/contrib/edi/tests/test_formulation.py @@ -147,7 +147,7 @@ def test_edi_formulation_runtimeconstraint_tuple(self): "Tests the runtime constraint constructor in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units - from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel, BBVariable + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel f = Formulation() x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') @@ -158,9 +158,9 @@ class UnitCircle(BlackBoxFunctionModel): def __init__(self): super(UnitCircle, self).__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append(BBVariable(name = 'x', size = 0, units = 'ft' , description = 'The x variable')) - self.inputs.append(BBVariable(name = 'y', size = 0, units = 'ft' , description = 'The y variable')) - self.outputs.append(BBVariable(name = 'z', size = 0, units = 'ft**2', description = 'Resultant of the unit circle evaluation')) + self.inputs.append(name = 'x', units = 'ft' , description = 'The x variable') + self.inputs.append(name = 'y', units = 'ft' , description = 'The y variable') + self.outputs.append(name = 'z', units = 'ft**2', description = 'Resultant of the unit circle evaluation') self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) def BlackBox(self, x, y): # The actual function that does things @@ -181,7 +181,7 @@ def test_edi_formulation_runtimeconstraint_list(self): "Tests the runtime constraint constructor in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units - from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel, BBVariable + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel f = Formulation() x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') @@ -192,9 +192,9 @@ class UnitCircle(BlackBoxFunctionModel): def __init__(self): super(UnitCircle, self).__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append(BBVariable(name = 'x', size = 0, units = 'ft' , description = 'The x variable')) - self.inputs.append(BBVariable(name = 'y', size = 0, units = 'ft' , description = 'The y variable')) - self.outputs.append(BBVariable(name = 'z', size = 0, units = 'ft**2', description = 'Resultant of the unit circle evaluation')) + self.inputs.append(name = 'x', units = 'ft' , description = 'The x variable') + self.inputs.append(name = 'y', units = 'ft' , description = 'The y variable') + self.outputs.append(name = 'z', units = 'ft**2', description = 'Resultant of the unit circle evaluation') self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) def BlackBox(self, x, y): # The actual function that does things @@ -216,7 +216,7 @@ def test_edi_formulation_runtimeconstraint_dict(self): "Tests the runtime constraint constructor in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units - from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel, BBVariable + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel f = Formulation() x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') @@ -227,9 +227,9 @@ class UnitCircle(BlackBoxFunctionModel): def __init__(self): super(UnitCircle, self).__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append(BBVariable(name = 'x', size = 0, units = 'ft' , description = 'The x variable')) - self.inputs.append(BBVariable(name = 'y', size = 0, units = 'ft' , description = 'The y variable')) - self.outputs.append(BBVariable(name = 'z', size = 0, units = 'ft**2', description = 'Resultant of the unit circle evaluation')) + self.inputs.append(name = 'x', units = 'ft' , description = 'The x variable') + self.inputs.append(name = 'y', units = 'ft' , description = 'The y variable') + self.outputs.append(name = 'z', units = 'ft**2', description = 'Resultant of the unit circle evaluation') self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) def BlackBox(self, x, y): # The actual function that does things @@ -251,7 +251,7 @@ def test_edi_formulation_constraintlist_1(self): "Tests the constraint list constructor in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units - from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel, BBVariable + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel f = Formulation() x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') @@ -263,9 +263,9 @@ class UnitCircle(BlackBoxFunctionModel): def __init__(self): super(UnitCircle, self).__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append(BBVariable(name = 'x', size = 0, units = 'ft' , description = 'The x variable')) - self.inputs.append(BBVariable(name = 'y', size = 0, units = 'ft' , description = 'The y variable')) - self.outputs.append(BBVariable(name = 'z', size = 0, units = 'ft**2', description = 'Resultant of the unit circle evaluation')) + self.inputs.append(name = 'x', units = 'ft' , description = 'The x variable') + self.inputs.append(name = 'y', units = 'ft' , description = 'The y variable') + self.outputs.append(name = 'z', units = 'ft**2', description = 'Resultant of the unit circle evaluation') self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) @@ -296,7 +296,7 @@ def test_edi_formulation_constraintlist_2(self): "Tests the constraint list constructor in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units - from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel, BBVariable + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel f = Formulation() x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') y = f.Variable(name = 'y', guess = 1.0, units = 'm**2' , description = 'The y variable') @@ -306,8 +306,8 @@ class Parabola(BlackBoxFunctionModel): def __init__(self): super(Parabola, self).__init__() self.description = 'This model evaluates the function: y = x**2' - self.inputs.append(BBVariable(name = 'x', size = 0, units = 'ft' , description = 'The x variable')) - self.outputs.append(BBVariable(name = 'y', size = 0, units = 'ft**2' , description = 'The y variable')) + self.inputs.append(name = 'x', units = 'ft' , description = 'The x variable') + self.outputs.append(name = 'y', units = 'ft**2' , description = 'The y variable') self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) @@ -334,7 +334,7 @@ def test_edi_formulation_constraintlist_3(self): "Tests the constraint list constructor in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units - from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel, BBVariable + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel f = Formulation() x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable', size=3) y = f.Variable(name = 'y', guess = 1.0, units = 'm**2' , description = 'The y variable', size=3) @@ -343,8 +343,8 @@ class Parabola(BlackBoxFunctionModel): def __init__(self): super(Parabola, self).__init__() self.description = 'This model evaluates the function: y = x**2' - self.inputs.append(BBVariable(name = 'x', size = 3, units = 'ft' , description = 'The x variable')) - self.outputs.append(BBVariable(name = 'y', size = 3, units = 'ft**2' , description = 'The y variable')) + self.inputs.append(name = 'x', size = 3, units = 'ft' , description = 'The x variable') + self.outputs.append(name = 'y', size = 3, units = 'ft**2' , description = 'The y variable') self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) @@ -373,7 +373,7 @@ def test_edi_formulation_runtimeconstraint_exceptions(self): "Tests the runtime constraint constructor in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units - from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel, BBVariable + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel f = Formulation() x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') @@ -384,9 +384,9 @@ class UnitCircle(BlackBoxFunctionModel): def __init__(self): super(UnitCircle, self).__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append(BBVariable(name = 'x', size = 0, units = 'ft' , description = 'The x variable')) - self.inputs.append(BBVariable(name = 'y', size = 0, units = 'ft' , description = 'The y variable')) - self.outputs.append(BBVariable(name = 'z', size = 0, units = 'ft**2', description = 'Resultant of the unit circle evaluation')) + self.inputs.append(name = 'x', units = 'ft' , description = 'The x variable') + self.inputs.append(name = 'y', units = 'ft' , description = 'The y variable') + self.outputs.append(name = 'z', units = 'ft**2', description = 'Resultant of the unit circle evaluation') self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) def BlackBox(self, x, y): # The actual function that does things @@ -456,7 +456,7 @@ def test_edi_formulation_getconstraints(self): # ================= import pyomo.environ as pyo from pyomo.environ import units - from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel, BBVariable + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel # =================== # Declare Formulation @@ -495,11 +495,11 @@ def __init__(self): # The initalization function self.description = 'This model evaluates the function: z = x**2 + y**2' # Declare the black box model inputs - self.inputs.append(BBVariable(name = 'x', size = 0, units = 'ft' , description = 'The x variable')) - self.inputs.append(BBVariable(name = 'y', size = 0, units = 'ft' , description = 'The y variable')) + self.inputs.append(name = 'x', units = 'ft' , description = 'The x variable') + self.inputs.append(name = 'y', units = 'ft' , description = 'The y variable') # Declare the black box model outputs - self.outputs.append(BBVariable(name = 'z', size = 0, units = 'ft**2', description = 'Resultant of the unit circle evaluation')) + self.outputs.append(name = 'z', units = 'ft**2', description = 'Resultant of the unit circle evaluation') # Declare the maximum available derivative self.availableDerivative = 1 From 871014c5756fccaee01d78f4168697a2e9d1afc7 Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Thu, 3 Aug 2023 16:55:13 -0600 Subject: [PATCH 25/61] finalizing for PR --- .../edi/additionaltips.rst | 4 + .../edi/advancedruntimeconstraints.rst | 21 +- .../edi/blackboxconstraints.rst | 3 +- .../contributed_packages/edi/examples.rst | 4 + pyomo/contrib/edi/blackBoxFunctionModel.py | 311 +++++++------ pyomo/contrib/edi/examples/aircraft_gp.py | 84 ++++ pyomo/contrib/edi/tests/test_blackbox.py | 409 +++++++++++++++++- pyomo/contrib/edi/tests/test_examples.py | 3 +- 8 files changed, 673 insertions(+), 166 deletions(-) create mode 100644 pyomo/contrib/edi/examples/aircraft_gp.py diff --git a/doc/OnlineDocs/contributed_packages/edi/additionaltips.rst b/doc/OnlineDocs/contributed_packages/edi/additionaltips.rst index c3f4fcdbbd7..a09b2b830bd 100644 --- a/doc/OnlineDocs/contributed_packages/edi/additionaltips.rst +++ b/doc/OnlineDocs/contributed_packages/edi/additionaltips.rst @@ -29,5 +29,9 @@ then open the file ```` cd /pyomo/pyomo/contrib/edi pytest --cov-report term-missing --cov=pyomo.contrib.edi -v ./tests/ +or generating html output: +:: + cd /pyomo/pyomo/contrib/edi + pytest --cov-report html --cov=pyomo.contrib.edi -v ./tests/ diff --git a/doc/OnlineDocs/contributed_packages/edi/advancedruntimeconstraints.rst b/doc/OnlineDocs/contributed_packages/edi/advancedruntimeconstraints.rst index 283ee7c85af..ce4d2bc3f77 100644 --- a/doc/OnlineDocs/contributed_packages/edi/advancedruntimeconstraints.rst +++ b/doc/OnlineDocs/contributed_packages/edi/advancedruntimeconstraints.rst @@ -9,10 +9,12 @@ Coding a black-box model often represents a significant effort, and it is theref The ``parseInputs()`` method enables this batch-like capability in a variety of forms: -.. py:function:: BlackBoxFunctionModel.parseInputs(*args, **kwargs) +.. py:function:: BlackBoxFunctionModel.parseInputs(self, *args, **kwargs) Parses the inputs to a black-box model into a list of run-cases + :param args: The self attribute in all python methods + :type self: black-box model :param args: index passed arguments :type args: list or tuple :param kwargs: keyword passed arguments @@ -30,13 +32,11 @@ The function definition is not particularly helpful, so let's dive in a bit. Fo .. code-block:: python - def BlackBox(*args, **kwargs): - args = list(args) # convert tuple to list - self = args.pop(0) # pop off the self argument + def BlackBox(self, *args, **kwargs): runCases, returnMode, extras = self.parseInputs(*args, **kwargs) -Essentially, ``parseInputs()`` is a pre-processor that directly takes the inputs of the black-box model after popping off the default ``self`` argument that is passed to all python class methods. The ``parseInputs()`` method will check all of the inputs, ensure that size and units are correct, split into run cases as appropriate, and return a run-cases list that is ready to operate on. +Essentially, ``parseInputs()`` is a pre-processor that directly takes the inputs of the black-box. The ``parseInputs()`` method will check all of the inputs, ensure that size and units are correct, split into run cases as appropriate, and return a run-cases list that is ready to operate on. The ``runCases`` return (which can be named as any valid python name) is a list of dicts, where the keys to the dict are the names of the inputs declared in the ``__init__()`` method. Ex: ``runCases[0]['x']`` will give the ``'x'`` input (in units specified in the ``__init__()`` method) in the first run-case. @@ -84,15 +84,14 @@ There are many ways this functionality can be used, we provide an example here t self.outputs.append( 'y', '', 'Dependent Variable') #Simple model description - self.description = 'This model evaluates the function: max([-6*x-6, x**4-3*x**2])' + self.description = ( 'This model evaluates the '+ + 'function: max([-6*x-6, x**4-3*x**2])' ) self.availableDerivative = 1 - #standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) - def BlackBox(*args, **kwargs): - args = list(args) # convert tuple to list - self = args.pop(0) # pop off the self argument + def BlackBox(self, *args, **kwargs): runCases, returnMode, extras = self.parseInputs(*args, **kwargs) + x = np.array([ pyo.value(runCases[i]['x']) for i in range(0,len(runCases)) ]) y = np.maximum(-6*x-6, x**4-3*x**2) @@ -145,7 +144,7 @@ There are many ways this functionality can be used, we provide an example here t Check outputs ------------- -There is a ``checkOutputs()`` method that can be used but is not currently fully operational. Contact the developers if you desire this functionality, but the following the practices described in this documentation should render the need for this moot. +There is a ``checkOutputs()`` method that is not supported in the current version. Contact the developers if you desire this functionality, but the following the practices described in this documentation should render the need for this moot. Cases of non-scalar inputs or outputs diff --git a/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst b/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst index 22cad842ef8..9cc63b1ae04 100644 --- a/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst +++ b/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst @@ -250,7 +250,7 @@ Runtime Constraints are declared one of two ways, just as regular constraints. self.inputs.append( name = 'y', units = 'ft' , description = 'The y variable' ) - self.outputs.append name = 'z', + self.outputs.append(name = 'z', units = 'ft**2', description = 'Output variable' ) self.availableDerivative = 1 @@ -455,6 +455,7 @@ A standard construction Tips ---- +* Use the pyomo ``tostr()`` function (``from pyomo.common.formatting import tostr``) to print the results of black-boxes for more meaningful printouts * Align input and ouptut declarations just as is recommended for optimization variable and constant declarations * Delcare an input/output all on one line, no matter what the style guides say * This interface is really designed for subject matter experts who are not python users to have a simple, easy path to include their tools into a python based optimization architecture. Try to let them build their own models as a means of fostering trust in the optimization tools diff --git a/doc/OnlineDocs/contributed_packages/edi/examples.rst b/doc/OnlineDocs/contributed_packages/edi/examples.rst index 4110c31f232..29e3470d7b8 100644 --- a/doc/OnlineDocs/contributed_packages/edi/examples.rst +++ b/doc/OnlineDocs/contributed_packages/edi/examples.rst @@ -11,3 +11,7 @@ Advanced Black-Box Modeling A Geometric Program for Aircraft Design --------------------------------------- + +.. literalinclude:: ../../../../pyomo/contrib/edi/examples/aircraft_gp.py + :language: python + diff --git a/pyomo/contrib/edi/blackBoxFunctionModel.py b/pyomo/contrib/edi/blackBoxFunctionModel.py index b97dbfe2c82..ce19262387f 100644 --- a/pyomo/contrib/edi/blackBoxFunctionModel.py +++ b/pyomo/contrib/edi/blackBoxFunctionModel.py @@ -60,6 +60,8 @@ def units(self, val): if isinstance(val, str): if val in ['-', '', ' ']: val = 'dimensionless' + if val is None: + val = 'dimensionless' if isinstance(val, str): self._units = pyomo_units.__getattr__(val) @@ -91,7 +93,7 @@ def size(self, val): self._size = val else: if val is None: - self._size = None + self._size = 0 # set to scalar elif isinstance(val, str): # is a 1D vector of unknown length, should be 'inf', but any string accepted self._size = -1 @@ -305,17 +307,16 @@ def fillCache(self): for i in range(0,len(self.inputVariables_optimization)): optimizationInput = self.inputVariables_optimization[i] - if isinstance(optimizationInput, (pyomo.core.base.var.IndexedVar, pyomo.core.base.var.ScalarVar)): - # optimizationInput = self.inputVariables_optimization[i] - ipt = self.inputs[i] + if not isinstance(optimizationInput, (pyomo.core.base.var.IndexedVar, pyomo.core.base.var.ScalarVar)): + raise ValueError("Invalid input variable type") - shape = [len(idx) for idx in optimizationInput.index_set().subsets()] - localShape = ipt.size + ipt = self.inputs[i] - optimizationUnits = self.inputVariables_optimization[i].get_units() - localUnits = ipt.units - else: - raise ValueError("Invalid input variable type") + shape = [len(idx) for idx in optimizationInput.index_set().subsets()] + localShape = ipt.size + + optimizationUnits = self.inputVariables_optimization[i].get_units() + localUnits = ipt.units if isinstance(optimizationInput, pyomo.core.base.var.IndexedVar): value = np.zeros(shape) @@ -350,6 +351,8 @@ def fillCache(self): jacobianList = bbo[1] for i in range(0,len(valueList)): optimizationOutput = self.outputVariables_optimization[i] + if not isinstance(optimizationOutput, (pyomo.core.base.var.IndexedVar, pyomo.core.base.var.ScalarVar)): + raise ValueError("Invalid output variable type") opt = self.outputs[i] modelOutputUnits = opt.units @@ -378,6 +381,9 @@ def fillCache(self): for i in range(0,len(jacobianList)): oopt = self.outputVariables_optimization[i] + # Checked about 20 lines above + # if not isinstance(oopt, (pyomo.core.base.var.ScalarVar,pyomo.core.base.var.IndexedVar)): + # raise ValueError("Invalid type for output variable") lopt = self.outputs[i] oounits = oopt.get_units() lounits = lopt.units @@ -385,6 +391,9 @@ def fillCache(self): ptr_col = 0 for j in range(0,len(self.inputs)): oipt = self.inputVariables_optimization[j] + # This is checked about 80 lines up + # if not isinstance(oipt, (pyomo.core.base.var.ScalarVar,pyomo.core.base.var.IndexedVar)): + # raise ValueError("Invalid type for output variable") lipt = self.inputs[j] oiunits = oipt.get_units() liunits = lipt.units @@ -404,17 +413,13 @@ def fillCache(self): if isinstance(oopt, pyomo.core.base.var.ScalarVar): oshape = 0 - elif isinstance(oopt, pyomo.core.base.var.IndexedVar): + else: # isinstance(oopt, pyomo.core.base.var.IndexedVar), checked above oshape = [len(idx) for idx in oopt.index_set().subsets()] - else: - raise ValueError("Invalid type for output variable") if isinstance(oipt, pyomo.core.base.var.ScalarVar): ishape = 0 - elif isinstance(oipt, pyomo.core.base.var.IndexedVar): + else: # isinstance(oipt, pyomo.core.base.var.IndexedVar), checked above ishape = [len(idx) for idx in oipt.index_set().subsets()] - else: - raise ValueError("Invalid type for input variable") if oshape == 0: validIndicies = list(oipt.index_set().data()) @@ -471,11 +476,46 @@ def fillCache(self): def BlackBox(*args, **kwargs): raise AttributeError(errorString) + + def convert(self, val, unts): + try: + val = val * pyomo_units.dimensionless + except: + pass ## will handle later + + if isinstance(val, (pyomo.core.base.units_container._PyomoUnit,pyomo.core.expr.numeric_expr.NPV_ProductExpression)): + return pyomo_units.convert(val, unts) + elif isinstance(val, pyomo.core.expr.numeric_expr.NumericNDArray): + shp = val.shape + ix = np.ndindex(*shp) + opt = np.zeros(shp) + for i in range(0,np.prod(shp)): + ixt = next(ix) + opt[ixt] = pyo.value(pyomo_units.convert(val[ixt],unts)) + return opt*unts + else: + raise ValueError('Invalid type passed to unit conversion function') + + + def pyomo_value(self, val): + try: + return pyo.value(val) + except: + if isinstance(val, pyomo.core.expr.numeric_expr.NumericNDArray): + shp = val.shape + ix = np.ndindex(*shp) + opt = np.zeros(shp) + for i in range(0,np.prod(shp)): + ixt = next(ix) + opt[ixt] = pyo.value(val[ixt]) + return opt + else: + raise ValueError('Invalid type passed to pyomo_value function') + # --------------------------------------------------------------------------------------------------------------------- # --------------------------------------------------------------------------------------------------------------------- - def parseInputs(*args, **kwargs): + def parseInputs(self, *args, **kwargs): args = list(args) # convert tuple to list - self = args.pop(0) # pop off the self argument inputNames = [self.inputs[i].name for i in range(0,len(self.inputs))] @@ -569,33 +609,47 @@ def parseInputs(*args, **kwargs): ix = inputNames.index(ky) valList[ix] = kwargs[ky] - if any([valList[i]==None for i in range(0,len(valList))]): - raise ValueError('Kewords did not properly fill in the remaining arguments. Check the inputs.') + # Not possible to reach due to other checks + # if any([valList[i]==None for i in range(0,len(valList))]): + # raise ValueError('Kewords did not properly fill in the remaining arguments. Check the inputs.') + + sips = self.sanitizeInputs(*valList) - sips = self.sanitizeInputs(*valList) if len(inputNames) == 1: sips = [sips] remainingKwargs = copy.deepcopy(kwargs) for nm in inputNames: - del remainingKwargs[nm] + try: + del remainingKwargs[nm] + except: + # was in args + pass return [dict(zip(inputNames,sips))], -self.availableDerivative-1, remainingKwargs # Mix of args and kwargs define inputs else: # all of the inputs are in the args try: sips = self.sanitizeInputs(*args[0:len(inputNames)]) + if len(inputNames) == 1: sips = [sips] remainingKwargs = copy.deepcopy(kwargs) remainingKwargs['remainingArgs'] = args[len(inputNames):] return [dict(zip(inputNames,sips))], -self.availableDerivative-1, remainingKwargs # all inputs are in args - except: - runCases, returnMode, extra_singleInput = self.parseInputs(args[0]) - remainingKwargs = copy.deepcopy(kwargs) - remainingKwargs['remainingArgs'] = args[len(inputNames):] - return runCases, returnMode, remainingKwargs + # except: + except Exception as e: + # not possible due to other checks + # if str(e) == 'Too many inputs': + # raise ValueError(e) + if str(e) == 'Not enough inputs': + raise ValueError(e) + else:#otherwise, proceed + runCases, returnMode, extra_singleInput = self.parseInputs(args[0]) + remainingKwargs = copy.deepcopy(kwargs) + remainingKwargs['remainingArgs'] = args[len(inputNames):] + return runCases, returnMode, remainingKwargs # --------------------------------------------------------------------------------------------------------------------- # --------------------------------------------------------------------------------------------------------------------- @@ -605,7 +659,7 @@ def sizeCheck(self, size, ipval_correctUnits): if isinstance(szVal, (pyomo.core.expr.numeric_expr.NPV_ProductExpression, pyomo.core.base.units_container._PyomoUnit)): if size != 0 and size != 1 : - raise ValueError('Size of %s did not match the expected size %s (ie: Scalar)'%(name, str(size))) + raise ValueError('Size did not match the expected size %s (ie: Scalar)'%(str(size))) elif isinstance(szVal, pyomo.core.expr.numeric_expr.NumericNDArray): shp = szVal.shape if isinstance(size,(int,float)): @@ -618,12 +672,13 @@ def sizeCheck(self, size, ipval_correctUnits): if size[j] != shp[j]: raise ValueError('Shapes/Sizes of %s does not match the expected %s'%(str(shp),str(size))) else: - raise ValueError('Invalid type detected when checking size (Should never display)') + raise ValueError('Invalid type detected when checking size') # --------------------------------------------------------------------------------------------------------------------- # --------------------------------------------------------------------------------------------------------------------- def sanitizeInputs(self, *args, **kwargs): nameList = [self.inputs[i].name for i in range(0,len(self.inputs))] + if len(args) + len(kwargs.values()) > len(nameList): raise ValueError('Too many inputs') if len(args) + len(kwargs.values()) < len(nameList): @@ -647,31 +702,30 @@ def sanitizeInputs(self, *args, **kwargs): unts = self.inputs[i].units size = self.inputs[i].size - if name != nameCheck: - raise RuntimeError('Something went wrong and values are not consistent. Check your defined inputs.') + # should be impossible + # if name != nameCheck: + # raise RuntimeError('Something went wrong and values are not consistent. Check your defined inputs.') ipval = inputDict[name] - if unts is not None: - if isinstance(ipval, pyomo.core.expr.numeric_expr.NumericNDArray): - for ii in range(0,len(ipval)): - try: - ipval[ii] = pyomo_units.convert(ipval[ii], unts)#ipval.to(unts) - except: - raise ValueError('Could not convert %s of %s to %s'%(name, str(ipval),str(unts))) - ipval_correctUnits = ipval - else: + if isinstance(ipval, pyomo.core.expr.numeric_expr.NumericNDArray): + for ii in range(0,len(ipval)): try: - ipval_correctUnits = pyomo_units.convert(ipval, unts)#ipval.to(unts) + ipval[ii] = self.convert(ipval[ii], unts)#ipval.to(unts) except: raise ValueError('Could not convert %s of %s to %s'%(name, str(ipval),str(unts))) - else: ipval_correctUnits = ipval + else: + try: + ipval_correctUnits = self.convert(ipval, unts)#ipval.to(unts) + except: + raise ValueError('Could not convert %s of %s to %s'%(name, str(ipval),str(unts))) - if not isinstance(ipval_correctUnits, (pyomo.core.expr.numeric_expr.NPV_ProductExpression, - pyomo.core.expr.numeric_expr.NumericNDArray, - pyomo.core.base.units_container._PyomoUnit)): - ipval_correctUnits = ipval_correctUnits * pyomo_units.dimensionless + # superceeded by the custom convert function + # if not isinstance(ipval_correctUnits, (pyomo.core.expr.numeric_expr.NPV_ProductExpression, + # pyomo.core.expr.numeric_expr.NumericNDArray, + # pyomo.core.base.units_container._PyomoUnit)): + # ipval_correctUnits = ipval_correctUnits * pyomo_units.dimensionless self.sizeCheck(size, ipval_correctUnits) @@ -684,55 +738,56 @@ def sanitizeInputs(self, *args, **kwargs): # --------------------------------------------------------------------------------------------------------------------- # --------------------------------------------------------------------------------------------------------------------- def checkOutputs(self, *args, **kwargs): - nameList = [self.outputs[i].name for i in range(0,len(self.outputs))] - if len(args) + len(kwargs.values()) > len(nameList): - raise ValueError('Too many outputs') - if len(args) + len(kwargs.values()) < len(nameList): - raise ValueError('Not enough outputs') - inputDict = {} - for i in range(0,len(args)): - rg = args[i] - inputDict[nameList[i]] = rg - - for ky, vl in kwargs.items(): - if ky in nameList: - inputDict[ky] = vl - else: - raise ValueError('Unexpected output keyword argument %s in the outputs'%(ky)) - - opts = [] - - for i in range(0,len(nameList)): - name = nameList[i] - nameCheck = self.outputs[i].name - unts = self.outputs[i].units - size = self.outputs[i].size + raise NotImplementedError('Contact developers to use this function') + # nameList = [self.outputs[i].name for i in range(0,len(self.outputs))] + # if len(args) + len(kwargs.values()) > len(nameList): + # raise ValueError('Too many outputs') + # if len(args) + len(kwargs.values()) < len(nameList): + # raise ValueError('Not enough outputs') + # inputDict = {} + # for i in range(0,len(args)): + # rg = args[i] + # inputDict[nameList[i]] = rg + + # for ky, vl in kwargs.items(): + # if ky in nameList: + # inputDict[ky] = vl + # else: + # raise ValueError('Unexpected output keyword argument %s in the outputs'%(ky)) + + # opts = [] + + # for i in range(0,len(nameList)): + # name = nameList[i] + # nameCheck = self.outputs[i].name + # unts = self.outputs[i].units + # size = self.outputs[i].size - if name != nameCheck: - raise RuntimeError('Something went wrong and values are not consistent. Check your defined inputs.') + # if name != nameCheck: + # raise RuntimeError('Something went wrong and values are not consistent. Check your defined inputs.') - ipval = inputDict[name] + # ipval = inputDict[name] - if unts is not None: - try: - ipval_correctUnits = pyomo_units.convert(ipval, unts) - except: - raise ValueError('Could not convert %s of %s to %s'%(name, str(ipval),str(unts))) - else: - ipval_correctUnits = ipval + # if unts is not None: + # try: + # ipval_correctUnits = self.convert(ipval, unts) + # except: + # raise ValueError('Could not convert %s of %s to %s'%(name, str(ipval),str(unts))) + # else: + # ipval_correctUnits = ipval - if not isinstance(ipval_correctUnits, (pyomo.core.expr.numeric_expr.NPV_ProductExpression, - pyomo.core.expr.numeric_expr.NumericNDArray, - pyomo.core.base.units_container._PyomoUnit)): - ipval_correctUnits = ipval_correctUnits * pyomo_units.dimensionless + # if not isinstance(ipval_correctUnits, (pyomo.core.expr.numeric_expr.NPV_ProductExpression, + # pyomo.core.expr.numeric_expr.NumericNDArray, + # pyomo.core.base.units_container._PyomoUnit)): + # ipval_correctUnits = ipval_correctUnits * pyomo_units.dimensionless - self.sizeCheck(size, ipval_correctUnits) + # self.sizeCheck(size, ipval_correctUnits) - opts.append(ipval_correctUnits) - if len(opts) == 1: - opts = opts[0] + # opts.append(ipval_correctUnits) + # if len(opts) == 1: + # opts = opts[0] - return opts + # return opts # --------------------------------------------------------------------------------------------------------------------- # --------------------------------------------------------------------------------------------------------------------- @@ -750,24 +805,18 @@ def getSummary(self, whitespace = 6): nml = len(ipt.name) if nml > longestName: longestName = nml - if ipt.units is None: - unts = 'None' - else: - unts = ipt.units.__str__()#_repr_html_() - # unts = unts.replace('','^') - # unts = unts.replace('','') - # unts = unts.replace('\[', '[') - # unts = unts.replace('\]', ']') + unts = ipt.units.__str__()#_repr_html_() + # unts = unts.replace('','^') + # unts = unts.replace('','') + # unts = unts.replace('\[', '[') + # unts = unts.replace('\]', ']') unl = len(unts) if unl > longestUnits: longestUnits = unl - if ipt.size is None: - lsz = 4 + if type(ipt.size) == list: + lsz = len(ipt.size.__repr__()) else: - if type(ipt.size) == list: - lsz = len(ipt.size.__repr__()) - else: - lsz = len(str(ipt.size)) + lsz = len(str(ipt.size)) if lsz > longestSize: longestSize = lsz namespace = max([4, longestName]) + whitespace @@ -792,19 +841,13 @@ def getSummary(self, whitespace = 6): pstr += '\n' for ipt in self.inputs: pstr += ipt.name.ljust(namespace) - if ipt.units is None: - unts = 'None' - else: - unts = ipt.units.__str__()#_repr_html_() - # unts = unts.replace('','^') - # unts = unts.replace('','') - # unts = unts.replace('\[', '[') - # unts = unts.replace('\]', ']') + unts = ipt.units.__str__()#_repr_html_() + # unts = unts.replace('','^') + # unts = unts.replace('','') + # unts = unts.replace('\[', '[') + # unts = unts.replace('\]', ']') pstr += unts.ljust(unitspace) - if ipt.size is None: - lnstr = 'None' - else: - lnstr = '%s'%(ipt.size.__repr__()) + lnstr = '%s'%(ipt.size.__repr__()) pstr += lnstr.ljust(sizespace) pstr += ipt.description pstr += '\n' @@ -817,24 +860,18 @@ def getSummary(self, whitespace = 6): nml = len(opt.name) if nml > longestName: longestName = nml - if opt.units is None: - unts = 'None' - else: - unts = opt.units.__str__()#_repr_html_() - # unts = unts.replace('','^') - # unts = unts.replace('','') - # unts = unts.replace('\[', '[') - # unts = unts.replace('\]', ']') + unts = opt.units.__str__()#_repr_html_() + # unts = unts.replace('','^') + # unts = unts.replace('','') + # unts = unts.replace('\[', '[') + # unts = unts.replace('\]', ']') unl = len(unts) if unl > longestUnits: longestUnits = unl - if opt.size is None: - lsz = 4 + if type(opt.size) == list: + lsz = len(opt.size.__repr__()) else: - if type(opt.size) == list: - lsz = len(opt.size.__repr__()) - else: - lsz = len(str(opt.size)) + lsz = len(str(opt.size)) if lsz > longestSize: longestSize = lsz namespace = max([4, longestName]) + whitespace @@ -859,19 +896,13 @@ def getSummary(self, whitespace = 6): pstr += '\n' for opt in self.outputs: pstr += opt.name.ljust(namespace) - if opt.units is None: - unts = 'None' - else: - unts = opt.units.__str__()#_repr_html_() - # unts = unts.replace('','^') - # unts = unts.replace('','') - # unts = unts.replace('\[', '[') - # unts = unts.replace('\]', ']') + unts = opt.units.__str__()#_repr_html_() + # unts = unts.replace('','^') + # unts = unts.replace('','') + # unts = unts.replace('\[', '[') + # unts = unts.replace('\]', ']') pstr += unts.ljust(unitspace) - if opt.size is None: - lnstr = 'None' - else: - lnstr = '%s'%(opt.size.__repr__()) + lnstr = '%s'%(opt.size.__repr__()) pstr += lnstr.ljust(sizespace) pstr += opt.description pstr += '\n' diff --git a/pyomo/contrib/edi/examples/aircraft_gp.py b/pyomo/contrib/edi/examples/aircraft_gp.py new file mode 100644 index 00000000000..095b0175caf --- /dev/null +++ b/pyomo/contrib/edi/examples/aircraft_gp.py @@ -0,0 +1,84 @@ +# =========== +# Description +# =========== +# A simple aircraft sizing problem, formulated as a Geometric Program +# From: Hoburg and Abbeel +# Geometric Programming for Aircraft Design Optimization +# AIAA Journal +# 2014 + +# ================= +# Import Statements +# ================= +import numpy as np +import pyomo.environ as pyo +from pyomo.environ import units +from pyomo.contrib.edi import Formulation +from pyomo.contrib.edi import BlackBoxFunctionModel + +# =================== +# Declare Formulation +# =================== +f = Formulation() + +# ================= +# Declare Variables +# ================= +D = f.Variable(name = "D", guess = 300, units = "N", description = "total drag force") +A = f.Variable(name = "A", guess = 10.0, units = "-", description = "aspect ratio") +S = f.Variable(name = "S", guess = 10.0, units = "m^2", description = "total wing area") +V = f.Variable(name = "V", guess = 30.0, units = "m/s", description = "cruising speed") +W = f.Variable(name = "W", guess = 10000.0, units = "N", description = "total aircraft weight") +Re = f.Variable(name = "Re", guess = 3e6, units = "-", description = "Reynold's number") +C_D = f.Variable(name = "C_D", guess = .025, units = "-", description = "Drag coefficient of wing") +C_L = f.Variable(name = "C_L", guess = .5, units = "-", description = "Lift coefficent of wing") +C_f = f.Variable(name = "C_f", guess = .003, units = "-", description = "skin friction coefficient") +W_w = f.Variable(name = "W_w", guess = 2500, units = "N", description = "wing weight") + +# ================= +# Declare Constants +# ================= +k = f.Constant(name = "k", value = 1.2, units ="-", description = "form factor") +e = f.Constant(name = "e", value = 0.96, units ="-", description = "Oswald efficiency factor") +mu = f.Constant(name = "mu", value = 1.78e-5, units ="kg/m/s", description = "viscosity of air") +rho = f.Constant(name = "rho", value = 1.23, units ="kg/m^3", description = "density of air") +tau = f.Constant(name = "tau", value = 0.12, units ="-", description = "airfoil thickness to chord ratio") +N_ult = f.Constant(name = "N_ult", value = 2.5, units ="-", description = "ultimate load factor") +V_min = f.Constant(name = "V_min", value = 22, units ="m/s", description = "takeoff speed") +C_Lmax = f.Constant(name = "C_Lmax", value = 2.0, units ="-", description = "max CL with flaps down") +S_wetratio = f.Constant(name = "Srat", value = 2.05, units ="-", description = "wetted area ratio") +W_W_coeff1 = f.Constant(name = "W_c1", value = 8.71e-5, units ="1/m", description = "Wing Weight Coefficent 1") +W_W_coeff2 = f.Constant(name = "W_c2", value = 45.24, units ="Pa", description = "Wing Weight Coefficent 2") +CDA0 = f.Constant(name = "CDA0", value = 0.0306, units ="m^2", description = "fuselage drag area") +W_0 = f.Constant(name = "W_0", value = 4940.0, units ="N", description = "aircraft weight excluding wing") + +# ===================== +# Declare the Objective +# ===================== +f.Objective( D ) + +# =================================== +# Declare some intermediate variables +# =================================== +pi = np.pi +C_D_fuse = CDA0/S +C_D_wpar = k*C_f*S_wetratio +C_D_ind = C_L**2/(pi*A*e) +W_w_strc = W_W_coeff1*(N_ult*A**1.5*(W_0*W*S)**0.5)/tau +W_w_surf = W_W_coeff2 * S + +# ======================= +# Declare the Constraints +# ======================= +f.ConstraintList( + [ + C_D >= C_D_fuse + C_D_wpar + C_D_ind, + W_w >= W_w_surf + W_w_strc, + D >= 0.5*rho*S*C_D*V**2, + Re == (rho/mu)*V*(S/A)**0.5, + C_f == 0.074/Re**0.2, + W == 0.5*rho*S*C_L*V**2, + W == 0.5*rho*S*C_Lmax*V_min**2, + W >= W_0 + W_w, + ] +) diff --git a/pyomo/contrib/edi/tests/test_blackbox.py b/pyomo/contrib/edi/tests/test_blackbox.py index 951720fafab..a16748e9e93 100644 --- a/pyomo/contrib/edi/tests/test_blackbox.py +++ b/pyomo/contrib/edi/tests/test_blackbox.py @@ -65,6 +65,8 @@ def test_edi_blackbox_variable(self): self.assertRaises(ValueError,x.__init__,*('x', '','',[2,1])) x.__init__('x', units.dimensionless, '', None) + x.__init__('x', None, '', None) + self.assertRaises(ValueError,x.__init__,*('x', '','', 1 )) self.assertRaises(ValueError,x.__init__,*('x', '','', {} )) self.assertRaises(ValueError,x.__init__,*('x', '', 1.0 )) @@ -244,6 +246,60 @@ def BlackBox(self, x, y): # The actual function that does things self.assertRaises(ValueError,f.__dict__['constraint_2'].get_external_model().fillCache,*()) + def test_edi_blackbox_etc_4(self): + "Tests a black box assertion issue" + import numpy as np + from pyomo.environ import units + import pyomo.environ as pyo + from pyomo.contrib.edi import Formulation + from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'y variable') + z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'Output var') + f.Objective( x + y ) + class UnitCircle(BlackBoxFunctionModel): + def __init__(self): + super().__init__() + self.description = 'This model evaluates the function: z = x**2 + y**2' + self.inputs.append( name = 'x', + units = 'ft' , + description = 'The x variable' ) + self.inputs.append( name = 'y', + units = 'ft' , + description = 'The y variable' ) + self.outputs.append(name = 'z', + units = 'ft**2', + description = 'Output variable' ) + self.availableDerivative = 1 + self.post_init_setup(len(self.inputs)) + def BlackBox(self, x, y): # The actual function that does things + # Converts to correct units then casts to float + x = pyo.value(units.convert(x,self.inputs[0].units)) + y = pyo.value(units.convert(y,self.inputs[1].units)) + z = x**2 + y**2 # Compute z + dzdx = 2*x # Compute dz/dx + dzdy = 2*y # Compute dz/dy + z *= units.ft**2 + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + + + f.ConstraintList( + [ + z <= 1*units.m**2 , + [ z, '==', [x,y], UnitCircle() ] , + ] + ) + + f.__dict__['constraint_2'].get_external_model().set_input_values(np.array([2.0, 2.0])) + f.__dict__['constraint_2'].get_external_model().outputVariables_optimization = [1] + # f.__dict__['constraint_2'].get_external_model().fillCache() + self.assertRaises(ValueError,f.__dict__['constraint_2'].get_external_model().fillCache,*()) + + def test_edi_blackbox_example_1(self): "Tests a black box example construction" from pyomo.environ import units @@ -303,8 +359,6 @@ def BlackBox(self, x, y): # The actual function that does things e_print = f.__dict__['constraint_2'].get_external_model().__repr__() - - def test_edi_blackbox_example_2(self): "Tests a black box example construction" import pyomo.environ as pyo @@ -323,9 +377,7 @@ def __init__(self): self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(*args, **kwargs):# The actual function that does things - args = list(args) - self = args.pop(0) + def BlackBox(self, *args, **kwargs):# The actual function that does things runCases, returnMode, remainingKwargs = self.parseInputs(*args, **kwargs) x = self.sanitizeInputs(runCases[0]['x']) @@ -361,10 +413,192 @@ def BlackBox(*args, **kwargs):# The actual function that does things sm = f.__dict__['constraint_1'].get_external_model().summary e_print = f.__dict__['constraint_1'].get_external_model().__repr__() + def test_edi_blackbox_example_3(self): + "Tests a black box example construction" + import numpy as np + from pyomo.environ import units + import pyomo.environ as pyo + from pyomo.contrib.edi import Formulation + from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = '' , description = 'x variable', size = 3) + y = f.Variable(name = 'y', guess = 1.0, units = '' , description = 'y variable') + f.Objective( y ) + class Norm_2(BlackBoxFunctionModel): + def __init__(self): + super().__init__() + self.description = 'This model evaluates the two norm' + self.inputs.append( name = 'x', + units = '' , + description = 'The x variable' , + size = 3) + self.outputs.append(name = 'y', + units = '' , + description = 'The y variable' ) + self.availableDerivative = 1 + self.post_init_setup(len(self.inputs)) + def BlackBox(self, *args, **kwargs):# The actual function that does things + runCases, returnMode, remainingKwargs = self.parseInputs(*args, **kwargs) + + x = self.sanitizeInputs(runCases[0]['x']) + x = np.array([pyo.value(xval) for xval in x], dtype=np.float64) + + y = x[0]**2 + x[1]**2 + x[2]**2 # Compute y + dydx0 = 2*x[0] # Compute dy/dx0 + dydx1 = 2*x[1] # Compute dy/dx1 + dydx2 = 2*x[2] # Compute dy/dx2 + + y = y * units.dimensionless + dydx = np.array([dydx0,dydx1,dydx2]) * units.dimensionless + # dydx0 = dydx0 * units.dimensionless + # dydx1 = dydx1 * units.dimensionless + # dydx2 = dydx2 * units.dimensionless + + return y, [dydx] # return z, grad(z), hess(z)... + + f.ConstraintList([ {'outputs':y, 'operators':'==', 'inputs':x, 'black_box':Norm_2() },]) + + f.__dict__['constraint_1'].get_external_model().set_input_values(np.ones(3)*2) + opt = f.__dict__['constraint_1'].get_external_model().evaluate_outputs() + jac = f.__dict__['constraint_1'].get_external_model().evaluate_jacobian_outputs().todense() + + self.assertAlmostEqual(opt[0],12) + self.assertAlmostEqual(jac[0,0],4) + self.assertAlmostEqual(jac[0,1],4) + self.assertAlmostEqual(jac[0,2],4) + + sm = f.__dict__['constraint_1'].get_external_model().summary + e_print = f.__dict__['constraint_1'].get_external_model().__repr__() + + + def test_edi_blackbox_example_4(self): + "Tests a black box example construction" + import numpy as np + from pyomo.environ import units + import pyomo.environ as pyo + from pyomo.contrib.edi import Formulation + from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = '' , description = 'x variable') + y = f.Variable(name = 'y', guess = 1.0, units = '' , description = 'y variable', size=3) + f.Objective( y[0]**2 + y[1]**2 + y[2]**2 ) + class VectorCast(BlackBoxFunctionModel): + def __init__(self): + super().__init__() + self.description = 'This model evaluates the two norm' + self.inputs.append( name = 'x', + units = '' , + description = 'The x variable' ) + self.outputs.append(name = 'y', + units = '' , + description = 'The y variable' , + size = 3) + self.availableDerivative = 1 + self.post_init_setup(len(self.inputs)) + + def BlackBox(self, *args, **kwargs):# The actual function that does things + runCases, returnMode, remainingKwargs = self.parseInputs(*args, **kwargs) + + x = pyo.value(self.sanitizeInputs(runCases[0]['x'])) + + y = np.array([x,x,x]) * units.dimensionless + dydx = np.array([1.0,1.0,1.0]) * units.dimensionless + + return y, [dydx] # return z, grad(z), hess(z)... + + f.ConstraintList([ {'outputs':y, 'operators':'==', 'inputs':x, 'black_box':VectorCast() },]) + + f.__dict__['constraint_1'].get_external_model().set_input_values(np.ones(3)*2) + opt = f.__dict__['constraint_1'].get_external_model().evaluate_outputs() + jac = f.__dict__['constraint_1'].get_external_model().evaluate_jacobian_outputs().todense() + + self.assertAlmostEqual(opt[0],2.0) + self.assertAlmostEqual(opt[1],2.0) + self.assertAlmostEqual(opt[2],2.0) + self.assertAlmostEqual(jac[0,0],1.0) + self.assertAlmostEqual(jac[1,0],1.0) + self.assertAlmostEqual(jac[2,0],1.0) + + sm = f.__dict__['constraint_1'].get_external_model().summary + e_print = f.__dict__['constraint_1'].get_external_model().__repr__() + + def test_edi_blackbox_badexample_1(self): + "Tests a black box example construction" + import numpy as np + from pyomo.environ import units + import pyomo.environ as pyo + from pyomo.contrib.edi import Formulation + from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = '' , description = 'x variable', size = 3) + y = f.Variable(name = 'y', guess = 1.0, units = '' , description = 'y variable') + f.Objective( y ) + class Norm_2(BlackBoxFunctionModel): + def __init__(self): + super().__init__() + self.description = 'This model evaluates the two norm' + self.inputs.append( name = 'x', + units = '' , + description = 'The x variable' , + size = 3) + self.outputs.append(name = 'y', + units = '' , + description = 'The y variable' ) + self.availableDerivative = 1 + self.post_init_setup(len(self.inputs)) + def BlackBox(self, *args, **kwargs):# The actual function that does things + runCases, returnMode, remainingKwargs = self.parseInputs(*args, **kwargs) + + x = self.sanitizeInputs(runCases[0]['x']) + x = np.array([pyo.value(xval) for xval in x], dtype=np.float64) + + y = x[0]**2 + x[1]**2 + x[2]**2 # Compute y + dydx0 = 2*x[0] # Compute dy/dx0 + dydx1 = 2*x[1] # Compute dy/dx1 + dydx2 = 2*x[2] # Compute dy/dx2 + + y = y * units.dimensionless + # dydx = np.array([dydx0,dydx1,dydx2]) * units.dimensionless + dydx0 = dydx0 * units.dimensionless + dydx1 = dydx1 * units.dimensionless + dydx2 = dydx2 * units.dimensionless + return y, [[dydx0, dydx1, dydx2]] # return z, grad(z), hess(z)... + + f.ConstraintList([ {'outputs':y, 'operators':'==', 'inputs':x, 'black_box':Norm_2() },]) - def test_edi_blackbox_todo2(self): - "TODO2" + f.__dict__['constraint_1'].get_external_model().set_input_values(np.ones(3)*2) + self.assertRaises(ValueError,f.__dict__['constraint_1'].get_external_model().evaluate_outputs,*()) + + + def test_edi_blackbox_smallfunctions(self): + "Tests the more general value and convert functions" + import numpy as np + from pyomo.environ import units + import pyomo.environ as pyo + from pyomo.contrib.edi import Formulation + from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + + bb = BlackBoxFunctionModel() + t1 = bb.convert(2*units.m, units.ft) + t2 = bb.convert(np.ones([2,2])*units.m, units.ft) + self.assertRaises(ValueError,bb.convert,*('err',units.ft)) + + t3 = bb.pyomo_value(2*units.m) + t3 = bb.pyomo_value(np.ones([2,2])*units.m) + self.assertRaises(ValueError,bb.pyomo_value,*(np.ones([2,2]),)) + + bb.sizeCheck([2,2],np.ones([2,2])*units.m) + self.assertRaises(ValueError,bb.sizeCheck,*(2,3*units.m,)) + self.assertRaises(ValueError,bb.sizeCheck,*([10,10,10],np.ones([2,2])*units.m,)) + self.assertRaises(ValueError,bb.sizeCheck,*([10,10],np.ones([2,2])*units.m,)) + self.assertRaises(ValueError,bb.sizeCheck,*([10,10],[],)) + + def test_edi_blackbox_bare_example_1(self): + "Tests a black box example construction without an optimization problem" import numpy as np import pyomo.environ as pyo from pyomo.environ import units @@ -387,9 +621,7 @@ def __init__(self): self.availableDerivative = 1 #standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) - def BlackBox(*args, **kwargs): - args = list(args) # convert tuple to list - self = args.pop(0) # pop off the self argument + def BlackBox(self, *args, **kwargs): runCases, returnMode, extras = self.parseInputs(*args, **kwargs) x = np.array([ pyo.value(runCases[i]['x']) for i in range(0,len(runCases)) ]) @@ -400,7 +632,7 @@ def BlackBox(*args, **kwargs): dydx[gradientSwitch] = -6 ddy_ddx[gradientSwitch] = 0 - y = [ self.checkOutputs(yval) for yval in y ] + y = [ yval * units.dimensionless for yval in y] dydx = [dydx[i] * units.dimensionless for i in range(0,len(dydx))] if returnMode < 0: @@ -438,9 +670,160 @@ def BlackBox(*args, **kwargs): bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)], True, optn=False) bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)], optn1=True, optn2=False) + sm = s.summary + + self.assertRaises(ValueError,s.BlackBox,*( [ [[1,2],2,3] , [1,2,3]] )) + self.assertRaises(ValueError,s.BlackBox,*( [ [np.ones(2),2,3] , [1,2,3]] )) + + self.assertRaises(ValueError,s.sanitizeInputs,*( )) + self.assertRaises(ValueError,s.sanitizeInputs,*( 1 , 2, 3 )) + self.assertRaises(ValueError,s.sanitizeInputs,**{'z':2.0*units.dimensionless}) + self.assertRaises(ValueError,s.sanitizeInputs,*(2.0*units.ft,)) + self.assertRaises(NotImplementedError,s.checkOutputs,*()) + + + def test_edi_blackbox_bare_example_2(self): + "Tests a black box example construction without an optimization problem" + import numpy as np + import pyomo.environ as pyo + from pyomo.environ import units + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + from pyomo.common.formatting import tostr + + class PassThrough(BlackBoxFunctionModel): + def __init__(self): + # Set up all the attributes by calling Model.__init__ + super().__init__() + + #Setup Inputs + self.inputs.append( 'x', '', 'Independent Variable', size = [2,2]) + + #Setup Outputs + self.outputs.append( 'y', '', 'Dependent Variable', size = [2,2]) + + #Simple model description + self.description = 'This model is a pass through)' + + self.availableDerivative = 1 + + #standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) + def BlackBox(self, *args, **kwargs): + runCases, returnMode, extras = self.parseInputs(*args, **kwargs) + x = [ self.pyomo_value(runCases[i]['x']) for i in range(0,len(runCases)) ] + + y = [] + dydx = [] + + for xval in x: + y.append(xval*units.dimensionless) + dydx_temp = np.zeros([2,2,2,2]) + dydx_temp[0,0,0,0] = 1.0 + dydx_temp[0,1,0,1] = 1.0 + dydx_temp[1,0,1,0] = 1.0 + dydx_temp[1,1,1,1] = 1.0 + + dydx.append(dydx_temp*units.dimensionless) + + if returnMode < 0: + returnMode = -1*(returnMode + 1) + if returnMode == 0: + return y[0] + if returnMode == 1: + return y[0], dydx + else: + if returnMode == 0: + opt = [] + for i in range(0,len(y)): + opt.append([ y[i] ]) + return opt + if returnMode == 1: + opt = [] + for i in range(0,len(y)): + opt.append([ [y[i]], [ [[dydx[i]]] ] ]) + return opt + + bb = PassThrough() + ivals = [[np.eye(2)*units.dimensionless], [np.ones([2,2])*units.dimensionless], [np.zeros([2,2])*units.dimensionless] ] + + xv = np.eye(2)*units.dimensionless + + # How the black box may be called using EDI + bbo = bb.BlackBox(**{'x':xv}) + bbo = bb.BlackBox({'x':xv}) + bbo = bb.BlackBox(**{'x':xv, 'optn':True}) + + # # Additional options available with parseInputs + bbo = bb.BlackBox(*[xv], **{'optn1': True, 'optn2': False}) + bbo = bb.BlackBox(*[xv,True], **{'optn': False}) + bbo = bb.BlackBox({'x':[x[0] for x in ivals]}) + bbo = bb.BlackBox([{'x':x[0]} for x in ivals]) + bbo = bb.BlackBox([ [x[0]] for x in ivals]) + bbo = bb.BlackBox([ [x[0]] for x in ivals], True, optn=False) + bbo = bb.BlackBox([ [x[0]] for x in ivals], optn1=True, optn2=False) + + sm = bb.summary + + self.assertRaises(ValueError,bb.sanitizeInputs,*(np.ones([2,2])*units.ft,)) + + + def test_edi_blackbox_bare_example_3(self): + "Tests a black box example construction" + import numpy as np + from pyomo.environ import units + import pyomo.environ as pyo + from pyomo.contrib.edi import Formulation + from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + + class Norm_2(BlackBoxFunctionModel): + def __init__(self): + super().__init__() + self.description = 'This model evaluates the two norm' + self.inputs.append( name = 'x', + units = '' , + description = 'The x variable' , + size = 3) + self.inputs.append( name = 'y', + units = '' , + description = 'The y variable' , + size = 2) + self.outputs.append(name = 'z', + units = '' , + description = 'The z variable') + self.availableDerivative = 1 + self.post_init_setup(len(self.inputs)) + def BlackBox(self, *args, **kwargs):# The actual function that does things + runCases, returnMode, remainingKwargs = self.parseInputs(*args, **kwargs) + + x = [ rc['x'] for rc in runCases][0] + x = np.array([self.pyomo_value(xval) for xval in x], dtype=np.float64) + + y = [ rc['y'] for rc in runCases][0] + y = np.array([self.pyomo_value(yval) for yval in y], dtype=np.float64) + + z = x[0]**2 + x[1]**2 + x[2]**2 + y[0]**2 + y[1]**2 + dzdx0 = 2*x[0] # Compute dy/dx0 + dzdx1 = 2*x[1] # Compute dy/dx1 + dzdx2 = 2*x[2] # Compute dy/dx2 + dzdy0 = 2*y[0] + dzdy1 = 2*y[1] + + z = z * units.dimensionless + dz = np.array([dzdx0,dzdx1,dzdx2,dzdy0,dzdy1]) * units.dimensionless + # dydx0 = dydx0 * units.dimensionless + # dydx1 = dydx1 * units.dimensionless + # dydx2 = dydx2 * units.dimensionless + + return z, [dz] # return z, grad(z), hess(z)... + + bb = Norm_2() + bbo = bb.BlackBox({'x':np.array([0,0,0])*units.dimensionless, 'y':np.array([0,0])*units.dimensionless}) + bbo = bb.BlackBox(np.array([0,0,0])*units.dimensionless, y=np.array([0,0])*units.dimensionless) + + self.assertRaises(ValueError,bb.BlackBox,*( {'er':np.array([0,0,0])*units.dimensionless, 'y':np.array([0,0])*units.dimensionless}, )) + self.assertRaises(ValueError,bb.BlackBox,*( 'err', )) + self.assertRaises(ValueError,bb.BlackBox,*( np.array([0,0,0])*units.dimensionless, np.array([0,0])*units.dimensionless ), **{'x':'err too many'}) + self.assertRaises(ValueError,bb.BlackBox,*( np.array([0,0,0])*units.dimensionless, ), **{'notY':np.array([0,0])*units.dimensionless}) - # def test_edi_formulation_variable(self): - # "Tests the variable constructor in edi.formulation" diff --git a/pyomo/contrib/edi/tests/test_examples.py b/pyomo/contrib/edi/tests/test_examples.py index fd25e2cb665..be34a25f0ba 100644 --- a/pyomo/contrib/edi/tests/test_examples.py +++ b/pyomo/contrib/edi/tests/test_examples.py @@ -46,7 +46,8 @@ def t_function(self): pythonFileList = [ - "readme_example.py" + "readme_example.py", + "aircraft_gp.py" ] for filename in pythonFileList: From 6c4ef32d1b5fa1b6c945a5b3a62e9d1649be0b2e Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Thu, 3 Aug 2023 17:06:01 -0600 Subject: [PATCH 26/61] adding black --- .../contributed_packages/edi/examples.rst | 8 - pyomo/contrib/edi/README.md | 8 +- pyomo/contrib/edi/__init__.py | 12 +- pyomo/contrib/edi/blackBoxFunctionModel.py | 659 +++++++----- pyomo/contrib/edi/examples/aircraft_gp.py | 92 +- pyomo/contrib/edi/examples/readme_example.py | 68 +- pyomo/contrib/edi/formulation.py | 328 ++++-- pyomo/contrib/edi/tests/test_blackbox.py | 971 ++++++++++-------- pyomo/contrib/edi/tests/test_examples.py | 38 +- pyomo/contrib/edi/tests/test_formulation.py | 905 +++++++++++----- 10 files changed, 1918 insertions(+), 1171 deletions(-) diff --git a/doc/OnlineDocs/contributed_packages/edi/examples.rst b/doc/OnlineDocs/contributed_packages/edi/examples.rst index 29e3470d7b8..e20c08d10b8 100644 --- a/doc/OnlineDocs/contributed_packages/edi/examples.rst +++ b/doc/OnlineDocs/contributed_packages/edi/examples.rst @@ -1,14 +1,6 @@ Examples ======== -Using Multi-Dimensional Variables ---------------------------------- - - -Advanced Black-Box Modeling ---------------------------- - - A Geometric Program for Aircraft Design --------------------------------------- diff --git a/pyomo/contrib/edi/README.md b/pyomo/contrib/edi/README.md index cfa7e8d71f7..8a6b592baa0 100644 --- a/pyomo/contrib/edi/README.md +++ b/pyomo/contrib/edi/README.md @@ -16,7 +16,7 @@ pip install pint ## Usage -The core object in EDI is the `Formulation` object, which inherits from the `pyomo.environ.ConcreteModel`. Essentally, a `Formulation` is a Pyomo `Model` with some extra stuff, but can be treated exactly as if it were a Pyomo `Model`. However, an EDI `Formulation` has some additional features that can help simplify model construction. +The core object in EDI is the `Formulation` object, which inherits from the `pyomo.environ.ConcreteModel`. Essentially, a `Formulation` is a Pyomo `Model` with some extra stuff, but can be treated exactly as if it were a Pyomo `Model`. However, an EDI `Formulation` has some additional features that can help simplify model construction. Below is a simple example to get started, but additional resources can be found in the [examples](https://github.com/Pyomo/pyomo/tree/main/pyomo/contrib/edi/examples) folder or in the EDI [documentation](https://pyomo.readthedocs.io/en/stable/contributed_packages/edi/index.html) @@ -56,9 +56,9 @@ f.Objective( # Declare a Black Box # =================== class UnitCircle(BlackBoxFunctionModel): - def __init__(self): # The initalization function + def __init__(self): # The initialization function - # Initalize the black box model + # Initialize the black box model super().__init__() # A brief description of the model @@ -74,7 +74,7 @@ class UnitCircle(BlackBoxFunctionModel): # Declare the maximum available derivative self.availableDerivative = 1 - # Post-initalization setup + # Post-initialization setup self.post_init_setup() def BlackBox(self, x, y): # The actual function that does things diff --git a/pyomo/contrib/edi/__init__.py b/pyomo/contrib/edi/__init__.py index cd2d5ff3063..f54f05bef96 100644 --- a/pyomo/contrib/edi/__init__.py +++ b/pyomo/contrib/edi/__init__.py @@ -23,6 +23,12 @@ # Import the black box modeling tools from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel -from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable as BlackBoxVariable -from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable as BBVariable -from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable as BBV \ No newline at end of file +from pyomo.contrib.edi.blackBoxFunctionModel import ( + BlackBoxFunctionModel_Variable as BlackBoxVariable, +) +from pyomo.contrib.edi.blackBoxFunctionModel import ( + BlackBoxFunctionModel_Variable as BBVariable, +) +from pyomo.contrib.edi.blackBoxFunctionModel import ( + BlackBoxFunctionModel_Variable as BBV, +) diff --git a/pyomo/contrib/edi/blackBoxFunctionModel.py b/pyomo/contrib/edi/blackBoxFunctionModel.py index ce19262387f..56a06531524 100644 --- a/pyomo/contrib/edi/blackBoxFunctionModel.py +++ b/pyomo/contrib/edi/blackBoxFunctionModel.py @@ -21,39 +21,47 @@ import pyomo.environ as pyo from pyomo.environ import units as pyomo_units import scipy.sparse as sps -from pyomo.contrib.pynumero.interfaces.external_grey_box import ExternalGreyBoxModel, ExternalGreyBoxBlock +from pyomo.contrib.pynumero.interfaces.external_grey_box import ( + ExternalGreyBoxModel, + ExternalGreyBoxBlock, +) class BlackBoxFunctionModel_Variable(object): - def __init__(self, name, units, description = '', size = 0): + def __init__(self, name, units, description='', size=0): # Order matters self.name = name self.units = units self.size = size self.description = description -# ===================================================================================================================== -# The printing function -# ===================================================================================================================== + + # ===================================================================================================================== + # The printing function + # ===================================================================================================================== def __repr__(self): - return self.name -# ===================================================================================================================== -# Define the name -# ===================================================================================================================== + return self.name + + # ===================================================================================================================== + # Define the name + # ===================================================================================================================== @property def name(self): return self._name + @name.setter - def name(self,val): + def name(self, val): if isinstance(val, str): self._name = val else: raise ValueError('Invalid name. Must be a string.') -# ===================================================================================================================== -# Define the units -# ===================================================================================================================== + + # ===================================================================================================================== + # Define the units + # ===================================================================================================================== @property def units(self): return self._units + @units.setter def units(self, val): # set dimensionless if a null string is passed in @@ -62,87 +70,101 @@ def units(self, val): val = 'dimensionless' if val is None: val = 'dimensionless' - + if isinstance(val, str): self._units = pyomo_units.__getattr__(val) elif isinstance(val, pyomo.core.base.units_container._PyomoUnit): self._units = val else: - raise ValueError('Invalid units. Must be a string compatible with pint or a unit instance.') -# ===================================================================================================================== -# Define the size -# ===================================================================================================================== + raise ValueError( + 'Invalid units. Must be a string compatible with pint or a unit instance.' + ) + + # ===================================================================================================================== + # Define the size + # ===================================================================================================================== @property def size(self): return self._size + @size.setter def size(self, val): invalid = False - if isinstance(val,(list, tuple)): + if isinstance(val, (list, tuple)): sizeTemp = [] for x in val: if isinstance(x, str): # is a vector of unknown length, should be 'inf', but any string accepted x = -1 # pass - elif not isinstance(x,int): - raise ValueError('Invalid size. Must be an integer or list/tuple of integers') + elif not isinstance(x, int): + raise ValueError( + 'Invalid size. Must be an integer or list/tuple of integers' + ) if x == 1: - raise ValueError('A value of 1 is not valid for defining size. Use fewer dimensions.') + raise ValueError( + 'A value of 1 is not valid for defining size. Use fewer dimensions.' + ) sizeTemp.append(x) self._size = val else: if val is None: - self._size = 0 # set to scalar + self._size = 0 # set to scalar elif isinstance(val, str): # is a 1D vector of unknown length, should be 'inf', but any string accepted self._size = -1 # pass elif isinstance(val, int): if val == 1: - raise ValueError('A value of 1 is not valid for defining size. Use 0 to indicate a scalar value.') + raise ValueError( + 'A value of 1 is not valid for defining size. Use 0 to indicate a scalar value.' + ) else: self._size = val else: - raise ValueError('Invalid size. Must be an integer or list/tuple of integers') -# ===================================================================================================================== -# Define the description -# ===================================================================================================================== + raise ValueError( + 'Invalid size. Must be an integer or list/tuple of integers' + ) + + # ===================================================================================================================== + # Define the description + # ===================================================================================================================== @property def description(self): return self._description + @description.setter def description(self, val): - if isinstance(val,str): + if isinstance(val, str): self._description = val else: raise ValueError('Invalid description. Must be a string.') class TypeCheckedList(list): - def __init__(self, checkItem, itemList = None): + def __init__(self, checkItem, itemList=None): super(TypeCheckedList, self).__init__() self.checkItem = checkItem - + if itemList is not None: if isinstance(itemList, list) or isinstance(itemList, tuple): for itm in itemList: self.append(itm) else: raise ValueError('Input to itemList is not iterable') - + def __setitem__(self, key, val): if isinstance(val, self.checkItem): super(TypeCheckedList, self).__setitem__(key, val) - elif isinstance(val, (tuple,list)): - cks = [isinstance(vl,self.checkItem) for vl in val] + elif isinstance(val, (tuple, list)): + cks = [isinstance(vl, self.checkItem) for vl in val] if sum(cks) == len(cks): super(TypeCheckedList, self).__setitem__(key, val) else: - raise ValueError('Input must be an instance of the defined type') + raise ValueError('Input must be an instance of the defined type') else: raise ValueError('Input must be an instance of the defined type') - + def append(self, val): if isinstance(val, self.checkItem): super(TypeCheckedList, self).append(val) @@ -152,7 +174,7 @@ def append(self, val): class BBList(TypeCheckedList): def __init__(self): - super(BBList, self).__init__(BlackBoxFunctionModel_Variable,[]) + super(BBList, self).__init__(BlackBoxFunctionModel_Variable, []) self._lookupDict = {} self._counter = 0 @@ -164,64 +186,75 @@ def __getitem__(self, val): else: raise ValueError('Input must be an integer or a valid variable name') - - def append(*args, **kwargs): args = list(args) self = args.pop(0) - + if len(args) + len(kwargs.values()) == 1: if len(args) == 1: inputData = args[0] if len(kwargs.values()) == 1: inputData = list(kwargs.values())[0] - + if isinstance(inputData, self.checkItem): if inputData.name in self._lookupDict.keys(): - raise ValueError("Key '%s' already exists in the input list"%(inputData.name)) + raise ValueError( + "Key '%s' already exists in the input list" % (inputData.name) + ) self._lookupDict[inputData.name] = self._counter self._counter += 1 super(BBList, self).append(inputData) else: if isinstance(inputData, str): - raise ValueError("Key '%s' not passed in to the black box variable constructor"%('units')) + raise ValueError( + "Key '%s' not passed in to the black box variable constructor" + % ('units') + ) else: raise ValueError('Invalid (single) input type') - + elif len(args) + len(kwargs.values()) <= 4: - argKeys = ['name','units','description','size'] - ipd = dict(zip(argKeys[0:len(args)],args)) + argKeys = ['name', 'units', 'description', 'size'] + ipd = dict(zip(argKeys[0 : len(args)], args)) for ky, vl in kwargs.items(): if ky in ipd: - raise ValueError("Key '%s' declared after non-keyword arguments and is out of order"%(ky)) + raise ValueError( + "Key '%s' declared after non-keyword arguments and is out of order" + % (ky) + ) else: - ipd[ky]=vl - + ipd[ky] = vl + for ak in argKeys: if ak not in ipd.keys(): if ak == 'description': - ipd['description']='' + ipd['description'] = '' elif ak == 'size': ipd['size'] = 0 else: - raise ValueError("Key '%s' not passed in to the black box variable constructor"%(ak)) - + raise ValueError( + "Key '%s' not passed in to the black box variable constructor" + % (ak) + ) if ipd['name'] in self._lookupDict.keys(): - raise ValueError("Key '%s' already exists in the input list"%(ipd['name'])) + raise ValueError( + "Key '%s' already exists in the input list" % (ipd['name']) + ) self._lookupDict[ipd['name']] = self._counter self._counter += 1 super(BBList, self).append(BlackBoxFunctionModel_Variable(**ipd)) - + else: raise ValueError('Too many inputs to a black box variable') errorString = 'This function is calling to the base class and has not been defined.' + class BlackBoxFunctionModel(ExternalGreyBoxModel): -# --------------------------------------------------------------------------------------------------------------------- -# --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- def __init__(self): super(BlackBoxFunctionModel, self).__init__() @@ -229,9 +262,9 @@ def __init__(self): self.inputs = BBList() self.outputs = BBList() - self.inputVariables_optimization = None + self.inputVariables_optimization = None self.outputVariables_optimization = None - + # A simple description of the model self.description = None @@ -240,29 +273,30 @@ def __init__(self): self._cache = None self._NunwrappedOutputs = None - self._NunwrappedInputs = None + self._NunwrappedInputs = None - def setOptimizationVariables(self, inputVariables_optimization, outputVariables_optimization): - self.inputVariables_optimization = inputVariables_optimization + def setOptimizationVariables( + self, inputVariables_optimization, outputVariables_optimization + ): + self.inputVariables_optimization = inputVariables_optimization self.outputVariables_optimization = outputVariables_optimization -# --------------------------------------------------------------------------------------------------------------------- -# pyomo things -# --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- + # pyomo things + # --------------------------------------------------------------------------------------------------------------------- def input_names(self): inputs_unwrapped = [] for ivar in self.inputVariables_optimization: if isinstance(ivar, pyomo.core.base.var.ScalarVar): inputs_unwrapped.append(ivar) elif isinstance(ivar, pyomo.core.base.var.IndexedVar): - validIndicies = list(ivar.index_set().data()) - for vi in validIndicies: + validIndices = list(ivar.index_set().data()) + for vi in validIndices: inputs_unwrapped.append(ivar[vi]) else: - raise ValueError("Invalid type for input variable") - - return [ip.__str__() for ip in inputs_unwrapped] + raise ValueError("Invalid type for input variable") + return [ip.__str__() for ip in inputs_unwrapped] def output_names(self): outputs_unwrapped = [] @@ -270,11 +304,11 @@ def output_names(self): if isinstance(ovar, pyomo.core.base.var.ScalarVar): outputs_unwrapped.append(ovar) elif isinstance(ovar, pyomo.core.base.var.IndexedVar): - validIndicies = list(ovar.index_set().data()) - for vi in validIndicies: + validIndices = list(ovar.index_set().data()) + for vi in validIndices: outputs_unwrapped.append(ovar[vi]) else: - raise ValueError("Invalid type for output variable") + raise ValueError("Invalid type for output variable") return [op.__str__() for op in outputs_unwrapped] @@ -286,16 +320,15 @@ def evaluate_outputs(self): self.fillCache() opts = self._cache['pyomo_outputs'] return opts - + def evaluate_jacobian_outputs(self): self.fillCache() jac = self._cache['pyomo_jacobian'] return jac - def post_init_setup(self, defaultVal = 1.0): + def post_init_setup(self, defaultVal=1.0): self._input_values = np.ones(self._NunwrappedInputs) * defaultVal - def fillCache(self): if self._cache is None: self._cache = {} @@ -305,95 +338,113 @@ def fillCache(self): ptr = 0 - for i in range(0,len(self.inputVariables_optimization)): + for i in range(0, len(self.inputVariables_optimization)): optimizationInput = self.inputVariables_optimization[i] - if not isinstance(optimizationInput, (pyomo.core.base.var.IndexedVar, pyomo.core.base.var.ScalarVar)): + if not isinstance( + optimizationInput, + (pyomo.core.base.var.IndexedVar, pyomo.core.base.var.ScalarVar), + ): raise ValueError("Invalid input variable type") - ipt = self.inputs[i] + ipt = self.inputs[i] - shape = [len(idx) for idx in optimizationInput.index_set().subsets()] - localShape = ipt.size + shape = [len(idx) for idx in optimizationInput.index_set().subsets()] + localShape = ipt.size optimizationUnits = self.inputVariables_optimization[i].get_units() - localUnits = ipt.units + localUnits = ipt.units if isinstance(optimizationInput, pyomo.core.base.var.IndexedVar): - value = np.zeros(shape) + value = np.zeros(shape) for vix in list(optimizationInput.index_set().data()): - raw_val = float(raw_inputs[ptr]) * optimizationUnits - raw_val_correctedUnits = pyomo_units.convert(raw_val, localUnits) + raw_val = float(raw_inputs[ptr]) * optimizationUnits + raw_val_correctedUnits = pyomo_units.convert( + raw_val, localUnits + ) value[vix] = pyo.value(raw_val_correctedUnits) ptr += 1 - self.sizeCheck(localShape, value*localUnits) - bb_inputs.append(value*localUnits) + self.sizeCheck(localShape, value * localUnits) + bb_inputs.append(value * localUnits) - else: # isinstance(optimizationInput, pyomo.core.base.var.ScalarVar):, just checked this + else: # isinstance(optimizationInput, pyomo.core.base.var.ScalarVar):, just checked this value = raw_inputs[ptr] * optimizationUnits ptr += 1 self.sizeCheck(localShape, value) value_correctedUnits = pyomo_units.convert(value, localUnits) bb_inputs.append(value_correctedUnits) - bbo = self.BlackBox(*bb_inputs) - self._cache['raw'] = bbo - self._cache['raw_value'] = bbo[0] - self._cache['raw_jacobian'] = bbo[1] + self._cache['raw'] = bbo + self._cache['raw_value'] = bbo[0] + self._cache['raw_jacobian'] = bbo[1] outputVector = [] - if not isinstance(bbo[0], (list,tuple)): + if not isinstance(bbo[0], (list, tuple)): valueList = [bbo[0]] jacobianList = [bbo[1]] else: valueList = bbo[0] jacobianList = bbo[1] - for i in range(0,len(valueList)): + for i in range(0, len(valueList)): optimizationOutput = self.outputVariables_optimization[i] - if not isinstance(optimizationOutput, (pyomo.core.base.var.IndexedVar, pyomo.core.base.var.ScalarVar)): + if not isinstance( + optimizationOutput, + (pyomo.core.base.var.IndexedVar, pyomo.core.base.var.ScalarVar), + ): raise ValueError("Invalid output variable type") - opt = self.outputs[i] + opt = self.outputs[i] - modelOutputUnits = opt.units - ouptutOptimizationUnits = optimizationOutput.get_units() + modelOutputUnits = opt.units + outputOptimizationUnits = optimizationOutput.get_units() vl = valueList[i] if isinstance(vl, pyomo.core.expr.numeric_expr.NumericNDArray): validIndexList = optimizationOutput.index_set().data() - for j in range(0,len(validIndexList)): + for j in range(0, len(validIndexList)): vi = validIndexList[j] - corrected_value = pyo.value(pyomo_units.convert(vl[vi], ouptutOptimizationUnits)) # now unitless in correct units - outputVector.append(corrected_value) - - elif isinstance(vl, (pyomo.core.expr.numeric_expr.NPV_ProductExpression, - pyomo.core.base.units_container._PyomoUnit)): - corrected_value = pyo.value(pyomo_units.convert(vl, ouptutOptimizationUnits)) # now unitless in correct units - outputVector.append(corrected_value) + corrected_value = pyo.value( + pyomo_units.convert(vl[vi], outputOptimizationUnits) + ) # now unitless in correct units + outputVector.append(corrected_value) + + elif isinstance( + vl, + ( + pyomo.core.expr.numeric_expr.NPV_ProductExpression, + pyomo.core.base.units_container._PyomoUnit, + ), + ): + corrected_value = pyo.value( + pyomo_units.convert(vl, outputOptimizationUnits) + ) # now unitless in correct units + outputVector.append(corrected_value) else: raise ValueError("Invalid output variable type") self._cache['pyomo_outputs'] = outputVector - outputJacobian = np.ones([self._NunwrappedOutputs, self._NunwrappedInputs]) * -1 + outputJacobian = ( + np.ones([self._NunwrappedOutputs, self._NunwrappedInputs]) * -1 + ) ptr_row = 0 - ptr_col = 0 + ptr_col = 0 - for i in range(0,len(jacobianList)): + for i in range(0, len(jacobianList)): oopt = self.outputVariables_optimization[i] # Checked about 20 lines above # if not isinstance(oopt, (pyomo.core.base.var.ScalarVar,pyomo.core.base.var.IndexedVar)): - # raise ValueError("Invalid type for output variable") + # raise ValueError("Invalid type for output variable") lopt = self.outputs[i] oounits = oopt.get_units() lounits = lopt.units # oshape = [len(idx) for idx in oopt.index_set().subsets()] ptr_col = 0 - for j in range(0,len(self.inputs)): + for j in range(0, len(self.inputs)): oipt = self.inputVariables_optimization[j] # This is checked about 80 lines up # if not isinstance(oipt, (pyomo.core.base.var.ScalarVar,pyomo.core.base.var.IndexedVar)): - # raise ValueError("Invalid type for output variable") + # raise ValueError("Invalid type for output variable") lipt = self.inputs[j] oiunits = oipt.get_units() liunits = lipt.units @@ -401,102 +452,128 @@ def fillCache(self): jacobianValue_raw = jacobianList[i][j] - if isinstance(jacobianValue_raw, (pyomo.core.expr.numeric_expr.NPV_ProductExpression, - pyomo.core.base.units_container._PyomoUnit)): - corrected_value = pyo.value(pyomo_units.convert(jacobianValue_raw, oounits/oiunits)) # now unitless in correct units - outputJacobian[ptr_row,ptr_col] = corrected_value + if isinstance( + jacobianValue_raw, + ( + pyomo.core.expr.numeric_expr.NPV_ProductExpression, + pyomo.core.base.units_container._PyomoUnit, + ), + ): + corrected_value = pyo.value( + pyomo_units.convert(jacobianValue_raw, oounits / oiunits) + ) # now unitless in correct units + outputJacobian[ptr_row, ptr_col] = corrected_value ptr_col += 1 ptr_row_step = 1 - elif isinstance(jacobianValue_raw,pyomo.core.expr.numeric_expr.NumericNDArray): + elif isinstance( + jacobianValue_raw, pyomo.core.expr.numeric_expr.NumericNDArray + ): jshape = jacobianValue_raw.shape if isinstance(oopt, pyomo.core.base.var.ScalarVar): oshape = 0 - else: # isinstance(oopt, pyomo.core.base.var.IndexedVar), checked above - oshape = [len(idx) for idx in oopt.index_set().subsets()] + else: # isinstance(oopt, pyomo.core.base.var.IndexedVar), checked above + oshape = [len(idx) for idx in oopt.index_set().subsets()] if isinstance(oipt, pyomo.core.base.var.ScalarVar): ishape = 0 - else: # isinstance(oipt, pyomo.core.base.var.IndexedVar), checked above - ishape = [len(idx) for idx in oipt.index_set().subsets()] + else: # isinstance(oipt, pyomo.core.base.var.IndexedVar), checked above + ishape = [len(idx) for idx in oipt.index_set().subsets()] if oshape == 0: - validIndicies = list(oipt.index_set().data()) - for vi in validIndicies: - corrected_value = pyo.value(pyomo_units.convert(jacobianValue_raw[vi], oounits/oiunits)) # now unitless in correct units - outputJacobian[ptr_row,ptr_col] = corrected_value + validIndices = list(oipt.index_set().data()) + for vi in validIndices: + corrected_value = pyo.value( + pyomo_units.convert( + jacobianValue_raw[vi], oounits / oiunits + ) + ) # now unitless in correct units + outputJacobian[ptr_row, ptr_col] = corrected_value ptr_col += 1 ptr_row_step = 1 elif ishape == 0: ptr_row_cache = ptr_row - validIndicies = list(oopt.index_set().data()) - for vi in validIndicies: - corrected_value = pyo.value(pyomo_units.convert(jacobianValue_raw[vi], oounits/oiunits)) # now unitless in correct units - outputJacobian[ptr_row,ptr_col] = corrected_value + validIndices = list(oopt.index_set().data()) + for vi in validIndices: + corrected_value = pyo.value( + pyomo_units.convert( + jacobianValue_raw[vi], oounits / oiunits + ) + ) # now unitless in correct units + outputJacobian[ptr_row, ptr_col] = corrected_value ptr_row += 1 ptr_row = ptr_row_cache - ptr_row_step = len(validIndicies) + ptr_row_step = len(validIndices) # elif ishape == 0 and oshape == 0: # Handled by the scalar case above - else: + else: # both are dimensioned vectors - #oshape, ishape, jshape + # oshape, ishape, jshape ptr_row_cache = ptr_row ptr_col_cache = ptr_col - validIndicies_o = list(oopt.index_set().data()) - validIndicies_i = list(oipt.index_set().data()) + validIndices_o = list(oopt.index_set().data()) + validIndices_i = list(oipt.index_set().data()) - for vio in validIndicies_o: - if isinstance(vio, (float,int)): + for vio in validIndices_o: + if isinstance(vio, (float, int)): vio = (vio,) - for vii in validIndicies_i: - if isinstance(vii, (float,int)): + for vii in validIndices_i: + if isinstance(vii, (float, int)): vii = (vii,) - corrected_value = pyo.value(pyomo_units.convert(jacobianValue_raw[vio + vii], oounits/oiunits)) # now unitless in correct units - outputJacobian[ptr_row,ptr_col] = corrected_value + corrected_value = pyo.value( + pyomo_units.convert( + jacobianValue_raw[vio + vii], + oounits / oiunits, + ) + ) # now unitless in correct units + outputJacobian[ptr_row, ptr_col] = corrected_value ptr_col += 1 ptr_col = ptr_col_cache ptr_row += 1 ptr_row = ptr_row_cache - ptr_row_step = len(validIndicies_o) + ptr_row_step = len(validIndices_o) else: raise ValueError("Invalid jacobian type") - ptr_row += ptr_row_step + ptr_row += ptr_row_step self._cache['pyomo_jacobian'] = sps.coo_matrix(outputJacobian) -# --------------------------------------------------------------------------------------------------------------------- -# --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- # These models must be defined in each individual model, just placeholders here def BlackBox(*args, **kwargs): raise AttributeError(errorString) - def convert(self, val, unts): try: val = val * pyomo_units.dimensionless except: - pass ## will handle later - - if isinstance(val, (pyomo.core.base.units_container._PyomoUnit,pyomo.core.expr.numeric_expr.NPV_ProductExpression)): + pass ## will handle later + + if isinstance( + val, + ( + pyomo.core.base.units_container._PyomoUnit, + pyomo.core.expr.numeric_expr.NPV_ProductExpression, + ), + ): return pyomo_units.convert(val, unts) elif isinstance(val, pyomo.core.expr.numeric_expr.NumericNDArray): shp = val.shape ix = np.ndindex(*shp) opt = np.zeros(shp) - for i in range(0,np.prod(shp)): + for i in range(0, np.prod(shp)): ixt = next(ix) - opt[ixt] = pyo.value(pyomo_units.convert(val[ixt],unts)) - return opt*unts + opt[ixt] = pyo.value(pyomo_units.convert(val[ixt], unts)) + return opt * unts else: raise ValueError('Invalid type passed to unit conversion function') - def pyomo_value(self, val): try: return pyo.value(val) @@ -505,19 +582,19 @@ def pyomo_value(self, val): shp = val.shape ix = np.ndindex(*shp) opt = np.zeros(shp) - for i in range(0,np.prod(shp)): + for i in range(0, np.prod(shp)): ixt = next(ix) opt[ixt] = pyo.value(val[ixt]) return opt else: raise ValueError('Invalid type passed to pyomo_value function') -# --------------------------------------------------------------------------------------------------------------------- -# --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- def parseInputs(self, *args, **kwargs): - args = list(args) # convert tuple to list + args = list(args) # convert tuple to list - inputNames = [self.inputs[i].name for i in range(0,len(self.inputs))] + inputNames = [self.inputs[i].name for i in range(0, len(self.inputs))] # ------------------------------ # ------------------------------ @@ -530,32 +607,44 @@ def parseInputs(self, *args, **kwargs): if len(inputNames) == 1: try: rs = self.sanitizeInputs(inputData) - return [dict(zip(inputNames,[rs]))], -self.availableDerivative-1, {} # one input being passed in + return ( + [dict(zip(inputNames, [rs]))], + -self.availableDerivative - 1, + {}, + ) # one input being passed in except: - pass #otherwise, proceed - - if isinstance(inputData, (list,tuple)): + pass # otherwise, proceed + + if isinstance(inputData, (list, tuple)): dataRuns = [] for idc in inputData: if isinstance(idc, dict): sips = self.sanitizeInputs(**idc) if len(inputNames) == 1: sips = [sips] - runDictS = dict(zip(inputNames,sips)) - dataRuns.append(runDictS) # the BlackBox([{'x1':x1, 'x2':x2},{'x1':x1, 'x2':x2},...]) case - elif isinstance(idc,(list,tuple)): + runDictS = dict(zip(inputNames, sips)) + dataRuns.append( + runDictS + ) # the BlackBox([{'x1':x1, 'x2':x2},{'x1':x1, 'x2':x2},...]) case + elif isinstance(idc, (list, tuple)): if len(idc) == len(inputNames): sips = self.sanitizeInputs(*idc) if len(inputNames) == 1: sips = [sips] - runDictS = dict(zip(inputNames,sips)) - dataRuns.append(runDictS) # the BlackBox([ [x1, x2], [x1, x2],...]) case + runDictS = dict(zip(inputNames, sips)) + dataRuns.append( + runDictS + ) # the BlackBox([ [x1, x2], [x1, x2],...]) case else: - raise ValueError('Entry in input data list has improper length') + raise ValueError( + 'Entry in input data list has improper length' + ) else: - raise ValueError("Invalid data type in the input list. Note that BlackBox([x1,x2,y]) must be passed in as BlackBox([[x1,x2,y]]) or "+ - "BlackBox(*[x1,x2,y]) or BlackBox({'x1':x1,'x2':x2,'y':y}) or simply BlackBox(x1, x2, y) to avoid processing singularities. "+ - "Best practice is BlackBox({'x1':x1,'x2':x2,'y':y})") + raise ValueError( + "Invalid data type in the input list. Note that BlackBox([x1,x2,y]) must be passed in as BlackBox([[x1,x2,y]]) or " + + "BlackBox(*[x1,x2,y]) or BlackBox({'x1':x1,'x2':x2,'y':y}) or simply BlackBox(x1, x2, y) to avoid processing singularities. " + + "Best practice is BlackBox({'x1':x1,'x2':x2,'y':y})" + ) return dataRuns, self.availableDerivative, {} elif isinstance(inputData, dict): @@ -566,54 +655,82 @@ def parseInputs(self, *args, **kwargs): sips = self.sanitizeInputs(**inputData) if len(inputNames) == 1: sips = [sips] - return [dict(zip(inputNames,sips))], -self.availableDerivative-1, {} # the BlackBox(*{'x1':x1, 'x2':x2}) case, somewhat likely - - if not all([inputLengths[i] == inputLengths[0] for i in range(0,len(inputLengths))]): + return ( + [dict(zip(inputNames, sips))], + -self.availableDerivative - 1, + {}, + ) # the BlackBox(*{'x1':x1, 'x2':x2}) case, somewhat likely + + if not all( + [ + inputLengths[i] == inputLengths[0] + for i in range(0, len(inputLengths)) + ] + ): sips = self.sanitizeInputs(**inputData) - return [dict(zip(inputNames,sips))], -self.availableDerivative-1, {} # the BlackBox(*{'x1':x1, 'x2':x2}) case where vectors for x1... are passed in (likely to fail on previous line) + return ( + [dict(zip(inputNames, sips))], + -self.availableDerivative - 1, + {}, + ) # the BlackBox(*{'x1':x1, 'x2':x2}) case where vectors for x1... are passed in (likely to fail on previous line) else: try: sips = self.sanitizeInputs(**inputData) - return [dict(zip(inputNames,sips))], -self.availableDerivative-1, {} # the BlackBox(*{'x1':x1, 'x2':x2}) case where vectors all inputs have same length intentionally (likely to fail on previous line) + return ( + [dict(zip(inputNames, sips))], + -self.availableDerivative - 1, + {}, + ) # the BlackBox(*{'x1':x1, 'x2':x2}) case where vectors all inputs have same length intentionally (likely to fail on previous line) except: dataRuns = [] - for i in range(0,inputLengths[0]): + for i in range(0, inputLengths[0]): runDict = {} - for ky,vl in inputData.items(): + for ky, vl in inputData.items(): runDict[ky] = vl[i] sips = self.sanitizeInputs(**runDict) if len(inputNames) == 1: sips = [sips] - runDictS = dict(zip(inputNames,sips)) + runDictS = dict(zip(inputNames, sips)) dataRuns.append(runDictS) - return dataRuns, self.availableDerivative, {} # the BlackBox({'x1':x1_vec, 'x2':x2_vec}) case, most likely + return ( + dataRuns, + self.availableDerivative, + {}, + ) # the BlackBox({'x1':x1_vec, 'x2':x2_vec}) case, most likely else: - raise ValueError('Keywords did not match the exptected list') + raise ValueError('Keywords did not match the expected list') else: - raise ValueError('Got unexpected data type %s'%(str(type(inputData)))) + raise ValueError('Got unexpected data type %s' % (str(type(inputData)))) # ------------------------------ # ------------------------------ else: - if any([ list(kwargs.keys())[i] in inputNames for i in range(0,len(list(kwargs.keys()))) ]): + if any( + [ + list(kwargs.keys())[i] in inputNames + for i in range(0, len(list(kwargs.keys()))) + ] + ): # some of the inputs are defined in the kwargs if len(args) >= len(inputNames): - raise ValueError('A keyword input is defining an input, but there are too many unkeyed arguments for this to occour. Check the inputs.') + raise ValueError( + 'A keyword input is defining an input, but there are too many unkeyed arguments for this to occur. Check the inputs.' + ) else: if len(args) != 0: - availableKeywords = inputNames[-len(args):] - else: + availableKeywords = inputNames[-len(args) :] + else: availableKeywords = inputNames - valList = args + [None]*(len(inputNames)-len(args)) + valList = args + [None] * (len(inputNames) - len(args)) for ky in availableKeywords: ix = inputNames.index(ky) valList[ix] = kwargs[ky] # Not possible to reach due to other checks # if any([valList[i]==None for i in range(0,len(valList))]): - # raise ValueError('Kewords did not properly fill in the remaining arguments. Check the inputs.') + # raise ValueError('Keywords did not properly fill in the remaining arguments. Check the inputs.') - sips = self.sanitizeInputs(*valList) + sips = self.sanitizeInputs(*valList) if len(inputNames) == 1: sips = [sips] @@ -626,18 +743,26 @@ def parseInputs(self, *args, **kwargs): # was in args pass - return [dict(zip(inputNames,sips))], -self.availableDerivative-1, remainingKwargs # Mix of args and kwargs define inputs + return ( + [dict(zip(inputNames, sips))], + -self.availableDerivative - 1, + remainingKwargs, + ) # Mix of args and kwargs define inputs else: # all of the inputs are in the args try: - sips = self.sanitizeInputs(*args[0:len(inputNames)]) + sips = self.sanitizeInputs(*args[0 : len(inputNames)]) if len(inputNames) == 1: sips = [sips] remainingKwargs = copy.deepcopy(kwargs) - remainingKwargs['remainingArgs'] = args[len(inputNames):] - return [dict(zip(inputNames,sips))], -self.availableDerivative-1, remainingKwargs # all inputs are in args + remainingKwargs['remainingArgs'] = args[len(inputNames) :] + return ( + [dict(zip(inputNames, sips))], + -self.availableDerivative - 1, + remainingKwargs, + ) # all inputs are in args # except: except Exception as e: # not possible due to other checks @@ -645,46 +770,62 @@ def parseInputs(self, *args, **kwargs): # raise ValueError(e) if str(e) == 'Not enough inputs': raise ValueError(e) - else:#otherwise, proceed - runCases, returnMode, extra_singleInput = self.parseInputs(args[0]) + else: # otherwise, proceed + runCases, returnMode, extra_singleInput = self.parseInputs( + args[0] + ) remainingKwargs = copy.deepcopy(kwargs) - remainingKwargs['remainingArgs'] = args[len(inputNames):] + remainingKwargs['remainingArgs'] = args[len(inputNames) :] return runCases, returnMode, remainingKwargs -# --------------------------------------------------------------------------------------------------------------------- -# --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- def sizeCheck(self, size, ipval_correctUnits): if size is not None: szVal = ipval_correctUnits - if isinstance(szVal, (pyomo.core.expr.numeric_expr.NPV_ProductExpression, - pyomo.core.base.units_container._PyomoUnit)): - if size != 0 and size != 1 : - raise ValueError('Size did not match the expected size %s (ie: Scalar)'%(str(size))) + if isinstance( + szVal, + ( + pyomo.core.expr.numeric_expr.NPV_ProductExpression, + pyomo.core.base.units_container._PyomoUnit, + ), + ): + if size != 0 and size != 1: + raise ValueError( + 'Size did not match the expected size %s (ie: Scalar)' + % (str(size)) + ) elif isinstance(szVal, pyomo.core.expr.numeric_expr.NumericNDArray): shp = szVal.shape - if isinstance(size,(int,float)): + if isinstance(size, (int, float)): size = [size] # else: if len(shp) != len(size): - raise ValueError('Shapes/Sizes of %s does not match the expected %s'%(str(shp),str(size))) - for j in range(0,len(shp)): + raise ValueError( + 'Shapes/Sizes of %s does not match the expected %s' + % (str(shp), str(size)) + ) + for j in range(0, len(shp)): if size[j] != -1: # was declared of flexible length if size[j] != shp[j]: - raise ValueError('Shapes/Sizes of %s does not match the expected %s'%(str(shp),str(size))) + raise ValueError( + 'Shapes/Sizes of %s does not match the expected %s' + % (str(shp), str(size)) + ) else: raise ValueError('Invalid type detected when checking size') -# --------------------------------------------------------------------------------------------------------------------- -# --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- def sanitizeInputs(self, *args, **kwargs): - nameList = [self.inputs[i].name for i in range(0,len(self.inputs))] + nameList = [self.inputs[i].name for i in range(0, len(self.inputs))] if len(args) + len(kwargs.values()) > len(nameList): raise ValueError('Too many inputs') if len(args) + len(kwargs.values()) < len(nameList): raise ValueError('Not enough inputs') inputDict = {} - for i in range(0,len(args)): + for i in range(0, len(args)): rg = args[i] inputDict[nameList[i]] = rg @@ -692,36 +833,44 @@ def sanitizeInputs(self, *args, **kwargs): if ky in nameList: inputDict[ky] = vl else: - raise ValueError('Unexpected input keyword argument %s in the inputs'%(ky)) + raise ValueError( + 'Unexpected input keyword argument %s in the inputs' % (ky) + ) opts = [] - for i in range(0,len(nameList)): + for i in range(0, len(nameList)): name = nameList[i] nameCheck = self.inputs[i].name unts = self.inputs[i].units size = self.inputs[i].size - + # should be impossible # if name != nameCheck: # raise RuntimeError('Something went wrong and values are not consistent. Check your defined inputs.') ipval = inputDict[name] - if isinstance(ipval, pyomo.core.expr.numeric_expr.NumericNDArray): - for ii in range(0,len(ipval)): + if isinstance(ipval, pyomo.core.expr.numeric_expr.NumericNDArray): + for ii in range(0, len(ipval)): try: - ipval[ii] = self.convert(ipval[ii], unts)#ipval.to(unts) + ipval[ii] = self.convert(ipval[ii], unts) # ipval.to(unts) except: - raise ValueError('Could not convert %s of %s to %s'%(name, str(ipval),str(unts))) + raise ValueError( + 'Could not convert %s of %s to %s' + % (name, str(ipval), str(unts)) + ) ipval_correctUnits = ipval else: try: - ipval_correctUnits = self.convert(ipval, unts)#ipval.to(unts) + ipval_correctUnits = self.convert(ipval, unts) # ipval.to(unts) except: - raise ValueError('Could not convert %s of %s to %s'%(name, str(ipval),str(unts))) + raise ValueError( + 'Could not convert %s of %s to %s' + % (name, str(ipval), str(unts)) + ) - # superceeded by the custom convert function + # superseded by the custom convert function # if not isinstance(ipval_correctUnits, (pyomo.core.expr.numeric_expr.NPV_ProductExpression, # pyomo.core.expr.numeric_expr.NumericNDArray, # pyomo.core.base.units_container._PyomoUnit)): @@ -735,8 +884,8 @@ def sanitizeInputs(self, *args, **kwargs): return opts -# --------------------------------------------------------------------------------------------------------------------- -# --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- def checkOutputs(self, *args, **kwargs): raise NotImplementedError('Contact developers to use this function') # nameList = [self.outputs[i].name for i in range(0,len(self.outputs))] @@ -762,7 +911,7 @@ def checkOutputs(self, *args, **kwargs): # nameCheck = self.outputs[i].name # unts = self.outputs[i].units # size = self.outputs[i].size - + # if name != nameCheck: # raise RuntimeError('Something went wrong and values are not consistent. Check your defined inputs.') @@ -789,15 +938,15 @@ def checkOutputs(self, *args, **kwargs): # return opts -# --------------------------------------------------------------------------------------------------------------------- -# --------------------------------------------------------------------------------------------------------------------- - def getSummary(self, whitespace = 6): + # --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- + def getSummary(self, whitespace=6): pstr = '\n' pstr += 'Model Description\n' pstr += '=================\n' descr_str = self.description.__repr__() pstr += descr_str[1:-1] + '\n\n' - + longestName = 0 longestUnits = 0 longestSize = 0 @@ -805,7 +954,7 @@ def getSummary(self, whitespace = 6): nml = len(ipt.name) if nml > longestName: longestName = nml - unts = ipt.units.__str__()#_repr_html_() + unts = ipt.units.__str__() # _repr_html_() # unts = unts.replace('','^') # unts = unts.replace('','') # unts = unts.replace('\[', '[') @@ -822,37 +971,37 @@ def getSummary(self, whitespace = 6): namespace = max([4, longestName]) + whitespace unitspace = max([5, longestUnits]) + whitespace sizespace = max([4, longestSize]) + whitespace - fulllength = namespace+unitspace+sizespace+11 + fulllength = namespace + unitspace + sizespace + 11 pstr += 'Inputs\n' - pstr += '='*fulllength + pstr += '=' * fulllength pstr += '\n' pstr += 'Name'.ljust(namespace) pstr += 'Units'.ljust(unitspace) pstr += 'Size'.ljust(sizespace) pstr += 'Description' pstr += '\n' - pstr += '-'*(namespace - whitespace) - pstr += ' '*whitespace - pstr += '-'*(unitspace - whitespace) - pstr += ' '*whitespace - pstr += '-'*(sizespace - whitespace) - pstr += ' '*whitespace + pstr += '-' * (namespace - whitespace) + pstr += ' ' * whitespace + pstr += '-' * (unitspace - whitespace) + pstr += ' ' * whitespace + pstr += '-' * (sizespace - whitespace) + pstr += ' ' * whitespace pstr += '-----------' pstr += '\n' for ipt in self.inputs: pstr += ipt.name.ljust(namespace) - unts = ipt.units.__str__()#_repr_html_() + unts = ipt.units.__str__() # _repr_html_() # unts = unts.replace('','^') # unts = unts.replace('','') # unts = unts.replace('\[', '[') # unts = unts.replace('\]', ']') pstr += unts.ljust(unitspace) - lnstr = '%s'%(ipt.size.__repr__()) + lnstr = '%s' % (ipt.size.__repr__()) pstr += lnstr.ljust(sizespace) pstr += ipt.description pstr += '\n' pstr += '\n' - + longestName = 0 longestUnits = 0 longestSize = 0 @@ -860,7 +1009,7 @@ def getSummary(self, whitespace = 6): nml = len(opt.name) if nml > longestName: longestName = nml - unts = opt.units.__str__()#_repr_html_() + unts = opt.units.__str__() # _repr_html_() # unts = unts.replace('','^') # unts = unts.replace('','') # unts = unts.replace('\[', '[') @@ -877,51 +1026,51 @@ def getSummary(self, whitespace = 6): namespace = max([4, longestName]) + whitespace unitspace = max([5, longestUnits]) + whitespace sizespace = max([4, longestSize]) + whitespace - fulllength = namespace+unitspace+sizespace+11 + fulllength = namespace + unitspace + sizespace + 11 pstr += 'Outputs\n' - pstr += '='*fulllength + pstr += '=' * fulllength pstr += '\n' pstr += 'Name'.ljust(namespace) pstr += 'Units'.ljust(unitspace) pstr += 'Size'.ljust(sizespace) pstr += 'Description' pstr += '\n' - pstr += '-'*(namespace - whitespace) - pstr += ' '*whitespace - pstr += '-'*(unitspace - whitespace) - pstr += ' '*whitespace - pstr += '-'*(sizespace - whitespace) - pstr += ' '*whitespace + pstr += '-' * (namespace - whitespace) + pstr += ' ' * whitespace + pstr += '-' * (unitspace - whitespace) + pstr += ' ' * whitespace + pstr += '-' * (sizespace - whitespace) + pstr += ' ' * whitespace pstr += '-----------' pstr += '\n' for opt in self.outputs: pstr += opt.name.ljust(namespace) - unts = opt.units.__str__()#_repr_html_() + unts = opt.units.__str__() # _repr_html_() # unts = unts.replace('','^') # unts = unts.replace('','') # unts = unts.replace('\[', '[') # unts = unts.replace('\]', ']') pstr += unts.ljust(unitspace) - lnstr = '%s'%(opt.size.__repr__()) + lnstr = '%s' % (opt.size.__repr__()) pstr += lnstr.ljust(sizespace) pstr += opt.description pstr += '\n' pstr += '\n' - + return pstr -# --------------------------------------------------------------------------------------------------------------------- -# --------------------------------------------------------------------------------------------------------------------- - + # --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- + @property def summary(self): return self.getSummary() -# --------------------------------------------------------------------------------------------------------------------- -# --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- def __repr__(self): pstr = 'AnalysisModel( [' - for i in range(0,len(self.outputs)): + for i in range(0, len(self.outputs)): pstr += self.outputs[i].name pstr += ',' pstr = pstr[0:-1] diff --git a/pyomo/contrib/edi/examples/aircraft_gp.py b/pyomo/contrib/edi/examples/aircraft_gp.py index 095b0175caf..4d6b4be3259 100644 --- a/pyomo/contrib/edi/examples/aircraft_gp.py +++ b/pyomo/contrib/edi/examples/aircraft_gp.py @@ -24,47 +24,71 @@ # ================= # Declare Variables # ================= -D = f.Variable(name = "D", guess = 300, units = "N", description = "total drag force") -A = f.Variable(name = "A", guess = 10.0, units = "-", description = "aspect ratio") -S = f.Variable(name = "S", guess = 10.0, units = "m^2", description = "total wing area") -V = f.Variable(name = "V", guess = 30.0, units = "m/s", description = "cruising speed") -W = f.Variable(name = "W", guess = 10000.0, units = "N", description = "total aircraft weight") -Re = f.Variable(name = "Re", guess = 3e6, units = "-", description = "Reynold's number") -C_D = f.Variable(name = "C_D", guess = .025, units = "-", description = "Drag coefficient of wing") -C_L = f.Variable(name = "C_L", guess = .5, units = "-", description = "Lift coefficent of wing") -C_f = f.Variable(name = "C_f", guess = .003, units = "-", description = "skin friction coefficient") -W_w = f.Variable(name = "W_w", guess = 2500, units = "N", description = "wing weight") +D = f.Variable(name="D", guess=300, units="N", description="total drag force") +A = f.Variable(name="A", guess=10.0, units="-", description="aspect ratio") +S = f.Variable(name="S", guess=10.0, units="m^2", description="total wing area") +V = f.Variable(name="V", guess=30.0, units="m/s", description="cruising speed") +W = f.Variable(name="W", guess=10000.0, units="N", description="total aircraft weight") +Re = f.Variable(name="Re", guess=3e6, units="-", description="Reynold's number") +C_D = f.Variable( + name="C_D", guess=0.025, units="-", description="Drag coefficient of wing" +) +C_L = f.Variable( + name="C_L", guess=0.5, units="-", description="Lift coefficient of wing" +) +C_f = f.Variable( + name="C_f", guess=0.003, units="-", description="skin friction coefficient" +) +W_w = f.Variable(name="W_w", guess=2500, units="N", description="wing weight") # ================= # Declare Constants # ================= -k = f.Constant(name = "k", value = 1.2, units ="-", description = "form factor") -e = f.Constant(name = "e", value = 0.96, units ="-", description = "Oswald efficiency factor") -mu = f.Constant(name = "mu", value = 1.78e-5, units ="kg/m/s", description = "viscosity of air") -rho = f.Constant(name = "rho", value = 1.23, units ="kg/m^3", description = "density of air") -tau = f.Constant(name = "tau", value = 0.12, units ="-", description = "airfoil thickness to chord ratio") -N_ult = f.Constant(name = "N_ult", value = 2.5, units ="-", description = "ultimate load factor") -V_min = f.Constant(name = "V_min", value = 22, units ="m/s", description = "takeoff speed") -C_Lmax = f.Constant(name = "C_Lmax", value = 2.0, units ="-", description = "max CL with flaps down") -S_wetratio = f.Constant(name = "Srat", value = 2.05, units ="-", description = "wetted area ratio") -W_W_coeff1 = f.Constant(name = "W_c1", value = 8.71e-5, units ="1/m", description = "Wing Weight Coefficent 1") -W_W_coeff2 = f.Constant(name = "W_c2", value = 45.24, units ="Pa", description = "Wing Weight Coefficent 2") -CDA0 = f.Constant(name = "CDA0", value = 0.0306, units ="m^2", description = "fuselage drag area") -W_0 = f.Constant(name = "W_0", value = 4940.0, units ="N", description = "aircraft weight excluding wing") +k = f.Constant(name="k", value=1.2, units="-", description="form factor") +e = f.Constant(name="e", value=0.96, units="-", description="Oswald efficiency factor") +mu = f.Constant( + name="mu", value=1.78e-5, units="kg/m/s", description="viscosity of air" +) +rho = f.Constant(name="rho", value=1.23, units="kg/m^3", description="density of air") +tau = f.Constant( + name="tau", value=0.12, units="-", description="airfoil thickness to chord ratio" +) +N_ult = f.Constant( + name="N_ult", value=2.5, units="-", description="ultimate load factor" +) +V_min = f.Constant(name="V_min", value=22, units="m/s", description="takeoff speed") +C_Lmax = f.Constant( + name="C_Lmax", value=2.0, units="-", description="max CL with flaps down" +) +S_wetratio = f.Constant( + name="Srat", value=2.05, units="-", description="wetted area ratio" +) +W_W_coeff1 = f.Constant( + name="W_c1", value=8.71e-5, units="1/m", description="Wing Weight Coefficient 1" +) +W_W_coeff2 = f.Constant( + name="W_c2", value=45.24, units="Pa", description="Wing Weight Coefficient 2" +) +CDA0 = f.Constant( + name="CDA0", value=0.0306, units="m^2", description="fuselage drag area" +) +W_0 = f.Constant( + name="W_0", value=4940.0, units="N", description="aircraft weight excluding wing" +) # ===================== # Declare the Objective # ===================== -f.Objective( D ) +f.Objective(D) # =================================== # Declare some intermediate variables # =================================== pi = np.pi -C_D_fuse = CDA0/S -C_D_wpar = k*C_f*S_wetratio -C_D_ind = C_L**2/(pi*A*e) -W_w_strc = W_W_coeff1*(N_ult*A**1.5*(W_0*W*S)**0.5)/tau +C_D_fuse = CDA0 / S +C_D_wpar = k * C_f * S_wetratio +C_D_ind = C_L**2 / (pi * A * e) +W_w_strc = W_W_coeff1 * (N_ult * A**1.5 * (W_0 * W * S) ** 0.5) / tau W_w_surf = W_W_coeff2 * S # ======================= @@ -74,11 +98,11 @@ [ C_D >= C_D_fuse + C_D_wpar + C_D_ind, W_w >= W_w_surf + W_w_strc, - D >= 0.5*rho*S*C_D*V**2, - Re == (rho/mu)*V*(S/A)**0.5, - C_f == 0.074/Re**0.2, - W == 0.5*rho*S*C_L*V**2, - W == 0.5*rho*S*C_Lmax*V_min**2, + D >= 0.5 * rho * S * C_D * V**2, + Re == (rho / mu) * V * (S / A) ** 0.5, + C_f == 0.074 / Re**0.2, + W == 0.5 * rho * S * C_L * V**2, + W == 0.5 * rho * S * C_Lmax * V_min**2, W >= W_0 + W_w, - ] + ] ) diff --git a/pyomo/contrib/edi/examples/readme_example.py b/pyomo/contrib/edi/examples/readme_example.py index c6264677306..aa27a0e0914 100644 --- a/pyomo/contrib/edi/examples/readme_example.py +++ b/pyomo/contrib/edi/examples/readme_example.py @@ -14,75 +14,61 @@ # ================= # Declare Variables # ================= -x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') -y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') -z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'Model output') +x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') +y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') +z = f.Variable(name='z', guess=1.0, units='m^2', description='Model output') # ================= # Declare Constants # ================= -c = f.Constant( name = 'c', - value = 1.0, - units = '', - description = 'A constant c', - size = 2 ) +c = f.Constant(name='c', value=1.0, units='', description='A constant c', size=2) # ===================== # Declare the Objective # ===================== -f.Objective( - c[0]*x + c[1]*y -) +f.Objective(c[0] * x + c[1] * y) + # =================== # Declare a Black Box # =================== class UnitCircle(BlackBoxFunctionModel): - def __init__(self): # The initalization function - - # Initalize the black box model + def __init__(self): # The initialization function + # Initialize the black box model super().__init__() # A brief description of the model self.description = 'This model evaluates the function: z = x**2 + y**2' - + # Declare the black box model inputs - self.inputs.append( name = 'x', - units = 'ft' , - description = 'The x variable') - self.inputs.append( name = 'y', - units = 'ft' , - description = 'The y variable') + self.inputs.append(name='x', units='ft', description='The x variable') + self.inputs.append(name='y', units='ft', description='The y variable') # Declare the black box model outputs - self.outputs.append( name = 'z', - units = 'ft**2', - description = 'Resultant of the unit circle') + self.outputs.append( + name='z', units='ft**2', description='Resultant of the unit circle' + ) # Declare the maximum available derivative self.availableDerivative = 1 - - def BlackBox(self, x, y): # The actual function that does things + + def BlackBox(self, x, y): # The actual function that does things # Converts to correct units then casts to float - x = pyo.value(units.convert(x,self.inputs['x'].units)) - y = pyo.value(units.convert(y,self.inputs['y'].units)) + x = pyo.value(units.convert(x, self.inputs['x'].units)) + y = pyo.value(units.convert(y, self.inputs['y'].units)) - z = x**2 + y**2 # Compute z - dzdx = 2*x # Compute dz/dx - dzdy = 2*y # Compute dz/dy + z = x**2 + y**2 # Compute z + dzdx = 2 * x # Compute dz/dx + dzdy = 2 * y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + # ======================= # Declare the Constraints # ======================= -f.ConstraintList( - [ - [ z, '==', [x,y], UnitCircle() ] , - z <= 1*units.m**2 - ] -) \ No newline at end of file +f.ConstraintList([[z, '==', [x, y], UnitCircle()], z <= 1 * units.m**2]) diff --git a/pyomo/contrib/edi/formulation.py b/pyomo/contrib/edi/formulation.py index a7e300bacbe..f422c50cea3 100644 --- a/pyomo/contrib/edi/formulation.py +++ b/pyomo/contrib/edi/formulation.py @@ -16,13 +16,16 @@ # ___________________________________________________________________________ import pyomo -import pyomo.environ as pyo +import pyomo.environ as pyo from pyomo.util.check_units import assert_units_consistent from pyomo.environ import ConcreteModel from pyomo.environ import Var, Param, Objective, Constraint, Set from pyomo.environ import maximize, minimize from pyomo.environ import units as pyomo_units -from pyomo.contrib.pynumero.interfaces.external_grey_box import ExternalGreyBoxModel, ExternalGreyBoxBlock +from pyomo.contrib.pynumero.interfaces.external_grey_box import ( + ExternalGreyBoxModel, + ExternalGreyBoxBlock, +) from pyomo.environ import ( Reals, PositiveReals, @@ -43,7 +46,7 @@ PercentFraction, RealInterval, IntegerInterval, -) +) domainList = [ Reals, @@ -67,15 +70,17 @@ IntegerInterval, ] + def decodeUnits(u_val): - if isinstance(u_val,str): - if u_val in ['','-','None',' ','dimensionless']: + if isinstance(u_val, str): + if u_val in ['', '-', 'None', ' ', 'dimensionless']: return pyomo_units.__getattr__('dimensionless') else: return pyomo_units.__getattr__(u_val) else: return u_val - + + class Formulation(ConcreteModel): def __init__(self): super(Formulation, self).__init__() @@ -84,17 +89,18 @@ def __init__(self): self._objective_counter = 0 self._constraint_counter = 0 - self._variable_keys = [] - self._constant_keys = [] + self._variable_keys = [] + self._constant_keys = [] self._objective_keys = [] self._runtimeObjective_keys = [] self._objective_keys = [] self._runtimeConstraint_keys = [] self._constraint_keys = [] self._allConstraint_keys = [] - - def Variable(self, name, guess, units, description='', size = None, bounds=None, domain=None): + def Variable( + self, name, guess, units, description='', size=None, bounds=None, domain=None + ): if domain is None: domain = Reals else: @@ -102,47 +108,102 @@ def Variable(self, name, guess, units, description='', size = None, bounds=None, raise RuntimeError("Invalid domain") if bounds is not None: - if not isinstance(bounds,(list,tuple)): - raise ValueError('The keyword bounds must be a 2 length list or tuple of floats') - if len(bounds)!=2: - raise ValueError('The keyword bounds must be a 2 length list or tuple of floats') + if not isinstance(bounds, (list, tuple)): + raise ValueError( + 'The keyword bounds must be a 2 length list or tuple of floats' + ) + if len(bounds) != 2: + raise ValueError( + 'The keyword bounds must be a 2 length list or tuple of floats' + ) if not isinstance(bounds[0], (float, int)): - raise ValueError('The keyword bounds must be a 2 length list or tuple of floats') + raise ValueError( + 'The keyword bounds must be a 2 length list or tuple of floats' + ) if not isinstance(bounds[1], (float, int)): - raise ValueError('The keyword bounds must be a 2 length list or tuple of floats') + raise ValueError( + 'The keyword bounds must be a 2 length list or tuple of floats' + ) if bounds[0] > bounds[1]: raise ValueError("Lower bound is higher than upper bound") - if size is not None: - if isinstance(size,(list, tuple)): - for i in range(0,len(size)): - if not isinstance(size[i],int): - raise ValueError('Invalid size. Must be an integer or list/tuple of integers') - if size[i] == 1 or size[i] == 0 : - raise ValueError('A value of 0 or 1 is not valid for defining size. Use fewer dimensions.') + if isinstance(size, (list, tuple)): + for i in range(0, len(size)): + if not isinstance(size[i], int): + raise ValueError( + 'Invalid size. Must be an integer or list/tuple of integers' + ) + if size[i] == 1 or size[i] == 0: + raise ValueError( + 'A value of 0 or 1 is not valid for defining size. Use fewer dimensions.' + ) if i == 0: - st = Set(initialize=list(range(0,size[i]))) + st = Set(initialize=list(range(0, size[i]))) else: - st *= Set(initialize=list(range(0,size[i]))) + st *= Set(initialize=list(range(0, size[i]))) st.construct() - self.add_component(name, Var(st, name=name, initialize=guess, domain=domain, bounds=bounds, doc=description, units=decodeUnits(units))) + self.add_component( + name, + Var( + st, + name=name, + initialize=guess, + domain=domain, + bounds=bounds, + doc=description, + units=decodeUnits(units), + ), + ) else: if isinstance(size, int): if size == 1 or size == 0: - self.add_component(name, Var(name=name, initialize=guess, domain=domain, bounds=bounds, doc=description, units=decodeUnits(units))) + self.add_component( + name, + Var( + name=name, + initialize=guess, + domain=domain, + bounds=bounds, + doc=description, + units=decodeUnits(units), + ), + ) else: - st = Set(initialize=list(range(0,size))) + st = Set(initialize=list(range(0, size))) st.construct() - self.add_component(name, Var(st, name=name, initialize=guess, domain=domain, bounds=bounds, doc=description, units=decodeUnits(units))) + self.add_component( + name, + Var( + st, + name=name, + initialize=guess, + domain=domain, + bounds=bounds, + doc=description, + units=decodeUnits(units), + ), + ) else: - raise ValueError('Invalid size. Must be an integer or list/tuple of integers') + raise ValueError( + 'Invalid size. Must be an integer or list/tuple of integers' + ) else: - self.add_component(name, Var(name=name, initialize=guess, domain=domain, bounds=bounds, doc=description, units=decodeUnits(units))) + self.add_component( + name, + Var( + name=name, + initialize=guess, + domain=domain, + bounds=bounds, + doc=description, + units=decodeUnits(units), + ), + ) self.__dict__[name].construct() self._variable_keys.append(name) return self.__dict__[name] - + def Constant(self, name, value, units, description='', size=None, within=None): if within is None: within = Reals @@ -151,76 +212,130 @@ def Constant(self, name, value, units, description='', size=None, within=None): raise RuntimeError("Invalid within") if size is not None: - if isinstance(size,(list, tuple)): - for i in range(0,len(size)): - if not isinstance(size[i],int): - raise ValueError('Invalid size. Must be an integer or list/tuple of integers') + if isinstance(size, (list, tuple)): + for i in range(0, len(size)): + if not isinstance(size[i], int): + raise ValueError( + 'Invalid size. Must be an integer or list/tuple of integers' + ) if size[i] == 1: - raise ValueError('A value of 1 is not valid for defining size. Use fewer dimensions.') + raise ValueError( + 'A value of 1 is not valid for defining size. Use fewer dimensions.' + ) if i == 0: - st = Set(initialize=list(range(0,size[i]))) + st = Set(initialize=list(range(0, size[i]))) else: - st *= Set(initialize=list(range(0,size[i]))) + st *= Set(initialize=list(range(0, size[i]))) st.construct() - self.add_component(name, Param(st, name=name, initialize=value, within=within, doc=description, units=decodeUnits(units), mutable=True)) + self.add_component( + name, + Param( + st, + name=name, + initialize=value, + within=within, + doc=description, + units=decodeUnits(units), + mutable=True, + ), + ) else: if isinstance(size, int): if size == 1 or size == 0: - self.add_component(name, Param(name=name, initialize=value, within=within, doc=description, units=decodeUnits(units), mutable=True)) + self.add_component( + name, + Param( + name=name, + initialize=value, + within=within, + doc=description, + units=decodeUnits(units), + mutable=True, + ), + ) else: - st = Set(initialize=list(range(0,size))) + st = Set(initialize=list(range(0, size))) st.construct() - self.add_component(name, Param(st, name=name, initialize=value, within=within, doc=description, units=decodeUnits(units), mutable=True)) + self.add_component( + name, + Param( + st, + name=name, + initialize=value, + within=within, + doc=description, + units=decodeUnits(units), + mutable=True, + ), + ) else: - raise ValueError('Invalid size. Must be an integer or list/tuple of integers') + raise ValueError( + 'Invalid size. Must be an integer or list/tuple of integers' + ) else: - self.add_component(name, Param(name=name, initialize=value, within=within, doc=description, units=decodeUnits(units), mutable=True)) - + self.add_component( + name, + Param( + name=name, + initialize=value, + within=within, + doc=description, + units=decodeUnits(units), + mutable=True, + ), + ) + self.__dict__[name].construct() self._constant_keys.append(name) return self.__dict__[name] - + def Objective(self, expr, sense=minimize): self._objective_counter += 1 - self.add_component('objective_'+str(self._objective_counter) , Objective(expr=expr,sense=sense)) - self._objective_keys.append('objective_'+str(self._objective_counter)) - self.__dict__['objective_'+str(self._objective_counter)].construct() - + self.add_component( + 'objective_' + str(self._objective_counter), + Objective(expr=expr, sense=sense), + ) + self._objective_keys.append('objective_' + str(self._objective_counter)) + self.__dict__['objective_' + str(self._objective_counter)].construct() + # def RuntimeObjective(self): # pass - + def Constraint(self, expr): self._constraint_counter += 1 - conName = 'constraint_'+str(self._constraint_counter) + conName = 'constraint_' + str(self._constraint_counter) self.add_component(conName, Constraint(expr=expr)) self._constraint_keys.append(conName) self._allConstraint_keys.append(conName) self.__dict__[conName].construct() - def RuntimeConstraint(self, outputs, operators, inputs, black_box): self._constraint_counter += 1 - conName = 'constraint_'+str(self._constraint_counter) + conName = 'constraint_' + str(self._constraint_counter) self._runtimeConstraint_keys.append(conName) self._allConstraint_keys.append(conName) - self.add_component(conName, ExternalGreyBoxBlock() ) + self.add_component(conName, ExternalGreyBoxBlock()) self.__dict__[conName].construct() # TODO: Need to include operators after Michael fixes things - inputs_raw = inputs - outputs_raw = outputs + inputs_raw = inputs + outputs_raw = outputs operators_raw = operators - if isinstance(inputs_raw, (pyomo.core.base.var.IndexedVar,pyomo.core.base.var.ScalarVar)): + if isinstance( + inputs_raw, (pyomo.core.base.var.IndexedVar, pyomo.core.base.var.ScalarVar) + ): inputs_raw = [inputs_raw] elif isinstance(inputs_raw, (list, tuple)): inputs_raw = list(inputs_raw) else: raise ValueError("Invalid type for input variables") - if isinstance(outputs_raw, (pyomo.core.base.var.IndexedVar,pyomo.core.base.var.ScalarVar)): + if isinstance( + outputs_raw, (pyomo.core.base.var.IndexedVar, pyomo.core.base.var.ScalarVar) + ): outputs_raw = [outputs_raw] elif isinstance(outputs_raw, (list, tuple)): outputs_raw = list(outputs_raw) @@ -228,17 +343,19 @@ def RuntimeConstraint(self, outputs, operators, inputs, black_box): raise ValueError("Invalid type for output variables") for lst in [outputs_raw, inputs_raw]: for vr in lst: - if not isinstance(vr, (pyomo.core.base.var.IndexedVar,pyomo.core.base.var.ScalarVar)): - raise ValueError("Invalid type when checking inputs and outputs") + if not isinstance( + vr, (pyomo.core.base.var.IndexedVar, pyomo.core.base.var.ScalarVar) + ): + raise ValueError("Invalid type when checking inputs and outputs") if isinstance(operators_raw, (list, tuple)): operators_raw = list(operators_raw) - elif isinstance(operators_raw,str): + elif isinstance(operators_raw, str): operators_raw = [operators_raw] else: raise ValueError("Invalid type for operators") for opr in operators_raw: - if opr not in ["==",">=","<="]: + if opr not in ["==", ">=", "<="]: raise ValueError("Invalid operator") black_box.setOptimizationVariables(inputs_raw, outputs_raw) @@ -250,33 +367,34 @@ def RuntimeConstraint(self, outputs, operators, inputs, black_box): for ovar in outputs_raw: if isinstance(ovar, pyomo.core.base.var.ScalarVar): outputs_unwrapped.append(ovar) - else: # isinstance(ovar, pyomo.core.base.var.IndexedVar), validated above - validIndicies = list(ovar.index_set().data()) - for vi in validIndicies: + else: # isinstance(ovar, pyomo.core.base.var.IndexedVar), validated above + validIndices = list(ovar.index_set().data()) + for vi in validIndices: outputs_unwrapped.append(ovar[vi]) inputs_unwrapped = [] for ivar in inputs_raw: if isinstance(ivar, pyomo.core.base.var.ScalarVar): inputs_unwrapped.append(ivar) - else: # isinstance(ivar, pyomo.core.base.var.IndexedVar), validated above - validIndicies = list(ivar.index_set().data()) - for vi in validIndicies: - inputs_unwrapped.append(ivar[vi]) + else: # isinstance(ivar, pyomo.core.base.var.IndexedVar), validated above + validIndices = list(ivar.index_set().data()) + for vi in validIndices: + inputs_unwrapped.append(ivar[vi]) black_box._NunwrappedOutputs = len(outputs_unwrapped) - black_box._NunwrappedInputs = len(inputs_unwrapped) + black_box._NunwrappedInputs = len(inputs_unwrapped) black_box.post_init_setup() - # TODO: Need to unwrap operators - + # TODO: Need to unwrap operators - self.__dict__[conName].set_external_model(black_box, inputs=inputs_unwrapped, outputs=outputs_unwrapped)#,operators=operators_unwrapped) + self.__dict__[conName].set_external_model( + black_box, inputs=inputs_unwrapped, outputs=outputs_unwrapped + ) # ,operators=operators_unwrapped) def ConstraintList(self, conList): - for i in range(0,len(conList)): + for i in range(0, len(conList)): con = conList[i] - if isinstance(con, (tuple,list)): + if isinstance(con, (tuple, list)): self.RuntimeConstraint(*con) elif isinstance(con, dict): self.RuntimeConstraint(**con) @@ -284,30 +402,54 @@ def ConstraintList(self, conList): self.Constraint(con) def get_variables(self): - return [self.__dict__[nm] for nm in self.__dict__.keys() if nm in self._variable_keys] + return [ + self.__dict__[nm] + for nm in self.__dict__.keys() + if nm in self._variable_keys + ] def get_constants(self): - return [self.__dict__[nm] for nm in self.__dict__.keys() if nm in self._constant_keys] + return [ + self.__dict__[nm] + for nm in self.__dict__.keys() + if nm in self._constant_keys + ] def get_objectives(self): - return [self.__dict__[nm] for nm in self.__dict__.keys() if nm in self._objective_keys] - + return [ + self.__dict__[nm] + for nm in self.__dict__.keys() + if nm in self._objective_keys + ] + def get_constraints(self): - return [self.__dict__[nm] for nm in self.__dict__.keys() if nm in self._allConstraint_keys] + return [ + self.__dict__[nm] + for nm in self.__dict__.keys() + if nm in self._allConstraint_keys + ] def get_explicitConstraints(self): - return [self.__dict__[nm] for nm in self.__dict__.keys() if nm in self._constraint_keys] + return [ + self.__dict__[nm] + for nm in self.__dict__.keys() + if nm in self._constraint_keys + ] def get_runtimeConstraints(self): - return [self.__dict__[nm] for nm in self.__dict__.keys() if nm in self._runtimeConstraint_keys] + return [ + self.__dict__[nm] + for nm in self.__dict__.keys() + if nm in self._runtimeConstraint_keys + ] def check_units(self): - for i in range(1,self._objective_counter+1): - assert_units_consistent(self.__dict__['objective_'+str(i)]) - - for i in range(1,self._constraint_counter+1): - if not isinstance(self.__dict__['constraint_'+str(i)],pyomo.contrib.pynumero.interfaces.external_grey_box.ExternalGreyBoxBlock): - assert_units_consistent(self.__dict__['constraint_'+str(i)]) - - - + for i in range(1, self._objective_counter + 1): + assert_units_consistent(self.__dict__['objective_' + str(i)]) + + for i in range(1, self._constraint_counter + 1): + if not isinstance( + self.__dict__['constraint_' + str(i)], + pyomo.contrib.pynumero.interfaces.external_grey_box.ExternalGreyBoxBlock, + ): + assert_units_consistent(self.__dict__['constraint_' + str(i)]) diff --git a/pyomo/contrib/edi/tests/test_blackbox.py b/pyomo/contrib/edi/tests/test_blackbox.py index a16748e9e93..1dae7f24e99 100644 --- a/pyomo/contrib/edi/tests/test_blackbox.py +++ b/pyomo/contrib/edi/tests/test_blackbox.py @@ -19,22 +19,17 @@ import pyomo.environ as pyo from pyomo.common.dependencies import attempt_import -from pyomo.core.base.units_container import ( - pint_available, -) +from pyomo.core.base.units_container import pint_available -np, numpy_available = attempt_import( - 'numpy', 'edi requires numpy' -) +np, numpy_available = attempt_import('numpy', 'edi requires numpy') # scipy, scipy_available = attempt_import( # 'scipy', 'inverse_reduced_hessian requires scipy' # ) # if not (numpy_available and scipy_available): if not numpy_available: - raise unittest.SkipTest( - 'edi.formulation tests require numpy' - ) + raise unittest.SkipTest('edi.formulation tests require numpy') + @unittest.skipIf(not pint_available, 'Testing units requires pint') class TestEDIBlackBox(unittest.TestCase): @@ -42,105 +37,124 @@ def test_edi_blackbox_variable(self): "Tests the black box variable class" from pyomo.environ import units from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + from pyomo.contrib.edi.blackBoxFunctionModel import ( + BlackBoxFunctionModel_Variable, + TypeCheckedList, + BBList, + BlackBoxFunctionModel, + ) - x = BlackBoxFunctionModel_Variable('x','') + x = BlackBoxFunctionModel_Variable('x', '') x_print = x.__repr__() x_name = x.name - x_units = x.units - x_size = x.size + x_units = x.units + x_size = x.size x_desc = x.description - self.assertRaises(ValueError,x.__init__,*(1.0, '')) + self.assertRaises(ValueError, x.__init__, *(1.0, '')) x.__init__('x', units.dimensionless) - self.assertRaises(ValueError,x.__init__,*('x', 1.0)) + self.assertRaises(ValueError, x.__init__, *('x', 1.0)) x.__init__('x', units.dimensionless, '', 'flex') - x.__init__('x', units.dimensionless, '', ['flex',2]) + x.__init__('x', units.dimensionless, '', ['flex', 2]) x.__init__('x', units.dimensionless, '', 2) - x.__init__('x', units.dimensionless, '', [2,2]) + x.__init__('x', units.dimensionless, '', [2, 2]) - self.assertRaises(ValueError,x.__init__,*('x', '','',[[],2])) - self.assertRaises(ValueError,x.__init__,*('x', '','',[2,1])) + self.assertRaises(ValueError, x.__init__, *('x', '', '', [[], 2])) + self.assertRaises(ValueError, x.__init__, *('x', '', '', [2, 1])) x.__init__('x', units.dimensionless, '', None) x.__init__('x', None, '', None) - self.assertRaises(ValueError,x.__init__,*('x', '','', 1 )) - self.assertRaises(ValueError,x.__init__,*('x', '','', {} )) - self.assertRaises(ValueError,x.__init__,*('x', '', 1.0 )) - - - + self.assertRaises(ValueError, x.__init__, *('x', '', '', 1)) + self.assertRaises(ValueError, x.__init__, *('x', '', '', {})) + self.assertRaises(ValueError, x.__init__, *('x', '', 1.0)) def test_edi_blackbox_tcl(self): "Tests the black box type checked list class" from pyomo.environ import units from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + from pyomo.contrib.edi.blackBoxFunctionModel import ( + BlackBoxFunctionModel_Variable, + TypeCheckedList, + BBList, + BlackBoxFunctionModel, + ) - tcl = TypeCheckedList(int, [1,2,3]) + tcl = TypeCheckedList(int, [1, 2, 3]) tcl[1] = 1 - tcl[0:2] = [1,2] - - self.assertRaises(ValueError,tcl.__init__,*( int, 1 )) - self.assertRaises(ValueError,tcl.__setitem__,*( 1, 3.333 )) - self.assertRaises(ValueError,tcl.__setitem__,*( 1, [1, 2.222] )) - self.assertRaises(ValueError,tcl.append,*( 2.222 ,)) - + tcl[0:2] = [1, 2] + self.assertRaises(ValueError, tcl.__init__, *(int, 1)) + self.assertRaises(ValueError, tcl.__setitem__, *(1, 3.333)) + self.assertRaises(ValueError, tcl.__setitem__, *(1, [1, 2.222])) + self.assertRaises(ValueError, tcl.append, *(2.222,)) def test_edi_blackbox_bbl(self): "Tests the black box BBList class" from pyomo.environ import units from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + from pyomo.contrib.edi.blackBoxFunctionModel import ( + BlackBoxFunctionModel_Variable, + TypeCheckedList, + BBList, + BlackBoxFunctionModel, + ) bbl = BBList() - bbl.append('x','') - bbl.append('y','') - bbl.append(BlackBoxFunctionModel_Variable('z','')) - bbl.append(var = BlackBoxFunctionModel_Variable('u','')) - self.assertRaises(ValueError,bbl.append,*( BlackBoxFunctionModel_Variable('u',''),)) - self.assertRaises(ValueError,bbl.append,*( 'badvar',)) - self.assertRaises(ValueError,bbl.append,*( 2.222,)) - - self.assertRaises(ValueError,bbl.append,*( 'bv','',''),**{'units':'m'}) - self.assertRaises(ValueError,bbl.append,**{'units':'m','description':'hi'}) - self.assertRaises(ValueError,bbl.append,**{'name':'x', 'units':''}) - self.assertRaises(ValueError,bbl.append,*( 'bv','','',0,'extra')) + bbl.append('x', '') + bbl.append('y', '') + bbl.append(BlackBoxFunctionModel_Variable('z', '')) + bbl.append(var=BlackBoxFunctionModel_Variable('u', '')) + self.assertRaises( + ValueError, bbl.append, *(BlackBoxFunctionModel_Variable('u', ''),) + ) + self.assertRaises(ValueError, bbl.append, *('badvar',)) + self.assertRaises(ValueError, bbl.append, *(2.222,)) + + self.assertRaises(ValueError, bbl.append, *('bv', '', ''), **{'units': 'm'}) + self.assertRaises(ValueError, bbl.append, **{'units': 'm', 'description': 'hi'}) + self.assertRaises(ValueError, bbl.append, **{'name': 'x', 'units': ''}) + self.assertRaises(ValueError, bbl.append, *('bv', '', '', 0, 'extra')) xv = bbl['x'] xv2 = bbl[0] - self.assertRaises(ValueError,bbl.__getitem__,*( 2.22, )) - + self.assertRaises(ValueError, bbl.__getitem__, *(2.22,)) def test_edi_blackbox_someexceptions(self): "Tests some of the exceptions in the black box model class" import numpy as np from pyomo.environ import units from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + from pyomo.contrib.edi.blackBoxFunctionModel import ( + BlackBoxFunctionModel_Variable, + TypeCheckedList, + BBList, + BlackBoxFunctionModel, + ) bb = BlackBoxFunctionModel() - bb.inputVariables_optimization = [1,2,3] + bb.inputVariables_optimization = [1, 2, 3] # bb.set_input_values(np.array([1,2,3])) - self.assertRaises(ValueError,bb.input_names,*( )) + self.assertRaises(ValueError, bb.input_names, *()) # self.assertRaises(ValueError,bb.fillCache,*( )) - bb = BlackBoxFunctionModel() - bb.outputVariables_optimization = [1,2,3] - self.assertRaises(ValueError,bb.output_names,*( )) - + bb.outputVariables_optimization = [1, 2, 3] + self.assertRaises(ValueError, bb.output_names, *()) def test_edi_blackbox_etc_1(self): "Tests a black box assertion issue" - from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel - bbfm = BlackBoxFunctionModel() - self.assertRaises(AttributeError,bbfm.BlackBox,()) + from pyomo.contrib.edi.blackBoxFunctionModel import ( + BlackBoxFunctionModel_Variable, + TypeCheckedList, + BBList, + BlackBoxFunctionModel, + ) + bbfm = BlackBoxFunctionModel() + self.assertRaises(AttributeError, bbfm.BlackBox, ()) def test_edi_blackbox_etc_2(self): "Tests a black box assertion issue" @@ -148,53 +162,56 @@ def test_edi_blackbox_etc_2(self): from pyomo.environ import units import pyomo.environ as pyo from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + from pyomo.contrib.edi.blackBoxFunctionModel import ( + BlackBoxFunctionModel_Variable, + TypeCheckedList, + BBList, + BlackBoxFunctionModel, + ) f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'y variable') - z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'Output var') - f.Objective( x + y ) + x = f.Variable(name='x', guess=1.0, units='m', description='x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='y variable') + z = f.Variable(name='z', guess=1.0, units='m^2', description='Output var') + f.Objective(x + y) + class UnitCircle(BlackBoxFunctionModel): def __init__(self): super().__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append( name = 'x', - units = 'ft' , - description = 'The x variable' ) - self.inputs.append( name = 'y', - units = 'ft' , - description = 'The y variable' ) - self.outputs.append(name = 'z', - units = 'ft**2', - description = 'Output variable' ) + self.inputs.append(name='x', units='ft', description='The x variable') + self.inputs.append(name='y', units='ft', description='The y variable') + self.outputs.append( + name='z', units='ft**2', description='Output variable' + ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, x, y): # The actual function that does things + + def BlackBox(self, x, y): # The actual function that does things # Converts to correct units then casts to float - x = pyo.value(units.convert(x,self.inputs[0].units)) - y = pyo.value(units.convert(y,self.inputs[1].units)) - z = x**2 + y**2 # Compute z - dzdx = 2*x # Compute dz/dx - dzdy = 2*y # Compute dz/dy + x = pyo.value(units.convert(x, self.inputs[0].units)) + y = pyo.value(units.convert(y, self.inputs[1].units)) + z = x**2 + y**2 # Compute z + dzdx = 2 * x # Compute dz/dx + dzdy = 2 * y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + f.ConstraintList([z <= 1 * units.m**2, [z, '==', [x, y], UnitCircle()]]) - f.ConstraintList( - [ - z <= 1*units.m**2 , - [ z, '==', [x,y], UnitCircle() ] , - ] + f.__dict__['constraint_2'].get_external_model().set_input_values( + np.array([2.0, 2.0]) ) - - f.__dict__['constraint_2'].get_external_model().set_input_values(np.array([2.0, 2.0])) - f.__dict__['constraint_2'].get_external_model().inputVariables_optimization = [1,2] + f.__dict__['constraint_2'].get_external_model().inputVariables_optimization = [ + 1, + 2, + ] # f.__dict__['constraint_2'].get_external_model().fillCache() - self.assertRaises(ValueError,f.__dict__['constraint_2'].get_external_model().fillCache,*()) - + self.assertRaises( + ValueError, f.__dict__['constraint_2'].get_external_model().fillCache, *() + ) def test_edi_blackbox_etc_3(self): "Tests a black box assertion issue" @@ -202,215 +219,239 @@ def test_edi_blackbox_etc_3(self): from pyomo.environ import units import pyomo.environ as pyo from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + from pyomo.contrib.edi.blackBoxFunctionModel import ( + BlackBoxFunctionModel_Variable, + TypeCheckedList, + BBList, + BlackBoxFunctionModel, + ) f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'y variable') - z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'Output var') - f.Objective( x + y ) + x = f.Variable(name='x', guess=1.0, units='m', description='x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='y variable') + z = f.Variable(name='z', guess=1.0, units='m^2', description='Output var') + f.Objective(x + y) + class UnitCircle(BlackBoxFunctionModel): def __init__(self): super().__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append( name = 'x', - units = 'ft' , - description = 'The x variable' ) - self.inputs.append( name = 'y', - units = 'ft' , - description = 'The y variable' ) - self.outputs.append(name = 'z', - units = 'ft**2', - description = 'Output variable' ) + self.inputs.append(name='x', units='ft', description='The x variable') + self.inputs.append(name='y', units='ft', description='The y variable') + self.outputs.append( + name='z', units='ft**2', description='Output variable' + ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, x, y): # The actual function that does things + + def BlackBox(self, x, y): # The actual function that does things # Converts to correct units then casts to float - x = pyo.value(units.convert(x,self.inputs[0].units)) - y = pyo.value(units.convert(y,self.inputs[1].units)) - z = x**2 + y**2 # Compute z - dzdx = 2*x # Compute dz/dx - dzdy = 2*y # Compute dz/dy + x = pyo.value(units.convert(x, self.inputs[0].units)) + y = pyo.value(units.convert(y, self.inputs[1].units)) + z = x**2 + y**2 # Compute z + dzdx = 2 * x # Compute dz/dx + dzdy = 2 * y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return ['err'], [[dzdx, dzdy]] # return z, grad(z), hess(z)... + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return ['err'], [[dzdx, dzdy]] # return z, grad(z), hess(z)... + f.ConstraintList([z <= 1 * units.m**2, [z, '==', [x, y], UnitCircle()]]) - f.ConstraintList( - [ - z <= 1*units.m**2 , - [ z, '==', [x,y], UnitCircle() ] , - ] + self.assertRaises( + ValueError, f.__dict__['constraint_2'].get_external_model().fillCache, *() ) - self.assertRaises(ValueError,f.__dict__['constraint_2'].get_external_model().fillCache,*()) - def test_edi_blackbox_etc_4(self): "Tests a black box assertion issue" import numpy as np from pyomo.environ import units import pyomo.environ as pyo from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + from pyomo.contrib.edi.blackBoxFunctionModel import ( + BlackBoxFunctionModel_Variable, + TypeCheckedList, + BBList, + BlackBoxFunctionModel, + ) f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'y variable') - z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'Output var') - f.Objective( x + y ) + x = f.Variable(name='x', guess=1.0, units='m', description='x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='y variable') + z = f.Variable(name='z', guess=1.0, units='m^2', description='Output var') + f.Objective(x + y) + class UnitCircle(BlackBoxFunctionModel): def __init__(self): super().__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append( name = 'x', - units = 'ft' , - description = 'The x variable' ) - self.inputs.append( name = 'y', - units = 'ft' , - description = 'The y variable' ) - self.outputs.append(name = 'z', - units = 'ft**2', - description = 'Output variable' ) + self.inputs.append(name='x', units='ft', description='The x variable') + self.inputs.append(name='y', units='ft', description='The y variable') + self.outputs.append( + name='z', units='ft**2', description='Output variable' + ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, x, y): # The actual function that does things + + def BlackBox(self, x, y): # The actual function that does things # Converts to correct units then casts to float - x = pyo.value(units.convert(x,self.inputs[0].units)) - y = pyo.value(units.convert(y,self.inputs[1].units)) - z = x**2 + y**2 # Compute z - dzdx = 2*x # Compute dz/dx - dzdy = 2*y # Compute dz/dy + x = pyo.value(units.convert(x, self.inputs[0].units)) + y = pyo.value(units.convert(y, self.inputs[1].units)) + z = x**2 + y**2 # Compute z + dzdx = 2 * x # Compute dz/dx + dzdy = 2 * y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + f.ConstraintList([z <= 1 * units.m**2, [z, '==', [x, y], UnitCircle()]]) - f.ConstraintList( - [ - z <= 1*units.m**2 , - [ z, '==', [x,y], UnitCircle() ] , - ] + f.__dict__['constraint_2'].get_external_model().set_input_values( + np.array([2.0, 2.0]) ) - - f.__dict__['constraint_2'].get_external_model().set_input_values(np.array([2.0, 2.0])) - f.__dict__['constraint_2'].get_external_model().outputVariables_optimization = [1] + f.__dict__['constraint_2'].get_external_model().outputVariables_optimization = [ + 1 + ] # f.__dict__['constraint_2'].get_external_model().fillCache() - self.assertRaises(ValueError,f.__dict__['constraint_2'].get_external_model().fillCache,*()) - + self.assertRaises( + ValueError, f.__dict__['constraint_2'].get_external_model().fillCache, *() + ) def test_edi_blackbox_example_1(self): "Tests a black box example construction" from pyomo.environ import units import pyomo.environ as pyo from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + from pyomo.contrib.edi.blackBoxFunctionModel import ( + BlackBoxFunctionModel_Variable, + TypeCheckedList, + BBList, + BlackBoxFunctionModel, + ) f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'y variable') - z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'Output var') - f.Objective( x + y ) + x = f.Variable(name='x', guess=1.0, units='m', description='x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='y variable') + z = f.Variable(name='z', guess=1.0, units='m^2', description='Output var') + f.Objective(x + y) + class UnitCircle(BlackBoxFunctionModel): def __init__(self): super().__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append( name = 'x', - units = 'ft' , - description = 'The x variable' ) - self.inputs.append( name = 'y', - units = 'ft' , - description = 'The y variable' ) - self.outputs.append(name = 'z', - units = 'ft**2', - description = 'Output variable' ) + self.inputs.append(name='x', units='ft', description='The x variable') + self.inputs.append(name='y', units='ft', description='The y variable') + self.outputs.append( + name='z', units='ft**2', description='Output variable' + ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, x, y): # The actual function that does things + + def BlackBox(self, x, y): # The actual function that does things # Converts to correct units then casts to float - x = pyo.value(units.convert(x,self.inputs[0].units)) - y = pyo.value(units.convert(y,self.inputs[1].units)) - z = x**2 + y**2 # Compute z - dzdx = 2*x # Compute dz/dx - dzdy = 2*y # Compute dz/dy + x = pyo.value(units.convert(x, self.inputs[0].units)) + y = pyo.value(units.convert(y, self.inputs[1].units)) + z = x**2 + y**2 # Compute z + dzdx = 2 * x # Compute dz/dx + dzdy = 2 * y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + f.ConstraintList([z <= 1 * units.m**2, [z, '==', [x, y], UnitCircle()]]) - f.ConstraintList( - [ - z <= 1*units.m**2 , - [ z, '==', [x,y], UnitCircle() ] , - ] + f.__dict__['constraint_2'].get_external_model().set_input_values( + np.array([2.0, 2.0]) + ) + opt = f.__dict__['constraint_2'].get_external_model().evaluate_outputs() + jac = ( + f.__dict__['constraint_2'] + .get_external_model() + .evaluate_jacobian_outputs() + .todense() ) - f.__dict__['constraint_2'].get_external_model().set_input_values(np.array([2.0, 2.0])) - opt = f.__dict__['constraint_2'].get_external_model().evaluate_outputs() - jac = f.__dict__['constraint_2'].get_external_model().evaluate_jacobian_outputs().todense() - - self.assertAlmostEqual(opt[0],8) - self.assertAlmostEqual(jac[0,0],4) - self.assertAlmostEqual(jac[0,1],4) + self.assertAlmostEqual(opt[0], 8) + self.assertAlmostEqual(jac[0, 0], 4) + self.assertAlmostEqual(jac[0, 1], 4) - sm = f.__dict__['constraint_2'].get_external_model().summary + sm = f.__dict__['constraint_2'].get_external_model().summary e_print = f.__dict__['constraint_2'].get_external_model().__repr__() - def test_edi_blackbox_example_2(self): "Tests a black box example construction" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable', size=3) - y = f.Variable(name = 'y', guess = 1.0, units = 'm**2' , description = 'The y variable', size=3) - f.Objective( y[0] + y[1] + y[2] ) + x = f.Variable( + name='x', guess=1.0, units='m', description='The x variable', size=3 + ) + y = f.Variable( + name='y', guess=1.0, units='m**2', description='The y variable', size=3 + ) + f.Objective(y[0] + y[1] + y[2]) + class Parabola(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super(Parabola, self).__init__() self.description = 'This model evaluates the function: y = x**2' - self.inputs.append(name = 'x', size = 3, units = 'ft' , description = 'The x variable') - self.outputs.append(name = 'y', size = 3, units = 'ft**2' , description = 'The y variable') + self.inputs.append( + name='x', size=3, units='ft', description='The x variable' + ) + self.outputs.append( + name='y', size=3, units='ft**2', description='The y variable' + ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, *args, **kwargs):# The actual function that does things - runCases, returnMode, remainingKwargs = self.parseInputs(*args, **kwargs) - + def BlackBox(self, *args, **kwargs): # The actual function that does things + runCases, returnMode, remainingKwargs = self.parseInputs( + *args, **kwargs + ) + x = self.sanitizeInputs(runCases[0]['x']) x = np.array([pyo.value(xval) for xval in x], dtype=np.float64) - y = x**2 # Compute y - dydx = 2*x # Compute dy/dx - + y = x**2 # Compute y + dydx = 2 * x # Compute dy/dx + y = y * units.ft**2 dydx = np.diag(dydx) - dydx = dydx * units.ft # units.ft**2 / units.ft - - return y, [dydx] # return z, grad(z), hess(z)... - f.ConstraintList([ {'outputs':y, 'operators':'==', 'inputs':x, 'black_box':Parabola() },]) + dydx = dydx * units.ft # units.ft**2 / units.ft + + return y, [dydx] # return z, grad(z), hess(z)... + + f.ConstraintList( + [{'outputs': y, 'operators': '==', 'inputs': x, 'black_box': Parabola()}] + ) - f.__dict__['constraint_1'].get_external_model().set_input_values(np.ones(3)*2) + f.__dict__['constraint_1'].get_external_model().set_input_values(np.ones(3) * 2) opt = f.__dict__['constraint_1'].get_external_model().evaluate_outputs() - jac = f.__dict__['constraint_1'].get_external_model().evaluate_jacobian_outputs().todense() - - self.assertAlmostEqual(opt[0],4) - self.assertAlmostEqual(opt[1],4) - self.assertAlmostEqual(opt[2],4) - self.assertAlmostEqual(jac[0,0],4) - self.assertAlmostEqual(jac[0,1],0) - self.assertAlmostEqual(jac[0,2],0) - self.assertAlmostEqual(jac[0,1],0) - self.assertAlmostEqual(jac[1,1],4) - self.assertAlmostEqual(jac[2,1],0) - self.assertAlmostEqual(jac[2,0],0) - self.assertAlmostEqual(jac[2,1],0) - self.assertAlmostEqual(jac[2,2],4) - - sm = f.__dict__['constraint_1'].get_external_model().summary + jac = ( + f.__dict__['constraint_1'] + .get_external_model() + .evaluate_jacobian_outputs() + .todense() + ) + + self.assertAlmostEqual(opt[0], 4) + self.assertAlmostEqual(opt[1], 4) + self.assertAlmostEqual(opt[2], 4) + self.assertAlmostEqual(jac[0, 0], 4) + self.assertAlmostEqual(jac[0, 1], 0) + self.assertAlmostEqual(jac[0, 2], 0) + self.assertAlmostEqual(jac[0, 1], 0) + self.assertAlmostEqual(jac[1, 1], 4) + self.assertAlmostEqual(jac[2, 1], 0) + self.assertAlmostEqual(jac[2, 0], 0) + self.assertAlmostEqual(jac[2, 1], 0) + self.assertAlmostEqual(jac[2, 2], 4) + + sm = f.__dict__['constraint_1'].get_external_model().summary e_print = f.__dict__['constraint_1'].get_external_model().__repr__() def test_edi_blackbox_example_3(self): @@ -419,109 +460,133 @@ def test_edi_blackbox_example_3(self): from pyomo.environ import units import pyomo.environ as pyo from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + from pyomo.contrib.edi.blackBoxFunctionModel import ( + BlackBoxFunctionModel_Variable, + TypeCheckedList, + BBList, + BlackBoxFunctionModel, + ) f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = '' , description = 'x variable', size = 3) - y = f.Variable(name = 'y', guess = 1.0, units = '' , description = 'y variable') - f.Objective( y ) + x = f.Variable(name='x', guess=1.0, units='', description='x variable', size=3) + y = f.Variable(name='y', guess=1.0, units='', description='y variable') + f.Objective(y) + class Norm_2(BlackBoxFunctionModel): def __init__(self): super().__init__() self.description = 'This model evaluates the two norm' - self.inputs.append( name = 'x', - units = '' , - description = 'The x variable' , - size = 3) - self.outputs.append(name = 'y', - units = '' , - description = 'The y variable' ) + self.inputs.append( + name='x', units='', description='The x variable', size=3 + ) + self.outputs.append(name='y', units='', description='The y variable') self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, *args, **kwargs):# The actual function that does things - runCases, returnMode, remainingKwargs = self.parseInputs(*args, **kwargs) + + def BlackBox(self, *args, **kwargs): # The actual function that does things + runCases, returnMode, remainingKwargs = self.parseInputs( + *args, **kwargs + ) x = self.sanitizeInputs(runCases[0]['x']) x = np.array([pyo.value(xval) for xval in x], dtype=np.float64) - y = x[0]**2 + x[1]**2 + x[2]**2 # Compute y - dydx0 = 2*x[0] # Compute dy/dx0 - dydx1 = 2*x[1] # Compute dy/dx1 - dydx2 = 2*x[2] # Compute dy/dx2 + y = x[0] ** 2 + x[1] ** 2 + x[2] ** 2 # Compute y + dydx0 = 2 * x[0] # Compute dy/dx0 + dydx1 = 2 * x[1] # Compute dy/dx1 + dydx2 = 2 * x[2] # Compute dy/dx2 y = y * units.dimensionless - dydx = np.array([dydx0,dydx1,dydx2]) * units.dimensionless + dydx = np.array([dydx0, dydx1, dydx2]) * units.dimensionless # dydx0 = dydx0 * units.dimensionless # dydx1 = dydx1 * units.dimensionless # dydx2 = dydx2 * units.dimensionless - return y, [dydx] # return z, grad(z), hess(z)... - - f.ConstraintList([ {'outputs':y, 'operators':'==', 'inputs':x, 'black_box':Norm_2() },]) + return y, [dydx] # return z, grad(z), hess(z)... + + f.ConstraintList( + [{'outputs': y, 'operators': '==', 'inputs': x, 'black_box': Norm_2()}] + ) - f.__dict__['constraint_1'].get_external_model().set_input_values(np.ones(3)*2) + f.__dict__['constraint_1'].get_external_model().set_input_values(np.ones(3) * 2) opt = f.__dict__['constraint_1'].get_external_model().evaluate_outputs() - jac = f.__dict__['constraint_1'].get_external_model().evaluate_jacobian_outputs().todense() + jac = ( + f.__dict__['constraint_1'] + .get_external_model() + .evaluate_jacobian_outputs() + .todense() + ) - self.assertAlmostEqual(opt[0],12) - self.assertAlmostEqual(jac[0,0],4) - self.assertAlmostEqual(jac[0,1],4) - self.assertAlmostEqual(jac[0,2],4) + self.assertAlmostEqual(opt[0], 12) + self.assertAlmostEqual(jac[0, 0], 4) + self.assertAlmostEqual(jac[0, 1], 4) + self.assertAlmostEqual(jac[0, 2], 4) - sm = f.__dict__['constraint_1'].get_external_model().summary + sm = f.__dict__['constraint_1'].get_external_model().summary e_print = f.__dict__['constraint_1'].get_external_model().__repr__() - def test_edi_blackbox_example_4(self): "Tests a black box example construction" import numpy as np from pyomo.environ import units import pyomo.environ as pyo from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + from pyomo.contrib.edi.blackBoxFunctionModel import ( + BlackBoxFunctionModel_Variable, + TypeCheckedList, + BBList, + BlackBoxFunctionModel, + ) f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = '' , description = 'x variable') - y = f.Variable(name = 'y', guess = 1.0, units = '' , description = 'y variable', size=3) - f.Objective( y[0]**2 + y[1]**2 + y[2]**2 ) + x = f.Variable(name='x', guess=1.0, units='', description='x variable') + y = f.Variable(name='y', guess=1.0, units='', description='y variable', size=3) + f.Objective(y[0] ** 2 + y[1] ** 2 + y[2] ** 2) + class VectorCast(BlackBoxFunctionModel): def __init__(self): super().__init__() self.description = 'This model evaluates the two norm' - self.inputs.append( name = 'x', - units = '' , - description = 'The x variable' ) - self.outputs.append(name = 'y', - units = '' , - description = 'The y variable' , - size = 3) + self.inputs.append(name='x', units='', description='The x variable') + self.outputs.append( + name='y', units='', description='The y variable', size=3 + ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, *args, **kwargs):# The actual function that does things - runCases, returnMode, remainingKwargs = self.parseInputs(*args, **kwargs) + def BlackBox(self, *args, **kwargs): # The actual function that does things + runCases, returnMode, remainingKwargs = self.parseInputs( + *args, **kwargs + ) x = pyo.value(self.sanitizeInputs(runCases[0]['x'])) - y = np.array([x,x,x]) * units.dimensionless - dydx = np.array([1.0,1.0,1.0]) * units.dimensionless + y = np.array([x, x, x]) * units.dimensionless + dydx = np.array([1.0, 1.0, 1.0]) * units.dimensionless - return y, [dydx] # return z, grad(z), hess(z)... + return y, [dydx] # return z, grad(z), hess(z)... - f.ConstraintList([ {'outputs':y, 'operators':'==', 'inputs':x, 'black_box':VectorCast() },]) + f.ConstraintList( + [{'outputs': y, 'operators': '==', 'inputs': x, 'black_box': VectorCast()}] + ) - f.__dict__['constraint_1'].get_external_model().set_input_values(np.ones(3)*2) + f.__dict__['constraint_1'].get_external_model().set_input_values(np.ones(3) * 2) opt = f.__dict__['constraint_1'].get_external_model().evaluate_outputs() - jac = f.__dict__['constraint_1'].get_external_model().evaluate_jacobian_outputs().todense() + jac = ( + f.__dict__['constraint_1'] + .get_external_model() + .evaluate_jacobian_outputs() + .todense() + ) - self.assertAlmostEqual(opt[0],2.0) - self.assertAlmostEqual(opt[1],2.0) - self.assertAlmostEqual(opt[2],2.0) - self.assertAlmostEqual(jac[0,0],1.0) - self.assertAlmostEqual(jac[1,0],1.0) - self.assertAlmostEqual(jac[2,0],1.0) + self.assertAlmostEqual(opt[0], 2.0) + self.assertAlmostEqual(opt[1], 2.0) + self.assertAlmostEqual(opt[2], 2.0) + self.assertAlmostEqual(jac[0, 0], 1.0) + self.assertAlmostEqual(jac[1, 0], 1.0) + self.assertAlmostEqual(jac[2, 0], 1.0) - sm = f.__dict__['constraint_1'].get_external_model().summary + sm = f.__dict__['constraint_1'].get_external_model().summary e_print = f.__dict__['constraint_1'].get_external_model().__repr__() def test_edi_blackbox_badexample_1(self): @@ -530,35 +595,41 @@ def test_edi_blackbox_badexample_1(self): from pyomo.environ import units import pyomo.environ as pyo from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + from pyomo.contrib.edi.blackBoxFunctionModel import ( + BlackBoxFunctionModel_Variable, + TypeCheckedList, + BBList, + BlackBoxFunctionModel, + ) f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = '' , description = 'x variable', size = 3) - y = f.Variable(name = 'y', guess = 1.0, units = '' , description = 'y variable') - f.Objective( y ) + x = f.Variable(name='x', guess=1.0, units='', description='x variable', size=3) + y = f.Variable(name='y', guess=1.0, units='', description='y variable') + f.Objective(y) + class Norm_2(BlackBoxFunctionModel): def __init__(self): super().__init__() self.description = 'This model evaluates the two norm' - self.inputs.append( name = 'x', - units = '' , - description = 'The x variable' , - size = 3) - self.outputs.append(name = 'y', - units = '' , - description = 'The y variable' ) + self.inputs.append( + name='x', units='', description='The x variable', size=3 + ) + self.outputs.append(name='y', units='', description='The y variable') self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, *args, **kwargs):# The actual function that does things - runCases, returnMode, remainingKwargs = self.parseInputs(*args, **kwargs) + + def BlackBox(self, *args, **kwargs): # The actual function that does things + runCases, returnMode, remainingKwargs = self.parseInputs( + *args, **kwargs + ) x = self.sanitizeInputs(runCases[0]['x']) x = np.array([pyo.value(xval) for xval in x], dtype=np.float64) - y = x[0]**2 + x[1]**2 + x[2]**2 # Compute y - dydx0 = 2*x[0] # Compute dy/dx0 - dydx1 = 2*x[1] # Compute dy/dx1 - dydx2 = 2*x[2] # Compute dy/dx2 + y = x[0] ** 2 + x[1] ** 2 + x[2] ** 2 # Compute y + dydx0 = 2 * x[0] # Compute dy/dx0 + dydx1 = 2 * x[1] # Compute dy/dx1 + dydx2 = 2 * x[2] # Compute dy/dx2 y = y * units.dimensionless # dydx = np.array([dydx0,dydx1,dydx2]) * units.dimensionless @@ -566,13 +637,18 @@ def BlackBox(self, *args, **kwargs):# The actual function that does things dydx1 = dydx1 * units.dimensionless dydx2 = dydx2 * units.dimensionless - return y, [[dydx0, dydx1, dydx2]] # return z, grad(z), hess(z)... - - f.ConstraintList([ {'outputs':y, 'operators':'==', 'inputs':x, 'black_box':Norm_2() },]) + return y, [[dydx0, dydx1, dydx2]] # return z, grad(z), hess(z)... - f.__dict__['constraint_1'].get_external_model().set_input_values(np.ones(3)*2) - self.assertRaises(ValueError,f.__dict__['constraint_1'].get_external_model().evaluate_outputs,*()) + f.ConstraintList( + [{'outputs': y, 'operators': '==', 'inputs': x, 'black_box': Norm_2()}] + ) + f.__dict__['constraint_1'].get_external_model().set_input_values(np.ones(3) * 2) + self.assertRaises( + ValueError, + f.__dict__['constraint_1'].get_external_model().evaluate_outputs, + *() + ) def test_edi_blackbox_smallfunctions(self): "Tests the more general value and convert functions" @@ -580,22 +656,31 @@ def test_edi_blackbox_smallfunctions(self): from pyomo.environ import units import pyomo.environ as pyo from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + from pyomo.contrib.edi.blackBoxFunctionModel import ( + BlackBoxFunctionModel_Variable, + TypeCheckedList, + BBList, + BlackBoxFunctionModel, + ) bb = BlackBoxFunctionModel() - t1 = bb.convert(2*units.m, units.ft) - t2 = bb.convert(np.ones([2,2])*units.m, units.ft) - self.assertRaises(ValueError,bb.convert,*('err',units.ft)) - - t3 = bb.pyomo_value(2*units.m) - t3 = bb.pyomo_value(np.ones([2,2])*units.m) - self.assertRaises(ValueError,bb.pyomo_value,*(np.ones([2,2]),)) - - bb.sizeCheck([2,2],np.ones([2,2])*units.m) - self.assertRaises(ValueError,bb.sizeCheck,*(2,3*units.m,)) - self.assertRaises(ValueError,bb.sizeCheck,*([10,10,10],np.ones([2,2])*units.m,)) - self.assertRaises(ValueError,bb.sizeCheck,*([10,10],np.ones([2,2])*units.m,)) - self.assertRaises(ValueError,bb.sizeCheck,*([10,10],[],)) + t1 = bb.convert(2 * units.m, units.ft) + t2 = bb.convert(np.ones([2, 2]) * units.m, units.ft) + self.assertRaises(ValueError, bb.convert, *('err', units.ft)) + + t3 = bb.pyomo_value(2 * units.m) + t3 = bb.pyomo_value(np.ones([2, 2]) * units.m) + self.assertRaises(ValueError, bb.pyomo_value, *(np.ones([2, 2]),)) + + bb.sizeCheck([2, 2], np.ones([2, 2]) * units.m) + self.assertRaises(ValueError, bb.sizeCheck, *(2, 3 * units.m)) + self.assertRaises( + ValueError, bb.sizeCheck, *([10, 10, 10], np.ones([2, 2]) * units.m) + ) + self.assertRaises( + ValueError, bb.sizeCheck, *([10, 10], np.ones([2, 2]) * units.m) + ) + self.assertRaises(ValueError, bb.sizeCheck, *([10, 10], [])) def test_edi_blackbox_bare_example_1(self): "Tests a black box example construction without an optimization problem" @@ -609,34 +694,38 @@ def __init__(self): # Set up all the attributes by calling Model.__init__ super().__init__() - #Setup Inputs - self.inputs.append( 'x', '', 'Independent Variable') + # Setup Inputs + self.inputs.append('x', '', 'Independent Variable') - #Setup Outputs - self.outputs.append( 'y', '', 'Dependent Variable') + # Setup Outputs + self.outputs.append('y', '', 'Dependent Variable') - #Simple model description - self.description = 'This model evaluates the function: max([-6*x-6, x**4-3*x**2])' + # Simple model description + self.description = ( + 'This model evaluates the function: max([-6*x-6, x**4-3*x**2])' + ) self.availableDerivative = 1 - #standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) + # standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) def BlackBox(self, *args, **kwargs): runCases, returnMode, extras = self.parseInputs(*args, **kwargs) - x = np.array([ pyo.value(runCases[i]['x']) for i in range(0,len(runCases)) ]) - - y = np.maximum(-6*x-6, x**4-3*x**2) - dydx = 4*x**3 - 6*x - ddy_ddx = 12*x**2 - 6 - gradientSwitch = -6*x-6 > x**4-3*x**2 + x = np.array( + [pyo.value(runCases[i]['x']) for i in range(0, len(runCases))] + ) + + y = np.maximum(-6 * x - 6, x**4 - 3 * x**2) + dydx = 4 * x**3 - 6 * x + ddy_ddx = 12 * x**2 - 6 + gradientSwitch = -6 * x - 6 > x**4 - 3 * x**2 dydx[gradientSwitch] = -6 ddy_ddx[gradientSwitch] = 0 - y = [ yval * units.dimensionless for yval in y] - dydx = [dydx[i] * units.dimensionless for i in range(0,len(dydx))] + y = [yval * units.dimensionless for yval in y] + dydx = [dydx[i] * units.dimensionless for i in range(0, len(dydx))] if returnMode < 0: - returnMode = -1*(returnMode + 1) + returnMode = -1 * (returnMode + 1) if returnMode == 0: return y[0] if returnMode == 1: @@ -644,43 +733,44 @@ def BlackBox(self, *args, **kwargs): else: if returnMode == 0: opt = [] - for i in range(0,len(y)): - opt.append([ y[i] ]) + for i in range(0, len(y)): + opt.append([y[i]]) return opt if returnMode == 1: opt = [] - for i in range(0,len(y)): - opt.append([ [y[i]], [ [[dydx[i]]] ] ]) + for i in range(0, len(y)): + opt.append([[y[i]], [[[dydx[i]]]]]) return opt s = SignomialTest() - ivals = [[x] for x in np.linspace(-2,2,11)] + ivals = [[x] for x in np.linspace(-2, 2, 11)] # How the black box may be called using EDI - bbo = s.BlackBox(**{'x':0.5}) - bbo = s.BlackBox({'x':0.5}) - bbo = s.BlackBox(**{'x':0.5, 'optn':True}) + bbo = s.BlackBox(**{'x': 0.5}) + bbo = s.BlackBox({'x': 0.5}) + bbo = s.BlackBox(**{'x': 0.5, 'optn': True}) # Additional options available with parseInputs bbo = s.BlackBox(*[0.5], **{'optn1': True, 'optn2': False}) - bbo = s.BlackBox(*[0.5,True], **{'optn': False}) - bbo = s.BlackBox({'x':[x for x in np.linspace(-2,2,11)]}) - bbo = s.BlackBox([{'x':x} for x in np.linspace(-2,2,11)]) - bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)]) - bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)], True, optn=False) - bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)], optn1=True, optn2=False) + bbo = s.BlackBox(*[0.5, True], **{'optn': False}) + bbo = s.BlackBox({'x': [x for x in np.linspace(-2, 2, 11)]}) + bbo = s.BlackBox([{'x': x} for x in np.linspace(-2, 2, 11)]) + bbo = s.BlackBox([[x] for x in np.linspace(-2, 2, 11)]) + bbo = s.BlackBox([[x] for x in np.linspace(-2, 2, 11)], True, optn=False) + bbo = s.BlackBox([[x] for x in np.linspace(-2, 2, 11)], optn1=True, optn2=False) sm = s.summary - self.assertRaises(ValueError,s.BlackBox,*( [ [[1,2],2,3] , [1,2,3]] )) - self.assertRaises(ValueError,s.BlackBox,*( [ [np.ones(2),2,3] , [1,2,3]] )) - - self.assertRaises(ValueError,s.sanitizeInputs,*( )) - self.assertRaises(ValueError,s.sanitizeInputs,*( 1 , 2, 3 )) - self.assertRaises(ValueError,s.sanitizeInputs,**{'z':2.0*units.dimensionless}) - self.assertRaises(ValueError,s.sanitizeInputs,*(2.0*units.ft,)) - self.assertRaises(NotImplementedError,s.checkOutputs,*()) + self.assertRaises(ValueError, s.BlackBox, *([[[1, 2], 2, 3], [1, 2, 3]])) + self.assertRaises(ValueError, s.BlackBox, *([[np.ones(2), 2, 3], [1, 2, 3]])) + self.assertRaises(ValueError, s.sanitizeInputs, *()) + self.assertRaises(ValueError, s.sanitizeInputs, *(1, 2, 3)) + self.assertRaises( + ValueError, s.sanitizeInputs, **{'z': 2.0 * units.dimensionless} + ) + self.assertRaises(ValueError, s.sanitizeInputs, *(2.0 * units.ft,)) + self.assertRaises(NotImplementedError, s.checkOutputs, *()) def test_edi_blackbox_bare_example_2(self): "Tests a black box example construction without an optimization problem" @@ -695,37 +785,39 @@ def __init__(self): # Set up all the attributes by calling Model.__init__ super().__init__() - #Setup Inputs - self.inputs.append( 'x', '', 'Independent Variable', size = [2,2]) + # Setup Inputs + self.inputs.append('x', '', 'Independent Variable', size=[2, 2]) - #Setup Outputs - self.outputs.append( 'y', '', 'Dependent Variable', size = [2,2]) + # Setup Outputs + self.outputs.append('y', '', 'Dependent Variable', size=[2, 2]) - #Simple model description + # Simple model description self.description = 'This model is a pass through)' self.availableDerivative = 1 - #standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) + # standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) def BlackBox(self, *args, **kwargs): runCases, returnMode, extras = self.parseInputs(*args, **kwargs) - x = [ self.pyomo_value(runCases[i]['x']) for i in range(0,len(runCases)) ] + x = [ + self.pyomo_value(runCases[i]['x']) for i in range(0, len(runCases)) + ] - y = [] + y = [] dydx = [] - + for xval in x: - y.append(xval*units.dimensionless) - dydx_temp = np.zeros([2,2,2,2]) - dydx_temp[0,0,0,0] = 1.0 - dydx_temp[0,1,0,1] = 1.0 - dydx_temp[1,0,1,0] = 1.0 - dydx_temp[1,1,1,1] = 1.0 - - dydx.append(dydx_temp*units.dimensionless) - + y.append(xval * units.dimensionless) + dydx_temp = np.zeros([2, 2, 2, 2]) + dydx_temp[0, 0, 0, 0] = 1.0 + dydx_temp[0, 1, 0, 1] = 1.0 + dydx_temp[1, 0, 1, 0] = 1.0 + dydx_temp[1, 1, 1, 1] = 1.0 + + dydx.append(dydx_temp * units.dimensionless) + if returnMode < 0: - returnMode = -1*(returnMode + 1) + returnMode = -1 * (returnMode + 1) if returnMode == 0: return y[0] if returnMode == 1: @@ -733,38 +825,41 @@ def BlackBox(self, *args, **kwargs): else: if returnMode == 0: opt = [] - for i in range(0,len(y)): - opt.append([ y[i] ]) + for i in range(0, len(y)): + opt.append([y[i]]) return opt if returnMode == 1: opt = [] - for i in range(0,len(y)): - opt.append([ [y[i]], [ [[dydx[i]]] ] ]) + for i in range(0, len(y)): + opt.append([[y[i]], [[[dydx[i]]]]]) return opt bb = PassThrough() - ivals = [[np.eye(2)*units.dimensionless], [np.ones([2,2])*units.dimensionless], [np.zeros([2,2])*units.dimensionless] ] + ivals = [ + [np.eye(2) * units.dimensionless], + [np.ones([2, 2]) * units.dimensionless], + [np.zeros([2, 2]) * units.dimensionless], + ] - xv = np.eye(2)*units.dimensionless + xv = np.eye(2) * units.dimensionless # How the black box may be called using EDI - bbo = bb.BlackBox(**{'x':xv}) - bbo = bb.BlackBox({'x':xv}) - bbo = bb.BlackBox(**{'x':xv, 'optn':True}) + bbo = bb.BlackBox(**{'x': xv}) + bbo = bb.BlackBox({'x': xv}) + bbo = bb.BlackBox(**{'x': xv, 'optn': True}) # # Additional options available with parseInputs bbo = bb.BlackBox(*[xv], **{'optn1': True, 'optn2': False}) - bbo = bb.BlackBox(*[xv,True], **{'optn': False}) - bbo = bb.BlackBox({'x':[x[0] for x in ivals]}) - bbo = bb.BlackBox([{'x':x[0]} for x in ivals]) - bbo = bb.BlackBox([ [x[0]] for x in ivals]) - bbo = bb.BlackBox([ [x[0]] for x in ivals], True, optn=False) - bbo = bb.BlackBox([ [x[0]] for x in ivals], optn1=True, optn2=False) + bbo = bb.BlackBox(*[xv, True], **{'optn': False}) + bbo = bb.BlackBox({'x': [x[0] for x in ivals]}) + bbo = bb.BlackBox([{'x': x[0]} for x in ivals]) + bbo = bb.BlackBox([[x[0]] for x in ivals]) + bbo = bb.BlackBox([[x[0]] for x in ivals], True, optn=False) + bbo = bb.BlackBox([[x[0]] for x in ivals], optn1=True, optn2=False) sm = bb.summary - self.assertRaises(ValueError,bb.sanitizeInputs,*(np.ones([2,2])*units.ft,)) - + self.assertRaises(ValueError, bb.sanitizeInputs, *(np.ones([2, 2]) * units.ft,)) def test_edi_blackbox_bare_example_3(self): "Tests a black box example construction" @@ -772,59 +867,91 @@ def test_edi_blackbox_bare_example_3(self): from pyomo.environ import units import pyomo.environ as pyo from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + from pyomo.contrib.edi.blackBoxFunctionModel import ( + BlackBoxFunctionModel_Variable, + TypeCheckedList, + BBList, + BlackBoxFunctionModel, + ) class Norm_2(BlackBoxFunctionModel): def __init__(self): super().__init__() self.description = 'This model evaluates the two norm' - self.inputs.append( name = 'x', - units = '' , - description = 'The x variable' , - size = 3) - self.inputs.append( name = 'y', - units = '' , - description = 'The y variable' , - size = 2) - self.outputs.append(name = 'z', - units = '' , - description = 'The z variable') + self.inputs.append( + name='x', units='', description='The x variable', size=3 + ) + self.inputs.append( + name='y', units='', description='The y variable', size=2 + ) + self.outputs.append(name='z', units='', description='The z variable') self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, *args, **kwargs):# The actual function that does things - runCases, returnMode, remainingKwargs = self.parseInputs(*args, **kwargs) - x = [ rc['x'] for rc in runCases][0] + def BlackBox(self, *args, **kwargs): # The actual function that does things + runCases, returnMode, remainingKwargs = self.parseInputs( + *args, **kwargs + ) + + x = [rc['x'] for rc in runCases][0] x = np.array([self.pyomo_value(xval) for xval in x], dtype=np.float64) - y = [ rc['y'] for rc in runCases][0] + y = [rc['y'] for rc in runCases][0] y = np.array([self.pyomo_value(yval) for yval in y], dtype=np.float64) - z = x[0]**2 + x[1]**2 + x[2]**2 + y[0]**2 + y[1]**2 - dzdx0 = 2*x[0] # Compute dy/dx0 - dzdx1 = 2*x[1] # Compute dy/dx1 - dzdx2 = 2*x[2] # Compute dy/dx2 - dzdy0 = 2*y[0] - dzdy1 = 2*y[1] + z = x[0] ** 2 + x[1] ** 2 + x[2] ** 2 + y[0] ** 2 + y[1] ** 2 + dzdx0 = 2 * x[0] # Compute dy/dx0 + dzdx1 = 2 * x[1] # Compute dy/dx1 + dzdx2 = 2 * x[2] # Compute dy/dx2 + dzdy0 = 2 * y[0] + dzdy1 = 2 * y[1] z = z * units.dimensionless - dz = np.array([dzdx0,dzdx1,dzdx2,dzdy0,dzdy1]) * units.dimensionless + dz = np.array([dzdx0, dzdx1, dzdx2, dzdy0, dzdy1]) * units.dimensionless # dydx0 = dydx0 * units.dimensionless # dydx1 = dydx1 * units.dimensionless # dydx2 = dydx2 * units.dimensionless - return z, [dz] # return z, grad(z), hess(z)... + return z, [dz] # return z, grad(z), hess(z)... bb = Norm_2() - bbo = bb.BlackBox({'x':np.array([0,0,0])*units.dimensionless, 'y':np.array([0,0])*units.dimensionless}) - bbo = bb.BlackBox(np.array([0,0,0])*units.dimensionless, y=np.array([0,0])*units.dimensionless) - - self.assertRaises(ValueError,bb.BlackBox,*( {'er':np.array([0,0,0])*units.dimensionless, 'y':np.array([0,0])*units.dimensionless}, )) - self.assertRaises(ValueError,bb.BlackBox,*( 'err', )) - self.assertRaises(ValueError,bb.BlackBox,*( np.array([0,0,0])*units.dimensionless, np.array([0,0])*units.dimensionless ), **{'x':'err too many'}) - self.assertRaises(ValueError,bb.BlackBox,*( np.array([0,0,0])*units.dimensionless, ), **{'notY':np.array([0,0])*units.dimensionless}) - + bbo = bb.BlackBox( + { + 'x': np.array([0, 0, 0]) * units.dimensionless, + 'y': np.array([0, 0]) * units.dimensionless, + } + ) + bbo = bb.BlackBox( + np.array([0, 0, 0]) * units.dimensionless, + y=np.array([0, 0]) * units.dimensionless, + ) + self.assertRaises( + ValueError, + bb.BlackBox, + *( + { + 'er': np.array([0, 0, 0]) * units.dimensionless, + 'y': np.array([0, 0]) * units.dimensionless, + }, + ) + ) + self.assertRaises(ValueError, bb.BlackBox, *('err',)) + self.assertRaises( + ValueError, + bb.BlackBox, + *( + np.array([0, 0, 0]) * units.dimensionless, + np.array([0, 0]) * units.dimensionless, + ), + **{'x': 'err too many'} + ) + self.assertRaises( + ValueError, + bb.BlackBox, + *(np.array([0, 0, 0]) * units.dimensionless,), + **{'notY': np.array([0, 0]) * units.dimensionless} + ) if __name__ == '__main__': diff --git a/pyomo/contrib/edi/tests/test_examples.py b/pyomo/contrib/edi/tests/test_examples.py index be34a25f0ba..9af15b75fdb 100644 --- a/pyomo/contrib/edi/tests/test_examples.py +++ b/pyomo/contrib/edi/tests/test_examples.py @@ -19,39 +19,36 @@ import pyomo.environ as pyo from pyomo.common.dependencies import attempt_import -from pyomo.core.base.units_container import ( - pint_available, -) +from pyomo.core.base.units_container import pint_available import importlib - testIndex = 0 + + class EDIExamples(unittest.TestCase): def test_edi_example_placeholder(self): "A placeholder" pass + def create_new(filename): def t_function(self): importName = filename[0:-3] # filename = ".."+filename try: - importlib.import_module("pyomo.contrib.edi.examples."+importName) + importlib.import_module("pyomo.contrib.edi.examples." + importName) except: - self.fail("This example is failing: %s"%(filename)) - return t_function + self.fail("This example is failing: %s" % (filename)) + return t_function -pythonFileList = [ - "readme_example.py", - "aircraft_gp.py" -] +pythonFileList = ["readme_example.py", "aircraft_gp.py"] for filename in pythonFileList: - testName = 'test_DocumentationExample_%d'%(testIndex) + testName = 'test_DocumentationExample_%d' % (testIndex) testIndex += 1 t_Function = create_new(filename) if pint_available: @@ -60,20 +57,3 @@ def t_function(self): if __name__ == '__main__': unittest.main() - - - - - - - - - - - - - - - - - diff --git a/pyomo/contrib/edi/tests/test_formulation.py b/pyomo/contrib/edi/tests/test_formulation.py index 3d6db035da2..e7de6cac038 100644 --- a/pyomo/contrib/edi/tests/test_formulation.py +++ b/pyomo/contrib/edi/tests/test_formulation.py @@ -17,12 +17,11 @@ import pyomo.common.unittest as unittest import pyomo.environ as pyo + # from pyomo.opt import check_optimal_termination from pyomo.common.dependencies import attempt_import -from pyomo.core.base.units_container import ( - pint_available, -) +from pyomo.core.base.units_container import pint_available # np, numpy_available = attempt_import( # 'numpy', 'edi requires numpy' @@ -37,28 +36,29 @@ # 'edi.formulation tests require numpy' # ) + @unittest.skipIf(not pint_available, 'Testing units requires pint') class TestEDIFormulation(unittest.TestCase): def test_edi_formulation_init(self): "Tests that a formulation initializes to the correct type and has proper data" from pyomo.environ import ConcreteModel from pyomo.contrib.edi import Formulation + f = Formulation() self.assertIsInstance(f, Formulation) self.assertIsInstance(f, ConcreteModel) - self.assertEqual(f._objective_counter , 0 ) - self.assertEqual(f._constraint_counter , 0 ) - self.assertEqual(f._variable_keys , [] ) - self.assertEqual(f._constant_keys , [] ) - self.assertEqual(f._objective_keys , [] ) - self.assertEqual(f._runtimeObjective_keys , [] ) - self.assertEqual(f._objective_keys , [] ) - self.assertEqual(f._runtimeConstraint_keys , [] ) - self.assertEqual(f._constraint_keys , [] ) - self.assertEqual(f._allConstraint_keys , [] ) - + self.assertEqual(f._objective_counter, 0) + self.assertEqual(f._constraint_counter, 0) + self.assertEqual(f._variable_keys, []) + self.assertEqual(f._constant_keys, []) + self.assertEqual(f._objective_keys, []) + self.assertEqual(f._runtimeObjective_keys, []) + self.assertEqual(f._objective_keys, []) + self.assertEqual(f._runtimeConstraint_keys, []) + self.assertEqual(f._constraint_keys, []) + self.assertEqual(f._allConstraint_keys, []) def test_edi_formulation_variable(self): "Tests the variable constructor in edi.formulation" @@ -68,30 +68,186 @@ def test_edi_formulation_variable(self): f = Formulation() - x1 = f.Variable(name = 'x1', guess = 1.0, units = 'm' , description = 'The x variable', size = None, bounds=None, domain=None ) - self.assertRaises(RuntimeError,f.Variable,*('x1', 1.0, 'm')) + x1 = f.Variable( + name='x1', + guess=1.0, + units='m', + description='The x variable', + size=None, + bounds=None, + domain=None, + ) + self.assertRaises(RuntimeError, f.Variable, *('x1', 1.0, 'm')) x2 = f.Variable('x2', 1.0, 'm') - x3 = f.Variable('x3', 1.0, 'm' , 'The x variable', None, None, None ) - x4 = f.Variable(name = 'x4', guess = 1.0, units = 'm' , description = 'The x variable', size = None, bounds=None, domain=PositiveReals ) - self.assertRaises(RuntimeError, f.Variable, **{'name':'x5', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': None, 'bounds':None, 'domain':"error" }) - - x6 = f.Variable(name = 'x6', guess = 1.0, units = 'm' , description = 'The x variable', size = 0, bounds=None, domain=None ) - x7 = f.Variable(name = 'x7', guess = 1.0, units = 'm' , description = 'The x variable', size = 5, bounds=None, domain=None ) - self.assertRaises(ValueError, f.Variable, **{'name':'x8', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': 'error', 'bounds':None, 'domain':None }) - x9 = f.Variable(name = 'x9', guess = 1.0, units = 'm' , description = 'The x variable', size = [2,2], bounds=None, domain=None ) - self.assertRaises(ValueError, f.Variable, **{'name':'x10', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': ['2','2'], 'bounds':None, 'domain':None }) - self.assertRaises(ValueError, f.Variable, **{'name':'x11', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': [2,1], 'bounds':None, 'domain':None }) - - x12 = f.Variable(name = 'x12', guess = 1.0, units = 'm' , description = 'The x variable', size = None, bounds=[-10,10], domain=None ) - self.assertRaises(ValueError, f.Variable, **{'name':'x13', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': None, 'bounds':[10,-10], 'domain':None }) - self.assertRaises(ValueError, f.Variable, **{'name':'x14', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': None, 'bounds':["-10","10"], 'domain':None }) - self.assertRaises(ValueError, f.Variable, **{'name':'x15', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': None, 'bounds':[1,2,3], 'domain':None }) - self.assertRaises(ValueError, f.Variable, **{'name':'x16', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': None, 'bounds':"error", 'domain':None }) - self.assertRaises(ValueError, f.Variable, **{'name':'x17', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': None, 'bounds':[0,"10"], 'domain':None }) + x3 = f.Variable('x3', 1.0, 'm', 'The x variable', None, None, None) + x4 = f.Variable( + name='x4', + guess=1.0, + units='m', + description='The x variable', + size=None, + bounds=None, + domain=PositiveReals, + ) + self.assertRaises( + RuntimeError, + f.Variable, + **{ + 'name': 'x5', + 'guess': 1.0, + 'units': 'm', + 'description': 'The x variable', + 'size': None, + 'bounds': None, + 'domain': "error", + } + ) + + x6 = f.Variable( + name='x6', + guess=1.0, + units='m', + description='The x variable', + size=0, + bounds=None, + domain=None, + ) + x7 = f.Variable( + name='x7', + guess=1.0, + units='m', + description='The x variable', + size=5, + bounds=None, + domain=None, + ) + self.assertRaises( + ValueError, + f.Variable, + **{ + 'name': 'x8', + 'guess': 1.0, + 'units': 'm', + 'description': 'The x variable', + 'size': 'error', + 'bounds': None, + 'domain': None, + } + ) + x9 = f.Variable( + name='x9', + guess=1.0, + units='m', + description='The x variable', + size=[2, 2], + bounds=None, + domain=None, + ) + self.assertRaises( + ValueError, + f.Variable, + **{ + 'name': 'x10', + 'guess': 1.0, + 'units': 'm', + 'description': 'The x variable', + 'size': ['2', '2'], + 'bounds': None, + 'domain': None, + } + ) + self.assertRaises( + ValueError, + f.Variable, + **{ + 'name': 'x11', + 'guess': 1.0, + 'units': 'm', + 'description': 'The x variable', + 'size': [2, 1], + 'bounds': None, + 'domain': None, + } + ) + + x12 = f.Variable( + name='x12', + guess=1.0, + units='m', + description='The x variable', + size=None, + bounds=[-10, 10], + domain=None, + ) + self.assertRaises( + ValueError, + f.Variable, + **{ + 'name': 'x13', + 'guess': 1.0, + 'units': 'm', + 'description': 'The x variable', + 'size': None, + 'bounds': [10, -10], + 'domain': None, + } + ) + self.assertRaises( + ValueError, + f.Variable, + **{ + 'name': 'x14', + 'guess': 1.0, + 'units': 'm', + 'description': 'The x variable', + 'size': None, + 'bounds': ["-10", "10"], + 'domain': None, + } + ) + self.assertRaises( + ValueError, + f.Variable, + **{ + 'name': 'x15', + 'guess': 1.0, + 'units': 'm', + 'description': 'The x variable', + 'size': None, + 'bounds': [1, 2, 3], + 'domain': None, + } + ) + self.assertRaises( + ValueError, + f.Variable, + **{ + 'name': 'x16', + 'guess': 1.0, + 'units': 'm', + 'description': 'The x variable', + 'size': None, + 'bounds': "error", + 'domain': None, + } + ) + self.assertRaises( + ValueError, + f.Variable, + **{ + 'name': 'x17', + 'guess': 1.0, + 'units': 'm', + 'description': 'The x variable', + 'size': None, + 'bounds': [0, "10"], + 'domain': None, + } + ) # verifies alternate unit construction x18 = f.Variable('x18', 1.0, pyo.units.m) - self.assertRaises(AttributeError, f.Variable, *('x19', 1.0, 'strng')) + self.assertRaises(AttributeError, f.Variable, *('x19', 1.0, 'string')) def test_edi_formulation_constant(self): "Tests the constant constructor in edi.formulation" @@ -100,20 +256,98 @@ def test_edi_formulation_constant(self): f = Formulation() - c1 = f.Constant(name = 'c1', value = 1.0, units = 'm', description = 'A constant c', size = None, within = None) - self.assertRaises(RuntimeError,f.Constant,*('c1', 1.0, 'm')) + c1 = f.Constant( + name='c1', + value=1.0, + units='m', + description='A constant c', + size=None, + within=None, + ) + self.assertRaises(RuntimeError, f.Constant, *('c1', 1.0, 'm')) c2 = f.Constant('c2', 1.0, 'm') c3 = f.Constant('c3', 1.0, 'm', 'A constant c', None, None) - c4 = f.Constant(name = 'c4', value = 1.0, units = 'm' , description = 'A constant c', size = None, within=PositiveReals ) - self.assertRaises(RuntimeError, f.Constant, **{'name':'c5', 'value':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': None, 'within':"error" }) - - c6 = f.Constant(name = 'c6', value = 1.0, units = 'm' , description = 'A constant c', size = 0, within=None ) - c7 = f.Constant(name = 'c7', value = 1.0, units = 'm' , description = 'A constant c', size = 5, within=None ) - self.assertRaises(ValueError, f.Constant, **{'name':'c8', 'value':1.0 , 'units' :'m' , 'description': 'A constant c', 'size': 'error', 'within':None }) - c9 = f.Constant(name = 'c9', value = 1.0, units = 'm' , description = 'A constant c', size = [2,2], within=None ) - self.assertRaises(ValueError, f.Constant, **{'name':'c10', 'value':1.0 , 'units' :'m' , 'description': 'A constant c', 'size': ['2','2'], 'within':None }) - self.assertRaises(ValueError, f.Constant, **{'name':'c11', 'value':1.0 , 'units' :'m' , 'description': 'A constant c', 'size': [2,1], 'within':None }) + c4 = f.Constant( + name='c4', + value=1.0, + units='m', + description='A constant c', + size=None, + within=PositiveReals, + ) + self.assertRaises( + RuntimeError, + f.Constant, + **{ + 'name': 'c5', + 'value': 1.0, + 'units': 'm', + 'description': 'The x variable', + 'size': None, + 'within': "error", + } + ) + c6 = f.Constant( + name='c6', + value=1.0, + units='m', + description='A constant c', + size=0, + within=None, + ) + c7 = f.Constant( + name='c7', + value=1.0, + units='m', + description='A constant c', + size=5, + within=None, + ) + self.assertRaises( + ValueError, + f.Constant, + **{ + 'name': 'c8', + 'value': 1.0, + 'units': 'm', + 'description': 'A constant c', + 'size': 'error', + 'within': None, + } + ) + c9 = f.Constant( + name='c9', + value=1.0, + units='m', + description='A constant c', + size=[2, 2], + within=None, + ) + self.assertRaises( + ValueError, + f.Constant, + **{ + 'name': 'c10', + 'value': 1.0, + 'units': 'm', + 'description': 'A constant c', + 'size': ['2', '2'], + 'within': None, + } + ) + self.assertRaises( + ValueError, + f.Constant, + **{ + 'name': 'c11', + 'value': 1.0, + 'units': 'm', + 'description': 'A constant c', + 'size': [2, 1], + 'within': None, + } + ) def test_edi_formulation_objective(self): "Tests the objective constructor in edi.formulation" @@ -122,9 +356,9 @@ def test_edi_formulation_objective(self): from pyomo.contrib.edi import Formulation f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - f.Objective( x + y ) + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + f.Objective(x + y) def test_edi_formulation_runtimeobjective(self): "Tests the runtime objective constructor in edi.formulation" @@ -136,317 +370,436 @@ def test_edi_formulation_constraint(self): import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - f.Objective(x+y) - f.Constraint(x+y <= 1.0*units.m) + f = Formulation() + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + f.Objective(x + y) + f.Constraint(x + y <= 1.0 * units.m) def test_edi_formulation_runtimeconstraint_tuple(self): "Tests the runtime constraint constructor in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The unit circle output') - c = f.Constant(name = 'c', value = 1.0, units = '', description = 'A constant c', size = 2) - f.Objective( x + y ) + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + z = f.Variable( + name='z', guess=1.0, units='m^2', description='The unit circle output' + ) + c = f.Constant( + name='c', value=1.0, units='', description='A constant c', size=2 + ) + f.Objective(x + y) + class UnitCircle(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super(UnitCircle, self).__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append(name = 'x', units = 'ft' , description = 'The x variable') - self.inputs.append(name = 'y', units = 'ft' , description = 'The y variable') - self.outputs.append(name = 'z', units = 'ft**2', description = 'Resultant of the unit circle evaluation') + self.inputs.append(name='x', units='ft', description='The x variable') + self.inputs.append(name='y', units='ft', description='The y variable') + self.outputs.append( + name='z', + units='ft**2', + description='Resultant of the unit circle evaluation', + ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, x, y): # The actual function that does things - x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float - y = pyo.value(units.convert(y,self.inputs[1].units)) # Converts to correct units then casts to float - z = x**2 + y**2 # Compute z - dzdx = 2*x # Compute dz/dx - dzdy = 2*y # Compute dz/dy + + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value( + units.convert(x, self.inputs[0].units) + ) # Converts to correct units then casts to float + y = pyo.value( + units.convert(y, self.inputs[1].units) + ) # Converts to correct units then casts to float + z = x**2 + y**2 # Compute z + dzdx = 2 * x # Compute dz/dx + dzdy = 2 * y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... - f.Constraint( z <= 1*units.m**2 ) + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + + f.Constraint(z <= 1 * units.m**2) - f.RuntimeConstraint( *( z, '==', [x,y], UnitCircle() ) ) + f.RuntimeConstraint(*(z, '==', [x, y], UnitCircle())) def test_edi_formulation_runtimeconstraint_list(self): "Tests the runtime constraint constructor in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The unit circle output') - c = f.Constant(name = 'c', value = 1.0, units = '', description = 'A constant c', size = 2) - f.Objective( x + y ) + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + z = f.Variable( + name='z', guess=1.0, units='m^2', description='The unit circle output' + ) + c = f.Constant( + name='c', value=1.0, units='', description='A constant c', size=2 + ) + f.Objective(x + y) + class UnitCircle(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super(UnitCircle, self).__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append(name = 'x', units = 'ft' , description = 'The x variable') - self.inputs.append(name = 'y', units = 'ft' , description = 'The y variable') - self.outputs.append(name = 'z', units = 'ft**2', description = 'Resultant of the unit circle evaluation') + self.inputs.append(name='x', units='ft', description='The x variable') + self.inputs.append(name='y', units='ft', description='The y variable') + self.outputs.append( + name='z', + units='ft**2', + description='Resultant of the unit circle evaluation', + ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, x, y): # The actual function that does things - x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float - y = pyo.value(units.convert(y,self.inputs[1].units)) # Converts to correct units then casts to float - z = x**2 + y**2 # Compute z - dzdx = 2*x # Compute dz/dx - dzdy = 2*y # Compute dz/dy + + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value( + units.convert(x, self.inputs[0].units) + ) # Converts to correct units then casts to float + y = pyo.value( + units.convert(y, self.inputs[1].units) + ) # Converts to correct units then casts to float + z = x**2 + y**2 # Compute z + dzdx = 2 * x # Compute dz/dx + dzdy = 2 * y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... - f.Constraint( z <= 1*units.m**2 ) - - f.RuntimeConstraint( *[ [z], ['=='], [x,y], UnitCircle() ] ) + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + f.Constraint(z <= 1 * units.m**2) + + f.RuntimeConstraint(*[[z], ['=='], [x, y], UnitCircle()]) def test_edi_formulation_runtimeconstraint_dict(self): "Tests the runtime constraint constructor in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The unit circle output') - c = f.Constant(name = 'c', value = 1.0, units = '', description = 'A constant c', size = 2) - f.Objective( x + y ) + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + z = f.Variable( + name='z', guess=1.0, units='m^2', description='The unit circle output' + ) + c = f.Constant( + name='c', value=1.0, units='', description='A constant c', size=2 + ) + f.Objective(x + y) + class UnitCircle(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super(UnitCircle, self).__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append(name = 'x', units = 'ft' , description = 'The x variable') - self.inputs.append(name = 'y', units = 'ft' , description = 'The y variable') - self.outputs.append(name = 'z', units = 'ft**2', description = 'Resultant of the unit circle evaluation') + self.inputs.append(name='x', units='ft', description='The x variable') + self.inputs.append(name='y', units='ft', description='The y variable') + self.outputs.append( + name='z', + units='ft**2', + description='Resultant of the unit circle evaluation', + ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, x, y): # The actual function that does things - x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float - y = pyo.value(units.convert(y,self.inputs[1].units)) # Converts to correct units then casts to float - z = x**2 + y**2 # Compute z - dzdx = 2*x # Compute dz/dx - dzdy = 2*y # Compute dz/dy + + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value( + units.convert(x, self.inputs[0].units) + ) # Converts to correct units then casts to float + y = pyo.value( + units.convert(y, self.inputs[1].units) + ) # Converts to correct units then casts to float + z = x**2 + y**2 # Compute z + dzdx = 2 * x # Compute dz/dx + dzdy = 2 * y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... - f.Constraint( z <= 1*units.m**2 ) - - f.RuntimeConstraint( **{ 'outputs':z, 'operators':'==', 'inputs':[x,y], 'black_box':UnitCircle() } ) - + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + + f.Constraint(z <= 1 * units.m**2) + + f.RuntimeConstraint( + **{ + 'outputs': z, + 'operators': '==', + 'inputs': [x, y], + 'black_box': UnitCircle(), + } + ) def test_edi_formulation_constraintlist_1(self): "Tests the constraint list constructor in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The unit circle output') - c = f.Constant(name = 'c', value = 1.0, units = '', description = 'A constant c', size = 2) - f.Objective( x + y ) + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + z = f.Variable( + name='z', guess=1.0, units='m^2', description='The unit circle output' + ) + c = f.Constant( + name='c', value=1.0, units='', description='A constant c', size=2 + ) + f.Objective(x + y) class UnitCircle(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super(UnitCircle, self).__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append(name = 'x', units = 'ft' , description = 'The x variable') - self.inputs.append(name = 'y', units = 'ft' , description = 'The y variable') - self.outputs.append(name = 'z', units = 'ft**2', description = 'Resultant of the unit circle evaluation') + self.inputs.append(name='x', units='ft', description='The x variable') + self.inputs.append(name='y', units='ft', description='The y variable') + self.outputs.append( + name='z', + units='ft**2', + description='Resultant of the unit circle evaluation', + ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, x, y): # The actual function that does things - x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float - y = pyo.value(units.convert(y,self.inputs[1].units)) # Converts to correct units then casts to float - z = x**2 + y**2 # Compute z - dzdx = 2*x # Compute dz/dx - dzdy = 2*y # Compute dz/dy + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value( + units.convert(x, self.inputs[0].units) + ) # Converts to correct units then casts to float + y = pyo.value( + units.convert(y, self.inputs[1].units) + ) # Converts to correct units then casts to float + z = x**2 + y**2 # Compute z + dzdx = 2 * x # Compute dz/dx + dzdy = 2 * y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... - f.ConstraintList( - [ - (z, '==', [x,y], UnitCircle() ) , - z <= 1*units.m**2 - ] - ) - - cl = f.get_constraints() + f.ConstraintList([(z, '==', [x, y], UnitCircle()), z <= 1 * units.m**2]) - self.assertTrue(len(cl)==2) + cl = f.get_constraints() + self.assertTrue(len(cl) == 2) def test_edi_formulation_constraintlist_2(self): "Tests the constraint list constructor in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm**2' , description = 'The y variable') - f.Objective( y ) + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m**2', description='The y variable') + f.Objective(y) class Parabola(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super(Parabola, self).__init__() self.description = 'This model evaluates the function: y = x**2' - self.inputs.append(name = 'x', units = 'ft' , description = 'The x variable') - self.outputs.append(name = 'y', units = 'ft**2' , description = 'The y variable') + self.inputs.append(name='x', units='ft', description='The x variable') + self.outputs.append( + name='y', units='ft**2', description='The y variable' + ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, x): # The actual function that does things - x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float - y = x**2 # Compute y - dydx = 2*x # Compute dy/dx + def BlackBox(self, x): # The actual function that does things + x = pyo.value( + units.convert(x, self.inputs[0].units) + ) # Converts to correct units then casts to float + y = x**2 # Compute y + dydx = 2 * x # Compute dy/dx y *= units.ft**2 - dydx *= units.ft # units.ft**2 / units.ft - return y, [dydx] # return z, grad(z), hess(z)... + dydx *= units.ft # units.ft**2 / units.ft + return y, [dydx] # return z, grad(z), hess(z)... f.ConstraintList( - [ - {'outputs':y, 'operators':'==', 'inputs':x, 'black_box':Parabola() }, - ] + [{'outputs': y, 'operators': '==', 'inputs': x, 'black_box': Parabola()}] ) - cl = f.get_constraints() - - self.assertTrue(len(cl)==1) + cl = f.get_constraints() + self.assertTrue(len(cl) == 1) def test_edi_formulation_constraintlist_3(self): "Tests the constraint list constructor in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable', size=3) - y = f.Variable(name = 'y', guess = 1.0, units = 'm**2' , description = 'The y variable', size=3) - f.Objective( y[0] + y[1] + y[2] ) + x = f.Variable( + name='x', guess=1.0, units='m', description='The x variable', size=3 + ) + y = f.Variable( + name='y', guess=1.0, units='m**2', description='The y variable', size=3 + ) + f.Objective(y[0] + y[1] + y[2]) + class Parabola(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super(Parabola, self).__init__() self.description = 'This model evaluates the function: y = x**2' - self.inputs.append(name = 'x', size = 3, units = 'ft' , description = 'The x variable') - self.outputs.append(name = 'y', size = 3, units = 'ft**2' , description = 'The y variable') + self.inputs.append( + name='x', size=3, units='ft', description='The x variable' + ) + self.outputs.append( + name='y', size=3, units='ft**2', description='The y variable' + ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(*args, **kwargs):# The actual function that does things + def BlackBox(*args, **kwargs): # The actual function that does things args = list(args) self = args.pop(0) - runCases, returnMode, remainingKwargs = self.parseInputs(*args, **kwargs) - + runCases, returnMode, remainingKwargs = self.parseInputs( + *args, **kwargs + ) + x = self.sanitizeInputs(runCases[0]['x']) x = np.array([pyo.value(xval) for xval in x], dtype=np.float64) - y = x**2 # Compute y - dydx = 2*x # Compute dy/dx - + y = x**2 # Compute y + dydx = 2 * x # Compute dy/dx + y = y * units.ft**2 dydx = np.diag(dydx) - dydx = dydx * units.ft # units.ft**2 / units.ft - - return y, [dydx] # return z, grad(z), hess(z)... - f.ConstraintList([ {'outputs':y, 'operators':'==', 'inputs':x, 'black_box':Parabola() },]) - cl = f.get_constraints() - self.assertTrue(len(cl)==1) + dydx = dydx * units.ft # units.ft**2 / units.ft + return y, [dydx] # return z, grad(z), hess(z)... + + f.ConstraintList( + [{'outputs': y, 'operators': '==', 'inputs': x, 'black_box': Parabola()}] + ) + cl = f.get_constraints() + self.assertTrue(len(cl) == 1) def test_edi_formulation_runtimeconstraint_exceptions(self): "Tests the runtime constraint constructor in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The unit circle output') - c = f.Constant(name = 'c', value = 1.0, units = '', description = 'A constant c', size = 2) - f.Objective( x + y ) + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + z = f.Variable( + name='z', guess=1.0, units='m^2', description='The unit circle output' + ) + c = f.Constant( + name='c', value=1.0, units='', description='A constant c', size=2 + ) + f.Objective(x + y) + class UnitCircle(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super(UnitCircle, self).__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append(name = 'x', units = 'ft' , description = 'The x variable') - self.inputs.append(name = 'y', units = 'ft' , description = 'The y variable') - self.outputs.append(name = 'z', units = 'ft**2', description = 'Resultant of the unit circle evaluation') + self.inputs.append(name='x', units='ft', description='The x variable') + self.inputs.append(name='y', units='ft', description='The y variable') + self.outputs.append( + name='z', + units='ft**2', + description='Resultant of the unit circle evaluation', + ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, x, y): # The actual function that does things - x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float - y = pyo.value(units.convert(y,self.inputs[1].units)) # Converts to correct units then casts to float - z = x**2 + y**2 # Compute z - dzdx = 2*x # Compute dz/dx - dzdy = 2*y # Compute dz/dy + + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value( + units.convert(x, self.inputs[0].units) + ) # Converts to correct units then casts to float + y = pyo.value( + units.convert(y, self.inputs[1].units) + ) # Converts to correct units then casts to float + z = x**2 + y**2 # Compute z + dzdx = 2 * x # Compute dz/dx + dzdy = 2 * y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... - f.Constraint( z <= 1*units.m**2 ) + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + + f.Constraint(z <= 1 * units.m**2) # flaggs the input or as bad before assigning the incorrect black box - self.assertRaises(ValueError, f.RuntimeConstraint, *(z, '==', 1.0 , UnitCircle() )) - self.assertRaises(ValueError, f.RuntimeConstraint, *(1.0, '==', [x,y] , UnitCircle() )) + self.assertRaises( + ValueError, f.RuntimeConstraint, *(z, '==', 1.0, UnitCircle()) + ) + self.assertRaises( + ValueError, f.RuntimeConstraint, *(1.0, '==', [x, y], UnitCircle()) + ) - self.assertRaises(ValueError, f.RuntimeConstraint, *(z, '==', [1.0,y], UnitCircle() )) - self.assertRaises(ValueError, f.RuntimeConstraint, *(z, 1.0 , [x ,y], UnitCircle() )) - self.assertRaises(ValueError, f.RuntimeConstraint, *(z, '=', [x ,y], UnitCircle() )) + self.assertRaises( + ValueError, f.RuntimeConstraint, *(z, '==', [1.0, y], UnitCircle()) + ) + self.assertRaises( + ValueError, f.RuntimeConstraint, *(z, 1.0, [x, y], UnitCircle()) + ) + self.assertRaises( + ValueError, f.RuntimeConstraint, *(z, '=', [x, y], UnitCircle()) + ) def test_edi_formulation_getvariables(self): "Tests the get_variables function in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') vrs = f.get_variables() - self.assertListEqual(vrs, [x,y]) + self.assertListEqual(vrs, [x, y]) def test_edi_formulation_getconstants(self): "Tests the get_constants function in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - c1 = f.Constant(name = 'c1', value = 1.0, units = 'm', description = 'A constant c1', size = None, within = None) - c2 = f.Constant(name = 'c2', value = 1.0, units = 'm', description = 'A constant c2', size = None, within = None) + f = Formulation() + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + + c1 = f.Constant( + name='c1', + value=1.0, + units='m', + description='A constant c1', + size=None, + within=None, + ) + c2 = f.Constant( + name='c2', + value=1.0, + units='m', + description='A constant c2', + size=None, + within=None, + ) csts = f.get_constants() - self.assertListEqual(csts, [c1,c2]) + self.assertListEqual(csts, [c1, c2]) def test_edi_formulation_getobjectives(self): "Tests the get_objectives function in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - f.Objective(x+y) + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + f.Objective(x + y) objList = f.get_objectives() - self.assertTrue(len(objList)==1) + self.assertTrue(len(objList) == 1) # not really sure how to check this, so I wont def test_edi_formulation_getconstraints(self): @@ -466,78 +819,82 @@ def test_edi_formulation_getconstraints(self): # ================= # Declare Variables # ================= - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The unit circle output') + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + z = f.Variable( + name='z', guess=1.0, units='m^2', description='The unit circle output' + ) # ================= # Declare Constants # ================= - c = f.Constant(name = 'c', value = 1.0, units = '', description = 'A constant c', size = 2) + c = f.Constant( + name='c', value=1.0, units='', description='A constant c', size=2 + ) # ===================== # Declare the Objective # ===================== - f.Objective( - c[0]*x + c[1]*y - ) + f.Objective(c[0] * x + c[1] * y) # =================== # Declare a Black Box # =================== class UnitCircle(BlackBoxFunctionModel): - def __init__(self): # The initalization function - - # Initalize the black box model + def __init__(self): # The initialization function + # Initialize the black box model super(UnitCircle, self).__init__() # A brief description of the model self.description = 'This model evaluates the function: z = x**2 + y**2' - + # Declare the black box model inputs - self.inputs.append(name = 'x', units = 'ft' , description = 'The x variable') - self.inputs.append(name = 'y', units = 'ft' , description = 'The y variable') + self.inputs.append(name='x', units='ft', description='The x variable') + self.inputs.append(name='y', units='ft', description='The y variable') # Declare the black box model outputs - self.outputs.append(name = 'z', units = 'ft**2', description = 'Resultant of the unit circle evaluation') + self.outputs.append( + name='z', + units='ft**2', + description='Resultant of the unit circle evaluation', + ) # Declare the maximum available derivative self.availableDerivative = 1 - # Post-initalization setup + # Post-initialization setup self.post_init_setup(len(self.inputs)) - def BlackBox(self, x, y): # The actual function that does things - x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float - y = pyo.value(units.convert(y,self.inputs[1].units)) # Converts to correct units then casts to float + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value( + units.convert(x, self.inputs[0].units) + ) # Converts to correct units then casts to float + y = pyo.value( + units.convert(y, self.inputs[1].units) + ) # Converts to correct units then casts to float - z = x**2 + y**2 # Compute z - dzdx = 2*x # Compute dz/dx - dzdy = 2*y # Compute dz/dy + z = x**2 + y**2 # Compute z + dzdx = 2 * x # Compute dz/dx + dzdy = 2 * y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... # ======================= # Declare the Constraints # ======================= - f.ConstraintList( - [ - (z, '==', [x,y], UnitCircle() ) , - z <= 1*units.m**2 - ] - ) + f.ConstraintList([(z, '==', [x, y], UnitCircle()), z <= 1 * units.m**2]) - cl = f.get_constraints() + cl = f.get_constraints() ecl = f.get_explicitConstraints() rcl = f.get_runtimeConstraints() - self.assertTrue(len(cl)==2) - self.assertTrue(len(ecl)==1) - self.assertTrue(len(rcl)==1) + self.assertTrue(len(cl) == 2) + self.assertTrue(len(ecl) == 1) + self.assertTrue(len(rcl) == 1) def test_edi_formulation_checkunits(self): "Tests the check_units function in edi.formulation" @@ -545,44 +902,28 @@ def test_edi_formulation_checkunits(self): import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') - f.Objective(x+y) - f.Constraint(x+y <= 1.0*units.m) + f.Objective(x + y) + f.Constraint(x + y <= 1.0 * units.m) f.check_units() - f.Constraint(2.0*x+y <= 1.0) - self.assertRaises(pyomo.core.base.units_container.UnitsError, f.check_units, *()) - + f.Constraint(2.0 * x + y <= 1.0) + self.assertRaises( + pyomo.core.base.units_container.UnitsError, f.check_units, *() + ) f2 = Formulation() - u = f2.Variable(name = 'u', guess = 1.0, units = 'm' , description = 'The u variable') - v = f2.Variable(name = 'v', guess = 1.0, units = 'kg' , description = 'The v variable') - f2.Objective(u+v) - self.assertRaises(pyomo.core.base.units_container.UnitsError, f2.check_units, *()) - - - + u = f2.Variable(name='u', guess=1.0, units='m', description='The u variable') + v = f2.Variable(name='v', guess=1.0, units='kg', description='The v variable') + f2.Objective(u + v) + self.assertRaises( + pyomo.core.base.units_container.UnitsError, f2.check_units, *() + ) if __name__ == '__main__': unittest.main() - - - - - - - - - - - - - - - - - From a6cde71628cef0dc7a609d5eb6055b20c1f2d707 Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Thu, 3 Aug 2023 17:10:18 -0600 Subject: [PATCH 27/61] typos in docs --- .../edi/blackboxconstraints.rst | 14 +++++++------- .../contributed_packages/edi/constants.rst | 2 +- .../contributed_packages/edi/constraints.rst | 2 +- .../contributed_packages/edi/variables.rst | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst b/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst index 9cc63b1ae04..b2c31caf03d 100644 --- a/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst +++ b/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst @@ -53,7 +53,7 @@ A simple example is shown below: ) # Set the highest available derivative - # Should be 1 for most cases but defauts to 0 + # Should be 1 for most cases but defaults to 0 self.availableDerivative = 1 def BlackBox(self, x): # The actual function that does things @@ -99,7 +99,7 @@ Next, you must tell the model what its inputs are by appending them to the ``sel :param name: The name of the variable, any valid python string. **Does not** have to match the name in the optimization formulation :type name: str - :param units: The units of the variable. Every entry in a vector variable must have the same units. Entries of '', ' ', '-', 'None', and 'dimensionless' all become units.dimensionless. The units **must** be convertable to the units used in the optimization formulation (ex, meters and feet), but **are not required** to be the same. Because of this, for example, a black-box can be written in imperial units, while an optimization formulation operates in metric. + :param units: The units of the variable. Every entry in a vector variable must have the same units. Entries of '', ' ', '-', 'None', and 'dimensionless' all become units.dimensionless. The units **must** be convertible to the units used in the optimization formulation (ex, meters and feet), but **are not required** to be the same. Because of this, for example, a black-box can be written in imperial units, while an optimization formulation operates in metric. :type units: str or pyomo.core.base.units_container._PyomoUnit :param description: A description of the variable :type description: str @@ -126,7 +126,7 @@ Next, outputs must be added to the model. This is done identically to inputs, h :param name: The name of the variable, any valid python string. **Does not** have to match the name in the optimization formulation :type name: str - :param units: The units of the variable. Every entry in a vector variable must have the same units. Entries of '', ' ', '-', 'None', and 'dimensionless' all become units.dimensionless. The units **must** be convertable to the units used in the optimization formulation (ex, meters and feet), but **are not required** to be the same. Because of this, for example, a black-box can be written in imperial units, while an optimization formulation operates in metric. + :param units: The units of the variable. Every entry in a vector variable must have the same units. Entries of '', ' ', '-', 'None', and 'dimensionless' all become units.dimensionless. The units **must** be convertible to the units used in the optimization formulation (ex, meters and feet), but **are not required** to be the same. Because of this, for example, a black-box can be written in imperial units, while an optimization formulation operates in metric. :type units: str or pyomo.core.base.units_container._PyomoUnit :param description: A description of the variable :type description: str @@ -149,7 +149,7 @@ Finally, the highest available derivative must be set. For models being used in .. code-block:: python # Set the highest available derivative - # Should be 1 for most cases but defauts to 0 + # Should be 1 for most cases but defaults to 0 self.availableDerivative = 1 @@ -456,11 +456,11 @@ Tips ---- * Use the pyomo ``tostr()`` function (``from pyomo.common.formatting import tostr``) to print the results of black-boxes for more meaningful printouts -* Align input and ouptut declarations just as is recommended for optimization variable and constant declarations -* Delcare an input/output all on one line, no matter what the style guides say +* Align input and output declarations just as is recommended for optimization variable and constant declarations +* Declare an input/output all on one line, no matter what the style guides say * This interface is really designed for subject matter experts who are not python users to have a simple, easy path to include their tools into a python based optimization architecture. Try to let them build their own models as a means of fostering trust in the optimization tools * Embrace units. They will save you so many times, it is well worth the minor additional overhead -* Pyomo units work sligtly diffenrently than pint (for those with pint experience), but those differences should be hidden from the model creator for the most part +* Pyomo units work slightly diffenrently than pint (for those with pint experience), but those differences should be hidden from the model creator for the most part * It is common to use this framework to call to a piece of software external to python * See the :doc:`advanced <./advancedruntimeconstraints>` documentation for extra tips and tricks diff --git a/doc/OnlineDocs/contributed_packages/edi/constants.rst b/doc/OnlineDocs/contributed_packages/edi/constants.rst index 10c41aecb1e..48e6b1211b0 100644 --- a/doc/OnlineDocs/contributed_packages/edi/constants.rst +++ b/doc/OnlineDocs/contributed_packages/edi/constants.rst @@ -37,7 +37,7 @@ Constants are constructed by 1) creating an instance of a new parameter in a EDI Relation to Pyomo Param ----------------------- -The fields: name and within, and bounds are directly passed to the pyomo ``Param`` constructor, with some minor checking. The value field is passed to the ``Param`` initalize field. The description field is passed to the doc field in the pyomo ``Param``. Units are passed directly with an additional check. All Constants set the pyomo ``Param`` mutable field to True. +The fields: name and within, and bounds are directly passed to the pyomo ``Param`` constructor, with some minor checking. The value field is passed to the ``Param`` initialize field. The description field is passed to the doc field in the pyomo ``Param``. Units are passed directly with an additional check. All Constants set the pyomo ``Param`` mutable field to True. Non-scalar constants are constructed using pyomo ``Sets``. Sets are constructed to be integer sets that fill the entire interval from lower bound to upper bound, ie a vector constant of length 5 would create a pyomo ``Set`` with valid indices [0,1,2,3,4] with no skips. In this way, non-scalar constatants are slightly less flexible than general non-scalar pyomo ``Param``. diff --git a/doc/OnlineDocs/contributed_packages/edi/constraints.rst b/doc/OnlineDocs/contributed_packages/edi/constraints.rst index 91968bd9549..30236d08ab8 100644 --- a/doc/OnlineDocs/contributed_packages/edi/constraints.rst +++ b/doc/OnlineDocs/contributed_packages/edi/constraints.rst @@ -135,5 +135,5 @@ Tips Known Issues ------------ -* Indexed variables must be broken up using either indicies or a pyomo rule (see `this issue `__) +* Indexed variables must be broken up using either indices or a pyomo rule (see `this issue `__) * Units that are inconsistent, but not the same (ie, meters and feet) will flag as invalid when checking units (see `this issue `__) diff --git a/doc/OnlineDocs/contributed_packages/edi/variables.rst b/doc/OnlineDocs/contributed_packages/edi/variables.rst index 988928b873d..316dbe736dd 100644 --- a/doc/OnlineDocs/contributed_packages/edi/variables.rst +++ b/doc/OnlineDocs/contributed_packages/edi/variables.rst @@ -41,7 +41,7 @@ Variables are constructed by 1) creating an instance of a new variable in a EDI Relation to Pyomo Var --------------------- -The fields: name, domain, and bounds are directly passed to the pyomo ``Var`` constructor, with some minor checking. The guess field is passed to initalize. The description field is passed to the doc field in the pyomo ``Var``. Units are passed directly with an additional check. +The fields: name, domain, and bounds are directly passed to the pyomo ``Var`` constructor, with some minor checking. The guess field is passed to initialize. The description field is passed to the doc field in the pyomo ``Var``. Units are passed directly with an additional check. Non-scalar variables are constructed using pyomo ``Sets``. Sets are constructed to be integer sets that fill the entire interval from lower bound to upper bound, ie a vector variable of length 5 would create a pyomo ``Set`` with valid indices [0,1,2,3,4] with no skips. In this way, non-scalar constatants are slightly less flexible than general non-scalar pyomo ``Params``. From f76f4974bce9f22d8be7742c0a97e98889f5844e Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Tue, 8 Aug 2023 01:06:22 -0600 Subject: [PATCH 28/61] addressing concerns in PR --- .../edi/advancedruntimeconstraints.rst | 100 +- .../edi/blackboxconstraints.rst | 377 ++---- .../contributed_packages/edi/constants.rst | 81 +- .../contributed_packages/edi/constraints.rst | 80 +- .../contributed_packages/edi/examples.rst | 1 - .../contributed_packages/edi/formulation.rst | 321 ++--- .../contributed_packages/edi/index.rst | 4 +- .../contributed_packages/edi/objectives.rst | 121 +- .../contributed_packages/edi/quickstart.rst | 2 - .../contributed_packages/edi/variables.rst | 113 +- doc/OnlineDocs/contributed_packages/index.rst | 2 +- pyomo/contrib/edi/README.md | 16 - pyomo/contrib/edi/blackBoxFunctionModel.py | 640 ++++------ pyomo/contrib/edi/examples/aircraft_gp.py | 95 +- pyomo/contrib/edi/examples/readme_example.py | 72 +- pyomo/contrib/edi/formulation.py | 322 ++--- pyomo/contrib/edi/tests/test_blackbox.py | 1050 ++++++++-------- pyomo/contrib/edi/tests/test_docSnippets.py | 1064 +++++++++++++++++ pyomo/contrib/edi/tests/test_examples.py | 30 +- pyomo/contrib/edi/tests/test_formulation.py | 914 +++++--------- 20 files changed, 2616 insertions(+), 2789 deletions(-) create mode 100644 pyomo/contrib/edi/tests/test_docSnippets.py diff --git a/doc/OnlineDocs/contributed_packages/edi/advancedruntimeconstraints.rst b/doc/OnlineDocs/contributed_packages/edi/advancedruntimeconstraints.rst index ce4d2bc3f77..53a716c2f53 100644 --- a/doc/OnlineDocs/contributed_packages/edi/advancedruntimeconstraints.rst +++ b/doc/OnlineDocs/contributed_packages/edi/advancedruntimeconstraints.rst @@ -30,10 +30,11 @@ The ``parseInputs()`` method enables this batch-like capability in a variety of The function definition is not particularly helpful, so let's dive in a bit. For the typical user, we recommend that the top of all ``BlackBox()`` methods appear as follows: -.. code-block:: python - - def BlackBox(self, *args, **kwargs): - runCases, returnMode, extras = self.parseInputs(*args, **kwargs) +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 12 + :start-after: # BEGIN: AdvancedRTC_Snippet_02 + :end-before: # END: AdvancedRTC_Snippet_02 Essentially, ``parseInputs()`` is a pre-processor that directly takes the inputs of the black-box. The ``parseInputs()`` method will check all of the inputs, ensure that size and units are correct, split into run cases as appropriate, and return a run-cases list that is ready to operate on. @@ -64,81 +65,11 @@ An Example There are many ways this functionality can be used, we provide an example here to get new users started - -.. code-block:: python - - import numpy as np - import pyomo.environ as pyo - from pyomo.environ import units - from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel - - class SignomialTest(BlackBoxFunctionModel): - def __init__(self): - # Set up all the attributes by calling Model.__init__ - super().__init__() - - #Setup Inputs - self.inputs.append( 'x', '', 'Independent Variable') - - #Setup Outputs - self.outputs.append( 'y', '', 'Dependent Variable') - - #Simple model description - self.description = ( 'This model evaluates the '+ - 'function: max([-6*x-6, x**4-3*x**2])' ) - - self.availableDerivative = 1 - - def BlackBox(self, *args, **kwargs): - runCases, returnMode, extras = self.parseInputs(*args, **kwargs) - - x = np.array([ pyo.value(runCases[i]['x']) for i in range(0,len(runCases)) ]) - - y = np.maximum(-6*x-6, x**4-3*x**2) - dydx = 4*x**3 - 6*x - ddy_ddx = 12*x**2 - 6 - gradientSwitch = -6*x-6 > x**4-3*x**2 - dydx[gradientSwitch] = -6 - ddy_ddx[gradientSwitch] = 0 - - y = [ self.checkOutputs(yval) for yval in y ] - dydx = [dydx[i] * units.dimensionless for i in range(0,len(dydx))] - - if returnMode < 0: - returnMode = -1*(returnMode + 1) - if returnMode == 0: - return y[0] - if returnMode == 1: - return y[0], dydx - else: - if returnMode == 0: - opt = [] - for i in range(0,len(y)): - opt.append([ y[i] ]) - return opt - if returnMode == 1: - opt = [] - for i in range(0,len(y)): - opt.append([ [y[i]], [ [[dydx[i]]] ] ]) - return opt - - s = SignomialTest() - ivals = [[x] for x in np.linspace(-2,2,11)] - - # How the black box may be called using EDI - bbo = s.BlackBox(**{'x':0.5}) - bbo = s.BlackBox({'x':0.5}) - bbo = s.BlackBox(**{'x':0.5, 'optn':True}) - - # Additional options available with parseInputs - bbo = s.BlackBox(*[0.5], **{'optn1': True, 'optn2': False}) - bbo = s.BlackBox(*[0.5,True], **{'optn': False}) - bbo = s.BlackBox({'x':[x for x in np.linspace(-2,2,11)]}) - bbo = s.BlackBox([{'x':x} for x in np.linspace(-2,2,11)]) - bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)]) - bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)], True, optn=False) - bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)], optn1=True, optn2=False) - +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: AdvancedRTC_Snippet_01 + :end-before: # END: AdvancedRTC_Snippet_01 Check outputs @@ -168,7 +99,7 @@ However, for outputs, the result will be an array with dimensions equal to the s jacobian_list_entry[(output_dim_1_ix, output_dim_2_ix, ..., input_dim_1_ix, input_dim_2_ix, ...)] = -For example, with an output that is ``NxN`` and an input that is also ``NxN`` +For example, with an output that is 2x2 and an input that is also 2x2 :: @@ -182,12 +113,3 @@ Tips ---- * A model summary can be printed by calling ``print(model_instance.summary)`` - - - - - - - - - diff --git a/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst b/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst index b2c31caf03d..b9d8f821711 100644 --- a/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst +++ b/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst @@ -27,55 +27,11 @@ First, we need to create an object which is visible to pyomo/EDI that calls the A simple example is shown below: -.. code-block:: python - - import pyomo.environ as pyo - from pyomo.environ import units - from pyomo.contrib.edi import BlackBoxFunctionModel - - class Parabola(BlackBoxFunctionModel): - def __init__(self): - - # Call parent init - super().__init__() - - # A brief description of the model - self.description = 'This model evaluates the function: y = x**2' - - # Append the model inputs - self.inputs.append( - name='x', units='ft' , description='The x variable' - ) - - # Append the model outputs - self.outputs.append( - name='y', units='ft**2' , description='The y variable' - ) - - # Set the highest available derivative - # Should be 1 for most cases but defaults to 0 - self.availableDerivative = 1 - - def BlackBox(self, x): # The actual function that does things - - # Convert to correct units and cast to a float - x = pyo.value(units.convert(x,self.inputs['x'].units)) - - # Compute y - y = x**2 - - # Compute dy/dx - dydx = 2*x - - # Add the units to the output - y = y * self.outputs['y'].units - - # Add the units to the derivative for output - dydx = dydx * self.outputs['y'].units / self.inputs['x'].units - - # Return - return y, [dydx] # return z, grad(z), hess(z)... - +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: RuntimeConstraints_Snippet_01 + :end-before: # END: RuntimeConstraints_Snippet_01 The inheriting classes can have any valid python name (in this case ``Parabola``) and have two methods ``__init__()`` and ``BlackBox()``. @@ -85,9 +41,11 @@ The init method The ``__init__()`` function sets up the model, and has 5 distinct steps. First, the parent class ``__init__()`` must be called: -.. code-block:: python - - super().__init__() +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 16 + :start-after: # BEGIN: RuntimeConstraints_Snippet_02 + :end-before: # END: RuntimeConstraints_Snippet_02 In general, this line can be used verbatim. @@ -109,11 +67,11 @@ Next, you must tell the model what its inputs are by appending them to the ``sel Models with multiple inputs simply call the ``self.input.append()`` command multiple times: -.. code-block:: python - - # Append the model inputs - self.inputs.append( name='x', units='ft' , description='The x variable' ) - self.inputs.append( name='y', units='ft' , description='The y variable' ) +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 16 + :start-after: # BEGIN: RuntimeConstraints_Snippet_03 + :end-before: # END: RuntimeConstraints_Snippet_03 Input names must be unique, and an error is raised if a repeated name is attempted to be set. @@ -136,21 +94,20 @@ Next, outputs must be added to the model. This is done identically to inputs, h and similarly: -.. code-block:: python - - # Append the model inputs - self.outputs.append( name='u', units='ft' , description='The u variable' ) - self.outputs.append( name='v', units='ft' , description='The v variable' ) +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 16 + :start-after: # BEGIN: RuntimeConstraints_Snippet_04 + :end-before: # END: RuntimeConstraints_Snippet_04 Finally, the highest available derivative must be set. For models being used in optimization, this will most often be ``1``, ie first derivative, gradient, or Jacobian information. - -.. code-block:: python - - # Set the highest available derivative - # Should be 1 for most cases but defaults to 0 - self.availableDerivative = 1 +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 16 + :start-after: # BEGIN: RuntimeConstraints_Snippet_05 + :end-before: # END: RuntimeConstraints_Snippet_05 The BlackBox method @@ -162,19 +119,20 @@ The ``BlackBox`` method assumes to take in the inputs as arguments in the order Since the units cannot be assumed on input, the first step in any black box is to convert to the model units: -.. code-block:: python - - from pyomo.environ import units - x = units.convert(x,self.inputs['x'].units) +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 16 + :start-after: # BEGIN: RuntimeConstraints_Snippet_06 + :end-before: # END: RuntimeConstraints_Snippet_06 And frequently, it is a good idea to cast these to a float value using ``pyomo.environ.value``: -.. code-block:: python - - import pyomo.environ as pyo - from pyomo.environ import units - x = pyo.value(units.convert(x,self.inputs['x'].units)) +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 16 + :start-after: # BEGIN: RuntimeConstraints_Snippet_07 + :end-before: # END: RuntimeConstraints_Snippet_07 The assumed units can now be added if desired, but this may cause a slowdown in performance. @@ -182,21 +140,20 @@ Operations can now be performed to compute the output and derivatives as desired When preparing the outputs, note that all outputs must have units: -.. code-block:: python - - # Add the units to the output - y = y * self.outputs['y'].units - - # Add the units to the derivative for output - dydx = dydx * self.outputs['y'].units / self.inputs['x'].units +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 16 + :start-after: # BEGIN: RuntimeConstraints_Snippet_08 + :end-before: # END: RuntimeConstraints_Snippet_08 The ``BlackBox`` method then outputs a tuple of length ``self.availableDerivative+1``. Entry [0] is the values specified during the ``__init__()``, entry [1] is first derivative information, and similar for higher order if available. -.. code-block:: python - - # Return - return y, [dydx] # return z, grad(z), hess(z)... +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 16 + :start-after: # BEGIN: RuntimeConstraints_Snippet_09 + :end-before: # END: RuntimeConstraints_Snippet_09 The full unpacking is as follows: @@ -226,49 +183,15 @@ See the :doc:`advanced <./advancedruntimeconstraints>` documentation for cases w Including a Black-Box in an EDI Formulation +++++++++++++++++++++++++++++++++++++++++++ -This second construction step is covered in the :doc:`Formulation <./formulation>` documentation, but is repeated here for completion. +This second construction step is covered in the :doc:`Formulation <./formulation>` documentation, but is repeated here for completion. Future versions may differentiate this section. Runtime Constraints are declared one of two ways, just as regular constraints. The ``f.RuntimeConstraint()`` constructor is available: -.. code-block:: python - - import pyomo.environ as pyo - from pyomo.environ import units - from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel - f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'y variable') - z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'Output var') - f.Objective( x + y ) - class UnitCircle(BlackBoxFunctionModel): - def __init__(self): - super().__init__() - self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append( name = 'x', - units = 'ft' , - description = 'The x variable' ) - self.inputs.append( name = 'y', - units = 'ft' , - description = 'The y variable' ) - self.outputs.append(name = 'z', - units = 'ft**2', - description = 'Output variable' ) - self.availableDerivative = 1 - self.post_init_setup(len(self.inputs)) - def BlackBox(self, x, y): # The actual function that does things - # Converts to correct units then casts to float - x = pyo.value(units.convert(x,self.inputs['x'].units)) - y = pyo.value(units.convert(y,self.inputs['y'].units)) - z = x**2 + y**2 # Compute z - dzdx = 2*x # Compute dz/dx - dzdy = 2*y # Compute dz/dy - z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... - f.Constraint( z <= 1*units.m**2 ) - - f.RuntimeConstraint( z, '==', [x,y], UnitCircle() ) +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Formulation_Snippet_09 + :end-before: # END: Formulation_Snippet_09 The ``f.RuntimeConstraint()`` constructor takes in the following inputs: @@ -289,113 +212,65 @@ The ``f.RuntimeConstraint()`` constructor takes in the following inputs: The following are alternative construction methods that may be of use: -.. code-block:: python - - f.RuntimeConstraint( *( z, '==', [x,y], UnitCircle() ) ) +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Formulation_Snippet_10 + :end-before: # END: Formulation_Snippet_10 + +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Formulation_Snippet_11 + :end-before: # END: Formulation_Snippet_11 + +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Formulation_Snippet_12 + :end-before: # END: Formulation_Snippet_12 + +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Formulation_Snippet_13 + :end-before: # END: Formulation_Snippet_13 +However, more commonly we expect users to construct Runtime Constraints as a part of a ``f.ConstraintList()`` declaration. Simply include a list, tuple, or dict as a part of the ConstraintList as follows: -.. code-block:: python - - f.RuntimeConstraint( *[ z, '==', [x,y], UnitCircle() ] ) - -.. code-block:: python +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Formulation_Snippet_14 + :end-before: # END: Formulation_Snippet_14 - f.RuntimeConstraint( **{ 'outputs' : z, - 'operators' : '==', - 'inputs' : [x,y], - 'black_box' : UnitCircle() } ) +Any of the alternative declarations above are valid to pass into the ``f.ConstraintList()`` constructor, for example: -.. code-block:: python +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Formulation_Snippet_15 + :end-before: # END: Formulation_Snippet_15 - f.RuntimeConstraint( *( [z], ['=='], [x,y], UnitCircle() ) ) +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Formulation_Snippet_16 + :end-before: # END: Formulation_Snippet_16 -However, more commonly we expect users to construct Runtime Constraints as a part of a ``f.ConstraintList()`` declaration. Simply include a list, tuple, or dict as a part of the ConstraintList as follows: +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Formulation_Snippet_17 + :end-before: # END: Formulation_Snippet_17 -.. code-block:: python - - import pyomo.environ as pyo - from pyomo.environ import units - from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel - f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'y variable') - z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'Output var') - f.Objective( x + y ) - class UnitCircle(BlackBoxFunctionModel): - def __init__(self): - super().__init__() - self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append( name = 'x', - units = 'ft' , - description = 'The x variable' ) - self.inputs.append( name = 'y', - units = 'ft' , - description = 'The y variable' ) - self.outputs.append(name = 'z', - units = 'ft**2', - description = 'Output variable' ) - self.availableDerivative = 1 - self.post_init_setup(len(self.inputs)) - def BlackBox(self, x, y): # The actual function that does things - # Converts to correct units then casts to float - x = pyo.value(units.convert(x,self.inputs[0].units)) - y = pyo.value(units.convert(y,self.inputs[1].units)) - z = x**2 + y**2 # Compute z - dzdx = 2*x # Compute dz/dx - dzdy = 2*y # Compute dz/dy - z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... - - - f.ConstraintList( - [ - z <= 1*units.m**2 , - [ z, '==', [x,y], UnitCircle() ] , - ] - ) +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Formulation_Snippet_18 + :end-before: # END: Formulation_Snippet_18 -Any of the alternative declarations above are valid to pass into the ``f.ConstraintList()`` constructor, for example: -.. code-block:: python - - f.ConstraintList( - [ - z <= 1*units.m**2 ) , - ( z, '==', [x,y], UnitCircle() ) , - ] - ) - -.. code-block:: python - - f.ConstraintList( - [ - z <= 1*units.m**2 ) , - [ z, '==', [x,y], UnitCircle() ] , - ] - ) - -.. code-block:: python - - f.ConstraintList( - [ - z <= 1*units.m**2 ) , - { 'outputs' : z, - 'operators' : '==', - 'inputs' : [x,y], - 'black_box' : UnitCircle() } , - ] - ) - -.. code-block:: python - - f.ConstraintList( - [ - z <= 1*units.m**2 ) , - ( [z], ['=='], [x,y], UnitCircle() ) , - ] - ) Examples -------- @@ -405,51 +280,11 @@ More examples are in the :doc:`advanced <./advancedruntimeconstraints>` document A standard construction +++++++++++++++++++++++ -.. code-block:: python - - import pyomo.environ as pyo - from pyomo.environ import units - from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel - f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The z variable') - - f.Objective( x + y ) - - class UnitCircle(BlackBoxFunctionModel): - def __init__(self): - super().__init__() - self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append( name = 'x', - units = 'ft' , - description = 'The x variable' ) - self.inputs.append( name = 'y', - units = 'ft' , - description = 'The y variable' ) - self.outputs.append( name = 'z', - units = 'ft**2', - description = 'Resultant of the unit circle evaluation' ) - self.availableDerivative = 1 - self.post_init_setup(len(self.inputs)) - - def BlackBox(self, x, y): # The actual function that does things - x = pyo.value(units.convert(x,self.inputs['x'].units)) - y = pyo.value(units.convert(y,self.inputs['y'].units)) - z = x**2 + y**2 - dzdx = 2*x - dzdy = 2*y - z = z * self.outputs['z'].units - dzdx = dzdx * self.outputs['z'].units / self.inputs['x'].units - dzdy = dzdy * self.outputs['z'].units / self.inputs['y'].units - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... - - f.ConstraintList( - [ - (z, '==', [x,y], UnitCircle() ) , - z <= 1*units.m**2 - ] - ) +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: RuntimeConstraints_Snippet_10 + :end-before: # END: RuntimeConstraints_Snippet_10 Tips diff --git a/doc/OnlineDocs/contributed_packages/edi/constants.rst b/doc/OnlineDocs/contributed_packages/edi/constants.rst index 48e6b1211b0..3f443886934 100644 --- a/doc/OnlineDocs/contributed_packages/edi/constants.rst +++ b/doc/OnlineDocs/contributed_packages/edi/constants.rst @@ -49,79 +49,61 @@ Examples A standard declaration statement ++++++++++++++++++++++++++++++++ -.. code-block:: python - - from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Constant(name = 'c', value = 1.0, units = 'm' , description = 'A constant c') +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Constants_Snippet_01 + :end-before: # END: Constants_Snippet_01 Shortest possible declaration +++++++++++++++++++++++++++++ -.. code-block:: python - - from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Constant('c', 1.0, 'm' ) +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Constants_Snippet_02 + :end-before: # END: Constants_Snippet_02 An alternative units definition +++++++++++++++++++++++++++++++ -.. code-block:: python - - from pyomo.environ import units - from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Constant( name = 'c', - value = 1.0, - units = units.m , - description = 'A constant c' ) +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Constants_Snippet_03 + :end-before: # END: Constants_Snippet_03 A vector constant +++++++++++++++++ -.. code-block:: python - - from pyomo.environ import units - from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Constant( name = 'c', - value = 1.0, - units = 'm' , - description = 'A constant c', - size = 5 ) +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Constants_Snippet_04 + :end-before: # END: Constants_Snippet_04 A matrix/tensor constant ++++++++++++++++++++++++ -.. code-block:: python - - from pyomo.environ import units - from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Constant( name = 'c', - value = 1.0, - units = 'm' , - description = 'A constant c', - size = [10,2] ) +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Constants_Snippet_05 + :end-before: # END: Constants_Snippet_05 More complicated units definition +++++++++++++++++++++++++++++++++ -.. code-block:: python - - from pyomo.environ import units - from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Constant( name = 'c', - value = 1.0, - units = 'kg*m/s**2' , - description = 'A constant c' ) +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Constants_Snippet_06 + :end-before: # END: Constants_Snippet_06 Tips @@ -133,6 +115,3 @@ Tips * Use the keyword names during constant declarations. Takes extra space, but is a massive boost to readability and intrepretability * Declare one constant on one single line with no breaks, no matter what style guides tell you. Again, this is a significant boost to readability * Do not skimp out on the description field, it is extremely helpful - - - diff --git a/doc/OnlineDocs/contributed_packages/edi/constraints.rst b/doc/OnlineDocs/contributed_packages/edi/constraints.rst index 30236d08ab8..7153c5331ff 100644 --- a/doc/OnlineDocs/contributed_packages/edi/constraints.rst +++ b/doc/OnlineDocs/contributed_packages/edi/constraints.rst @@ -51,80 +51,30 @@ Examples A standard declaration statement ++++++++++++++++++++++++++++++++ -.. code-block:: python - - from pyomo.environ import units - from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) - f.Objective( c*x + y ) - f.ConstraintList( - [ - x**2 + y**2 <= 1.0*units.m**2 , - x <= 0.75*units.m, - x >= y , - ] - ) +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Constraints_Snippet_01 + :end-before: # END: Constraints_Snippet_01 With the core constructor +++++++++++++++++++++++++ -.. code-block:: python - - from pyomo.environ import units - from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) - f.Objective( c*x + y ) - f.Constraint( x**2 + y**2 <= 1.0*units.m**2 ) - f.Constraint( x <= 0.75*units.m ) - f.Constraint( x >= y ) - +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Constraints_Snippet_02 + :end-before: # END: Constraints_Snippet_02 Using indexed variables and constants +++++++++++++++++++++++++++++++++++++ -.. code-block:: python - - from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable( name = 'x', - guess = 1.0, - units = 'm' , - description = 'The x variable', - bounds = [0,100] , - size = 3 ) - y = f.Variable( name = 'y', - guess = 1.0, - units = 'm' , - description = 'The y variable', - size = [2,2]) - c = f.Constant( name = 'c', - value = 1.0, - units = '' , - description = 'A constant c' , - size = 3 ) - f.Objective( c[0]*x[0] + c[1]*x[1] + c[2]*x[2] - + y[0,0]**4 - + y[0,1]**4 - + y[1,0]**4 - + y[1,1]**4 ) # Default is minimize - f.ConstraintList( - [ - x[0]**2 + x[1]**2 + x[2]**2 <= 1.0*units.m, - y[0,0] >= 1.0*units.m, - y[0,1] >= 1.0*units.m, - y[1,0] >= 1.0*units.m, - y[1,1] >= 1.0*units.m, - x[0] >= y[0,0] , - ] - ) - +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Constraints_Snippet_03 + :end-before: # END: Constraints_Snippet_03 Tips ---- diff --git a/doc/OnlineDocs/contributed_packages/edi/examples.rst b/doc/OnlineDocs/contributed_packages/edi/examples.rst index e20c08d10b8..659f1b86de7 100644 --- a/doc/OnlineDocs/contributed_packages/edi/examples.rst +++ b/doc/OnlineDocs/contributed_packages/edi/examples.rst @@ -6,4 +6,3 @@ A Geometric Program for Aircraft Design .. literalinclude:: ../../../../pyomo/contrib/edi/examples/aircraft_gp.py :language: python - diff --git a/doc/OnlineDocs/contributed_packages/edi/formulation.rst b/doc/OnlineDocs/contributed_packages/edi/formulation.rst index 5aa2dc93e48..6519722b9a6 100644 --- a/doc/OnlineDocs/contributed_packages/edi/formulation.rst +++ b/doc/OnlineDocs/contributed_packages/edi/formulation.rst @@ -14,10 +14,12 @@ Construction ------------ A *Formulation* is constructed as follows: -.. code-block:: python +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Formulation_Snippet_01 + :end-before: # END: Formulation_Snippet_01 - from pyomo.contrib.edi import Formulation - f = Formulation() Standard practice is to construct a formulation to namespace variable ``f``, but any valid python name can be used. Standard pyomo practice would be to construct this to ``model`` or ``m``. @@ -29,12 +31,11 @@ See the :doc:`Variables <./variables>` Documentation Variables are declared using the ``f.Variable()`` function. This function creates an instance of ``pyomo.environ.Var`` and adds it to the ``edi.contrib.Formulation``. The function returns an instance of a ``pyomo.environ.Var`` that can be used in later construction. -.. code-block:: python - - from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = '') - +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Formulation_Snippet_02 + :end-before: # END: Formulation_Snippet_02 Declaring Constants @@ -44,11 +45,12 @@ See the :doc:`Constants <./constants>` Documentation Constants (referred to in base pyomo as parameters or ``Params``) are declared using the ``f.Constant()`` function. This function creates an instance of a ``pyomo.environ.Param`` and adds it to the ``edi.contrib.Formulation``. This function also returns an instance of ``pyomo.environ.Param`` that can be used in later construction. -.. code-block:: python +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Formulation_Snippet_03 + :end-before: # END: Formulation_Snippet_03 - from pyomo.contrib.edi import Formulation - f = Formulation() - c = f.Constant(name = 'c', value = 1.0, units = '') Declaring Objectives @@ -58,33 +60,25 @@ See the :doc:`Objectives <./objectives>` Documentation Objectives are declared using the ``f.Objective()`` function. This function creates an instance of a ``pyomo.environ.Objective`` and adds it to the ``edi.contrib.Formulation``. Multiple objectives can be declared, but interpretation of multiple objectives will depend on the solver. The returned values of the ``f.Variable()`` and ``f.Constant()`` declarations can be used to construct the objective. Black-box (ie, Runtime) objectives are not supported at this time, but are planned in a future update. -.. code-block:: python - - from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = '') - y = f.Variable(name = 'y', guess = 1.0, units = '') - c = f.Constant(name = 'c', value = 1.0, units = '') - f.Objective( c*x + y ) +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Formulation_Snippet_04 + :end-before: # END: Formulation_Snippet_04 By default, objectives are minimized, but can be switched to a maximize using the ``sense`` keyword from pyomo: -.. code-block:: python - - from pyomo.contrib.edi import Formulation - from pyomo.environ import maximize, minimize - f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = '') - y = f.Variable(name = 'y', guess = 1.0, units = '') - c = f.Constant(name = 'c', value = 1.0, units = '') - f.Objective( c*x + y , sense = maximize) +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Formulation_Snippet_05 + :end-before: # END: Formulation_Snippet_05 Note: Future version will allow a string to be passed into ``sense`` (see `this issue `_) - Declaring Constraints --------------------- @@ -92,54 +86,27 @@ See the :doc:`Constraints <./constraints>` Documentation Constraints can be declared in two ways. First is using the standard ``f.Constraint()`` constructor. This function creates an instance of ``pyomo.environ.Constraint`` and adds it to the ``edi.contrib.Formulation``. The operators ``<=``, ``>=``, and ``==`` are used as constraint constructors. -.. code-block:: python - - from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = '') - y = f.Variable(name = 'y', guess = 1.0, units = '') - c = f.Constant(name = 'c', value = 1.0, units = '') - f.Objective( c*x + y ) - f.Constraint( x**2 + y**2 <= 1.0 ) - f.Constraint( x >= 0) - f.Constraint( y <= 0) +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Formulation_Snippet_06 + :end-before: # END: Formulation_Snippet_06 Constraints can also be declared using the ``f.ConstraintList()`` function. This function takes in a list of constraints and allows for multiple constraints to be declared in one go: -.. code-block:: python - - from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = '') - y = f.Variable(name = 'y', guess = 1.0, units = '') - c = f.Constant(name = 'c', value = 1.0, units = '') - f.Objective( c*x + y ) - f.ConstraintList( - [ - x**2 + y**2 <= 1.0 , - x >= 0, - y <= 0, - ] - ) +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Formulation_Snippet_07 + :end-before: # END: Formulation_Snippet_07 The constraint list can also be declared a priori and passed in to the ``f.ConstraintList()`` function, which may be beneficial for complex models: -.. code-block:: python - - from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = '') - y = f.Variable(name = 'y', guess = 1.0, units = '') - c = f.Constant(name = 'c', value = 1.0, units = '') - f.Objective( c*x + y ) - - constraintList = [ - x**2 + y**2 <= 1.0 , - x >= 0, - y <= 0, - ] - - f.ConstraintList( constraintList ) +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Formulation_Snippet_08 + :end-before: # END: Formulation_Snippet_08 We recommend that most users should be using the ``f.ConstraintList()`` function, with the ``f.Constraint()`` function being reserved for under-the-hood usage. @@ -157,45 +124,11 @@ The use of Runtime constraints requires a black box model that is discussed in d Runtime Constraints are declared one of two ways, just as regular constraints. The ``f.RuntimeConstraint()`` constructor is available: -.. code-block:: python - - import pyomo.environ as pyo - from pyomo.environ import units - from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel - f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'y variable') - z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'Output var') - f.Objective( x + y ) - class UnitCircle(BlackBoxFunctionModel): - def __init__(self): - super().__init__() - self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append( name = 'x', - units = 'ft' , - description = 'The x variable' ) - self.inputs.append( name = 'y', - units = 'ft' , - description = 'The y variable' ) - self.outputs.append( name = 'z', - units = 'ft**2', - description = 'Output variable' ) - self.availableDerivative = 1 - self.post_init_setup(len(self.inputs)) - def BlackBox(self, x, y): # The actual function that does things - # Converts to correct units then casts to float - x = pyo.value(units.convert(x,self.inputs['x'].units)) - y = pyo.value(units.convert(y,self.inputs['y'].units)) - z = x**2 + y**2 # Compute z - dzdx = 2*x # Compute dz/dx - dzdy = 2*y # Compute dz/dy - z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... - f.Constraint( z <= 1*units.m**2 ) - - f.RuntimeConstraint( z, '==', [x,y], UnitCircle() ) +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Formulation_Snippet_09 + :end-before: # END: Formulation_Snippet_09 The ``f.RuntimeConstraint()`` constructor takes in the following inputs: @@ -217,113 +150,63 @@ The ``f.RuntimeConstraint()`` constructor takes in the following inputs: The following are alternative construction methods that may be of use: -.. code-block:: python - - f.RuntimeConstraint( *( z, '==', [x,y], UnitCircle() ) ) - - -.. code-block:: python - - f.RuntimeConstraint( *[ z, '==', [x,y], UnitCircle() ] ) - -.. code-block:: python - - f.RuntimeConstraint( **{ 'outputs' : z, - 'operators' : '==', - 'inputs' : [x,y], - 'black_box' : UnitCircle() } ) - -.. code-block:: python - - f.RuntimeConstraint( *( [z], ['=='], [x,y], UnitCircle() ) ) +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Formulation_Snippet_10 + :end-before: # END: Formulation_Snippet_10 + +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Formulation_Snippet_11 + :end-before: # END: Formulation_Snippet_11 + +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Formulation_Snippet_12 + :end-before: # END: Formulation_Snippet_12 + +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Formulation_Snippet_13 + :end-before: # END: Formulation_Snippet_13 However, more commonly we expect users to construct Runtime Constraints as a part of a ``f.ConstraintList()`` declaration. Simply include a list, tuple, or dict as a part of the ConstraintList as follows: -.. code-block:: python - - import pyomo.environ as pyo - from pyomo.environ import units - from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel - f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'y variable') - z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'Output var') - f.Objective( x + y ) - class UnitCircle(BlackBoxFunctionModel): - def __init__(self): - super().__init__() - self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append( name = 'x', - units = 'ft' , - description = 'The x variable' ) - self.inputs.append( name = 'y', - units = 'ft' , - description = 'The y variable' ) - self.outputs.append( name = 'z', - units = 'ft**2', - description = 'Output variable' ) - self.availableDerivative = 1 - self.post_init_setup(len(self.inputs)) - def BlackBox(self, x, y): # The actual function that does things - # Converts to correct units then casts to float - x = pyo.value(units.convert(x,self.inputs[0].units)) - y = pyo.value(units.convert(y,self.inputs[1].units)) - z = x**2 + y**2 # Compute z - dzdx = 2*x # Compute dz/dx - dzdy = 2*y # Compute dz/dy - z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... - - - f.ConstraintList( - [ - z <= 1*units.m**2 , - [ z, '==', [x,y], UnitCircle() ] , - ] - ) +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Formulation_Snippet_14 + :end-before: # END: Formulation_Snippet_14 Any of the alternative declarations above are valid to pass into the ``f.ConstraintList()`` constructor, for example: -.. code-block:: python - - f.ConstraintList( - [ - z <= 1*units.m**2 ) , - ( z, '==', [x,y], UnitCircle() ) , - ] - ) - -.. code-block:: python - - f.ConstraintList( - [ - z <= 1*units.m**2 ) , - [ z, '==', [x,y], UnitCircle() ] , - ] - ) - -.. code-block:: python - - f.ConstraintList( - [ - z <= 1*units.m**2 ) , - { 'outputs' : z, - 'operators' : '==', - 'inputs' : [x,y], - 'black_box' : UnitCircle() } , - ] - ) - -.. code-block:: python - - f.ConstraintList( - [ - z <= 1*units.m**2 ) , - ( [z], ['=='], [x,y], UnitCircle() ) , - ] - ) +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Formulation_Snippet_15 + :end-before: # END: Formulation_Snippet_15 + +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Formulation_Snippet_16 + :end-before: # END: Formulation_Snippet_16 + +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Formulation_Snippet_17 + :end-before: # END: Formulation_Snippet_17 + +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Formulation_Snippet_18 + :end-before: # END: Formulation_Snippet_18 Support Functions ----------------- @@ -348,15 +231,3 @@ Returns a list of *runtime* (ie. black-box) constraints that have been defined i ``f.check_units()`` |br| Checks the units of each objective and constraint for consistency. Will only check objectives and constraints defined via EDI. - - - - - - - - - - - - diff --git a/doc/OnlineDocs/contributed_packages/edi/index.rst b/doc/OnlineDocs/contributed_packages/edi/index.rst index 3b9d974548a..00a139ee751 100644 --- a/doc/OnlineDocs/contributed_packages/edi/index.rst +++ b/doc/OnlineDocs/contributed_packages/edi/index.rst @@ -14,10 +14,12 @@ EDI installs as a part of the standard pyomo install: pip install pyomo -EDI also requires the pint package that is viewed as optional by pyomo: +EDI also requires packages that are optional in base pyomo: :: + pip install numpy + pip install scipy pip install pint diff --git a/doc/OnlineDocs/contributed_packages/edi/objectives.rst b/doc/OnlineDocs/contributed_packages/edi/objectives.rst index a5d5f20a97e..936e46b1637 100644 --- a/doc/OnlineDocs/contributed_packages/edi/objectives.rst +++ b/doc/OnlineDocs/contributed_packages/edi/objectives.rst @@ -39,120 +39,69 @@ Examples A standard declaration statement ++++++++++++++++++++++++++++++++ -.. code-block:: python - - from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) - f.Objective( c*x + y ) # Default is minimize - +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Objectives_Snippet_01 + :end-before: # END: Objectives_Snippet_01 With a non-linear objective +++++++++++++++++++++++++++ -.. code-block:: python - - from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) - f.Objective( c*x**4 + y**4 ) # Default is minimize - +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Objectives_Snippet_02 + :end-before: # END: Objectives_Snippet_02 Explicitly minimize +++++++++++++++++++ -.. code-block:: python - - from pyomo.contrib.edi import Formulation - from pyomo.environ import minimize, maximize - f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) - f.Objective( c*x**4 + y**4 , sense = minimize) - +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Objectives_Snippet_03 + :end-before: # END: Objectives_Snippet_03 Explicitly minimize using integer +++++++++++++++++++++++++++++++++ -.. code-block:: python - - from pyomo.contrib.edi import Formulation - from pyomo.environ import minimize, maximize - f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) - f.Objective( c*x**4 + y**4 , sense = 1 ) # 1 corresponds to minimize +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Objectives_Snippet_04 + :end-before: # END: Objectives_Snippet_04 Maximizing ++++++++++ -.. code-block:: python - - from pyomo.contrib.edi import Formulation - from pyomo.environ import minimize, maximize - f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) - f.Objective( -c*x**4 - y**4 , sense = maximize) - +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Objectives_Snippet_05 + :end-before: # END: Objectives_Snippet_05 Maximizing using integer ++++++++++++++++++++++++ -.. code-block:: python - - from pyomo.contrib.edi import Formulation - from pyomo.environ import minimize, maximize - f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) - f.Objective( -c*x**4 - y**4 , sense = -1 ) # -1 corresponds to maximize - +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Objectives_Snippet_06 + :end-before: # END: Objectives_Snippet_06 Using indexed variables and constants +++++++++++++++++++++++++++++++++++++ -.. code-block:: python - - from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable( name = 'x', - guess = 1.0, - units = 'm' , - description = 'The x variable', - bounds = [0,100] , - size = 3 ) - y = f.Variable( name = 'y', - guess = 1.0, - units = 'm' , - description = 'The y variable', - size = [2,2]) - c = f.Constant( name = 'c', - value = 1.0, - units = '' , - description = 'A constant c' , - size = 3 ) - f.Objective( c[0]*x[0] + c[1]*x[1] + c[2]*x[2] - + y[0,0]**4 - + y[0,1]**4 - + y[1,0]**4 - + y[1,1]**4 ) # Default is minimize - +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Objectives_Snippet_07 + :end-before: # END: Objectives_Snippet_07 Tips ---- * Objectives are a pretty natural place to break your file. Put at least one blank line above and below the objective constructor and use good sectioning to create a whitespace easily identifiable when scrolling quickly - - - diff --git a/doc/OnlineDocs/contributed_packages/edi/quickstart.rst b/doc/OnlineDocs/contributed_packages/edi/quickstart.rst index beef3924563..cb90b4e93ef 100644 --- a/doc/OnlineDocs/contributed_packages/edi/quickstart.rst +++ b/doc/OnlineDocs/contributed_packages/edi/quickstart.rst @@ -20,5 +20,3 @@ The example shown here minimizes a linear objective function subject to the inte .. literalinclude:: ../../../../pyomo/contrib/edi/examples/readme_example.py :language: python - - diff --git a/doc/OnlineDocs/contributed_packages/edi/variables.rst b/doc/OnlineDocs/contributed_packages/edi/variables.rst index 316dbe736dd..06f0c6581cf 100644 --- a/doc/OnlineDocs/contributed_packages/edi/variables.rst +++ b/doc/OnlineDocs/contributed_packages/edi/variables.rst @@ -53,108 +53,80 @@ Examples A standard declaration statement ++++++++++++++++++++++++++++++++ -.. code-block:: python - - from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Variables_Snippet_01 + :end-before: # END: Variables_Snippet_01 Shortest possible declaration +++++++++++++++++++++++++++++ -.. code-block:: python - - from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable('x', 1.0, 'm' ) +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Variables_Snippet_02 + :end-before: # END: Variables_Snippet_02 A variable with bounds ++++++++++++++++++++++ -.. code-block:: python - - from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable( name = 'x', - guess = 1.0, - units = 'm' , - description = 'The x variable', - bounds = [-10,10] ) +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Variables_Snippet_03 + :end-before: # END: Variables_Snippet_03 An integer variable +++++++++++++++++++ -.. code-block:: python - - from pyomo.contrib.edi import Formulation - from pyomo.environ import Integers - f = Formulation() - x = f.Variable( name = 'x', - guess = 1.0, - units = 'm' , - description = 'The x variable', - domain = Integers ) - +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Variables_Snippet_04 + :end-before: # END: Variables_Snippet_04 An alternative units definition +++++++++++++++++++++++++++++++ -.. code-block:: python - - from pyomo.environ import units - from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable( name = 'x', - guess = 1.0, - units = units.m , - description = 'The x variable' ) +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Variables_Snippet_05 + :end-before: # END: Variables_Snippet_05 A vector variable +++++++++++++++++ -.. code-block:: python - - from pyomo.environ import units - from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable( name = 'x', - guess = 1.0, - units = 'm' , - description = 'The x variable', - size = 5 ) +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Variables_Snippet_06 + :end-before: # END: Variables_Snippet_06 A matrix/tensor variable ++++++++++++++++++++++++ -.. code-block:: python - - from pyomo.environ import units - from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable( name = 'x', - guess = 1.0, - units = 'm' , - description = 'The x variable', - size = [10,2] ) +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Variables_Snippet_07 + :end-before: # END: Variables_Snippet_07 More complicated units definition +++++++++++++++++++++++++++++++++ -.. code-block:: python - - from pyomo.environ import units - from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable( name = 'x', - guess = 1.0, - units = 'kg*m/s**2' , - description = 'The x variable' ) +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: Variables_Snippet_08 + :end-before: # END: Variables_Snippet_08 Tips @@ -166,8 +138,3 @@ Tips * Use the keyword names during variable declarations. Takes extra space, but is a massive boost to readability and intrepretability * Declare one variable on one single line with no breaks, no matter what style guides tell you. Again, this is a significant boost to readability * Do not skimp out on the description field, it is extremely helpful - - - - - diff --git a/doc/OnlineDocs/contributed_packages/index.rst b/doc/OnlineDocs/contributed_packages/index.rst index 0278e2caa36..c1e6e180331 100644 --- a/doc/OnlineDocs/contributed_packages/index.rst +++ b/doc/OnlineDocs/contributed_packages/index.rst @@ -16,8 +16,8 @@ Contributed packages distributed with Pyomo: :maxdepth: 1 community.rst - edi/index.rst doe/doe.rst + edi/index.rst gdpopt.rst iis.rst incidence/index.rst diff --git a/pyomo/contrib/edi/README.md b/pyomo/contrib/edi/README.md index 8a6b592baa0..90f469c4000 100644 --- a/pyomo/contrib/edi/README.md +++ b/pyomo/contrib/edi/README.md @@ -101,19 +101,3 @@ f.ConstraintList( ] ) ``` - -## Acknowledgement - -Pyomo: Python Optimization Modeling Objects -Copyright (c) 2008-2023 -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. - -Development of this module was conducted as part of the Institute for -the Design of Advanced Energy Systems (IDAES) with support through the -Simulation-Based Engineering, Crosscutting Research Program within the -U.S. Department of Energy’s Office of Fossil Energy and Carbon Management. - -This software is distributed under the 3-clause BSD License. \ No newline at end of file diff --git a/pyomo/contrib/edi/blackBoxFunctionModel.py b/pyomo/contrib/edi/blackBoxFunctionModel.py index 56a06531524..d8647df4625 100644 --- a/pyomo/contrib/edi/blackBoxFunctionModel.py +++ b/pyomo/contrib/edi/blackBoxFunctionModel.py @@ -16,52 +16,55 @@ # ___________________________________________________________________________ import copy -import numpy as np import pyomo import pyomo.environ as pyo from pyomo.environ import units as pyomo_units -import scipy.sparse as sps -from pyomo.contrib.pynumero.interfaces.external_grey_box import ( - ExternalGreyBoxModel, - ExternalGreyBoxBlock, -) +try: + import numpy as np +except: + raise ImportError("pyomo.contrib.edi requires numpy to enable black box capability, fix with 'pip install numpy' ") + +try: + import scipy.sparse as sps +except: + raise ImportError("pyomo.contrib.edi requires scipy to enable black box capability, fix with 'pip install scipy' ") + +try: + from pyomo.contrib.pynumero.interfaces.external_grey_box import ExternalGreyBoxModel, ExternalGreyBoxBlock +except: + raise ImportError("pyomo.contrib.edi requires pyomo.contrib.pynumero to be installed to enable black box capability, this should have installed with base pyomo") class BlackBoxFunctionModel_Variable(object): - def __init__(self, name, units, description='', size=0): + def __init__(self, name, units, description = '', size = 0): # Order matters self.name = name self.units = units self.size = size self.description = description - - # ===================================================================================================================== - # The printing function - # ===================================================================================================================== +# ===================================================================================================================== +# The printing function +# ===================================================================================================================== def __repr__(self): - return self.name - - # ===================================================================================================================== - # Define the name - # ===================================================================================================================== + return self.name +# ===================================================================================================================== +# Define the name +# ===================================================================================================================== @property def name(self): return self._name - @name.setter - def name(self, val): + def name(self,val): if isinstance(val, str): self._name = val else: raise ValueError('Invalid name. Must be a string.') - - # ===================================================================================================================== - # Define the units - # ===================================================================================================================== +# ===================================================================================================================== +# Define the units +# ===================================================================================================================== @property def units(self): return self._units - @units.setter def units(self, val): # set dimensionless if a null string is passed in @@ -70,101 +73,87 @@ def units(self, val): val = 'dimensionless' if val is None: val = 'dimensionless' - + if isinstance(val, str): self._units = pyomo_units.__getattr__(val) elif isinstance(val, pyomo.core.base.units_container._PyomoUnit): self._units = val else: - raise ValueError( - 'Invalid units. Must be a string compatible with pint or a unit instance.' - ) - - # ===================================================================================================================== - # Define the size - # ===================================================================================================================== + raise ValueError('Invalid units. Must be a string compatible with pint or a unit instance.') +# ===================================================================================================================== +# Define the size +# ===================================================================================================================== @property def size(self): return self._size - @size.setter def size(self, val): invalid = False - if isinstance(val, (list, tuple)): + if isinstance(val,(list, tuple)): sizeTemp = [] for x in val: if isinstance(x, str): # is a vector of unknown length, should be 'inf', but any string accepted x = -1 # pass - elif not isinstance(x, int): - raise ValueError( - 'Invalid size. Must be an integer or list/tuple of integers' - ) + elif not isinstance(x,int): + raise ValueError('Invalid size. Must be an integer or list/tuple of integers') if x == 1: - raise ValueError( - 'A value of 1 is not valid for defining size. Use fewer dimensions.' - ) + raise ValueError('A value of 1 is not valid for defining size. Use fewer dimensions.') sizeTemp.append(x) self._size = val else: if val is None: - self._size = 0 # set to scalar + self._size = 0 # set to scalar elif isinstance(val, str): # is a 1D vector of unknown length, should be 'inf', but any string accepted self._size = -1 # pass elif isinstance(val, int): if val == 1: - raise ValueError( - 'A value of 1 is not valid for defining size. Use 0 to indicate a scalar value.' - ) + raise ValueError('A value of 1 is not valid for defining size. Use 0 to indicate a scalar value.') else: self._size = val else: - raise ValueError( - 'Invalid size. Must be an integer or list/tuple of integers' - ) - - # ===================================================================================================================== - # Define the description - # ===================================================================================================================== + raise ValueError('Invalid size. Must be an integer or list/tuple of integers') +# ===================================================================================================================== +# Define the description +# ===================================================================================================================== @property def description(self): return self._description - @description.setter def description(self, val): - if isinstance(val, str): + if isinstance(val,str): self._description = val else: raise ValueError('Invalid description. Must be a string.') class TypeCheckedList(list): - def __init__(self, checkItem, itemList=None): + def __init__(self, checkItem, itemList = None): super(TypeCheckedList, self).__init__() self.checkItem = checkItem - + if itemList is not None: if isinstance(itemList, list) or isinstance(itemList, tuple): for itm in itemList: self.append(itm) else: raise ValueError('Input to itemList is not iterable') - + def __setitem__(self, key, val): if isinstance(val, self.checkItem): super(TypeCheckedList, self).__setitem__(key, val) - elif isinstance(val, (tuple, list)): - cks = [isinstance(vl, self.checkItem) for vl in val] + elif isinstance(val, (tuple,list)): + cks = [isinstance(vl,self.checkItem) for vl in val] if sum(cks) == len(cks): super(TypeCheckedList, self).__setitem__(key, val) else: - raise ValueError('Input must be an instance of the defined type') + raise ValueError('Input must be an instance of the defined type') else: raise ValueError('Input must be an instance of the defined type') - + def append(self, val): if isinstance(val, self.checkItem): super(TypeCheckedList, self).append(val) @@ -174,7 +163,7 @@ def append(self, val): class BBList(TypeCheckedList): def __init__(self): - super(BBList, self).__init__(BlackBoxFunctionModel_Variable, []) + super(BBList, self).__init__(BlackBoxFunctionModel_Variable,[]) self._lookupDict = {} self._counter = 0 @@ -186,75 +175,64 @@ def __getitem__(self, val): else: raise ValueError('Input must be an integer or a valid variable name') + + def append(*args, **kwargs): args = list(args) self = args.pop(0) - + if len(args) + len(kwargs.values()) == 1: if len(args) == 1: inputData = args[0] if len(kwargs.values()) == 1: inputData = list(kwargs.values())[0] - + if isinstance(inputData, self.checkItem): if inputData.name in self._lookupDict.keys(): - raise ValueError( - "Key '%s' already exists in the input list" % (inputData.name) - ) + raise ValueError("Key '%s' already exists in the input list"%(inputData.name)) self._lookupDict[inputData.name] = self._counter self._counter += 1 super(BBList, self).append(inputData) else: if isinstance(inputData, str): - raise ValueError( - "Key '%s' not passed in to the black box variable constructor" - % ('units') - ) + raise ValueError("Key '%s' not passed in to the black box variable constructor"%('units')) else: raise ValueError('Invalid (single) input type') - + elif len(args) + len(kwargs.values()) <= 4: - argKeys = ['name', 'units', 'description', 'size'] - ipd = dict(zip(argKeys[0 : len(args)], args)) + argKeys = ['name','units','description','size'] + ipd = dict(zip(argKeys[0:len(args)],args)) for ky, vl in kwargs.items(): if ky in ipd: - raise ValueError( - "Key '%s' declared after non-keyword arguments and is out of order" - % (ky) - ) + raise ValueError("Key '%s' declared after non-keyword arguments and is out of order"%(ky)) else: - ipd[ky] = vl - + ipd[ky]=vl + for ak in argKeys: if ak not in ipd.keys(): if ak == 'description': - ipd['description'] = '' + ipd['description']='' elif ak == 'size': ipd['size'] = 0 else: - raise ValueError( - "Key '%s' not passed in to the black box variable constructor" - % (ak) - ) + raise ValueError("Key '%s' not passed in to the black box variable constructor"%(ak)) + if ipd['name'] in self._lookupDict.keys(): - raise ValueError( - "Key '%s' already exists in the input list" % (ipd['name']) - ) + raise ValueError("Key '%s' already exists in the input list"%(ipd['name'])) self._lookupDict[ipd['name']] = self._counter self._counter += 1 super(BBList, self).append(BlackBoxFunctionModel_Variable(**ipd)) - + else: raise ValueError('Too many inputs to a black box variable') errorString = 'This function is calling to the base class and has not been defined.' - class BlackBoxFunctionModel(ExternalGreyBoxModel): - # --------------------------------------------------------------------------------------------------------------------- - # --------------------------------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------------------------------- def __init__(self): super(BlackBoxFunctionModel, self).__init__() @@ -262,9 +240,9 @@ def __init__(self): self.inputs = BBList() self.outputs = BBList() - self.inputVariables_optimization = None + self.inputVariables_optimization = None self.outputVariables_optimization = None - + # A simple description of the model self.description = None @@ -273,17 +251,15 @@ def __init__(self): self._cache = None self._NunwrappedOutputs = None - self._NunwrappedInputs = None + self._NunwrappedInputs = None - def setOptimizationVariables( - self, inputVariables_optimization, outputVariables_optimization - ): - self.inputVariables_optimization = inputVariables_optimization + def setOptimizationVariables(self, inputVariables_optimization, outputVariables_optimization): + self.inputVariables_optimization = inputVariables_optimization self.outputVariables_optimization = outputVariables_optimization - # --------------------------------------------------------------------------------------------------------------------- - # pyomo things - # --------------------------------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------------------------------- +# pyomo things +# --------------------------------------------------------------------------------------------------------------------- def input_names(self): inputs_unwrapped = [] for ivar in self.inputVariables_optimization: @@ -294,9 +270,10 @@ def input_names(self): for vi in validIndices: inputs_unwrapped.append(ivar[vi]) else: - raise ValueError("Invalid type for input variable") + raise ValueError("Invalid type for input variable") + + return [ip.__str__() for ip in inputs_unwrapped] - return [ip.__str__() for ip in inputs_unwrapped] def output_names(self): outputs_unwrapped = [] @@ -308,7 +285,7 @@ def output_names(self): for vi in validIndices: outputs_unwrapped.append(ovar[vi]) else: - raise ValueError("Invalid type for output variable") + raise ValueError("Invalid type for output variable") return [op.__str__() for op in outputs_unwrapped] @@ -320,15 +297,16 @@ def evaluate_outputs(self): self.fillCache() opts = self._cache['pyomo_outputs'] return opts - + def evaluate_jacobian_outputs(self): self.fillCache() jac = self._cache['pyomo_jacobian'] return jac - def post_init_setup(self, defaultVal=1.0): + def post_init_setup(self, defaultVal = 1.0): self._input_values = np.ones(self._NunwrappedInputs) * defaultVal + def fillCache(self): if self._cache is None: self._cache = {} @@ -338,113 +316,95 @@ def fillCache(self): ptr = 0 - for i in range(0, len(self.inputVariables_optimization)): + for i in range(0,len(self.inputVariables_optimization)): optimizationInput = self.inputVariables_optimization[i] - if not isinstance( - optimizationInput, - (pyomo.core.base.var.IndexedVar, pyomo.core.base.var.ScalarVar), - ): + if not isinstance(optimizationInput, (pyomo.core.base.var.IndexedVar, pyomo.core.base.var.ScalarVar)): raise ValueError("Invalid input variable type") - ipt = self.inputs[i] + ipt = self.inputs[i] - shape = [len(idx) for idx in optimizationInput.index_set().subsets()] - localShape = ipt.size + shape = [len(idx) for idx in optimizationInput.index_set().subsets()] + localShape = ipt.size optimizationUnits = self.inputVariables_optimization[i].get_units() - localUnits = ipt.units + localUnits = ipt.units if isinstance(optimizationInput, pyomo.core.base.var.IndexedVar): - value = np.zeros(shape) + value = np.zeros(shape) for vix in list(optimizationInput.index_set().data()): - raw_val = float(raw_inputs[ptr]) * optimizationUnits - raw_val_correctedUnits = pyomo_units.convert( - raw_val, localUnits - ) + raw_val = float(raw_inputs[ptr]) * optimizationUnits + raw_val_correctedUnits = pyomo_units.convert(raw_val, localUnits) value[vix] = pyo.value(raw_val_correctedUnits) ptr += 1 - self.sizeCheck(localShape, value * localUnits) - bb_inputs.append(value * localUnits) + self.sizeCheck(localShape, value*localUnits) + bb_inputs.append(value*localUnits) - else: # isinstance(optimizationInput, pyomo.core.base.var.ScalarVar):, just checked this + else: # isinstance(optimizationInput, pyomo.core.base.var.ScalarVar):, just checked this value = raw_inputs[ptr] * optimizationUnits ptr += 1 self.sizeCheck(localShape, value) value_correctedUnits = pyomo_units.convert(value, localUnits) bb_inputs.append(value_correctedUnits) + bbo = self.BlackBox(*bb_inputs) - self._cache['raw'] = bbo - self._cache['raw_value'] = bbo[0] - self._cache['raw_jacobian'] = bbo[1] + self._cache['raw'] = bbo + self._cache['raw_value'] = bbo[0] + self._cache['raw_jacobian'] = bbo[1] outputVector = [] - if not isinstance(bbo[0], (list, tuple)): + if not isinstance(bbo[0], (list,tuple)): valueList = [bbo[0]] jacobianList = [bbo[1]] else: valueList = bbo[0] jacobianList = bbo[1] - for i in range(0, len(valueList)): + for i in range(0,len(valueList)): optimizationOutput = self.outputVariables_optimization[i] - if not isinstance( - optimizationOutput, - (pyomo.core.base.var.IndexedVar, pyomo.core.base.var.ScalarVar), - ): + if not isinstance(optimizationOutput, (pyomo.core.base.var.IndexedVar, pyomo.core.base.var.ScalarVar)): raise ValueError("Invalid output variable type") - opt = self.outputs[i] + opt = self.outputs[i] - modelOutputUnits = opt.units + modelOutputUnits = opt.units outputOptimizationUnits = optimizationOutput.get_units() vl = valueList[i] if isinstance(vl, pyomo.core.expr.numeric_expr.NumericNDArray): validIndexList = optimizationOutput.index_set().data() - for j in range(0, len(validIndexList)): + for j in range(0,len(validIndexList)): vi = validIndexList[j] - corrected_value = pyo.value( - pyomo_units.convert(vl[vi], outputOptimizationUnits) - ) # now unitless in correct units - outputVector.append(corrected_value) - - elif isinstance( - vl, - ( - pyomo.core.expr.numeric_expr.NPV_ProductExpression, - pyomo.core.base.units_container._PyomoUnit, - ), - ): - corrected_value = pyo.value( - pyomo_units.convert(vl, outputOptimizationUnits) - ) # now unitless in correct units - outputVector.append(corrected_value) + corrected_value = pyo.value(pyomo_units.convert(vl[vi], outputOptimizationUnits)) # now unitless in correct units + outputVector.append(corrected_value) + + elif isinstance(vl, (pyomo.core.expr.numeric_expr.NPV_ProductExpression, + pyomo.core.base.units_container._PyomoUnit)): + corrected_value = pyo.value(pyomo_units.convert(vl, outputOptimizationUnits)) # now unitless in correct units + outputVector.append(corrected_value) else: raise ValueError("Invalid output variable type") self._cache['pyomo_outputs'] = outputVector - outputJacobian = ( - np.ones([self._NunwrappedOutputs, self._NunwrappedInputs]) * -1 - ) + outputJacobian = np.ones([self._NunwrappedOutputs, self._NunwrappedInputs]) * -1 ptr_row = 0 - ptr_col = 0 + ptr_col = 0 - for i in range(0, len(jacobianList)): + for i in range(0,len(jacobianList)): oopt = self.outputVariables_optimization[i] # Checked about 20 lines above # if not isinstance(oopt, (pyomo.core.base.var.ScalarVar,pyomo.core.base.var.IndexedVar)): - # raise ValueError("Invalid type for output variable") + # raise ValueError("Invalid type for output variable") lopt = self.outputs[i] oounits = oopt.get_units() lounits = lopt.units # oshape = [len(idx) for idx in oopt.index_set().subsets()] ptr_col = 0 - for j in range(0, len(self.inputs)): + for j in range(0,len(self.inputs)): oipt = self.inputVariables_optimization[j] # This is checked about 80 lines up # if not isinstance(oipt, (pyomo.core.base.var.ScalarVar,pyomo.core.base.var.IndexedVar)): - # raise ValueError("Invalid type for output variable") + # raise ValueError("Invalid type for output variable") lipt = self.inputs[j] oiunits = oipt.get_units() liunits = lipt.units @@ -452,44 +412,31 @@ def fillCache(self): jacobianValue_raw = jacobianList[i][j] - if isinstance( - jacobianValue_raw, - ( - pyomo.core.expr.numeric_expr.NPV_ProductExpression, - pyomo.core.base.units_container._PyomoUnit, - ), - ): - corrected_value = pyo.value( - pyomo_units.convert(jacobianValue_raw, oounits / oiunits) - ) # now unitless in correct units - outputJacobian[ptr_row, ptr_col] = corrected_value + if isinstance(jacobianValue_raw, (pyomo.core.expr.numeric_expr.NPV_ProductExpression, + pyomo.core.base.units_container._PyomoUnit)): + corrected_value = pyo.value(pyomo_units.convert(jacobianValue_raw, oounits/oiunits)) # now unitless in correct units + outputJacobian[ptr_row,ptr_col] = corrected_value ptr_col += 1 ptr_row_step = 1 - elif isinstance( - jacobianValue_raw, pyomo.core.expr.numeric_expr.NumericNDArray - ): + elif isinstance(jacobianValue_raw,pyomo.core.expr.numeric_expr.NumericNDArray): jshape = jacobianValue_raw.shape if isinstance(oopt, pyomo.core.base.var.ScalarVar): oshape = 0 - else: # isinstance(oopt, pyomo.core.base.var.IndexedVar), checked above - oshape = [len(idx) for idx in oopt.index_set().subsets()] + else: # isinstance(oopt, pyomo.core.base.var.IndexedVar), checked above + oshape = [len(idx) for idx in oopt.index_set().subsets()] if isinstance(oipt, pyomo.core.base.var.ScalarVar): ishape = 0 - else: # isinstance(oipt, pyomo.core.base.var.IndexedVar), checked above - ishape = [len(idx) for idx in oipt.index_set().subsets()] + else: # isinstance(oipt, pyomo.core.base.var.IndexedVar), checked above + ishape = [len(idx) for idx in oipt.index_set().subsets()] if oshape == 0: validIndices = list(oipt.index_set().data()) for vi in validIndices: - corrected_value = pyo.value( - pyomo_units.convert( - jacobianValue_raw[vi], oounits / oiunits - ) - ) # now unitless in correct units - outputJacobian[ptr_row, ptr_col] = corrected_value + corrected_value = pyo.value(pyomo_units.convert(jacobianValue_raw[vi], oounits/oiunits)) # now unitless in correct units + outputJacobian[ptr_row,ptr_col] = corrected_value ptr_col += 1 ptr_row_step = 1 @@ -497,39 +444,30 @@ def fillCache(self): ptr_row_cache = ptr_row validIndices = list(oopt.index_set().data()) for vi in validIndices: - corrected_value = pyo.value( - pyomo_units.convert( - jacobianValue_raw[vi], oounits / oiunits - ) - ) # now unitless in correct units - outputJacobian[ptr_row, ptr_col] = corrected_value + corrected_value = pyo.value(pyomo_units.convert(jacobianValue_raw[vi], oounits/oiunits)) # now unitless in correct units + outputJacobian[ptr_row,ptr_col] = corrected_value ptr_row += 1 ptr_row = ptr_row_cache ptr_row_step = len(validIndices) # elif ishape == 0 and oshape == 0: # Handled by the scalar case above - else: + else: # both are dimensioned vectors - # oshape, ishape, jshape + #oshape, ishape, jshape ptr_row_cache = ptr_row ptr_col_cache = ptr_col validIndices_o = list(oopt.index_set().data()) validIndices_i = list(oipt.index_set().data()) for vio in validIndices_o: - if isinstance(vio, (float, int)): + if isinstance(vio, (float,int)): vio = (vio,) for vii in validIndices_i: - if isinstance(vii, (float, int)): + if isinstance(vii, (float,int)): vii = (vii,) - corrected_value = pyo.value( - pyomo_units.convert( - jacobianValue_raw[vio + vii], - oounits / oiunits, - ) - ) # now unitless in correct units - outputJacobian[ptr_row, ptr_col] = corrected_value + corrected_value = pyo.value(pyomo_units.convert(jacobianValue_raw[vio + vii], oounits/oiunits)) # now unitless in correct units + outputJacobian[ptr_row,ptr_col] = corrected_value ptr_col += 1 ptr_col = ptr_col_cache ptr_row += 1 @@ -539,41 +477,37 @@ def fillCache(self): else: raise ValueError("Invalid jacobian type") - ptr_row += ptr_row_step + ptr_row += ptr_row_step self._cache['pyomo_jacobian'] = sps.coo_matrix(outputJacobian) - # --------------------------------------------------------------------------------------------------------------------- - # --------------------------------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------------------------------- # These models must be defined in each individual model, just placeholders here def BlackBox(*args, **kwargs): raise AttributeError(errorString) + def convert(self, val, unts): try: val = val * pyomo_units.dimensionless except: - pass ## will handle later - - if isinstance( - val, - ( - pyomo.core.base.units_container._PyomoUnit, - pyomo.core.expr.numeric_expr.NPV_ProductExpression, - ), - ): + pass ## will handle later + + if isinstance(val, (pyomo.core.base.units_container._PyomoUnit,pyomo.core.expr.numeric_expr.NPV_ProductExpression)): return pyomo_units.convert(val, unts) elif isinstance(val, pyomo.core.expr.numeric_expr.NumericNDArray): shp = val.shape ix = np.ndindex(*shp) opt = np.zeros(shp) - for i in range(0, np.prod(shp)): + for i in range(0,np.prod(shp)): ixt = next(ix) - opt[ixt] = pyo.value(pyomo_units.convert(val[ixt], unts)) - return opt * unts + opt[ixt] = pyo.value(pyomo_units.convert(val[ixt],unts)) + return opt*unts else: raise ValueError('Invalid type passed to unit conversion function') + def pyomo_value(self, val): try: return pyo.value(val) @@ -582,19 +516,19 @@ def pyomo_value(self, val): shp = val.shape ix = np.ndindex(*shp) opt = np.zeros(shp) - for i in range(0, np.prod(shp)): + for i in range(0,np.prod(shp)): ixt = next(ix) opt[ixt] = pyo.value(val[ixt]) return opt else: raise ValueError('Invalid type passed to pyomo_value function') - # --------------------------------------------------------------------------------------------------------------------- - # --------------------------------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------------------------------- def parseInputs(self, *args, **kwargs): - args = list(args) # convert tuple to list + args = list(args) # convert tuple to list - inputNames = [self.inputs[i].name for i in range(0, len(self.inputs))] + inputNames = [self.inputs[i].name for i in range(0,len(self.inputs))] # ------------------------------ # ------------------------------ @@ -607,44 +541,32 @@ def parseInputs(self, *args, **kwargs): if len(inputNames) == 1: try: rs = self.sanitizeInputs(inputData) - return ( - [dict(zip(inputNames, [rs]))], - -self.availableDerivative - 1, - {}, - ) # one input being passed in + return [dict(zip(inputNames,[rs]))], -self.availableDerivative-1, {} # one input being passed in except: - pass # otherwise, proceed - - if isinstance(inputData, (list, tuple)): + pass #otherwise, proceed + + if isinstance(inputData, (list,tuple)): dataRuns = [] for idc in inputData: if isinstance(idc, dict): sips = self.sanitizeInputs(**idc) if len(inputNames) == 1: sips = [sips] - runDictS = dict(zip(inputNames, sips)) - dataRuns.append( - runDictS - ) # the BlackBox([{'x1':x1, 'x2':x2},{'x1':x1, 'x2':x2},...]) case - elif isinstance(idc, (list, tuple)): + runDictS = dict(zip(inputNames,sips)) + dataRuns.append(runDictS) # the BlackBox([{'x1':x1, 'x2':x2},{'x1':x1, 'x2':x2},...]) case + elif isinstance(idc,(list,tuple)): if len(idc) == len(inputNames): sips = self.sanitizeInputs(*idc) if len(inputNames) == 1: sips = [sips] - runDictS = dict(zip(inputNames, sips)) - dataRuns.append( - runDictS - ) # the BlackBox([ [x1, x2], [x1, x2],...]) case + runDictS = dict(zip(inputNames,sips)) + dataRuns.append(runDictS) # the BlackBox([ [x1, x2], [x1, x2],...]) case else: - raise ValueError( - 'Entry in input data list has improper length' - ) + raise ValueError('Entry in input data list has improper length') else: - raise ValueError( - "Invalid data type in the input list. Note that BlackBox([x1,x2,y]) must be passed in as BlackBox([[x1,x2,y]]) or " - + "BlackBox(*[x1,x2,y]) or BlackBox({'x1':x1,'x2':x2,'y':y}) or simply BlackBox(x1, x2, y) to avoid processing singularities. " - + "Best practice is BlackBox({'x1':x1,'x2':x2,'y':y})" - ) + raise ValueError("Invalid data type in the input list. Note that BlackBox([x1,x2,y]) must be passed in as BlackBox([[x1,x2,y]]) or "+ + "BlackBox(*[x1,x2,y]) or BlackBox({'x1':x1,'x2':x2,'y':y}) or simply BlackBox(x1, x2, y) to avoid processing singularities. "+ + "Best practice is BlackBox({'x1':x1,'x2':x2,'y':y})") return dataRuns, self.availableDerivative, {} elif isinstance(inputData, dict): @@ -655,73 +577,45 @@ def parseInputs(self, *args, **kwargs): sips = self.sanitizeInputs(**inputData) if len(inputNames) == 1: sips = [sips] - return ( - [dict(zip(inputNames, sips))], - -self.availableDerivative - 1, - {}, - ) # the BlackBox(*{'x1':x1, 'x2':x2}) case, somewhat likely - - if not all( - [ - inputLengths[i] == inputLengths[0] - for i in range(0, len(inputLengths)) - ] - ): + return [dict(zip(inputNames,sips))], -self.availableDerivative-1, {} # the BlackBox(*{'x1':x1, 'x2':x2}) case, somewhat likely + + if not all([inputLengths[i] == inputLengths[0] for i in range(0,len(inputLengths))]): sips = self.sanitizeInputs(**inputData) - return ( - [dict(zip(inputNames, sips))], - -self.availableDerivative - 1, - {}, - ) # the BlackBox(*{'x1':x1, 'x2':x2}) case where vectors for x1... are passed in (likely to fail on previous line) + return [dict(zip(inputNames,sips))], -self.availableDerivative-1, {} # the BlackBox(*{'x1':x1, 'x2':x2}) case where vectors for x1... are passed in (likely to fail on previous line) else: try: sips = self.sanitizeInputs(**inputData) - return ( - [dict(zip(inputNames, sips))], - -self.availableDerivative - 1, - {}, - ) # the BlackBox(*{'x1':x1, 'x2':x2}) case where vectors all inputs have same length intentionally (likely to fail on previous line) + return [dict(zip(inputNames,sips))], -self.availableDerivative-1, {} # the BlackBox(*{'x1':x1, 'x2':x2}) case where vectors all inputs have same length intentionally (likely to fail on previous line) except: dataRuns = [] - for i in range(0, inputLengths[0]): + for i in range(0,inputLengths[0]): runDict = {} - for ky, vl in inputData.items(): + for ky,vl in inputData.items(): runDict[ky] = vl[i] sips = self.sanitizeInputs(**runDict) if len(inputNames) == 1: sips = [sips] - runDictS = dict(zip(inputNames, sips)) + runDictS = dict(zip(inputNames,sips)) dataRuns.append(runDictS) - return ( - dataRuns, - self.availableDerivative, - {}, - ) # the BlackBox({'x1':x1_vec, 'x2':x2_vec}) case, most likely + return dataRuns, self.availableDerivative, {} # the BlackBox({'x1':x1_vec, 'x2':x2_vec}) case, most likely else: raise ValueError('Keywords did not match the expected list') else: - raise ValueError('Got unexpected data type %s' % (str(type(inputData)))) + raise ValueError('Got unexpected data type %s'%(str(type(inputData)))) # ------------------------------ # ------------------------------ else: - if any( - [ - list(kwargs.keys())[i] in inputNames - for i in range(0, len(list(kwargs.keys()))) - ] - ): + if any([ list(kwargs.keys())[i] in inputNames for i in range(0,len(list(kwargs.keys()))) ]): # some of the inputs are defined in the kwargs if len(args) >= len(inputNames): - raise ValueError( - 'A keyword input is defining an input, but there are too many unkeyed arguments for this to occur. Check the inputs.' - ) + raise ValueError('A keyword input is defining an input, but there are too many unkeyed arguments for this to occur. Check the inputs.') else: if len(args) != 0: - availableKeywords = inputNames[-len(args) :] - else: + availableKeywords = inputNames[-len(args):] + else: availableKeywords = inputNames - valList = args + [None] * (len(inputNames) - len(args)) + valList = args + [None]*(len(inputNames)-len(args)) for ky in availableKeywords: ix = inputNames.index(ky) valList[ix] = kwargs[ky] @@ -730,7 +624,7 @@ def parseInputs(self, *args, **kwargs): # if any([valList[i]==None for i in range(0,len(valList))]): # raise ValueError('Keywords did not properly fill in the remaining arguments. Check the inputs.') - sips = self.sanitizeInputs(*valList) + sips = self.sanitizeInputs(*valList) if len(inputNames) == 1: sips = [sips] @@ -743,26 +637,18 @@ def parseInputs(self, *args, **kwargs): # was in args pass - return ( - [dict(zip(inputNames, sips))], - -self.availableDerivative - 1, - remainingKwargs, - ) # Mix of args and kwargs define inputs + return [dict(zip(inputNames,sips))], -self.availableDerivative-1, remainingKwargs # Mix of args and kwargs define inputs else: # all of the inputs are in the args try: - sips = self.sanitizeInputs(*args[0 : len(inputNames)]) + sips = self.sanitizeInputs(*args[0:len(inputNames)]) if len(inputNames) == 1: sips = [sips] remainingKwargs = copy.deepcopy(kwargs) - remainingKwargs['remainingArgs'] = args[len(inputNames) :] - return ( - [dict(zip(inputNames, sips))], - -self.availableDerivative - 1, - remainingKwargs, - ) # all inputs are in args + remainingKwargs['remainingArgs'] = args[len(inputNames):] + return [dict(zip(inputNames,sips))], -self.availableDerivative-1, remainingKwargs # all inputs are in args # except: except Exception as e: # not possible due to other checks @@ -770,62 +656,46 @@ def parseInputs(self, *args, **kwargs): # raise ValueError(e) if str(e) == 'Not enough inputs': raise ValueError(e) - else: # otherwise, proceed - runCases, returnMode, extra_singleInput = self.parseInputs( - args[0] - ) + else:#otherwise, proceed + runCases, returnMode, extra_singleInput = self.parseInputs(args[0]) remainingKwargs = copy.deepcopy(kwargs) - remainingKwargs['remainingArgs'] = args[len(inputNames) :] + remainingKwargs['remainingArgs'] = args[len(inputNames):] return runCases, returnMode, remainingKwargs - # --------------------------------------------------------------------------------------------------------------------- - # --------------------------------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------------------------------- def sizeCheck(self, size, ipval_correctUnits): if size is not None: szVal = ipval_correctUnits - if isinstance( - szVal, - ( - pyomo.core.expr.numeric_expr.NPV_ProductExpression, - pyomo.core.base.units_container._PyomoUnit, - ), - ): - if size != 0 and size != 1: - raise ValueError( - 'Size did not match the expected size %s (ie: Scalar)' - % (str(size)) - ) + if isinstance(szVal, (pyomo.core.expr.numeric_expr.NPV_ProductExpression, + pyomo.core.base.units_container._PyomoUnit)): + if size != 0 and size != 1 : + raise ValueError('Size did not match the expected size %s (ie: Scalar)'%(str(size))) elif isinstance(szVal, pyomo.core.expr.numeric_expr.NumericNDArray): shp = szVal.shape - if isinstance(size, (int, float)): + if isinstance(size,(int,float)): size = [size] # else: if len(shp) != len(size): - raise ValueError( - 'Shapes/Sizes of %s does not match the expected %s' - % (str(shp), str(size)) - ) - for j in range(0, len(shp)): + raise ValueError('Shapes/Sizes of %s does not match the expected %s'%(str(shp),str(size))) + for j in range(0,len(shp)): if size[j] != -1: # was declared of flexible length if size[j] != shp[j]: - raise ValueError( - 'Shapes/Sizes of %s does not match the expected %s' - % (str(shp), str(size)) - ) + raise ValueError('Shapes/Sizes of %s does not match the expected %s'%(str(shp),str(size))) else: raise ValueError('Invalid type detected when checking size') - # --------------------------------------------------------------------------------------------------------------------- - # --------------------------------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------------------------------- def sanitizeInputs(self, *args, **kwargs): - nameList = [self.inputs[i].name for i in range(0, len(self.inputs))] + nameList = [self.inputs[i].name for i in range(0,len(self.inputs))] if len(args) + len(kwargs.values()) > len(nameList): raise ValueError('Too many inputs') if len(args) + len(kwargs.values()) < len(nameList): raise ValueError('Not enough inputs') inputDict = {} - for i in range(0, len(args)): + for i in range(0,len(args)): rg = args[i] inputDict[nameList[i]] = rg @@ -833,42 +703,34 @@ def sanitizeInputs(self, *args, **kwargs): if ky in nameList: inputDict[ky] = vl else: - raise ValueError( - 'Unexpected input keyword argument %s in the inputs' % (ky) - ) + raise ValueError('Unexpected input keyword argument %s in the inputs'%(ky)) opts = [] - for i in range(0, len(nameList)): + for i in range(0,len(nameList)): name = nameList[i] nameCheck = self.inputs[i].name unts = self.inputs[i].units size = self.inputs[i].size - + # should be impossible # if name != nameCheck: # raise RuntimeError('Something went wrong and values are not consistent. Check your defined inputs.') ipval = inputDict[name] - if isinstance(ipval, pyomo.core.expr.numeric_expr.NumericNDArray): - for ii in range(0, len(ipval)): + if isinstance(ipval, pyomo.core.expr.numeric_expr.NumericNDArray): + for ii in range(0,len(ipval)): try: - ipval[ii] = self.convert(ipval[ii], unts) # ipval.to(unts) + ipval[ii] = self.convert(ipval[ii], unts)#ipval.to(unts) except: - raise ValueError( - 'Could not convert %s of %s to %s' - % (name, str(ipval), str(unts)) - ) + raise ValueError('Could not convert %s of %s to %s'%(name, str(ipval),str(unts))) ipval_correctUnits = ipval else: try: - ipval_correctUnits = self.convert(ipval, unts) # ipval.to(unts) + ipval_correctUnits = self.convert(ipval, unts)#ipval.to(unts) except: - raise ValueError( - 'Could not convert %s of %s to %s' - % (name, str(ipval), str(unts)) - ) + raise ValueError('Could not convert %s of %s to %s'%(name, str(ipval),str(unts))) # superseded by the custom convert function # if not isinstance(ipval_correctUnits, (pyomo.core.expr.numeric_expr.NPV_ProductExpression, @@ -884,8 +746,8 @@ def sanitizeInputs(self, *args, **kwargs): return opts - # --------------------------------------------------------------------------------------------------------------------- - # --------------------------------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------------------------------- def checkOutputs(self, *args, **kwargs): raise NotImplementedError('Contact developers to use this function') # nameList = [self.outputs[i].name for i in range(0,len(self.outputs))] @@ -911,7 +773,7 @@ def checkOutputs(self, *args, **kwargs): # nameCheck = self.outputs[i].name # unts = self.outputs[i].units # size = self.outputs[i].size - + # if name != nameCheck: # raise RuntimeError('Something went wrong and values are not consistent. Check your defined inputs.') @@ -938,15 +800,15 @@ def checkOutputs(self, *args, **kwargs): # return opts - # --------------------------------------------------------------------------------------------------------------------- - # --------------------------------------------------------------------------------------------------------------------- - def getSummary(self, whitespace=6): +# --------------------------------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------------------------------- + def getSummary(self, whitespace = 6): pstr = '\n' pstr += 'Model Description\n' pstr += '=================\n' descr_str = self.description.__repr__() pstr += descr_str[1:-1] + '\n\n' - + longestName = 0 longestUnits = 0 longestSize = 0 @@ -954,7 +816,7 @@ def getSummary(self, whitespace=6): nml = len(ipt.name) if nml > longestName: longestName = nml - unts = ipt.units.__str__() # _repr_html_() + unts = ipt.units.__str__()#_repr_html_() # unts = unts.replace('','^') # unts = unts.replace('','') # unts = unts.replace('\[', '[') @@ -971,37 +833,37 @@ def getSummary(self, whitespace=6): namespace = max([4, longestName]) + whitespace unitspace = max([5, longestUnits]) + whitespace sizespace = max([4, longestSize]) + whitespace - fulllength = namespace + unitspace + sizespace + 11 + fulllength = namespace+unitspace+sizespace+11 pstr += 'Inputs\n' - pstr += '=' * fulllength + pstr += '='*fulllength pstr += '\n' pstr += 'Name'.ljust(namespace) pstr += 'Units'.ljust(unitspace) pstr += 'Size'.ljust(sizespace) pstr += 'Description' pstr += '\n' - pstr += '-' * (namespace - whitespace) - pstr += ' ' * whitespace - pstr += '-' * (unitspace - whitespace) - pstr += ' ' * whitespace - pstr += '-' * (sizespace - whitespace) - pstr += ' ' * whitespace + pstr += '-'*(namespace - whitespace) + pstr += ' '*whitespace + pstr += '-'*(unitspace - whitespace) + pstr += ' '*whitespace + pstr += '-'*(sizespace - whitespace) + pstr += ' '*whitespace pstr += '-----------' pstr += '\n' for ipt in self.inputs: pstr += ipt.name.ljust(namespace) - unts = ipt.units.__str__() # _repr_html_() + unts = ipt.units.__str__()#_repr_html_() # unts = unts.replace('','^') # unts = unts.replace('','') # unts = unts.replace('\[', '[') # unts = unts.replace('\]', ']') pstr += unts.ljust(unitspace) - lnstr = '%s' % (ipt.size.__repr__()) + lnstr = '%s'%(ipt.size.__repr__()) pstr += lnstr.ljust(sizespace) pstr += ipt.description pstr += '\n' pstr += '\n' - + longestName = 0 longestUnits = 0 longestSize = 0 @@ -1009,7 +871,7 @@ def getSummary(self, whitespace=6): nml = len(opt.name) if nml > longestName: longestName = nml - unts = opt.units.__str__() # _repr_html_() + unts = opt.units.__str__()#_repr_html_() # unts = unts.replace('','^') # unts = unts.replace('','') # unts = unts.replace('\[', '[') @@ -1026,51 +888,51 @@ def getSummary(self, whitespace=6): namespace = max([4, longestName]) + whitespace unitspace = max([5, longestUnits]) + whitespace sizespace = max([4, longestSize]) + whitespace - fulllength = namespace + unitspace + sizespace + 11 + fulllength = namespace+unitspace+sizespace+11 pstr += 'Outputs\n' - pstr += '=' * fulllength + pstr += '='*fulllength pstr += '\n' pstr += 'Name'.ljust(namespace) pstr += 'Units'.ljust(unitspace) pstr += 'Size'.ljust(sizespace) pstr += 'Description' pstr += '\n' - pstr += '-' * (namespace - whitespace) - pstr += ' ' * whitespace - pstr += '-' * (unitspace - whitespace) - pstr += ' ' * whitespace - pstr += '-' * (sizespace - whitespace) - pstr += ' ' * whitespace + pstr += '-'*(namespace - whitespace) + pstr += ' '*whitespace + pstr += '-'*(unitspace - whitespace) + pstr += ' '*whitespace + pstr += '-'*(sizespace - whitespace) + pstr += ' '*whitespace pstr += '-----------' pstr += '\n' for opt in self.outputs: pstr += opt.name.ljust(namespace) - unts = opt.units.__str__() # _repr_html_() + unts = opt.units.__str__()#_repr_html_() # unts = unts.replace('','^') # unts = unts.replace('','') # unts = unts.replace('\[', '[') # unts = unts.replace('\]', ']') pstr += unts.ljust(unitspace) - lnstr = '%s' % (opt.size.__repr__()) + lnstr = '%s'%(opt.size.__repr__()) pstr += lnstr.ljust(sizespace) pstr += opt.description pstr += '\n' pstr += '\n' - + return pstr - # --------------------------------------------------------------------------------------------------------------------- - # --------------------------------------------------------------------------------------------------------------------- - +# --------------------------------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------------------------------- + @property def summary(self): return self.getSummary() - # --------------------------------------------------------------------------------------------------------------------- - # --------------------------------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------------------------------- def __repr__(self): pstr = 'AnalysisModel( [' - for i in range(0, len(self.outputs)): + for i in range(0,len(self.outputs)): pstr += self.outputs[i].name pstr += ',' pstr = pstr[0:-1] @@ -1085,4 +947,4 @@ def __repr__(self): pstr += ' , ' pstr = pstr[0:-2] pstr += '])' - return pstr + return pstr \ No newline at end of file diff --git a/pyomo/contrib/edi/examples/aircraft_gp.py b/pyomo/contrib/edi/examples/aircraft_gp.py index 4d6b4be3259..c20f06f5497 100644 --- a/pyomo/contrib/edi/examples/aircraft_gp.py +++ b/pyomo/contrib/edi/examples/aircraft_gp.py @@ -24,71 +24,48 @@ # ================= # Declare Variables # ================= -D = f.Variable(name="D", guess=300, units="N", description="total drag force") -A = f.Variable(name="A", guess=10.0, units="-", description="aspect ratio") -S = f.Variable(name="S", guess=10.0, units="m^2", description="total wing area") -V = f.Variable(name="V", guess=30.0, units="m/s", description="cruising speed") -W = f.Variable(name="W", guess=10000.0, units="N", description="total aircraft weight") -Re = f.Variable(name="Re", guess=3e6, units="-", description="Reynold's number") -C_D = f.Variable( - name="C_D", guess=0.025, units="-", description="Drag coefficient of wing" -) -C_L = f.Variable( - name="C_L", guess=0.5, units="-", description="Lift coefficient of wing" -) -C_f = f.Variable( - name="C_f", guess=0.003, units="-", description="skin friction coefficient" -) -W_w = f.Variable(name="W_w", guess=2500, units="N", description="wing weight") + +A = f.Variable(name = "A", guess = 10.0, units = "-", description = "aspect ratio") +C_D = f.Variable(name = "C_D", guess = .025, units = "-", description = "Drag coefficient of wing") +C_f = f.Variable(name = "C_f", guess = .003, units = "-", description = "skin friction coefficient") +C_L = f.Variable(name = "C_L", guess = .5, units = "-", description = "Lift coefficient of wing") +D = f.Variable(name = "D", guess = 300, units = "N", description = "total drag force") +Re = f.Variable(name = "Re", guess = 3e6, units = "-", description = "Reynold's number") +S = f.Variable(name = "S", guess = 10.0, units = "m^2", description = "total wing area") +V = f.Variable(name = "V", guess = 30.0, units = "m/s", description = "cruising speed") +W = f.Variable(name = "W", guess = 10000.0, units = "N", description = "total aircraft weight") +W_w = f.Variable(name = "W_w", guess = 2500, units = "N", description = "wing weight") # ================= # Declare Constants # ================= -k = f.Constant(name="k", value=1.2, units="-", description="form factor") -e = f.Constant(name="e", value=0.96, units="-", description="Oswald efficiency factor") -mu = f.Constant( - name="mu", value=1.78e-5, units="kg/m/s", description="viscosity of air" -) -rho = f.Constant(name="rho", value=1.23, units="kg/m^3", description="density of air") -tau = f.Constant( - name="tau", value=0.12, units="-", description="airfoil thickness to chord ratio" -) -N_ult = f.Constant( - name="N_ult", value=2.5, units="-", description="ultimate load factor" -) -V_min = f.Constant(name="V_min", value=22, units="m/s", description="takeoff speed") -C_Lmax = f.Constant( - name="C_Lmax", value=2.0, units="-", description="max CL with flaps down" -) -S_wetratio = f.Constant( - name="Srat", value=2.05, units="-", description="wetted area ratio" -) -W_W_coeff1 = f.Constant( - name="W_c1", value=8.71e-5, units="1/m", description="Wing Weight Coefficient 1" -) -W_W_coeff2 = f.Constant( - name="W_c2", value=45.24, units="Pa", description="Wing Weight Coefficient 2" -) -CDA0 = f.Constant( - name="CDA0", value=0.0306, units="m^2", description="fuselage drag area" -) -W_0 = f.Constant( - name="W_0", value=4940.0, units="N", description="aircraft weight excluding wing" -) +C_Lmax = f.Constant(name = "C_Lmax", value = 2.0, units = "-", description = "max CL with flaps down") +CDA0 = f.Constant(name = "CDA0", value = 0.0306, units = "m^2", description = "fuselage drag area") +e = f.Constant(name = "e", value = 0.96, units = "-", description = "Oswald efficiency factor") +k = f.Constant(name = "k", value = 1.2, units = "-", description = "form factor") +mu = f.Constant(name = "mu", value = 1.78e-5, units = "kg/m/s", description = "viscosity of air") +N_ult = f.Constant(name = "N_ult", value = 2.5, units = "-", description = "ultimate load factor") +rho = f.Constant(name = "rho", value = 1.23, units = "kg/m^3", description = "density of air") +S_wetratio = f.Constant(name = "Srat", value = 2.05, units = "-", description = "wetted area ratio") +tau = f.Constant(name = "tau", value = 0.12, units = "-", description = "airfoil thickness to chord ratio") +V_min = f.Constant(name = "V_min", value = 22, units = "m/s", description = "takeoff speed") +W_0 = f.Constant(name = "W_0", value = 4940.0, units = "N", description = "aircraft weight excluding wing") +W_W_coeff1 = f.Constant(name = "W_c1", value = 8.71e-5, units = "1/m", description = "Wing Weight Coefficient 1") +W_W_coeff2 = f.Constant(name = "W_c2", value = 45.24, units = "Pa", description = "Wing Weight Coefficient 2") # ===================== # Declare the Objective # ===================== -f.Objective(D) +f.Objective( D ) # =================================== # Declare some intermediate variables # =================================== pi = np.pi -C_D_fuse = CDA0 / S -C_D_wpar = k * C_f * S_wetratio -C_D_ind = C_L**2 / (pi * A * e) -W_w_strc = W_W_coeff1 * (N_ult * A**1.5 * (W_0 * W * S) ** 0.5) / tau +C_D_fuse = CDA0/S +C_D_wpar = k*C_f*S_wetratio +C_D_ind = C_L**2/(pi*A*e) +W_w_strc = W_W_coeff1*(N_ult*A**1.5*(W_0*W*S)**0.5)/tau W_w_surf = W_W_coeff2 * S # ======================= @@ -98,11 +75,11 @@ [ C_D >= C_D_fuse + C_D_wpar + C_D_ind, W_w >= W_w_surf + W_w_strc, - D >= 0.5 * rho * S * C_D * V**2, - Re == (rho / mu) * V * (S / A) ** 0.5, - C_f == 0.074 / Re**0.2, - W == 0.5 * rho * S * C_L * V**2, - W == 0.5 * rho * S * C_Lmax * V_min**2, + D >= 0.5*rho*S*C_D*V**2, + Re == (rho/mu)*V*(S/A)**0.5, + C_f == 0.074/Re**0.2, + W == 0.5*rho*S*C_L*V**2, + W == 0.5*rho*S*C_Lmax*V_min**2, W >= W_0 + W_w, - ] -) + ] +) \ No newline at end of file diff --git a/pyomo/contrib/edi/examples/readme_example.py b/pyomo/contrib/edi/examples/readme_example.py index aa27a0e0914..d4c80eaefa0 100644 --- a/pyomo/contrib/edi/examples/readme_example.py +++ b/pyomo/contrib/edi/examples/readme_example.py @@ -14,61 +14,81 @@ # ================= # Declare Variables # ================= -x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') -y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') -z = f.Variable(name='z', guess=1.0, units='m^2', description='Model output') +x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') +y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') +z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'Model output') # ================= # Declare Constants # ================= -c = f.Constant(name='c', value=1.0, units='', description='A constant c', size=2) +c = f.Constant( name = 'c', + value = 1.0, + units = '', + description = 'A constant c', + size = 2 ) # ===================== # Declare the Objective # ===================== -f.Objective(c[0] * x + c[1] * y) - +f.Objective( + c[0]*x + c[1]*y +) # =================== # Declare a Black Box # =================== class UnitCircle(BlackBoxFunctionModel): - def __init__(self): # The initialization function + def __init__(self): # The initialization function + # Initialize the black box model super().__init__() # A brief description of the model self.description = 'This model evaluates the function: z = x**2 + y**2' - + # Declare the black box model inputs - self.inputs.append(name='x', units='ft', description='The x variable') - self.inputs.append(name='y', units='ft', description='The y variable') + self.inputs.append( name = 'x', + units = 'ft' , + description = 'The x variable') + self.inputs.append( name = 'y', + units = 'ft' , + description = 'The y variable') # Declare the black box model outputs - self.outputs.append( - name='z', units='ft**2', description='Resultant of the unit circle' - ) + self.outputs.append( name = 'z', + units = 'ft**2', + description = 'Resultant of the unit circle') # Declare the maximum available derivative self.availableDerivative = 1 - - def BlackBox(self, x, y): # The actual function that does things + + def BlackBox(self, x, y): # The actual function that does things # Converts to correct units then casts to float - x = pyo.value(units.convert(x, self.inputs['x'].units)) - y = pyo.value(units.convert(y, self.inputs['y'].units)) + x = pyo.value(units.convert(x,self.inputs['x'].units)) + y = pyo.value(units.convert(y,self.inputs['y'].units)) - z = x**2 + y**2 # Compute z - dzdx = 2 * x # Compute dz/dx - dzdy = 2 * y # Compute dz/dy + z = x**2 + y**2 # Compute z + dzdx = 2*x # Compute dz/dx + dzdy = 2*y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... - + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... # ======================= # Declare the Constraints # ======================= -f.ConstraintList([[z, '==', [x, y], UnitCircle()], z <= 1 * units.m**2]) +f.ConstraintList( + [ + [ z, '==', [x,y], UnitCircle() ] , + z <= 1*units.m**2 + ] +) + +# ============================================= +# Run the black box (improves coverage metrics) +# ============================================= +uc = UnitCircle() +bbo = uc.BlackBox(0.5*units.m , 0.5*units.m) diff --git a/pyomo/contrib/edi/formulation.py b/pyomo/contrib/edi/formulation.py index f422c50cea3..b57292ba540 100644 --- a/pyomo/contrib/edi/formulation.py +++ b/pyomo/contrib/edi/formulation.py @@ -16,16 +16,13 @@ # ___________________________________________________________________________ import pyomo -import pyomo.environ as pyo +import pyomo.environ as pyo from pyomo.util.check_units import assert_units_consistent from pyomo.environ import ConcreteModel from pyomo.environ import Var, Param, Objective, Constraint, Set from pyomo.environ import maximize, minimize from pyomo.environ import units as pyomo_units -from pyomo.contrib.pynumero.interfaces.external_grey_box import ( - ExternalGreyBoxModel, - ExternalGreyBoxBlock, -) +from pyomo.contrib.pynumero.interfaces.external_grey_box import ExternalGreyBoxModel, ExternalGreyBoxBlock from pyomo.environ import ( Reals, PositiveReals, @@ -46,7 +43,7 @@ PercentFraction, RealInterval, IntegerInterval, -) +) domainList = [ Reals, @@ -70,17 +67,15 @@ IntegerInterval, ] - def decodeUnits(u_val): - if isinstance(u_val, str): - if u_val in ['', '-', 'None', ' ', 'dimensionless']: + if isinstance(u_val,str): + if u_val in ['','-','None',' ','dimensionless']: return pyomo_units.__getattr__('dimensionless') else: return pyomo_units.__getattr__(u_val) else: return u_val - - + class Formulation(ConcreteModel): def __init__(self): super(Formulation, self).__init__() @@ -89,18 +84,17 @@ def __init__(self): self._objective_counter = 0 self._constraint_counter = 0 - self._variable_keys = [] - self._constant_keys = [] + self._variable_keys = [] + self._constant_keys = [] self._objective_keys = [] self._runtimeObjective_keys = [] self._objective_keys = [] self._runtimeConstraint_keys = [] self._constraint_keys = [] self._allConstraint_keys = [] + + def Variable(self, name, guess, units, description='', size = None, bounds=None, domain=None): - def Variable( - self, name, guess, units, description='', size=None, bounds=None, domain=None - ): if domain is None: domain = Reals else: @@ -108,102 +102,47 @@ def Variable( raise RuntimeError("Invalid domain") if bounds is not None: - if not isinstance(bounds, (list, tuple)): - raise ValueError( - 'The keyword bounds must be a 2 length list or tuple of floats' - ) - if len(bounds) != 2: - raise ValueError( - 'The keyword bounds must be a 2 length list or tuple of floats' - ) + if not isinstance(bounds,(list,tuple)): + raise ValueError('The keyword bounds must be a 2 length list or tuple of floats') + if len(bounds)!=2: + raise ValueError('The keyword bounds must be a 2 length list or tuple of floats') if not isinstance(bounds[0], (float, int)): - raise ValueError( - 'The keyword bounds must be a 2 length list or tuple of floats' - ) + raise ValueError('The keyword bounds must be a 2 length list or tuple of floats') if not isinstance(bounds[1], (float, int)): - raise ValueError( - 'The keyword bounds must be a 2 length list or tuple of floats' - ) + raise ValueError('The keyword bounds must be a 2 length list or tuple of floats') if bounds[0] > bounds[1]: raise ValueError("Lower bound is higher than upper bound") + if size is not None: - if isinstance(size, (list, tuple)): - for i in range(0, len(size)): - if not isinstance(size[i], int): - raise ValueError( - 'Invalid size. Must be an integer or list/tuple of integers' - ) - if size[i] == 1 or size[i] == 0: - raise ValueError( - 'A value of 0 or 1 is not valid for defining size. Use fewer dimensions.' - ) + if isinstance(size,(list, tuple)): + for i in range(0,len(size)): + if not isinstance(size[i],int): + raise ValueError('Invalid size. Must be an integer or list/tuple of integers') + if size[i] == 1 or size[i] == 0 : + raise ValueError('A value of 0 or 1 is not valid for defining size. Use fewer dimensions.') if i == 0: - st = Set(initialize=list(range(0, size[i]))) + st = pyo.Set(initialize=list(range(0,size[i]))) else: - st *= Set(initialize=list(range(0, size[i]))) + st *= pyo.Set(initialize=list(range(0,size[i]))) st.construct() - self.add_component( - name, - Var( - st, - name=name, - initialize=guess, - domain=domain, - bounds=bounds, - doc=description, - units=decodeUnits(units), - ), - ) + self.add_component(name, pyo.Var(st, name=name, initialize=guess, domain=domain, bounds=bounds, doc=description, units=decodeUnits(units))) else: if isinstance(size, int): if size == 1 or size == 0: - self.add_component( - name, - Var( - name=name, - initialize=guess, - domain=domain, - bounds=bounds, - doc=description, - units=decodeUnits(units), - ), - ) + self.add_component(name, pyo.Var(name=name, initialize=guess, domain=domain, bounds=bounds, doc=description, units=decodeUnits(units))) else: - st = Set(initialize=list(range(0, size))) + st = pyo.Set(initialize=list(range(0,size))) st.construct() - self.add_component( - name, - Var( - st, - name=name, - initialize=guess, - domain=domain, - bounds=bounds, - doc=description, - units=decodeUnits(units), - ), - ) + self.add_component(name, pyo.Var(st, name=name, initialize=guess, domain=domain, bounds=bounds, doc=description, units=decodeUnits(units))) else: - raise ValueError( - 'Invalid size. Must be an integer or list/tuple of integers' - ) + raise ValueError('Invalid size. Must be an integer or list/tuple of integers') else: - self.add_component( - name, - Var( - name=name, - initialize=guess, - domain=domain, - bounds=bounds, - doc=description, - units=decodeUnits(units), - ), - ) + self.add_component(name, pyo.Var(name=name, initialize=guess, domain=domain, bounds=bounds, doc=description, units=decodeUnits(units))) self.__dict__[name].construct() self._variable_keys.append(name) return self.__dict__[name] - + def Constant(self, name, value, units, description='', size=None, within=None): if within is None: within = Reals @@ -212,130 +151,76 @@ def Constant(self, name, value, units, description='', size=None, within=None): raise RuntimeError("Invalid within") if size is not None: - if isinstance(size, (list, tuple)): - for i in range(0, len(size)): - if not isinstance(size[i], int): - raise ValueError( - 'Invalid size. Must be an integer or list/tuple of integers' - ) + if isinstance(size,(list, tuple)): + for i in range(0,len(size)): + if not isinstance(size[i],int): + raise ValueError('Invalid size. Must be an integer or list/tuple of integers') if size[i] == 1: - raise ValueError( - 'A value of 1 is not valid for defining size. Use fewer dimensions.' - ) + raise ValueError('A value of 1 is not valid for defining size. Use fewer dimensions.') if i == 0: - st = Set(initialize=list(range(0, size[i]))) + st = pyo.Set(initialize=list(range(0,size[i]))) else: - st *= Set(initialize=list(range(0, size[i]))) + st *= pyo.Set(initialize=list(range(0,size[i]))) st.construct() - self.add_component( - name, - Param( - st, - name=name, - initialize=value, - within=within, - doc=description, - units=decodeUnits(units), - mutable=True, - ), - ) + self.add_component(name, pyo.Param(st, name=name, initialize=value, within=within, doc=description, units=decodeUnits(units), mutable=True)) else: if isinstance(size, int): if size == 1 or size == 0: - self.add_component( - name, - Param( - name=name, - initialize=value, - within=within, - doc=description, - units=decodeUnits(units), - mutable=True, - ), - ) + self.add_component(name, pyo.Param(name=name, initialize=value, within=within, doc=description, units=decodeUnits(units), mutable=True)) else: - st = Set(initialize=list(range(0, size))) + st = pyo.Set(initialize=list(range(0,size))) st.construct() - self.add_component( - name, - Param( - st, - name=name, - initialize=value, - within=within, - doc=description, - units=decodeUnits(units), - mutable=True, - ), - ) + self.add_component(name, pyo.Param(st, name=name, initialize=value, within=within, doc=description, units=decodeUnits(units), mutable=True)) else: - raise ValueError( - 'Invalid size. Must be an integer or list/tuple of integers' - ) + raise ValueError('Invalid size. Must be an integer or list/tuple of integers') else: - self.add_component( - name, - Param( - name=name, - initialize=value, - within=within, - doc=description, - units=decodeUnits(units), - mutable=True, - ), - ) - + self.add_component(name, pyo.Param(name=name, initialize=value, within=within, doc=description, units=decodeUnits(units), mutable=True)) + self.__dict__[name].construct() self._constant_keys.append(name) return self.__dict__[name] - + def Objective(self, expr, sense=minimize): self._objective_counter += 1 - self.add_component( - 'objective_' + str(self._objective_counter), - Objective(expr=expr, sense=sense), - ) - self._objective_keys.append('objective_' + str(self._objective_counter)) - self.__dict__['objective_' + str(self._objective_counter)].construct() - + self.add_component('objective_'+str(self._objective_counter) , pyo.Objective(expr=expr,sense=sense)) + self._objective_keys.append('objective_'+str(self._objective_counter)) + self.__dict__['objective_'+str(self._objective_counter)].construct() + # def RuntimeObjective(self): # pass - + def Constraint(self, expr): self._constraint_counter += 1 - conName = 'constraint_' + str(self._constraint_counter) - self.add_component(conName, Constraint(expr=expr)) + conName = 'constraint_'+str(self._constraint_counter) + self.add_component(conName, pyo.Constraint(expr=expr)) self._constraint_keys.append(conName) self._allConstraint_keys.append(conName) self.__dict__[conName].construct() + def RuntimeConstraint(self, outputs, operators, inputs, black_box): self._constraint_counter += 1 - conName = 'constraint_' + str(self._constraint_counter) + conName = 'constraint_'+str(self._constraint_counter) self._runtimeConstraint_keys.append(conName) self._allConstraint_keys.append(conName) - self.add_component(conName, ExternalGreyBoxBlock()) + self.add_component(conName, ExternalGreyBoxBlock() ) self.__dict__[conName].construct() # TODO: Need to include operators after Michael fixes things - inputs_raw = inputs - outputs_raw = outputs + inputs_raw = inputs + outputs_raw = outputs operators_raw = operators - if isinstance( - inputs_raw, (pyomo.core.base.var.IndexedVar, pyomo.core.base.var.ScalarVar) - ): + if isinstance(inputs_raw, (pyomo.core.base.var.IndexedVar,pyomo.core.base.var.ScalarVar)): inputs_raw = [inputs_raw] elif isinstance(inputs_raw, (list, tuple)): inputs_raw = list(inputs_raw) else: raise ValueError("Invalid type for input variables") - if isinstance( - outputs_raw, (pyomo.core.base.var.IndexedVar, pyomo.core.base.var.ScalarVar) - ): + if isinstance(outputs_raw, (pyomo.core.base.var.IndexedVar,pyomo.core.base.var.ScalarVar)): outputs_raw = [outputs_raw] elif isinstance(outputs_raw, (list, tuple)): outputs_raw = list(outputs_raw) @@ -343,19 +228,17 @@ def RuntimeConstraint(self, outputs, operators, inputs, black_box): raise ValueError("Invalid type for output variables") for lst in [outputs_raw, inputs_raw]: for vr in lst: - if not isinstance( - vr, (pyomo.core.base.var.IndexedVar, pyomo.core.base.var.ScalarVar) - ): - raise ValueError("Invalid type when checking inputs and outputs") + if not isinstance(vr, (pyomo.core.base.var.IndexedVar,pyomo.core.base.var.ScalarVar)): + raise ValueError("Invalid type when checking inputs and outputs") if isinstance(operators_raw, (list, tuple)): operators_raw = list(operators_raw) - elif isinstance(operators_raw, str): + elif isinstance(operators_raw,str): operators_raw = [operators_raw] else: raise ValueError("Invalid type for operators") for opr in operators_raw: - if opr not in ["==", ">=", "<="]: + if opr not in ["==",">=","<="]: raise ValueError("Invalid operator") black_box.setOptimizationVariables(inputs_raw, outputs_raw) @@ -367,7 +250,7 @@ def RuntimeConstraint(self, outputs, operators, inputs, black_box): for ovar in outputs_raw: if isinstance(ovar, pyomo.core.base.var.ScalarVar): outputs_unwrapped.append(ovar) - else: # isinstance(ovar, pyomo.core.base.var.IndexedVar), validated above + else: # isinstance(ovar, pyomo.core.base.var.IndexedVar), validated above validIndices = list(ovar.index_set().data()) for vi in validIndices: outputs_unwrapped.append(ovar[vi]) @@ -376,25 +259,24 @@ def RuntimeConstraint(self, outputs, operators, inputs, black_box): for ivar in inputs_raw: if isinstance(ivar, pyomo.core.base.var.ScalarVar): inputs_unwrapped.append(ivar) - else: # isinstance(ivar, pyomo.core.base.var.IndexedVar), validated above + else: # isinstance(ivar, pyomo.core.base.var.IndexedVar), validated above validIndices = list(ivar.index_set().data()) for vi in validIndices: - inputs_unwrapped.append(ivar[vi]) + inputs_unwrapped.append(ivar[vi]) black_box._NunwrappedOutputs = len(outputs_unwrapped) - black_box._NunwrappedInputs = len(inputs_unwrapped) + black_box._NunwrappedInputs = len(inputs_unwrapped) black_box.post_init_setup() - # TODO: Need to unwrap operators + # TODO: Need to unwrap operators + - self.__dict__[conName].set_external_model( - black_box, inputs=inputs_unwrapped, outputs=outputs_unwrapped - ) # ,operators=operators_unwrapped) + self.__dict__[conName].set_external_model(black_box, inputs=inputs_unwrapped, outputs=outputs_unwrapped)#,operators=operators_unwrapped) def ConstraintList(self, conList): - for i in range(0, len(conList)): + for i in range(0,len(conList)): con = conList[i] - if isinstance(con, (tuple, list)): + if isinstance(con, (tuple,list)): self.RuntimeConstraint(*con) elif isinstance(con, dict): self.RuntimeConstraint(**con) @@ -402,54 +284,30 @@ def ConstraintList(self, conList): self.Constraint(con) def get_variables(self): - return [ - self.__dict__[nm] - for nm in self.__dict__.keys() - if nm in self._variable_keys - ] + return [self.__dict__[nm] for nm in self.__dict__.keys() if nm in self._variable_keys] def get_constants(self): - return [ - self.__dict__[nm] - for nm in self.__dict__.keys() - if nm in self._constant_keys - ] + return [self.__dict__[nm] for nm in self.__dict__.keys() if nm in self._constant_keys] def get_objectives(self): - return [ - self.__dict__[nm] - for nm in self.__dict__.keys() - if nm in self._objective_keys - ] - + return [self.__dict__[nm] for nm in self.__dict__.keys() if nm in self._objective_keys] + def get_constraints(self): - return [ - self.__dict__[nm] - for nm in self.__dict__.keys() - if nm in self._allConstraint_keys - ] + return [self.__dict__[nm] for nm in self.__dict__.keys() if nm in self._allConstraint_keys] def get_explicitConstraints(self): - return [ - self.__dict__[nm] - for nm in self.__dict__.keys() - if nm in self._constraint_keys - ] + return [self.__dict__[nm] for nm in self.__dict__.keys() if nm in self._constraint_keys] def get_runtimeConstraints(self): - return [ - self.__dict__[nm] - for nm in self.__dict__.keys() - if nm in self._runtimeConstraint_keys - ] + return [self.__dict__[nm] for nm in self.__dict__.keys() if nm in self._runtimeConstraint_keys] def check_units(self): - for i in range(1, self._objective_counter + 1): - assert_units_consistent(self.__dict__['objective_' + str(i)]) - - for i in range(1, self._constraint_counter + 1): - if not isinstance( - self.__dict__['constraint_' + str(i)], - pyomo.contrib.pynumero.interfaces.external_grey_box.ExternalGreyBoxBlock, - ): - assert_units_consistent(self.__dict__['constraint_' + str(i)]) + for i in range(1,self._objective_counter+1): + assert_units_consistent(self.__dict__['objective_'+str(i)]) + + for i in range(1,self._constraint_counter+1): + if not isinstance(self.__dict__['constraint_'+str(i)],pyomo.contrib.pynumero.interfaces.external_grey_box.ExternalGreyBoxBlock): + assert_units_consistent(self.__dict__['constraint_'+str(i)]) + + + \ No newline at end of file diff --git a/pyomo/contrib/edi/tests/test_blackbox.py b/pyomo/contrib/edi/tests/test_blackbox.py index 1dae7f24e99..771cad7175d 100644 --- a/pyomo/contrib/edi/tests/test_blackbox.py +++ b/pyomo/contrib/edi/tests/test_blackbox.py @@ -19,142 +19,123 @@ import pyomo.environ as pyo from pyomo.common.dependencies import attempt_import -from pyomo.core.base.units_container import pint_available +from pyomo.core.base.units_container import ( + pint_available, +) -np, numpy_available = attempt_import('numpy', 'edi requires numpy') -# scipy, scipy_available = attempt_import( -# 'scipy', 'inverse_reduced_hessian requires scipy' -# ) +from pyomo.common.dependencies import numpy, numpy_available +from pyomo.common.dependencies import scipy, scipy_available -# if not (numpy_available and scipy_available): -if not numpy_available: - raise unittest.SkipTest('edi.formulation tests require numpy') +if numpy_available: + import numpy as np - -@unittest.skipIf(not pint_available, 'Testing units requires pint') +@unittest.skipIf(not numpy_available, 'Testing pyomo.contrib.edi requires numpy') +@unittest.skipIf(not scipy_available, 'Testing pyomo.contrib.edi requires scipy') +@unittest.skipIf(not pint_available, 'Testing units requires pint') class TestEDIBlackBox(unittest.TestCase): def test_edi_blackbox_variable(self): "Tests the black box variable class" from pyomo.environ import units from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import ( - BlackBoxFunctionModel_Variable, - TypeCheckedList, - BBList, - BlackBoxFunctionModel, - ) + from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel - x = BlackBoxFunctionModel_Variable('x', '') + x = BlackBoxFunctionModel_Variable('x','') x_print = x.__repr__() x_name = x.name - x_units = x.units - x_size = x.size + x_units = x.units + x_size = x.size x_desc = x.description - self.assertRaises(ValueError, x.__init__, *(1.0, '')) + self.assertRaises(ValueError,x.__init__,*(1.0, '')) x.__init__('x', units.dimensionless) - self.assertRaises(ValueError, x.__init__, *('x', 1.0)) + self.assertRaises(ValueError,x.__init__,*('x', 1.0)) x.__init__('x', units.dimensionless, '', 'flex') - x.__init__('x', units.dimensionless, '', ['flex', 2]) + x.__init__('x', units.dimensionless, '', ['flex',2]) x.__init__('x', units.dimensionless, '', 2) - x.__init__('x', units.dimensionless, '', [2, 2]) + x.__init__('x', units.dimensionless, '', [2,2]) - self.assertRaises(ValueError, x.__init__, *('x', '', '', [[], 2])) - self.assertRaises(ValueError, x.__init__, *('x', '', '', [2, 1])) + self.assertRaises(ValueError,x.__init__,*('x', '','',[[],2])) + self.assertRaises(ValueError,x.__init__,*('x', '','',[2,1])) x.__init__('x', units.dimensionless, '', None) x.__init__('x', None, '', None) - self.assertRaises(ValueError, x.__init__, *('x', '', '', 1)) - self.assertRaises(ValueError, x.__init__, *('x', '', '', {})) - self.assertRaises(ValueError, x.__init__, *('x', '', 1.0)) + self.assertRaises(ValueError,x.__init__,*('x', '','', 1 )) + self.assertRaises(ValueError,x.__init__,*('x', '','', {} )) + self.assertRaises(ValueError,x.__init__,*('x', '', 1.0 )) + + + def test_edi_blackbox_tcl(self): "Tests the black box type checked list class" from pyomo.environ import units from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import ( - BlackBoxFunctionModel_Variable, - TypeCheckedList, - BBList, - BlackBoxFunctionModel, - ) + from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel - tcl = TypeCheckedList(int, [1, 2, 3]) + tcl = TypeCheckedList(int, [1,2,3]) tcl[1] = 1 - tcl[0:2] = [1, 2] + tcl[0:2] = [1,2] + + self.assertRaises(ValueError,tcl.__init__,*( int, 1 )) + self.assertRaises(ValueError,tcl.__setitem__,*( 1, 3.333 )) + self.assertRaises(ValueError,tcl.__setitem__,*( 1, [1, 2.222] )) + self.assertRaises(ValueError,tcl.append,*( 2.222 ,)) + - self.assertRaises(ValueError, tcl.__init__, *(int, 1)) - self.assertRaises(ValueError, tcl.__setitem__, *(1, 3.333)) - self.assertRaises(ValueError, tcl.__setitem__, *(1, [1, 2.222])) - self.assertRaises(ValueError, tcl.append, *(2.222,)) def test_edi_blackbox_bbl(self): "Tests the black box BBList class" from pyomo.environ import units from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import ( - BlackBoxFunctionModel_Variable, - TypeCheckedList, - BBList, - BlackBoxFunctionModel, - ) + from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel bbl = BBList() - bbl.append('x', '') - bbl.append('y', '') - bbl.append(BlackBoxFunctionModel_Variable('z', '')) - bbl.append(var=BlackBoxFunctionModel_Variable('u', '')) - self.assertRaises( - ValueError, bbl.append, *(BlackBoxFunctionModel_Variable('u', ''),) - ) - self.assertRaises(ValueError, bbl.append, *('badvar',)) - self.assertRaises(ValueError, bbl.append, *(2.222,)) - - self.assertRaises(ValueError, bbl.append, *('bv', '', ''), **{'units': 'm'}) - self.assertRaises(ValueError, bbl.append, **{'units': 'm', 'description': 'hi'}) - self.assertRaises(ValueError, bbl.append, **{'name': 'x', 'units': ''}) - self.assertRaises(ValueError, bbl.append, *('bv', '', '', 0, 'extra')) + bbl.append('x','') + bbl.append('y','') + bbl.append(BlackBoxFunctionModel_Variable('z','')) + bbl.append(var = BlackBoxFunctionModel_Variable('u','')) + self.assertRaises(ValueError,bbl.append,*( BlackBoxFunctionModel_Variable('u',''),)) + self.assertRaises(ValueError,bbl.append,*( 'badvar',)) + self.assertRaises(ValueError,bbl.append,*( 2.222,)) + + self.assertRaises(ValueError,bbl.append,*( 'bv','',''),**{'units':'m'}) + self.assertRaises(ValueError,bbl.append,**{'units':'m','description':'hi'}) + self.assertRaises(ValueError,bbl.append,**{'name':'x', 'units':''}) + self.assertRaises(ValueError,bbl.append,*( 'bv','','',0,'extra')) xv = bbl['x'] xv2 = bbl[0] - self.assertRaises(ValueError, bbl.__getitem__, *(2.22,)) + self.assertRaises(ValueError,bbl.__getitem__,*( 2.22, )) + def test_edi_blackbox_someexceptions(self): "Tests some of the exceptions in the black box model class" import numpy as np from pyomo.environ import units from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import ( - BlackBoxFunctionModel_Variable, - TypeCheckedList, - BBList, - BlackBoxFunctionModel, - ) + from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel bb = BlackBoxFunctionModel() - bb.inputVariables_optimization = [1, 2, 3] + bb.inputVariables_optimization = [1,2,3] # bb.set_input_values(np.array([1,2,3])) - self.assertRaises(ValueError, bb.input_names, *()) + self.assertRaises(ValueError,bb.input_names,*( )) # self.assertRaises(ValueError,bb.fillCache,*( )) + bb = BlackBoxFunctionModel() - bb.outputVariables_optimization = [1, 2, 3] - self.assertRaises(ValueError, bb.output_names, *()) + bb.outputVariables_optimization = [1,2,3] + self.assertRaises(ValueError,bb.output_names,*( )) + def test_edi_blackbox_etc_1(self): "Tests a black box assertion issue" - from pyomo.contrib.edi.blackBoxFunctionModel import ( - BlackBoxFunctionModel_Variable, - TypeCheckedList, - BBList, - BlackBoxFunctionModel, - ) - + from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel bbfm = BlackBoxFunctionModel() - self.assertRaises(AttributeError, bbfm.BlackBox, ()) + self.assertRaises(AttributeError,bbfm.BlackBox,()) + def test_edi_blackbox_etc_2(self): "Tests a black box assertion issue" @@ -162,56 +143,53 @@ def test_edi_blackbox_etc_2(self): from pyomo.environ import units import pyomo.environ as pyo from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import ( - BlackBoxFunctionModel_Variable, - TypeCheckedList, - BBList, - BlackBoxFunctionModel, - ) + from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel f = Formulation() - x = f.Variable(name='x', guess=1.0, units='m', description='x variable') - y = f.Variable(name='y', guess=1.0, units='m', description='y variable') - z = f.Variable(name='z', guess=1.0, units='m^2', description='Output var') - f.Objective(x + y) - + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'y variable') + z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'Output var') + f.Objective( x + y ) class UnitCircle(BlackBoxFunctionModel): def __init__(self): super().__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append(name='x', units='ft', description='The x variable') - self.inputs.append(name='y', units='ft', description='The y variable') - self.outputs.append( - name='z', units='ft**2', description='Output variable' - ) + self.inputs.append( name = 'x', + units = 'ft' , + description = 'The x variable' ) + self.inputs.append( name = 'y', + units = 'ft' , + description = 'The y variable' ) + self.outputs.append(name = 'z', + units = 'ft**2', + description = 'Output variable' ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - - def BlackBox(self, x, y): # The actual function that does things + def BlackBox(self, x, y): # The actual function that does things # Converts to correct units then casts to float - x = pyo.value(units.convert(x, self.inputs[0].units)) - y = pyo.value(units.convert(y, self.inputs[1].units)) - z = x**2 + y**2 # Compute z - dzdx = 2 * x # Compute dz/dx - dzdy = 2 * y # Compute dz/dy + x = pyo.value(units.convert(x,self.inputs[0].units)) + y = pyo.value(units.convert(y,self.inputs[1].units)) + z = x**2 + y**2 # Compute z + dzdx = 2*x # Compute dz/dx + dzdy = 2*y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... - f.ConstraintList([z <= 1 * units.m**2, [z, '==', [x, y], UnitCircle()]]) - f.__dict__['constraint_2'].get_external_model().set_input_values( - np.array([2.0, 2.0]) + f.ConstraintList( + [ + z <= 1*units.m**2 , + [ z, '==', [x,y], UnitCircle() ] , + ] ) - f.__dict__['constraint_2'].get_external_model().inputVariables_optimization = [ - 1, - 2, - ] + + f.__dict__['constraint_2'].get_external_model().set_input_values(np.array([2.0, 2.0])) + f.__dict__['constraint_2'].get_external_model().inputVariables_optimization = [1,2] # f.__dict__['constraint_2'].get_external_model().fillCache() - self.assertRaises( - ValueError, f.__dict__['constraint_2'].get_external_model().fillCache, *() - ) + self.assertRaises(ValueError,f.__dict__['constraint_2'].get_external_model().fillCache,*()) + def test_edi_blackbox_etc_3(self): "Tests a black box assertion issue" @@ -219,239 +197,215 @@ def test_edi_blackbox_etc_3(self): from pyomo.environ import units import pyomo.environ as pyo from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import ( - BlackBoxFunctionModel_Variable, - TypeCheckedList, - BBList, - BlackBoxFunctionModel, - ) + from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel f = Formulation() - x = f.Variable(name='x', guess=1.0, units='m', description='x variable') - y = f.Variable(name='y', guess=1.0, units='m', description='y variable') - z = f.Variable(name='z', guess=1.0, units='m^2', description='Output var') - f.Objective(x + y) - + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'y variable') + z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'Output var') + f.Objective( x + y ) class UnitCircle(BlackBoxFunctionModel): def __init__(self): super().__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append(name='x', units='ft', description='The x variable') - self.inputs.append(name='y', units='ft', description='The y variable') - self.outputs.append( - name='z', units='ft**2', description='Output variable' - ) + self.inputs.append( name = 'x', + units = 'ft' , + description = 'The x variable' ) + self.inputs.append( name = 'y', + units = 'ft' , + description = 'The y variable' ) + self.outputs.append(name = 'z', + units = 'ft**2', + description = 'Output variable' ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - - def BlackBox(self, x, y): # The actual function that does things + def BlackBox(self, x, y): # The actual function that does things # Converts to correct units then casts to float - x = pyo.value(units.convert(x, self.inputs[0].units)) - y = pyo.value(units.convert(y, self.inputs[1].units)) - z = x**2 + y**2 # Compute z - dzdx = 2 * x # Compute dz/dx - dzdy = 2 * y # Compute dz/dy + x = pyo.value(units.convert(x,self.inputs[0].units)) + y = pyo.value(units.convert(y,self.inputs[1].units)) + z = x**2 + y**2 # Compute z + dzdx = 2*x # Compute dz/dx + dzdy = 2*y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return ['err'], [[dzdx, dzdy]] # return z, grad(z), hess(z)... + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return ['err'], [[dzdx, dzdy]] # return z, grad(z), hess(z)... - f.ConstraintList([z <= 1 * units.m**2, [z, '==', [x, y], UnitCircle()]]) - self.assertRaises( - ValueError, f.__dict__['constraint_2'].get_external_model().fillCache, *() + f.ConstraintList( + [ + z <= 1*units.m**2 , + [ z, '==', [x,y], UnitCircle() ] , + ] ) + self.assertRaises(ValueError,f.__dict__['constraint_2'].get_external_model().fillCache,*()) + def test_edi_blackbox_etc_4(self): "Tests a black box assertion issue" import numpy as np from pyomo.environ import units import pyomo.environ as pyo from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import ( - BlackBoxFunctionModel_Variable, - TypeCheckedList, - BBList, - BlackBoxFunctionModel, - ) + from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel f = Formulation() - x = f.Variable(name='x', guess=1.0, units='m', description='x variable') - y = f.Variable(name='y', guess=1.0, units='m', description='y variable') - z = f.Variable(name='z', guess=1.0, units='m^2', description='Output var') - f.Objective(x + y) - + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'y variable') + z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'Output var') + f.Objective( x + y ) class UnitCircle(BlackBoxFunctionModel): def __init__(self): super().__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append(name='x', units='ft', description='The x variable') - self.inputs.append(name='y', units='ft', description='The y variable') - self.outputs.append( - name='z', units='ft**2', description='Output variable' - ) + self.inputs.append( name = 'x', + units = 'ft' , + description = 'The x variable' ) + self.inputs.append( name = 'y', + units = 'ft' , + description = 'The y variable' ) + self.outputs.append(name = 'z', + units = 'ft**2', + description = 'Output variable' ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - - def BlackBox(self, x, y): # The actual function that does things + def BlackBox(self, x, y): # The actual function that does things # Converts to correct units then casts to float - x = pyo.value(units.convert(x, self.inputs[0].units)) - y = pyo.value(units.convert(y, self.inputs[1].units)) - z = x**2 + y**2 # Compute z - dzdx = 2 * x # Compute dz/dx - dzdy = 2 * y # Compute dz/dy + x = pyo.value(units.convert(x,self.inputs[0].units)) + y = pyo.value(units.convert(y,self.inputs[1].units)) + z = x**2 + y**2 # Compute z + dzdx = 2*x # Compute dz/dx + dzdy = 2*y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... - f.ConstraintList([z <= 1 * units.m**2, [z, '==', [x, y], UnitCircle()]]) - f.__dict__['constraint_2'].get_external_model().set_input_values( - np.array([2.0, 2.0]) + f.ConstraintList( + [ + z <= 1*units.m**2 , + [ z, '==', [x,y], UnitCircle() ] , + ] ) - f.__dict__['constraint_2'].get_external_model().outputVariables_optimization = [ - 1 - ] + + f.__dict__['constraint_2'].get_external_model().set_input_values(np.array([2.0, 2.0])) + f.__dict__['constraint_2'].get_external_model().outputVariables_optimization = [1] # f.__dict__['constraint_2'].get_external_model().fillCache() - self.assertRaises( - ValueError, f.__dict__['constraint_2'].get_external_model().fillCache, *() - ) + self.assertRaises(ValueError,f.__dict__['constraint_2'].get_external_model().fillCache,*()) + def test_edi_blackbox_example_1(self): "Tests a black box example construction" from pyomo.environ import units import pyomo.environ as pyo from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import ( - BlackBoxFunctionModel_Variable, - TypeCheckedList, - BBList, - BlackBoxFunctionModel, - ) + from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel f = Formulation() - x = f.Variable(name='x', guess=1.0, units='m', description='x variable') - y = f.Variable(name='y', guess=1.0, units='m', description='y variable') - z = f.Variable(name='z', guess=1.0, units='m^2', description='Output var') - f.Objective(x + y) - + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'y variable') + z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'Output var') + f.Objective( x + y ) class UnitCircle(BlackBoxFunctionModel): def __init__(self): super().__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append(name='x', units='ft', description='The x variable') - self.inputs.append(name='y', units='ft', description='The y variable') - self.outputs.append( - name='z', units='ft**2', description='Output variable' - ) + self.inputs.append( name = 'x', + units = 'ft' , + description = 'The x variable' ) + self.inputs.append( name = 'y', + units = 'ft' , + description = 'The y variable' ) + self.outputs.append(name = 'z', + units = 'ft**2', + description = 'Output variable' ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - - def BlackBox(self, x, y): # The actual function that does things + def BlackBox(self, x, y): # The actual function that does things # Converts to correct units then casts to float - x = pyo.value(units.convert(x, self.inputs[0].units)) - y = pyo.value(units.convert(y, self.inputs[1].units)) - z = x**2 + y**2 # Compute z - dzdx = 2 * x # Compute dz/dx - dzdy = 2 * y # Compute dz/dy + x = pyo.value(units.convert(x,self.inputs[0].units)) + y = pyo.value(units.convert(y,self.inputs[1].units)) + z = x**2 + y**2 # Compute z + dzdx = 2*x # Compute dz/dx + dzdy = 2*y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... - f.ConstraintList([z <= 1 * units.m**2, [z, '==', [x, y], UnitCircle()]]) - f.__dict__['constraint_2'].get_external_model().set_input_values( - np.array([2.0, 2.0]) - ) - opt = f.__dict__['constraint_2'].get_external_model().evaluate_outputs() - jac = ( - f.__dict__['constraint_2'] - .get_external_model() - .evaluate_jacobian_outputs() - .todense() + f.ConstraintList( + [ + z <= 1*units.m**2 , + [ z, '==', [x,y], UnitCircle() ] , + ] ) - self.assertAlmostEqual(opt[0], 8) - self.assertAlmostEqual(jac[0, 0], 4) - self.assertAlmostEqual(jac[0, 1], 4) + f.__dict__['constraint_2'].get_external_model().set_input_values(np.array([2.0, 2.0])) + opt = f.__dict__['constraint_2'].get_external_model().evaluate_outputs() + jac = f.__dict__['constraint_2'].get_external_model().evaluate_jacobian_outputs().todense() + + self.assertAlmostEqual(opt[0],8) + self.assertAlmostEqual(jac[0,0],4) + self.assertAlmostEqual(jac[0,1],4) - sm = f.__dict__['constraint_2'].get_external_model().summary + sm = f.__dict__['constraint_2'].get_external_model().summary e_print = f.__dict__['constraint_2'].get_external_model().__repr__() + def test_edi_blackbox_example_2(self): "Tests a black box example construction" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel - f = Formulation() - x = f.Variable( - name='x', guess=1.0, units='m', description='The x variable', size=3 - ) - y = f.Variable( - name='y', guess=1.0, units='m**2', description='The y variable', size=3 - ) - f.Objective(y[0] + y[1] + y[2]) - + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable', size=3) + y = f.Variable(name = 'y', guess = 1.0, units = 'm**2' , description = 'The y variable', size=3) + f.Objective( y[0] + y[1] + y[2] ) class Parabola(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super(Parabola, self).__init__() self.description = 'This model evaluates the function: y = x**2' - self.inputs.append( - name='x', size=3, units='ft', description='The x variable' - ) - self.outputs.append( - name='y', size=3, units='ft**2', description='The y variable' - ) + self.inputs.append(name = 'x', size = 3, units = 'ft' , description = 'The x variable') + self.outputs.append(name = 'y', size = 3, units = 'ft**2' , description = 'The y variable') self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, *args, **kwargs): # The actual function that does things - runCases, returnMode, remainingKwargs = self.parseInputs( - *args, **kwargs - ) - + def BlackBox(self, *args, **kwargs):# The actual function that does things + runCases, returnMode, remainingKwargs = self.parseInputs(*args, **kwargs) + x = self.sanitizeInputs(runCases[0]['x']) x = np.array([pyo.value(xval) for xval in x], dtype=np.float64) - y = x**2 # Compute y - dydx = 2 * x # Compute dy/dx - + y = x**2 # Compute y + dydx = 2*x # Compute dy/dx + y = y * units.ft**2 dydx = np.diag(dydx) - dydx = dydx * units.ft # units.ft**2 / units.ft + dydx = dydx * units.ft # units.ft**2 / units.ft + + return y, [dydx] # return z, grad(z), hess(z)... + f.ConstraintList([ {'outputs':y, 'operators':'==', 'inputs':x, 'black_box':Parabola() },]) - return y, [dydx] # return z, grad(z), hess(z)... - - f.ConstraintList( - [{'outputs': y, 'operators': '==', 'inputs': x, 'black_box': Parabola()}] - ) - - f.__dict__['constraint_1'].get_external_model().set_input_values(np.ones(3) * 2) + f.__dict__['constraint_1'].get_external_model().set_input_values(np.ones(3)*2) opt = f.__dict__['constraint_1'].get_external_model().evaluate_outputs() - jac = ( - f.__dict__['constraint_1'] - .get_external_model() - .evaluate_jacobian_outputs() - .todense() - ) - - self.assertAlmostEqual(opt[0], 4) - self.assertAlmostEqual(opt[1], 4) - self.assertAlmostEqual(opt[2], 4) - self.assertAlmostEqual(jac[0, 0], 4) - self.assertAlmostEqual(jac[0, 1], 0) - self.assertAlmostEqual(jac[0, 2], 0) - self.assertAlmostEqual(jac[0, 1], 0) - self.assertAlmostEqual(jac[1, 1], 4) - self.assertAlmostEqual(jac[2, 1], 0) - self.assertAlmostEqual(jac[2, 0], 0) - self.assertAlmostEqual(jac[2, 1], 0) - self.assertAlmostEqual(jac[2, 2], 4) - - sm = f.__dict__['constraint_1'].get_external_model().summary + jac = f.__dict__['constraint_1'].get_external_model().evaluate_jacobian_outputs().todense() + + self.assertAlmostEqual(opt[0],4) + self.assertAlmostEqual(opt[1],4) + self.assertAlmostEqual(opt[2],4) + self.assertAlmostEqual(jac[0,0],4) + self.assertAlmostEqual(jac[0,1],0) + self.assertAlmostEqual(jac[0,2],0) + self.assertAlmostEqual(jac[0,1],0) + self.assertAlmostEqual(jac[1,1],4) + self.assertAlmostEqual(jac[2,1],0) + self.assertAlmostEqual(jac[2,0],0) + self.assertAlmostEqual(jac[2,1],0) + self.assertAlmostEqual(jac[2,2],4) + + sm = f.__dict__['constraint_1'].get_external_model().summary e_print = f.__dict__['constraint_1'].get_external_model().__repr__() def test_edi_blackbox_example_3(self): @@ -460,133 +414,109 @@ def test_edi_blackbox_example_3(self): from pyomo.environ import units import pyomo.environ as pyo from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import ( - BlackBoxFunctionModel_Variable, - TypeCheckedList, - BBList, - BlackBoxFunctionModel, - ) + from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel f = Formulation() - x = f.Variable(name='x', guess=1.0, units='', description='x variable', size=3) - y = f.Variable(name='y', guess=1.0, units='', description='y variable') - f.Objective(y) - + x = f.Variable(name = 'x', guess = 1.0, units = '' , description = 'x variable', size = 3) + y = f.Variable(name = 'y', guess = 1.0, units = '' , description = 'y variable') + f.Objective( y ) class Norm_2(BlackBoxFunctionModel): def __init__(self): super().__init__() self.description = 'This model evaluates the two norm' - self.inputs.append( - name='x', units='', description='The x variable', size=3 - ) - self.outputs.append(name='y', units='', description='The y variable') + self.inputs.append( name = 'x', + units = '' , + description = 'The x variable' , + size = 3) + self.outputs.append(name = 'y', + units = '' , + description = 'The y variable' ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - - def BlackBox(self, *args, **kwargs): # The actual function that does things - runCases, returnMode, remainingKwargs = self.parseInputs( - *args, **kwargs - ) + def BlackBox(self, *args, **kwargs):# The actual function that does things + runCases, returnMode, remainingKwargs = self.parseInputs(*args, **kwargs) x = self.sanitizeInputs(runCases[0]['x']) x = np.array([pyo.value(xval) for xval in x], dtype=np.float64) - y = x[0] ** 2 + x[1] ** 2 + x[2] ** 2 # Compute y - dydx0 = 2 * x[0] # Compute dy/dx0 - dydx1 = 2 * x[1] # Compute dy/dx1 - dydx2 = 2 * x[2] # Compute dy/dx2 + y = x[0]**2 + x[1]**2 + x[2]**2 # Compute y + dydx0 = 2*x[0] # Compute dy/dx0 + dydx1 = 2*x[1] # Compute dy/dx1 + dydx2 = 2*x[2] # Compute dy/dx2 y = y * units.dimensionless - dydx = np.array([dydx0, dydx1, dydx2]) * units.dimensionless + dydx = np.array([dydx0,dydx1,dydx2]) * units.dimensionless # dydx0 = dydx0 * units.dimensionless # dydx1 = dydx1 * units.dimensionless # dydx2 = dydx2 * units.dimensionless - return y, [dydx] # return z, grad(z), hess(z)... - - f.ConstraintList( - [{'outputs': y, 'operators': '==', 'inputs': x, 'black_box': Norm_2()}] - ) + return y, [dydx] # return z, grad(z), hess(z)... + + f.ConstraintList([ {'outputs':y, 'operators':'==', 'inputs':x, 'black_box':Norm_2() },]) - f.__dict__['constraint_1'].get_external_model().set_input_values(np.ones(3) * 2) + f.__dict__['constraint_1'].get_external_model().set_input_values(np.ones(3)*2) opt = f.__dict__['constraint_1'].get_external_model().evaluate_outputs() - jac = ( - f.__dict__['constraint_1'] - .get_external_model() - .evaluate_jacobian_outputs() - .todense() - ) + jac = f.__dict__['constraint_1'].get_external_model().evaluate_jacobian_outputs().todense() - self.assertAlmostEqual(opt[0], 12) - self.assertAlmostEqual(jac[0, 0], 4) - self.assertAlmostEqual(jac[0, 1], 4) - self.assertAlmostEqual(jac[0, 2], 4) + self.assertAlmostEqual(opt[0],12) + self.assertAlmostEqual(jac[0,0],4) + self.assertAlmostEqual(jac[0,1],4) + self.assertAlmostEqual(jac[0,2],4) - sm = f.__dict__['constraint_1'].get_external_model().summary + sm = f.__dict__['constraint_1'].get_external_model().summary e_print = f.__dict__['constraint_1'].get_external_model().__repr__() + def test_edi_blackbox_example_4(self): "Tests a black box example construction" import numpy as np from pyomo.environ import units import pyomo.environ as pyo from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import ( - BlackBoxFunctionModel_Variable, - TypeCheckedList, - BBList, - BlackBoxFunctionModel, - ) + from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel f = Formulation() - x = f.Variable(name='x', guess=1.0, units='', description='x variable') - y = f.Variable(name='y', guess=1.0, units='', description='y variable', size=3) - f.Objective(y[0] ** 2 + y[1] ** 2 + y[2] ** 2) - + x = f.Variable(name = 'x', guess = 1.0, units = '' , description = 'x variable') + y = f.Variable(name = 'y', guess = 1.0, units = '' , description = 'y variable', size=3) + f.Objective( y[0]**2 + y[1]**2 + y[2]**2 ) class VectorCast(BlackBoxFunctionModel): def __init__(self): super().__init__() self.description = 'This model evaluates the two norm' - self.inputs.append(name='x', units='', description='The x variable') - self.outputs.append( - name='y', units='', description='The y variable', size=3 - ) + self.inputs.append( name = 'x', + units = '' , + description = 'The x variable' ) + self.outputs.append(name = 'y', + units = '' , + description = 'The y variable' , + size = 3) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, *args, **kwargs): # The actual function that does things - runCases, returnMode, remainingKwargs = self.parseInputs( - *args, **kwargs - ) + def BlackBox(self, *args, **kwargs):# The actual function that does things + runCases, returnMode, remainingKwargs = self.parseInputs(*args, **kwargs) x = pyo.value(self.sanitizeInputs(runCases[0]['x'])) - y = np.array([x, x, x]) * units.dimensionless - dydx = np.array([1.0, 1.0, 1.0]) * units.dimensionless + y = np.array([x,x,x]) * units.dimensionless + dydx = np.array([1.0,1.0,1.0]) * units.dimensionless - return y, [dydx] # return z, grad(z), hess(z)... + return y, [dydx] # return z, grad(z), hess(z)... - f.ConstraintList( - [{'outputs': y, 'operators': '==', 'inputs': x, 'black_box': VectorCast()}] - ) + f.ConstraintList([ {'outputs':y, 'operators':'==', 'inputs':x, 'black_box':VectorCast() },]) - f.__dict__['constraint_1'].get_external_model().set_input_values(np.ones(3) * 2) + f.__dict__['constraint_1'].get_external_model().set_input_values(np.ones(3)*2) opt = f.__dict__['constraint_1'].get_external_model().evaluate_outputs() - jac = ( - f.__dict__['constraint_1'] - .get_external_model() - .evaluate_jacobian_outputs() - .todense() - ) + jac = f.__dict__['constraint_1'].get_external_model().evaluate_jacobian_outputs().todense() - self.assertAlmostEqual(opt[0], 2.0) - self.assertAlmostEqual(opt[1], 2.0) - self.assertAlmostEqual(opt[2], 2.0) - self.assertAlmostEqual(jac[0, 0], 1.0) - self.assertAlmostEqual(jac[1, 0], 1.0) - self.assertAlmostEqual(jac[2, 0], 1.0) + self.assertAlmostEqual(opt[0],2.0) + self.assertAlmostEqual(opt[1],2.0) + self.assertAlmostEqual(opt[2],2.0) + self.assertAlmostEqual(jac[0,0],1.0) + self.assertAlmostEqual(jac[1,0],1.0) + self.assertAlmostEqual(jac[2,0],1.0) - sm = f.__dict__['constraint_1'].get_external_model().summary + sm = f.__dict__['constraint_1'].get_external_model().summary e_print = f.__dict__['constraint_1'].get_external_model().__repr__() def test_edi_blackbox_badexample_1(self): @@ -595,41 +525,35 @@ def test_edi_blackbox_badexample_1(self): from pyomo.environ import units import pyomo.environ as pyo from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import ( - BlackBoxFunctionModel_Variable, - TypeCheckedList, - BBList, - BlackBoxFunctionModel, - ) + from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel f = Formulation() - x = f.Variable(name='x', guess=1.0, units='', description='x variable', size=3) - y = f.Variable(name='y', guess=1.0, units='', description='y variable') - f.Objective(y) - + x = f.Variable(name = 'x', guess = 1.0, units = '' , description = 'x variable', size = 3) + y = f.Variable(name = 'y', guess = 1.0, units = '' , description = 'y variable') + f.Objective( y ) class Norm_2(BlackBoxFunctionModel): def __init__(self): super().__init__() self.description = 'This model evaluates the two norm' - self.inputs.append( - name='x', units='', description='The x variable', size=3 - ) - self.outputs.append(name='y', units='', description='The y variable') + self.inputs.append( name = 'x', + units = '' , + description = 'The x variable' , + size = 3) + self.outputs.append(name = 'y', + units = '' , + description = 'The y variable' ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - - def BlackBox(self, *args, **kwargs): # The actual function that does things - runCases, returnMode, remainingKwargs = self.parseInputs( - *args, **kwargs - ) + def BlackBox(self, *args, **kwargs):# The actual function that does things + runCases, returnMode, remainingKwargs = self.parseInputs(*args, **kwargs) x = self.sanitizeInputs(runCases[0]['x']) x = np.array([pyo.value(xval) for xval in x], dtype=np.float64) - y = x[0] ** 2 + x[1] ** 2 + x[2] ** 2 # Compute y - dydx0 = 2 * x[0] # Compute dy/dx0 - dydx1 = 2 * x[1] # Compute dy/dx1 - dydx2 = 2 * x[2] # Compute dy/dx2 + y = x[0]**2 + x[1]**2 + x[2]**2 # Compute y + dydx0 = 2*x[0] # Compute dy/dx0 + dydx1 = 2*x[1] # Compute dy/dx1 + dydx2 = 2*x[2] # Compute dy/dx2 y = y * units.dimensionless # dydx = np.array([dydx0,dydx1,dydx2]) * units.dimensionless @@ -637,18 +561,13 @@ def BlackBox(self, *args, **kwargs): # The actual function that does things dydx1 = dydx1 * units.dimensionless dydx2 = dydx2 * units.dimensionless - return y, [[dydx0, dydx1, dydx2]] # return z, grad(z), hess(z)... + return y, [[dydx0, dydx1, dydx2]] # return z, grad(z), hess(z)... + + f.ConstraintList([ {'outputs':y, 'operators':'==', 'inputs':x, 'black_box':Norm_2() },]) - f.ConstraintList( - [{'outputs': y, 'operators': '==', 'inputs': x, 'black_box': Norm_2()}] - ) + f.__dict__['constraint_1'].get_external_model().set_input_values(np.ones(3)*2) + self.assertRaises(ValueError,f.__dict__['constraint_1'].get_external_model().evaluate_outputs,*()) - f.__dict__['constraint_1'].get_external_model().set_input_values(np.ones(3) * 2) - self.assertRaises( - ValueError, - f.__dict__['constraint_1'].get_external_model().evaluate_outputs, - *() - ) def test_edi_blackbox_smallfunctions(self): "Tests the more general value and convert functions" @@ -656,31 +575,22 @@ def test_edi_blackbox_smallfunctions(self): from pyomo.environ import units import pyomo.environ as pyo from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import ( - BlackBoxFunctionModel_Variable, - TypeCheckedList, - BBList, - BlackBoxFunctionModel, - ) + from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel bb = BlackBoxFunctionModel() - t1 = bb.convert(2 * units.m, units.ft) - t2 = bb.convert(np.ones([2, 2]) * units.m, units.ft) - self.assertRaises(ValueError, bb.convert, *('err', units.ft)) - - t3 = bb.pyomo_value(2 * units.m) - t3 = bb.pyomo_value(np.ones([2, 2]) * units.m) - self.assertRaises(ValueError, bb.pyomo_value, *(np.ones([2, 2]),)) - - bb.sizeCheck([2, 2], np.ones([2, 2]) * units.m) - self.assertRaises(ValueError, bb.sizeCheck, *(2, 3 * units.m)) - self.assertRaises( - ValueError, bb.sizeCheck, *([10, 10, 10], np.ones([2, 2]) * units.m) - ) - self.assertRaises( - ValueError, bb.sizeCheck, *([10, 10], np.ones([2, 2]) * units.m) - ) - self.assertRaises(ValueError, bb.sizeCheck, *([10, 10], [])) + t1 = bb.convert(2*units.m, units.ft) + t2 = bb.convert(np.ones([2,2])*units.m, units.ft) + self.assertRaises(ValueError,bb.convert,*('err',units.ft)) + + t3 = bb.pyomo_value(2*units.m) + t3 = bb.pyomo_value(np.ones([2,2])*units.m) + self.assertRaises(ValueError,bb.pyomo_value,*(np.ones([2,2]),)) + + bb.sizeCheck([2,2],np.ones([2,2])*units.m) + self.assertRaises(ValueError,bb.sizeCheck,*(2,3*units.m,)) + self.assertRaises(ValueError,bb.sizeCheck,*([10,10,10],np.ones([2,2])*units.m,)) + self.assertRaises(ValueError,bb.sizeCheck,*([10,10],np.ones([2,2])*units.m,)) + self.assertRaises(ValueError,bb.sizeCheck,*([10,10],[],)) def test_edi_blackbox_bare_example_1(self): "Tests a black box example construction without an optimization problem" @@ -694,38 +604,34 @@ def __init__(self): # Set up all the attributes by calling Model.__init__ super().__init__() - # Setup Inputs - self.inputs.append('x', '', 'Independent Variable') + #Setup Inputs + self.inputs.append( 'x', '', 'Independent Variable') - # Setup Outputs - self.outputs.append('y', '', 'Dependent Variable') + #Setup Outputs + self.outputs.append( 'y', '', 'Dependent Variable') - # Simple model description - self.description = ( - 'This model evaluates the function: max([-6*x-6, x**4-3*x**2])' - ) + #Simple model description + self.description = 'This model evaluates the function: max([-6*x-6, x**4-3*x**2])' self.availableDerivative = 1 - # standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) + #standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) def BlackBox(self, *args, **kwargs): runCases, returnMode, extras = self.parseInputs(*args, **kwargs) - x = np.array( - [pyo.value(runCases[i]['x']) for i in range(0, len(runCases))] - ) - - y = np.maximum(-6 * x - 6, x**4 - 3 * x**2) - dydx = 4 * x**3 - 6 * x - ddy_ddx = 12 * x**2 - 6 - gradientSwitch = -6 * x - 6 > x**4 - 3 * x**2 + x = np.array([ pyo.value(runCases[i]['x']) for i in range(0,len(runCases)) ]) + + y = np.maximum(-6*x-6, x**4-3*x**2) + dydx = 4*x**3 - 6*x + ddy_ddx = 12*x**2 - 6 + gradientSwitch = -6*x-6 > x**4-3*x**2 dydx[gradientSwitch] = -6 ddy_ddx[gradientSwitch] = 0 - y = [yval * units.dimensionless for yval in y] - dydx = [dydx[i] * units.dimensionless for i in range(0, len(dydx))] + y = [ yval * units.dimensionless for yval in y] + dydx = [dydx[i] * units.dimensionless for i in range(0,len(dydx))] if returnMode < 0: - returnMode = -1 * (returnMode + 1) + returnMode = -1*(returnMode + 1) if returnMode == 0: return y[0] if returnMode == 1: @@ -733,44 +639,43 @@ def BlackBox(self, *args, **kwargs): else: if returnMode == 0: opt = [] - for i in range(0, len(y)): - opt.append([y[i]]) + for i in range(0,len(y)): + opt.append([ y[i] ]) return opt if returnMode == 1: opt = [] - for i in range(0, len(y)): - opt.append([[y[i]], [[[dydx[i]]]]]) + for i in range(0,len(y)): + opt.append([ [y[i]], [ [[dydx[i]]] ] ]) return opt s = SignomialTest() - ivals = [[x] for x in np.linspace(-2, 2, 11)] + ivals = [[x] for x in np.linspace(-2,2,11)] # How the black box may be called using EDI - bbo = s.BlackBox(**{'x': 0.5}) - bbo = s.BlackBox({'x': 0.5}) - bbo = s.BlackBox(**{'x': 0.5, 'optn': True}) + bbo = s.BlackBox(**{'x':0.5}) + bbo = s.BlackBox({'x':0.5}) + bbo = s.BlackBox(**{'x':0.5, 'optn':True}) # Additional options available with parseInputs bbo = s.BlackBox(*[0.5], **{'optn1': True, 'optn2': False}) - bbo = s.BlackBox(*[0.5, True], **{'optn': False}) - bbo = s.BlackBox({'x': [x for x in np.linspace(-2, 2, 11)]}) - bbo = s.BlackBox([{'x': x} for x in np.linspace(-2, 2, 11)]) - bbo = s.BlackBox([[x] for x in np.linspace(-2, 2, 11)]) - bbo = s.BlackBox([[x] for x in np.linspace(-2, 2, 11)], True, optn=False) - bbo = s.BlackBox([[x] for x in np.linspace(-2, 2, 11)], optn1=True, optn2=False) + bbo = s.BlackBox(*[0.5,True], **{'optn': False}) + bbo = s.BlackBox({'x':[x for x in np.linspace(-2,2,11)]}) + bbo = s.BlackBox([{'x':x} for x in np.linspace(-2,2,11)]) + bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)]) + bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)], True, optn=False) + bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)], optn1=True, optn2=False) sm = s.summary - self.assertRaises(ValueError, s.BlackBox, *([[[1, 2], 2, 3], [1, 2, 3]])) - self.assertRaises(ValueError, s.BlackBox, *([[np.ones(2), 2, 3], [1, 2, 3]])) + self.assertRaises(ValueError,s.BlackBox,*( [ [[1,2],2,3] , [1,2,3]] )) + self.assertRaises(ValueError,s.BlackBox,*( [ [np.ones(2),2,3] , [1,2,3]] )) + + self.assertRaises(ValueError,s.sanitizeInputs,*( )) + self.assertRaises(ValueError,s.sanitizeInputs,*( 1 , 2, 3 )) + self.assertRaises(ValueError,s.sanitizeInputs,**{'z':2.0*units.dimensionless}) + self.assertRaises(ValueError,s.sanitizeInputs,*(2.0*units.ft,)) + self.assertRaises(NotImplementedError,s.checkOutputs,*()) - self.assertRaises(ValueError, s.sanitizeInputs, *()) - self.assertRaises(ValueError, s.sanitizeInputs, *(1, 2, 3)) - self.assertRaises( - ValueError, s.sanitizeInputs, **{'z': 2.0 * units.dimensionless} - ) - self.assertRaises(ValueError, s.sanitizeInputs, *(2.0 * units.ft,)) - self.assertRaises(NotImplementedError, s.checkOutputs, *()) def test_edi_blackbox_bare_example_2(self): "Tests a black box example construction without an optimization problem" @@ -785,39 +690,37 @@ def __init__(self): # Set up all the attributes by calling Model.__init__ super().__init__() - # Setup Inputs - self.inputs.append('x', '', 'Independent Variable', size=[2, 2]) + #Setup Inputs + self.inputs.append( 'x', '', 'Independent Variable', size = [2,2]) - # Setup Outputs - self.outputs.append('y', '', 'Dependent Variable', size=[2, 2]) + #Setup Outputs + self.outputs.append( 'y', '', 'Dependent Variable', size = [2,2]) - # Simple model description + #Simple model description self.description = 'This model is a pass through)' self.availableDerivative = 1 - # standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) + #standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) def BlackBox(self, *args, **kwargs): runCases, returnMode, extras = self.parseInputs(*args, **kwargs) - x = [ - self.pyomo_value(runCases[i]['x']) for i in range(0, len(runCases)) - ] + x = [ self.pyomo_value(runCases[i]['x']) for i in range(0,len(runCases)) ] - y = [] + y = [] dydx = [] - + for xval in x: - y.append(xval * units.dimensionless) - dydx_temp = np.zeros([2, 2, 2, 2]) - dydx_temp[0, 0, 0, 0] = 1.0 - dydx_temp[0, 1, 0, 1] = 1.0 - dydx_temp[1, 0, 1, 0] = 1.0 - dydx_temp[1, 1, 1, 1] = 1.0 - - dydx.append(dydx_temp * units.dimensionless) - + y.append(xval*units.dimensionless) + dydx_temp = np.zeros([2,2,2,2]) + dydx_temp[0,0,0,0] = 1.0 + dydx_temp[0,1,0,1] = 1.0 + dydx_temp[1,0,1,0] = 1.0 + dydx_temp[1,1,1,1] = 1.0 + + dydx.append(dydx_temp*units.dimensionless) + if returnMode < 0: - returnMode = -1 * (returnMode + 1) + returnMode = -1*(returnMode + 1) if returnMode == 0: return y[0] if returnMode == 1: @@ -825,41 +728,38 @@ def BlackBox(self, *args, **kwargs): else: if returnMode == 0: opt = [] - for i in range(0, len(y)): - opt.append([y[i]]) + for i in range(0,len(y)): + opt.append([ y[i] ]) return opt if returnMode == 1: opt = [] - for i in range(0, len(y)): - opt.append([[y[i]], [[[dydx[i]]]]]) + for i in range(0,len(y)): + opt.append([ [y[i]], [ [[dydx[i]]] ] ]) return opt bb = PassThrough() - ivals = [ - [np.eye(2) * units.dimensionless], - [np.ones([2, 2]) * units.dimensionless], - [np.zeros([2, 2]) * units.dimensionless], - ] + ivals = [[np.eye(2)*units.dimensionless], [np.ones([2,2])*units.dimensionless], [np.zeros([2,2])*units.dimensionless] ] - xv = np.eye(2) * units.dimensionless + xv = np.eye(2)*units.dimensionless # How the black box may be called using EDI - bbo = bb.BlackBox(**{'x': xv}) - bbo = bb.BlackBox({'x': xv}) - bbo = bb.BlackBox(**{'x': xv, 'optn': True}) + bbo = bb.BlackBox(**{'x':xv}) + bbo = bb.BlackBox({'x':xv}) + bbo = bb.BlackBox(**{'x':xv, 'optn':True}) # # Additional options available with parseInputs bbo = bb.BlackBox(*[xv], **{'optn1': True, 'optn2': False}) - bbo = bb.BlackBox(*[xv, True], **{'optn': False}) - bbo = bb.BlackBox({'x': [x[0] for x in ivals]}) - bbo = bb.BlackBox([{'x': x[0]} for x in ivals]) - bbo = bb.BlackBox([[x[0]] for x in ivals]) - bbo = bb.BlackBox([[x[0]] for x in ivals], True, optn=False) - bbo = bb.BlackBox([[x[0]] for x in ivals], optn1=True, optn2=False) + bbo = bb.BlackBox(*[xv,True], **{'optn': False}) + bbo = bb.BlackBox({'x':[x[0] for x in ivals]}) + bbo = bb.BlackBox([{'x':x[0]} for x in ivals]) + bbo = bb.BlackBox([ [x[0]] for x in ivals]) + bbo = bb.BlackBox([ [x[0]] for x in ivals], True, optn=False) + bbo = bb.BlackBox([ [x[0]] for x in ivals], optn1=True, optn2=False) sm = bb.summary - self.assertRaises(ValueError, bb.sanitizeInputs, *(np.ones([2, 2]) * units.ft,)) + self.assertRaises(ValueError,bb.sanitizeInputs,*(np.ones([2,2])*units.ft,)) + def test_edi_blackbox_bare_example_3(self): "Tests a black box example construction" @@ -867,92 +767,130 @@ def test_edi_blackbox_bare_example_3(self): from pyomo.environ import units import pyomo.environ as pyo from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import ( - BlackBoxFunctionModel_Variable, - TypeCheckedList, - BBList, - BlackBoxFunctionModel, - ) + from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel class Norm_2(BlackBoxFunctionModel): def __init__(self): super().__init__() self.description = 'This model evaluates the two norm' - self.inputs.append( - name='x', units='', description='The x variable', size=3 - ) - self.inputs.append( - name='y', units='', description='The y variable', size=2 - ) - self.outputs.append(name='z', units='', description='The z variable') + self.inputs.append( name = 'x', + units = '' , + description = 'The x variable' , + size = 3) + self.inputs.append( name = 'y', + units = '' , + description = 'The y variable' , + size = 2) + self.outputs.append(name = 'z', + units = '' , + description = 'The z variable') self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) + def BlackBox(self, *args, **kwargs):# The actual function that does things + runCases, returnMode, remainingKwargs = self.parseInputs(*args, **kwargs) - def BlackBox(self, *args, **kwargs): # The actual function that does things - runCases, returnMode, remainingKwargs = self.parseInputs( - *args, **kwargs - ) - - x = [rc['x'] for rc in runCases][0] + x = [ rc['x'] for rc in runCases][0] x = np.array([self.pyomo_value(xval) for xval in x], dtype=np.float64) - y = [rc['y'] for rc in runCases][0] + y = [ rc['y'] for rc in runCases][0] y = np.array([self.pyomo_value(yval) for yval in y], dtype=np.float64) - z = x[0] ** 2 + x[1] ** 2 + x[2] ** 2 + y[0] ** 2 + y[1] ** 2 - dzdx0 = 2 * x[0] # Compute dy/dx0 - dzdx1 = 2 * x[1] # Compute dy/dx1 - dzdx2 = 2 * x[2] # Compute dy/dx2 - dzdy0 = 2 * y[0] - dzdy1 = 2 * y[1] + z = x[0]**2 + x[1]**2 + x[2]**2 + y[0]**2 + y[1]**2 + dzdx0 = 2*x[0] # Compute dy/dx0 + dzdx1 = 2*x[1] # Compute dy/dx1 + dzdx2 = 2*x[2] # Compute dy/dx2 + dzdy0 = 2*y[0] + dzdy1 = 2*y[1] z = z * units.dimensionless - dz = np.array([dzdx0, dzdx1, dzdx2, dzdy0, dzdy1]) * units.dimensionless + dz = np.array([dzdx0,dzdx1,dzdx2,dzdy0,dzdy1]) * units.dimensionless # dydx0 = dydx0 * units.dimensionless # dydx1 = dydx1 * units.dimensionless # dydx2 = dydx2 * units.dimensionless - return z, [dz] # return z, grad(z), hess(z)... + return z, [dz] # return z, grad(z), hess(z)... bb = Norm_2() - bbo = bb.BlackBox( - { - 'x': np.array([0, 0, 0]) * units.dimensionless, - 'y': np.array([0, 0]) * units.dimensionless, - } - ) - bbo = bb.BlackBox( - np.array([0, 0, 0]) * units.dimensionless, - y=np.array([0, 0]) * units.dimensionless, - ) + bbo = bb.BlackBox({'x':np.array([0,0,0])*units.dimensionless, 'y':np.array([0,0])*units.dimensionless}) + bbo = bb.BlackBox(np.array([0,0,0])*units.dimensionless, y=np.array([0,0])*units.dimensionless) - self.assertRaises( - ValueError, - bb.BlackBox, - *( - { - 'er': np.array([0, 0, 0]) * units.dimensionless, - 'y': np.array([0, 0]) * units.dimensionless, - }, - ) - ) - self.assertRaises(ValueError, bb.BlackBox, *('err',)) - self.assertRaises( - ValueError, - bb.BlackBox, - *( - np.array([0, 0, 0]) * units.dimensionless, - np.array([0, 0]) * units.dimensionless, - ), - **{'x': 'err too many'} - ) - self.assertRaises( - ValueError, - bb.BlackBox, - *(np.array([0, 0, 0]) * units.dimensionless,), - **{'notY': np.array([0, 0]) * units.dimensionless} - ) + self.assertRaises(ValueError,bb.BlackBox,*( {'er':np.array([0,0,0])*units.dimensionless, 'y':np.array([0,0])*units.dimensionless}, )) + self.assertRaises(ValueError,bb.BlackBox,*( 'err', )) + self.assertRaises(ValueError,bb.BlackBox,*( np.array([0,0,0])*units.dimensionless, np.array([0,0])*units.dimensionless ), **{'x':'err too many'}) + self.assertRaises(ValueError,bb.BlackBox,*( np.array([0,0,0])*units.dimensionless, ), **{'notY':np.array([0,0])*units.dimensionless}) + + + def test_edi_blackbox_bare_example_4(self): + "Tests a black box example construction without an optimization problem" + import numpy as np + import pyomo.environ as pyo + from pyomo.environ import units + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + from pyomo.common.formatting import tostr + + class PassThrough(BlackBoxFunctionModel): + def __init__(self): + # Set up all the attributes by calling Model.__init__ + super().__init__() + + #Setup Inputs + self.inputs.append( 'x', '', 'X Variable') + self.inputs.append( 'y', '', 'Y Variable') + + #Setup Outputs + self.outputs.append( 'u', '', 'U Variable') + self.outputs.append( 'v', '', 'V Variable') + + #Simple model description + self.description = 'This model is a pass through)' + + self.availableDerivative = 1 + + #standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) + def BlackBox(self, *args, **kwargs): + runCases, returnMode, extras = self.parseInputs(*args, **kwargs) + x = [ self.pyomo_value(runCases[i]['x']) for i in range(0,len(runCases)) ] + y = [ self.pyomo_value(runCases[i]['x']) for i in range(0,len(runCases)) ] + + u = [] + dudx = [] + dudy = [] + v = [] + dvdx = [] + dvdy = [] + + for xval in x: + u.append(xval*units.dimensionless) + dudx.append(1.0*units.dimensionless) + dudy.append(0.0*units.dimensionless) + + for yval in y: + v.append(yval*units.dimensionless) + dvdx.append(0.0*units.dimensionless) + dvdy.append(1.0*units.dimensionless) + + if returnMode < 0: + returnMode = -1*(returnMode + 1) + if returnMode == 0: + return [u[0], v[0]] + if returnMode == 1: + return [u[0], v[0]] , [[dudx[0], dudy[0]], [dvdx[0], dvdy[0]]] + else: + if returnMode == 0: + opt = [] + for i in range(0,len(y)): + opt.append([ u[i], v[i] ]) + return opt + if returnMode == 1: + opt = [] + for i in range(0,len(y)): + opt.append([ [u[i], v[i]], [ [ dudx[i], dudy[i]], [dvdx[i], dvdy[i]] ] ]) + return opt + + bb = PassThrough() + bbo = bb.BlackBox(1.0,1.0) + bbo = bb.BlackBox({'x':np.linspace(0,10,11),'y':np.linspace(0,10,11)}) if __name__ == '__main__': - unittest.main() + unittest.main() \ No newline at end of file diff --git a/pyomo/contrib/edi/tests/test_docSnippets.py b/pyomo/contrib/edi/tests/test_docSnippets.py new file mode 100644 index 00000000000..71caa2fda77 --- /dev/null +++ b/pyomo/contrib/edi/tests/test_docSnippets.py @@ -0,0 +1,1064 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright (c) 2008-2023 +# 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. +# +# Development of this module was conducted as part of the Institute for +# the Design of Advanced Energy Systems (IDAES) with support through the +# Simulation-Based Engineering, Crosscutting Research Program within the +# U.S. Department of Energy’s Office of Fossil Energy and Carbon Management. +# +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + +import pyomo.common.unittest as unittest +import pyomo.environ as pyo +from pyomo.common.dependencies import attempt_import + +from pyomo.core.base.units_container import ( + pint_available, +) + +from pyomo.common.dependencies import numpy, numpy_available +from pyomo.common.dependencies import scipy, scipy_available + +if numpy_available: + import numpy as np + +@unittest.skipIf(not numpy_available, 'Testing pyomo.contrib.edi requires numpy') +@unittest.skipIf(not scipy_available, 'Testing pyomo.contrib.edi requires scipy') +@unittest.skipIf(not pint_available, 'Testing units requires pint') +class TestEDISnippets(unittest.TestCase): + def test_edi_snippet_formuation_01(self): + # BEGIN: Formulation_Snippet_01 + from pyomo.contrib.edi import Formulation + f = Formulation() + # END: Formulation_Snippet_01 + + def test_edi_snippet_formuation_02(self): + # BEGIN: Formulation_Snippet_02 + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = '') + # END: Formulation_Snippet_02 + + def test_edi_snippet_formuation_03(self): + # BEGIN: Formulation_Snippet_03 + from pyomo.contrib.edi import Formulation + f = Formulation() + c = f.Constant(name = 'c', value = 1.0, units = '') + # END: Formulation_Snippet_03 + + def test_edi_snippet_formuation_04(self): + # BEGIN: Formulation_Snippet_04 + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = '') + y = f.Variable(name = 'y', guess = 1.0, units = '') + c = f.Constant(name = 'c', value = 1.0, units = '') + f.Objective( c*x + y ) + # END: Formulation_Snippet_04 + + def test_edi_snippet_formuation_05(self): + # BEGIN: Formulation_Snippet_05 + from pyomo.contrib.edi import Formulation + from pyomo.environ import maximize, minimize + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = '') + y = f.Variable(name = 'y', guess = 1.0, units = '') + c = f.Constant(name = 'c', value = 1.0, units = '') + f.Objective( c*x + y , sense = maximize) + # END: Formulation_Snippet_05 + + def test_edi_snippet_formuation_06(self): + # BEGIN: Formulation_Snippet_06 + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = '') + y = f.Variable(name = 'y', guess = 1.0, units = '') + c = f.Constant(name = 'c', value = 1.0, units = '') + f.Objective( c*x + y ) + f.Constraint( x**2 + y**2 <= 1.0 ) + f.Constraint( x >= 0) + f.Constraint( y <= 0) + # END: Formulation_Snippet_06 + + def test_edi_snippet_formuation_07(self): + # BEGIN: Formulation_Snippet_07 + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = '') + y = f.Variable(name = 'y', guess = 1.0, units = '') + c = f.Constant(name = 'c', value = 1.0, units = '') + f.Objective( c*x + y ) + f.ConstraintList( + [ + x**2 + y**2 <= 1.0 , + x >= 0, + y <= 0, + ] + ) + # END: Formulation_Snippet_07 + + def test_edi_snippet_formuation_08(self): + # BEGIN: Formulation_Snippet_08 + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = '') + y = f.Variable(name = 'y', guess = 1.0, units = '') + c = f.Constant(name = 'c', value = 1.0, units = '') + f.Objective( c*x + y ) + + constraintList = [ + x**2 + y**2 <= 1.0 , + x >= 0, + y <= 0, + ] + + f.ConstraintList( constraintList ) + # END: Formulation_Snippet_08 + + def test_edi_snippet_formuation_09(self): + # BEGIN: Formulation_Snippet_09 + import pyomo.environ as pyo + from pyomo.environ import units + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'y variable') + z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'Output var') + f.Objective( x + y ) + class UnitCircle(BlackBoxFunctionModel): + def __init__(self): + super().__init__() + self.description = 'This model evaluates the function: z = x**2 + y**2' + self.inputs.append( name = 'x', + units = 'ft' , + description = 'The x variable' ) + self.inputs.append( name = 'y', + units = 'ft' , + description = 'The y variable' ) + self.outputs.append( name = 'z', + units = 'ft**2', + description = 'Output variable' ) + self.availableDerivative = 1 + self.post_init_setup(len(self.inputs)) + def BlackBox(self, x, y): # The actual function that does things + # Converts to correct units then casts to float + x = pyo.value(units.convert(x,self.inputs['x'].units)) + y = pyo.value(units.convert(y,self.inputs['y'].units)) + z = x**2 + y**2 # Compute z + dzdx = 2*x # Compute dz/dx + dzdy = 2*y # Compute dz/dy + z *= units.ft**2 + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + f.Constraint( z <= 1*units.m**2 ) + + f.RuntimeConstraint( z, '==', [x,y], UnitCircle() ) + # END: Formulation_Snippet_09 + + ### This will fail validatation, but should construct appropriately + + # BEGIN: Formulation_Snippet_10 + f.RuntimeConstraint( *( z, '==', [x,y], UnitCircle() ) ) + # END: Formulation_Snippet_10 + + # BEGIN: Formulation_Snippet_11 + f.RuntimeConstraint( *[ z, '==', [x,y], UnitCircle() ] ) + # END: Formulation_Snippet_11 + + # BEGIN: Formulation_Snippet_12 + f.RuntimeConstraint( **{ 'outputs' : z, + 'operators' : '==', + 'inputs' : [x,y], + 'black_box' : UnitCircle() } ) + # END: Formulation_Snippet_12 + + # BEGIN: Formulation_Snippet_13 + f.RuntimeConstraint( *( [z], ['=='], [x,y], UnitCircle() ) ) + # END: Formulation_Snippet_13 + + def test_edi_snippet_formuation_14(self): + # BEGIN: Formulation_Snippet_14 + import pyomo.environ as pyo + from pyomo.environ import units + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'y variable') + z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'Output var') + f.Objective( x + y ) + class UnitCircle(BlackBoxFunctionModel): + def __init__(self): + super().__init__() + self.description = 'This model evaluates the function: z = x**2 + y**2' + self.inputs.append( name = 'x', + units = 'ft' , + description = 'The x variable' ) + self.inputs.append( name = 'y', + units = 'ft' , + description = 'The y variable' ) + self.outputs.append( name = 'z', + units = 'ft**2', + description = 'Output variable' ) + self.availableDerivative = 1 + self.post_init_setup(len(self.inputs)) + def BlackBox(self, x, y): # The actual function that does things + # Converts to correct units then casts to float + x = pyo.value(units.convert(x,self.inputs[0].units)) + y = pyo.value(units.convert(y,self.inputs[1].units)) + z = x**2 + y**2 # Compute z + dzdx = 2*x # Compute dz/dx + dzdy = 2*y # Compute dz/dy + z *= units.ft**2 + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + + f.ConstraintList( + [ + z <= 1*units.m**2 , + [ z, '==', [x,y], UnitCircle() ] , + ] + ) + # END: Formulation_Snippet_14 + + ### This will fail validatation, but should construct appropriately + + # BEGIN: Formulation_Snippet_15 + f.ConstraintList( + [ + z <= 1*units.m**2 , + ( z, '==', [x,y], UnitCircle() ) , + ] + ) + # END: Formulation_Snippet_15 + + # BEGIN: Formulation_Snippet_16 + f.ConstraintList( + [ + z <= 1*units.m**2 , + [ z, '==', [x,y], UnitCircle() ] , + ] + ) + # END: Formulation_Snippet_16 + + # BEGIN: Formulation_Snippet_17 + f.ConstraintList( + [ + z <= 1*units.m**2 , + { 'outputs' : z, + 'operators' : '==', + 'inputs' : [x,y], + 'black_box' : UnitCircle() } , + ] + ) + # END: Formulation_Snippet_17 + + # BEGIN: Formulation_Snippet_18 + f.ConstraintList( + [ + z <= 1*units.m**2 , + ( [z], ['=='], [x,y], UnitCircle() ) , + ] + ) + # END: Formulation_Snippet_18 + + def test_edi_snippet_variables_01(self): + # BEGIN: Variables_Snippet_01 + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + # END: Variables_Snippet_01 + + def test_edi_snippet_variables_02(self): + # BEGIN: Variables_Snippet_02 + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Variable('x', 1.0, 'm' ) + # END: Variables_Snippet_02 + + def test_edi_snippet_variables_03(self): + # BEGIN: Variables_Snippet_03 + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Variable( name = 'x', + guess = 1.0, + units = 'm' , + description = 'The x variable', + bounds = [-10,10] ) + # END: Variables_Snippet_03 + + def test_edi_snippet_variables_04(self): + # BEGIN: Variables_Snippet_04 + from pyomo.contrib.edi import Formulation + from pyomo.environ import Integers + f = Formulation() + x = f.Variable( name = 'x', + guess = 1.0, + units = 'm' , + description = 'The x variable', + domain = Integers ) + # END: Variables_Snippet_04 + + def test_edi_snippet_variables_05(self): + # BEGIN: Variables_Snippet_05 + from pyomo.environ import units + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Variable( name = 'x', + guess = 1.0, + units = units.m , + description = 'The x variable' ) + # END: Variables_Snippet_05 + + def test_edi_snippet_variables_06(self): + # BEGIN: Variables_Snippet_06 + from pyomo.environ import units + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Variable( name = 'x', + guess = 1.0, + units = 'm' , + description = 'The x variable', + size = 5 ) + # END: Variables_Snippet_06 + + def test_edi_snippet_variables_07(self): + # BEGIN: Variables_Snippet_07 + from pyomo.environ import units + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Variable( name = 'x', + guess = 1.0, + units = 'm' , + description = 'The x variable', + size = [10,2] ) + # END: Variables_Snippet_07 + + def test_edi_snippet_variables_08(self): + # BEGIN: Variables_Snippet_08 + from pyomo.environ import units + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Variable( name = 'x', + guess = 1.0, + units = 'kg*m/s**2' , + description = 'The x variable' ) + # END: Variables_Snippet_08 + + def test_edi_snippet_constants_01(self): + # BEGIN: Constants_Snippet_01 + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Constant(name = 'c', value = 1.0, units = 'm' , description = 'A constant c') + # END: Constants_Snippet_01 + + def test_edi_snippet_constants_02(self): + # BEGIN: Constants_Snippet_02 + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Constant('c', 1.0, 'm' ) + # END: Constants_Snippet_02 + + def test_edi_snippet_constants_03(self): + # BEGIN: Constants_Snippet_03 + from pyomo.environ import units + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Constant( name = 'c', + value = 1.0, + units = units.m , + description = 'A constant c' ) + # END: Constants_Snippet_03 + + def test_edi_snippet_constants_04(self): + # BEGIN: Constants_Snippet_04 + from pyomo.environ import units + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Constant( name = 'c', + value = 1.0, + units = 'm' , + description = 'A constant c', + size = 5 ) + # END: Constants_Snippet_04 + + def test_edi_snippet_constants_05(self): + # BEGIN: Constants_Snippet_05 + from pyomo.environ import units + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Constant( name = 'c', + value = 1.0, + units = 'm' , + description = 'A constant c', + size = [10,2] ) + # END: Constants_Snippet_05 + + def test_edi_snippet_constants_06(self): + # BEGIN: Constants_Snippet_06 + from pyomo.environ import units + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Constant( name = 'c', + value = 1.0, + units = 'kg*m/s**2' , + description = 'A constant c' ) + # END: Constants_Snippet_06 + + def test_edi_snippet_objectives_01(self): + # BEGIN: Objectives_Snippet_01 + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) + f.Objective( c*x + y ) # Default is minimize + # END: Objectives_Snippet_01 + + def test_edi_snippet_objectives_02(self): + # BEGIN: Objectives_Snippet_02 + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) + f.Objective( c*x**4 + y**4 ) # Default is minimize + # END: Objectives_Snippet_02 + + def test_edi_snippet_objectives_03(self): + # BEGIN: Objectives_Snippet_03 + from pyomo.contrib.edi import Formulation + from pyomo.environ import minimize, maximize + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) + f.Objective( c*x**4 + y**4 , sense = minimize) + # END: Objectives_Snippet_03 + + def test_edi_snippet_objectives_04(self): + # BEGIN: Objectives_Snippet_04 + from pyomo.contrib.edi import Formulation + from pyomo.environ import minimize, maximize + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) + f.Objective( c*x**4 + y**4 , sense = 1 ) # 1 corresponds to minimize + # END: Objectives_Snippet_04 + + def test_edi_snippet_objectives_05(self): + # BEGIN: Objectives_Snippet_05 + from pyomo.contrib.edi import Formulation + from pyomo.environ import minimize, maximize + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) + f.Objective( -c*x**4 - y**4 , sense = maximize) + # END: Objectives_Snippet_05 + + def test_edi_snippet_objectives_06(self): + # BEGIN: Objectives_Snippet_06 + from pyomo.contrib.edi import Formulation + from pyomo.environ import minimize, maximize + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) + f.Objective( -c*x**4 - y**4 , sense = -1 ) # -1 corresponds to maximize + # END: Objectives_Snippet_06 + + def test_edi_snippet_objectives_07(self): + # BEGIN: Objectives_Snippet_07 + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Variable( name = 'x', + guess = 1.0, + units = 'm' , + description = 'The x variable', + bounds = [0,100] , + size = 3 ) + y = f.Variable( name = 'y', + guess = 1.0, + units = 'm' , + description = 'The y variable', + size = [2,2]) + c = f.Constant( name = 'c', + value = 1.0, + units = '' , + description = 'A constant c' , + size = 3 ) + f.Objective( c[0]*x[0] + c[1]*x[1] + c[2]*x[2] + + y[0,0]**4 + + y[0,1]**4 + + y[1,0]**4 + + y[1,1]**4 ) # Default is minimize + # END: Objectives_Snippet_07 + + def test_edi_snippet_constraints_01(self): + # BEGIN: Constraints_Snippet_01 + from pyomo.environ import units + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) + f.Objective( c*x + y ) + f.ConstraintList( + [ + x**2 + y**2 <= 1.0*units.m**2 , + x <= 0.75*units.m, + x >= y , + ] + ) + # END: Constraints_Snippet_01 + + def test_edi_snippet_constraints_02(self): + # BEGIN: Constraints_Snippet_02 + from pyomo.environ import units + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) + f.Objective( c*x + y ) + f.Constraint( x**2 + y**2 <= 1.0*units.m**2 ) + f.Constraint( x <= 0.75*units.m ) + f.Constraint( x >= y ) + # END: Constraints_Snippet_02 + + def test_edi_snippet_constraints_03(self): + # BEGIN: Constraints_Snippet_03 + from pyomo.environ import units + from pyomo.contrib.edi import Formulation + f = Formulation() + x = f.Variable( name = 'x', + guess = 1.0, + units = 'm' , + description = 'The x variable', + bounds = [0,100] , + size = 3 ) + y = f.Variable( name = 'y', + guess = 1.0, + units = 'm' , + description = 'The y variable', + size = [2,2]) + c = f.Constant( name = 'c', + value = 1.0, + units = '' , + description = 'A constant c' , + size = 3 ) + f.Objective( c[0]*x[0] + c[1]*x[1] + c[2]*x[2] + + y[0,0]**4 + + y[0,1]**4 + + y[1,0]**4 + + y[1,1]**4 ) # Default is minimize + f.ConstraintList( + [ + x[0]**2 + x[1]**2 + x[2]**2 <= 1.0*units.m, + y[0,0] >= 1.0*units.m, + y[0,1] >= 1.0*units.m, + y[1,0] >= 1.0*units.m, + y[1,1] >= 1.0*units.m, + x[0] >= y[0,0] , + ] + ) + # END: Constraints_Snippet_03 + + + + + + + + + + + + + + + + def test_edi_snippet_runtimeconstraints_01(self): + # BEGIN: RuntimeConstraints_Snippet_01 + import pyomo.environ as pyo + from pyomo.environ import units + from pyomo.contrib.edi import BlackBoxFunctionModel + + class Parabola(BlackBoxFunctionModel): + def __init__(self): + + # Call parent init + super().__init__() + + # A brief description of the model + self.description = 'This model evaluates the function: y = x**2' + + # Append the model inputs + self.inputs.append( + name='x', units='ft' , description='The x variable' + ) + + # Append the model outputs + self.outputs.append( + name='y', units='ft**2' , description='The y variable' + ) + + # Set the highest available derivative + # Should be 1 for most cases but defaults to 0 + self.availableDerivative = 1 + + def BlackBox(self, x): # The actual function that does things + + # Convert to correct units and cast to a float + x = pyo.value(units.convert(x,self.inputs['x'].units)) + + # Compute y + y = x**2 + + # Compute dy/dx + dydx = 2*x + + # Add the units to the output + y = y * self.outputs['y'].units + + # Add the units to the derivative for output + dydx = dydx * self.outputs['y'].units / self.inputs['x'].units + + # Return using the output packing guidelines described in the documentation: + # returnVal[0] = output_value + # returnVal[1] = jacobian + # returnVal[1][0] = derivative_scalarOutput_wrt_0th_input + return y, [dydx] + # END: RuntimeConstraints_Snippet_01 + + + def test_edi_snippet_runtimeconstraints_02(self): + + import pyomo.environ as pyo + from pyomo.environ import units + from pyomo.contrib.edi import BlackBoxFunctionModel + + class Parabola(BlackBoxFunctionModel): + def __init__(self): + + # BEGIN: RuntimeConstraints_Snippet_02 + # Call parent init + super().__init__() + # END: RuntimeConstraints_Snippet_02 + + # A brief description of the model + self.description = 'This model evaluates the function: y = x**2' + + # Append the model inputs + self.inputs.append( + name='x', units='ft' , description='The x variable' + ) + + # Append the model outputs + self.outputs.append( + name='y', units='ft**2' , description='The y variable' + ) + + # BEGIN: RuntimeConstraints_Snippet_05 + # Set the highest available derivative + # Should be 1 for most cases but defaults to 0 + self.availableDerivative = 1 + # END: RuntimeConstraints_Snippet_05 + + def BlackBox(self, x): # The actual function that does things + + storeX = x + + # BEGIN: RuntimeConstraints_Snippet_06 + x = units.convert(x,self.inputs['x'].units) + # END: RuntimeConstraints_Snippet_06 + + x = storeX + + # BEGIN: RuntimeConstraints_Snippet_07 + # Convert to correct units and cast to a float + x = pyo.value(units.convert(x,self.inputs['x'].units)) + # END: RuntimeConstraints_Snippet_07 + + # Compute y + y = x**2 + + # Compute dy/dx + dydx = 2*x + + # BEGIN: RuntimeConstraints_Snippet_08 + # Add the units to the output + y = y * self.outputs['y'].units + + # Add the units to the derivative for output + dydx = dydx * self.outputs['y'].units / self.inputs['x'].units + # END: RuntimeConstraints_Snippet_08 + + # BEGIN: RuntimeConstraints_Snippet_09 + # Return using the output packing guidelines described in the documentation: + # returnVal[0] = output_value + # returnVal[1] = jacobian + # returnVal[1][0] = derivative_scalarOutput_wrt_0th_input + return y, [dydx] + # END: RuntimeConstraints_Snippet_09 + + def test_edi_snippet_runtimeconstraints_03(self): + import pyomo.environ as pyo + from pyomo.environ import units + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The z variable') + + f.Objective( x + y ) + + class UnitCircle(BlackBoxFunctionModel): + def __init__(self): + super().__init__() + self.description = 'This model evaluates the function: z = x**2 + y**2' + # BEGIN: RuntimeConstraints_Snippet_03 + self.inputs.append( name = 'x', + units = 'ft' , + description = 'The x variable' ) + self.inputs.append( name = 'y', + units = 'ft' , + description = 'The y variable' ) + # END: RuntimeConstraints_Snippet_03 + self.outputs.append( name = 'z', + units = 'ft**2', + description = 'Resultant of the unit circle evaluation' ) + self.availableDerivative = 1 + self.post_init_setup(len(self.inputs)) + + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value(units.convert(x,self.inputs['x'].units)) + y = pyo.value(units.convert(y,self.inputs['y'].units)) + z = x**2 + y**2 + dzdx = 2*x + dzdy = 2*y + z = z * self.outputs['z'].units + dzdx = dzdx * self.outputs['z'].units / self.inputs['x'].units + dzdy = dzdy * self.outputs['z'].units / self.inputs['y'].units + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + + f.ConstraintList( + [ + (z, '==', [x,y], UnitCircle() ) , + z <= 1*units.m**2 + ] + ) + + def test_edi_snippet_runtimeconstraints_04(self): + import numpy as np + import pyomo.environ as pyo + from pyomo.environ import units + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + from pyomo.common.formatting import tostr + + class PassThrough(BlackBoxFunctionModel): + def __init__(self): + # Set up all the attributes by calling Model.__init__ + super().__init__() + + #Setup Inputs + self.inputs.append( 'x', '', 'X Variable') + self.inputs.append( 'y', '', 'Y Variable') + + # BEGIN: RuntimeConstraints_Snippet_04 + #Setup Outputs + self.outputs.append( 'u', '', 'U Variable') + self.outputs.append( 'v', '', 'V Variable') + # END: RuntimeConstraints_Snippet_04 + + #Simple model description + self.description = 'This model is a pass through)' + + self.availableDerivative = 1 + + #standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) + def BlackBox(self, *args, **kwargs): + runCases, returnMode, extras = self.parseInputs(*args, **kwargs) + x = [ self.pyomo_value(runCases[i]['x']) for i in range(0,len(runCases)) ] + y = [ self.pyomo_value(runCases[i]['x']) for i in range(0,len(runCases)) ] + + u = [] + dudx = [] + dudy = [] + v = [] + dvdx = [] + dvdy = [] + + for xval in x: + u.append(xval*units.dimensionless) + dudx.append(1.0*units.dimensionless) + dudy.append(0.0*units.dimensionless) + + for yval in y: + v.append(yval*units.dimensionless) + dvdx.append(0.0*units.dimensionless) + dvdy.append(1.0*units.dimensionless) + + if returnMode < 0: + returnMode = -1*(returnMode + 1) + if returnMode == 0: + return [u[0], v[0]] + if returnMode == 1: + return [u[0], v[0]] , [[dudx[0], dudy[0]], [dvdx[0], dvdy[0]]] + else: + if returnMode == 0: + opt = [] + for i in range(0,len(y)): + opt.append([ u[i], v[i] ]) + return opt + if returnMode == 1: + opt = [] + for i in range(0,len(y)): + opt.append([ [u[i], v[i]], [ [ dudx[i], dudy[i]], [dvdx[i], dvdy[i]] ] ]) + return opt + + bb = PassThrough() + bbo = bb.BlackBox(1.0,1.0) + bbo = bb.BlackBox({'x':np.linspace(0,10,11),'y':np.linspace(0,10,11)}) + + def test_edi_snippet_runtimeconstraints_10(self): + # BEGIN: RuntimeConstraints_Snippet_10 + import pyomo.environ as pyo + from pyomo.environ import units + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + f = Formulation() + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The z variable') + + f.Objective( x + y ) + + class UnitCircle(BlackBoxFunctionModel): + def __init__(self): + super().__init__() + self.description = 'This model evaluates the function: z = x**2 + y**2' + self.inputs.append( name = 'x', + units = 'ft' , + description = 'The x variable' ) + self.inputs.append( name = 'y', + units = 'ft' , + description = 'The y variable' ) + self.outputs.append( name = 'z', + units = 'ft**2', + description = 'Resultant of the unit circle evaluation' ) + self.availableDerivative = 1 + self.post_init_setup(len(self.inputs)) + + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value(units.convert(x,self.inputs['x'].units)) + y = pyo.value(units.convert(y,self.inputs['y'].units)) + z = x**2 + y**2 + dzdx = 2*x + dzdy = 2*y + z = z * self.outputs['z'].units + dzdx = dzdx * self.outputs['z'].units / self.inputs['x'].units + dzdy = dzdy * self.outputs['z'].units / self.inputs['y'].units + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + + f.ConstraintList( + [ + (z, '==', [x,y], UnitCircle() ) , + z <= 1*units.m**2 + ] + ) + # END: RuntimeConstraints_Snippet_10 + + + + + + + + + + + + + + + + + + + + + + + + + + + + def test_edi_snippet_advancedRTC_01(self): + # BEGIN: AdvancedRTC_Snippet_01 + import numpy as np + import pyomo.environ as pyo + from pyomo.environ import units + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + + class SignomialTest(BlackBoxFunctionModel): + def __init__(self): + # Set up all the attributes by calling Model.__init__ + super().__init__() + + #Setup Inputs + self.inputs.append( 'x', '', 'Independent Variable') + + #Setup Outputs + self.outputs.append( 'y', '', 'Dependent Variable') + + #Simple model description + self.description = ( 'This model evaluates the '+ + 'function: max([-6*x-6, x**4-3*x**2])' ) + + self.availableDerivative = 1 + + def BlackBox(self, *args, **kwargs): + runCases, returnMode, extras = self.parseInputs(*args, **kwargs) + + x = np.array([ pyo.value(runCases[i]['x']) for i in range(0,len(runCases)) ]) + + y = np.maximum(-6*x-6, x**4-3*x**2) + dydx = 4*x**3 - 6*x + ddy_ddx = 12*x**2 - 6 + gradientSwitch = -6*x-6 > x**4-3*x**2 + dydx[gradientSwitch] = -6 + ddy_ddx[gradientSwitch] = 0 + + y = [ yval*units.dimensionless for yval in y ] + dydx = [dydx[i] * units.dimensionless for i in range(0,len(dydx))] + + if returnMode < 0: + returnMode = -1*(returnMode + 1) + if returnMode == 0: + return y[0] + if returnMode == 1: + return y[0], dydx + else: + if returnMode == 0: + opt = [] + for i in range(0,len(y)): + opt.append([ y[i] ]) + return opt + if returnMode == 1: + opt = [] + for i in range(0,len(y)): + opt.append([ [y[i]], [ [[dydx[i]]] ] ]) + return opt + + s = SignomialTest() + ivals = [[x] for x in np.linspace(-2,2,11)] + + # How the black box may be called using EDI + bbo = s.BlackBox(**{'x':0.5}) + bbo = s.BlackBox({'x':0.5}) + bbo = s.BlackBox(**{'x':0.5, 'optn':True}) + + # Additional options available with parseInputs + bbo = s.BlackBox(*[0.5], **{'optn1': True, 'optn2': False}) + bbo = s.BlackBox(*[0.5,True], **{'optn': False}) + bbo = s.BlackBox({'x':[x for x in np.linspace(-2,2,11)]}) + bbo = s.BlackBox([{'x':x} for x in np.linspace(-2,2,11)]) + bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)]) + bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)], True, optn=False) + bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)], optn1=True, optn2=False) + # END: AdvancedRTC_Snippet_01 + + + def test_edi_snippet_advancedRTC_02(self): + import numpy as np + import pyomo.environ as pyo + from pyomo.environ import units + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + + class SignomialTest(BlackBoxFunctionModel): + def __init__(self): + # Set up all the attributes by calling Model.__init__ + super().__init__() + + #Setup Inputs + self.inputs.append( 'x', '', 'Independent Variable') + + #Setup Outputs + self.outputs.append( 'y', '', 'Dependent Variable') + + #Simple model description + self.description = ( 'This model evaluates the '+ + 'function: max([-6*x-6, x**4-3*x**2])' ) + + self.availableDerivative = 1 + + # BEGIN: AdvancedRTC_Snippet_02 + def BlackBox(self, *args, **kwargs): + runCases, returnMode, extras = self.parseInputs(*args, **kwargs) + # END: AdvancedRTC_Snippet_02 + + x = np.array([ pyo.value(runCases[i]['x']) for i in range(0,len(runCases)) ]) + + y = np.maximum(-6*x-6, x**4-3*x**2) + dydx = 4*x**3 - 6*x + ddy_ddx = 12*x**2 - 6 + gradientSwitch = -6*x-6 > x**4-3*x**2 + dydx[gradientSwitch] = -6 + ddy_ddx[gradientSwitch] = 0 + + y = [ yval*units.dimensionless for yval in y ] + dydx = [dydx[i] * units.dimensionless for i in range(0,len(dydx))] + + if returnMode < 0: + returnMode = -1*(returnMode + 1) + if returnMode == 0: + return y[0] + if returnMode == 1: + return y[0], dydx + else: + if returnMode == 0: + opt = [] + for i in range(0,len(y)): + opt.append([ y[i] ]) + return opt + if returnMode == 1: + opt = [] + for i in range(0,len(y)): + opt.append([ [y[i]], [ [[dydx[i]]] ] ]) + return opt + + s = SignomialTest() + ivals = [[x] for x in np.linspace(-2,2,11)] + + # How the black box may be called using EDI + bbo = s.BlackBox(**{'x':0.5}) + bbo = s.BlackBox({'x':0.5}) + bbo = s.BlackBox(**{'x':0.5, 'optn':True}) + + # Additional options available with parseInputs + bbo = s.BlackBox(*[0.5], **{'optn1': True, 'optn2': False}) + bbo = s.BlackBox(*[0.5,True], **{'optn': False}) + bbo = s.BlackBox({'x':[x for x in np.linspace(-2,2,11)]}) + bbo = s.BlackBox([{'x':x} for x in np.linspace(-2,2,11)]) + bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)]) + bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)], True, optn=False) + bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)], optn1=True, optn2=False) + + + + + + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/pyomo/contrib/edi/tests/test_examples.py b/pyomo/contrib/edi/tests/test_examples.py index 9af15b75fdb..4f8d11f1c76 100644 --- a/pyomo/contrib/edi/tests/test_examples.py +++ b/pyomo/contrib/edi/tests/test_examples.py @@ -18,37 +18,43 @@ import pyomo.common.unittest as unittest import pyomo.environ as pyo from pyomo.common.dependencies import attempt_import - -from pyomo.core.base.units_container import pint_available - import importlib - - testIndex = 0 +from pyomo.core.base.units_container import ( + pint_available, +) + +from pyomo.common.dependencies import numpy, numpy_available +from pyomo.common.dependencies import scipy, scipy_available +@unittest.skipIf(not numpy_available, 'Testing pyomo.contrib.edi requires numpy') +@unittest.skipIf(not scipy_available, 'Testing pyomo.contrib.edi requires scipy') +@unittest.skipIf(not pint_available, 'Testing units requires pint') class EDIExamples(unittest.TestCase): def test_edi_example_placeholder(self): "A placeholder" pass - def create_new(filename): def t_function(self): importName = filename[0:-3] # filename = ".."+filename try: - importlib.import_module("pyomo.contrib.edi.examples." + importName) + importlib.import_module("pyomo.contrib.edi.examples."+importName) except: - self.fail("This example is failing: %s" % (filename)) - + self.fail("This example is failing: %s"%(filename)) return t_function -pythonFileList = ["readme_example.py", "aircraft_gp.py"] + +pythonFileList = [ + "readme_example.py", + "aircraft_gp.py" +] for filename in pythonFileList: - testName = 'test_DocumentationExample_%d' % (testIndex) + testName = 'test_DocumentationExample_%d'%(testIndex) testIndex += 1 t_Function = create_new(filename) if pint_available: @@ -57,3 +63,5 @@ def t_function(self): if __name__ == '__main__': unittest.main() + + diff --git a/pyomo/contrib/edi/tests/test_formulation.py b/pyomo/contrib/edi/tests/test_formulation.py index e7de6cac038..5b85c22aeb5 100644 --- a/pyomo/contrib/edi/tests/test_formulation.py +++ b/pyomo/contrib/edi/tests/test_formulation.py @@ -17,48 +17,34 @@ import pyomo.common.unittest as unittest import pyomo.environ as pyo - -# from pyomo.opt import check_optimal_termination from pyomo.common.dependencies import attempt_import -from pyomo.core.base.units_container import pint_available - -# np, numpy_available = attempt_import( -# 'numpy', 'edi requires numpy' -# ) -# scipy, scipy_available = attempt_import( -# 'scipy', 'inverse_reduced_hessian requires scipy' -# ) - -# if not (numpy_available and scipy_available): -# if not numpy_available: -# raise unittest.SkipTest( -# 'edi.formulation tests require numpy' -# ) - +from pyomo.core.base.units_container import ( + pint_available, +) -@unittest.skipIf(not pint_available, 'Testing units requires pint') +@unittest.skipIf(not pint_available, 'Testing pyomo.contrib.edi requires pint') class TestEDIFormulation(unittest.TestCase): def test_edi_formulation_init(self): "Tests that a formulation initializes to the correct type and has proper data" from pyomo.environ import ConcreteModel from pyomo.contrib.edi import Formulation - f = Formulation() self.assertIsInstance(f, Formulation) self.assertIsInstance(f, ConcreteModel) - self.assertEqual(f._objective_counter, 0) - self.assertEqual(f._constraint_counter, 0) - self.assertEqual(f._variable_keys, []) - self.assertEqual(f._constant_keys, []) - self.assertEqual(f._objective_keys, []) - self.assertEqual(f._runtimeObjective_keys, []) - self.assertEqual(f._objective_keys, []) - self.assertEqual(f._runtimeConstraint_keys, []) - self.assertEqual(f._constraint_keys, []) - self.assertEqual(f._allConstraint_keys, []) + self.assertEqual(f._objective_counter , 0 ) + self.assertEqual(f._constraint_counter , 0 ) + self.assertEqual(f._variable_keys , [] ) + self.assertEqual(f._constant_keys , [] ) + self.assertEqual(f._objective_keys , [] ) + self.assertEqual(f._runtimeObjective_keys , [] ) + self.assertEqual(f._objective_keys , [] ) + self.assertEqual(f._runtimeConstraint_keys , [] ) + self.assertEqual(f._constraint_keys , [] ) + self.assertEqual(f._allConstraint_keys , [] ) + def test_edi_formulation_variable(self): "Tests the variable constructor in edi.formulation" @@ -68,182 +54,26 @@ def test_edi_formulation_variable(self): f = Formulation() - x1 = f.Variable( - name='x1', - guess=1.0, - units='m', - description='The x variable', - size=None, - bounds=None, - domain=None, - ) - self.assertRaises(RuntimeError, f.Variable, *('x1', 1.0, 'm')) + x1 = f.Variable(name = 'x1', guess = 1.0, units = 'm' , description = 'The x variable', size = None, bounds=None, domain=None ) + self.assertRaises(RuntimeError,f.Variable,*('x1', 1.0, 'm')) x2 = f.Variable('x2', 1.0, 'm') - x3 = f.Variable('x3', 1.0, 'm', 'The x variable', None, None, None) - x4 = f.Variable( - name='x4', - guess=1.0, - units='m', - description='The x variable', - size=None, - bounds=None, - domain=PositiveReals, - ) - self.assertRaises( - RuntimeError, - f.Variable, - **{ - 'name': 'x5', - 'guess': 1.0, - 'units': 'm', - 'description': 'The x variable', - 'size': None, - 'bounds': None, - 'domain': "error", - } - ) - - x6 = f.Variable( - name='x6', - guess=1.0, - units='m', - description='The x variable', - size=0, - bounds=None, - domain=None, - ) - x7 = f.Variable( - name='x7', - guess=1.0, - units='m', - description='The x variable', - size=5, - bounds=None, - domain=None, - ) - self.assertRaises( - ValueError, - f.Variable, - **{ - 'name': 'x8', - 'guess': 1.0, - 'units': 'm', - 'description': 'The x variable', - 'size': 'error', - 'bounds': None, - 'domain': None, - } - ) - x9 = f.Variable( - name='x9', - guess=1.0, - units='m', - description='The x variable', - size=[2, 2], - bounds=None, - domain=None, - ) - self.assertRaises( - ValueError, - f.Variable, - **{ - 'name': 'x10', - 'guess': 1.0, - 'units': 'm', - 'description': 'The x variable', - 'size': ['2', '2'], - 'bounds': None, - 'domain': None, - } - ) - self.assertRaises( - ValueError, - f.Variable, - **{ - 'name': 'x11', - 'guess': 1.0, - 'units': 'm', - 'description': 'The x variable', - 'size': [2, 1], - 'bounds': None, - 'domain': None, - } - ) - - x12 = f.Variable( - name='x12', - guess=1.0, - units='m', - description='The x variable', - size=None, - bounds=[-10, 10], - domain=None, - ) - self.assertRaises( - ValueError, - f.Variable, - **{ - 'name': 'x13', - 'guess': 1.0, - 'units': 'm', - 'description': 'The x variable', - 'size': None, - 'bounds': [10, -10], - 'domain': None, - } - ) - self.assertRaises( - ValueError, - f.Variable, - **{ - 'name': 'x14', - 'guess': 1.0, - 'units': 'm', - 'description': 'The x variable', - 'size': None, - 'bounds': ["-10", "10"], - 'domain': None, - } - ) - self.assertRaises( - ValueError, - f.Variable, - **{ - 'name': 'x15', - 'guess': 1.0, - 'units': 'm', - 'description': 'The x variable', - 'size': None, - 'bounds': [1, 2, 3], - 'domain': None, - } - ) - self.assertRaises( - ValueError, - f.Variable, - **{ - 'name': 'x16', - 'guess': 1.0, - 'units': 'm', - 'description': 'The x variable', - 'size': None, - 'bounds': "error", - 'domain': None, - } - ) - self.assertRaises( - ValueError, - f.Variable, - **{ - 'name': 'x17', - 'guess': 1.0, - 'units': 'm', - 'description': 'The x variable', - 'size': None, - 'bounds': [0, "10"], - 'domain': None, - } - ) + x3 = f.Variable('x3', 1.0, 'm' , 'The x variable', None, None, None ) + x4 = f.Variable(name = 'x4', guess = 1.0, units = 'm' , description = 'The x variable', size = None, bounds=None, domain=PositiveReals ) + self.assertRaises(RuntimeError, f.Variable, **{'name':'x5', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': None, 'bounds':None, 'domain':"error" }) + + x6 = f.Variable(name = 'x6', guess = 1.0, units = 'm' , description = 'The x variable', size = 0, bounds=None, domain=None ) + x7 = f.Variable(name = 'x7', guess = 1.0, units = 'm' , description = 'The x variable', size = 5, bounds=None, domain=None ) + self.assertRaises(ValueError, f.Variable, **{'name':'x8', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': 'error', 'bounds':None, 'domain':None }) + x9 = f.Variable(name = 'x9', guess = 1.0, units = 'm' , description = 'The x variable', size = [2,2], bounds=None, domain=None ) + self.assertRaises(ValueError, f.Variable, **{'name':'x10', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': ['2','2'], 'bounds':None, 'domain':None }) + self.assertRaises(ValueError, f.Variable, **{'name':'x11', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': [2,1], 'bounds':None, 'domain':None }) + + x12 = f.Variable(name = 'x12', guess = 1.0, units = 'm' , description = 'The x variable', size = None, bounds=[-10,10], domain=None ) + self.assertRaises(ValueError, f.Variable, **{'name':'x13', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': None, 'bounds':[10,-10], 'domain':None }) + self.assertRaises(ValueError, f.Variable, **{'name':'x14', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': None, 'bounds':["-10","10"], 'domain':None }) + self.assertRaises(ValueError, f.Variable, **{'name':'x15', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': None, 'bounds':[1,2,3], 'domain':None }) + self.assertRaises(ValueError, f.Variable, **{'name':'x16', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': None, 'bounds':"error", 'domain':None }) + self.assertRaises(ValueError, f.Variable, **{'name':'x17', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': None, 'bounds':[0,"10"], 'domain':None }) # verifies alternate unit construction x18 = f.Variable('x18', 1.0, pyo.units.m) @@ -256,98 +86,20 @@ def test_edi_formulation_constant(self): f = Formulation() - c1 = f.Constant( - name='c1', - value=1.0, - units='m', - description='A constant c', - size=None, - within=None, - ) - self.assertRaises(RuntimeError, f.Constant, *('c1', 1.0, 'm')) + c1 = f.Constant(name = 'c1', value = 1.0, units = 'm', description = 'A constant c', size = None, within = None) + self.assertRaises(RuntimeError,f.Constant,*('c1', 1.0, 'm')) c2 = f.Constant('c2', 1.0, 'm') c3 = f.Constant('c3', 1.0, 'm', 'A constant c', None, None) - c4 = f.Constant( - name='c4', - value=1.0, - units='m', - description='A constant c', - size=None, - within=PositiveReals, - ) - self.assertRaises( - RuntimeError, - f.Constant, - **{ - 'name': 'c5', - 'value': 1.0, - 'units': 'm', - 'description': 'The x variable', - 'size': None, - 'within': "error", - } - ) + c4 = f.Constant(name = 'c4', value = 1.0, units = 'm' , description = 'A constant c', size = None, within=PositiveReals ) + self.assertRaises(RuntimeError, f.Constant, **{'name':'c5', 'value':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': None, 'within':"error" }) + + c6 = f.Constant(name = 'c6', value = 1.0, units = 'm' , description = 'A constant c', size = 0, within=None ) + c7 = f.Constant(name = 'c7', value = 1.0, units = 'm' , description = 'A constant c', size = 5, within=None ) + self.assertRaises(ValueError, f.Constant, **{'name':'c8', 'value':1.0 , 'units' :'m' , 'description': 'A constant c', 'size': 'error', 'within':None }) + c9 = f.Constant(name = 'c9', value = 1.0, units = 'm' , description = 'A constant c', size = [2,2], within=None ) + self.assertRaises(ValueError, f.Constant, **{'name':'c10', 'value':1.0 , 'units' :'m' , 'description': 'A constant c', 'size': ['2','2'], 'within':None }) + self.assertRaises(ValueError, f.Constant, **{'name':'c11', 'value':1.0 , 'units' :'m' , 'description': 'A constant c', 'size': [2,1], 'within':None }) - c6 = f.Constant( - name='c6', - value=1.0, - units='m', - description='A constant c', - size=0, - within=None, - ) - c7 = f.Constant( - name='c7', - value=1.0, - units='m', - description='A constant c', - size=5, - within=None, - ) - self.assertRaises( - ValueError, - f.Constant, - **{ - 'name': 'c8', - 'value': 1.0, - 'units': 'm', - 'description': 'A constant c', - 'size': 'error', - 'within': None, - } - ) - c9 = f.Constant( - name='c9', - value=1.0, - units='m', - description='A constant c', - size=[2, 2], - within=None, - ) - self.assertRaises( - ValueError, - f.Constant, - **{ - 'name': 'c10', - 'value': 1.0, - 'units': 'm', - 'description': 'A constant c', - 'size': ['2', '2'], - 'within': None, - } - ) - self.assertRaises( - ValueError, - f.Constant, - **{ - 'name': 'c11', - 'value': 1.0, - 'units': 'm', - 'description': 'A constant c', - 'size': [2, 1], - 'within': None, - } - ) def test_edi_formulation_objective(self): "Tests the objective constructor in edi.formulation" @@ -356,9 +108,9 @@ def test_edi_formulation_objective(self): from pyomo.contrib.edi import Formulation f = Formulation() - x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') - y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') - f.Objective(x + y) + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + f.Objective( x + y ) def test_edi_formulation_runtimeobjective(self): "Tests the runtime objective constructor in edi.formulation" @@ -370,436 +122,317 @@ def test_edi_formulation_constraint(self): import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') - y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') - f.Objective(x + y) - f.Constraint(x + y <= 1.0 * units.m) + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + f.Objective(x+y) + f.Constraint(x+y <= 1.0*units.m) + def test_edi_formulation_runtimeconstraint_tuple(self): "Tests the runtime constraint constructor in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel - f = Formulation() - x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') - y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') - z = f.Variable( - name='z', guess=1.0, units='m^2', description='The unit circle output' - ) - c = f.Constant( - name='c', value=1.0, units='', description='A constant c', size=2 - ) - f.Objective(x + y) - + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The unit circle output') + c = f.Constant(name = 'c', value = 1.0, units = '', description = 'A constant c', size = 2) + f.Objective( x + y ) class UnitCircle(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super(UnitCircle, self).__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append(name='x', units='ft', description='The x variable') - self.inputs.append(name='y', units='ft', description='The y variable') - self.outputs.append( - name='z', - units='ft**2', - description='Resultant of the unit circle evaluation', - ) + self.inputs.append(name = 'x', units = 'ft' , description = 'The x variable') + self.inputs.append(name = 'y', units = 'ft' , description = 'The y variable') + self.outputs.append(name = 'z', units = 'ft**2', description = 'Resultant of the unit circle evaluation') self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - - def BlackBox(self, x, y): # The actual function that does things - x = pyo.value( - units.convert(x, self.inputs[0].units) - ) # Converts to correct units then casts to float - y = pyo.value( - units.convert(y, self.inputs[1].units) - ) # Converts to correct units then casts to float - z = x**2 + y**2 # Compute z - dzdx = 2 * x # Compute dz/dx - dzdy = 2 * y # Compute dz/dy + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float + y = pyo.value(units.convert(y,self.inputs[1].units)) # Converts to correct units then casts to float + z = x**2 + y**2 # Compute z + dzdx = 2*x # Compute dz/dx + dzdy = 2*y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... - - f.Constraint(z <= 1 * units.m**2) + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + f.Constraint( z <= 1*units.m**2 ) - f.RuntimeConstraint(*(z, '==', [x, y], UnitCircle())) + f.RuntimeConstraint( *( z, '==', [x,y], UnitCircle() ) ) def test_edi_formulation_runtimeconstraint_list(self): "Tests the runtime constraint constructor in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel - f = Formulation() - x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') - y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') - z = f.Variable( - name='z', guess=1.0, units='m^2', description='The unit circle output' - ) - c = f.Constant( - name='c', value=1.0, units='', description='A constant c', size=2 - ) - f.Objective(x + y) - + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The unit circle output') + c = f.Constant(name = 'c', value = 1.0, units = '', description = 'A constant c', size = 2) + f.Objective( x + y ) class UnitCircle(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super(UnitCircle, self).__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append(name='x', units='ft', description='The x variable') - self.inputs.append(name='y', units='ft', description='The y variable') - self.outputs.append( - name='z', - units='ft**2', - description='Resultant of the unit circle evaluation', - ) + self.inputs.append(name = 'x', units = 'ft' , description = 'The x variable') + self.inputs.append(name = 'y', units = 'ft' , description = 'The y variable') + self.outputs.append(name = 'z', units = 'ft**2', description = 'Resultant of the unit circle evaluation') self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - - def BlackBox(self, x, y): # The actual function that does things - x = pyo.value( - units.convert(x, self.inputs[0].units) - ) # Converts to correct units then casts to float - y = pyo.value( - units.convert(y, self.inputs[1].units) - ) # Converts to correct units then casts to float - z = x**2 + y**2 # Compute z - dzdx = 2 * x # Compute dz/dx - dzdy = 2 * y # Compute dz/dy + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float + y = pyo.value(units.convert(y,self.inputs[1].units)) # Converts to correct units then casts to float + z = x**2 + y**2 # Compute z + dzdx = 2*x # Compute dz/dx + dzdy = 2*y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + f.Constraint( z <= 1*units.m**2 ) + + f.RuntimeConstraint( *[ [z], ['=='], [x,y], UnitCircle() ] ) - f.Constraint(z <= 1 * units.m**2) - - f.RuntimeConstraint(*[[z], ['=='], [x, y], UnitCircle()]) def test_edi_formulation_runtimeconstraint_dict(self): "Tests the runtime constraint constructor in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel - f = Formulation() - x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') - y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') - z = f.Variable( - name='z', guess=1.0, units='m^2', description='The unit circle output' - ) - c = f.Constant( - name='c', value=1.0, units='', description='A constant c', size=2 - ) - f.Objective(x + y) - + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The unit circle output') + c = f.Constant(name = 'c', value = 1.0, units = '', description = 'A constant c', size = 2) + f.Objective( x + y ) class UnitCircle(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super(UnitCircle, self).__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append(name='x', units='ft', description='The x variable') - self.inputs.append(name='y', units='ft', description='The y variable') - self.outputs.append( - name='z', - units='ft**2', - description='Resultant of the unit circle evaluation', - ) + self.inputs.append(name = 'x', units = 'ft' , description = 'The x variable') + self.inputs.append(name = 'y', units = 'ft' , description = 'The y variable') + self.outputs.append(name = 'z', units = 'ft**2', description = 'Resultant of the unit circle evaluation') self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - - def BlackBox(self, x, y): # The actual function that does things - x = pyo.value( - units.convert(x, self.inputs[0].units) - ) # Converts to correct units then casts to float - y = pyo.value( - units.convert(y, self.inputs[1].units) - ) # Converts to correct units then casts to float - z = x**2 + y**2 # Compute z - dzdx = 2 * x # Compute dz/dx - dzdy = 2 * y # Compute dz/dy + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float + y = pyo.value(units.convert(y,self.inputs[1].units)) # Converts to correct units then casts to float + z = x**2 + y**2 # Compute z + dzdx = 2*x # Compute dz/dx + dzdy = 2*y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... - - f.Constraint(z <= 1 * units.m**2) - - f.RuntimeConstraint( - **{ - 'outputs': z, - 'operators': '==', - 'inputs': [x, y], - 'black_box': UnitCircle(), - } - ) + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + f.Constraint( z <= 1*units.m**2 ) + + f.RuntimeConstraint( **{ 'outputs':z, 'operators':'==', 'inputs':[x,y], 'black_box':UnitCircle() } ) + def test_edi_formulation_constraintlist_1(self): "Tests the constraint list constructor in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel - f = Formulation() - x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') - y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') - z = f.Variable( - name='z', guess=1.0, units='m^2', description='The unit circle output' - ) - c = f.Constant( - name='c', value=1.0, units='', description='A constant c', size=2 - ) - f.Objective(x + y) + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The unit circle output') + c = f.Constant(name = 'c', value = 1.0, units = '', description = 'A constant c', size = 2) + f.Objective( x + y ) class UnitCircle(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super(UnitCircle, self).__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append(name='x', units='ft', description='The x variable') - self.inputs.append(name='y', units='ft', description='The y variable') - self.outputs.append( - name='z', - units='ft**2', - description='Resultant of the unit circle evaluation', - ) + self.inputs.append(name = 'x', units = 'ft' , description = 'The x variable') + self.inputs.append(name = 'y', units = 'ft' , description = 'The y variable') + self.outputs.append(name = 'z', units = 'ft**2', description = 'Resultant of the unit circle evaluation') self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, x, y): # The actual function that does things - x = pyo.value( - units.convert(x, self.inputs[0].units) - ) # Converts to correct units then casts to float - y = pyo.value( - units.convert(y, self.inputs[1].units) - ) # Converts to correct units then casts to float - z = x**2 + y**2 # Compute z - dzdx = 2 * x # Compute dz/dx - dzdy = 2 * y # Compute dz/dy + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float + y = pyo.value(units.convert(y,self.inputs[1].units)) # Converts to correct units then casts to float + z = x**2 + y**2 # Compute z + dzdx = 2*x # Compute dz/dx + dzdy = 2*y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + + f.ConstraintList( + [ + (z, '==', [x,y], UnitCircle() ) , + z <= 1*units.m**2 + ] + ) - f.ConstraintList([(z, '==', [x, y], UnitCircle()), z <= 1 * units.m**2]) + cl = f.get_constraints() - cl = f.get_constraints() + self.assertTrue(len(cl)==2) - self.assertTrue(len(cl) == 2) def test_edi_formulation_constraintlist_2(self): "Tests the constraint list constructor in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel - f = Formulation() - x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') - y = f.Variable(name='y', guess=1.0, units='m**2', description='The y variable') - f.Objective(y) + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm**2' , description = 'The y variable') + f.Objective( y ) class Parabola(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super(Parabola, self).__init__() self.description = 'This model evaluates the function: y = x**2' - self.inputs.append(name='x', units='ft', description='The x variable') - self.outputs.append( - name='y', units='ft**2', description='The y variable' - ) + self.inputs.append(name = 'x', units = 'ft' , description = 'The x variable') + self.outputs.append(name = 'y', units = 'ft**2' , description = 'The y variable') self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, x): # The actual function that does things - x = pyo.value( - units.convert(x, self.inputs[0].units) - ) # Converts to correct units then casts to float - y = x**2 # Compute y - dydx = 2 * x # Compute dy/dx + def BlackBox(self, x): # The actual function that does things + x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float + y = x**2 # Compute y + dydx = 2*x # Compute dy/dx y *= units.ft**2 - dydx *= units.ft # units.ft**2 / units.ft - return y, [dydx] # return z, grad(z), hess(z)... + dydx *= units.ft # units.ft**2 / units.ft + return y, [dydx] # return z, grad(z), hess(z)... f.ConstraintList( - [{'outputs': y, 'operators': '==', 'inputs': x, 'black_box': Parabola()}] + [ + {'outputs':y, 'operators':'==', 'inputs':x, 'black_box':Parabola() }, + ] ) - cl = f.get_constraints() + cl = f.get_constraints() + + self.assertTrue(len(cl)==1) - self.assertTrue(len(cl) == 1) def test_edi_formulation_constraintlist_3(self): "Tests the constraint list constructor in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel - f = Formulation() - x = f.Variable( - name='x', guess=1.0, units='m', description='The x variable', size=3 - ) - y = f.Variable( - name='y', guess=1.0, units='m**2', description='The y variable', size=3 - ) - f.Objective(y[0] + y[1] + y[2]) - + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable', size=3) + y = f.Variable(name = 'y', guess = 1.0, units = 'm**2' , description = 'The y variable', size=3) + f.Objective( y[0] + y[1] + y[2] ) class Parabola(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super(Parabola, self).__init__() self.description = 'This model evaluates the function: y = x**2' - self.inputs.append( - name='x', size=3, units='ft', description='The x variable' - ) - self.outputs.append( - name='y', size=3, units='ft**2', description='The y variable' - ) + self.inputs.append(name = 'x', size = 3, units = 'ft' , description = 'The x variable') + self.outputs.append(name = 'y', size = 3, units = 'ft**2' , description = 'The y variable') self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(*args, **kwargs): # The actual function that does things + def BlackBox(*args, **kwargs):# The actual function that does things args = list(args) self = args.pop(0) - runCases, returnMode, remainingKwargs = self.parseInputs( - *args, **kwargs - ) - + runCases, returnMode, remainingKwargs = self.parseInputs(*args, **kwargs) + x = self.sanitizeInputs(runCases[0]['x']) x = np.array([pyo.value(xval) for xval in x], dtype=np.float64) - y = x**2 # Compute y - dydx = 2 * x # Compute dy/dx - + y = x**2 # Compute y + dydx = 2*x # Compute dy/dx + y = y * units.ft**2 dydx = np.diag(dydx) - dydx = dydx * units.ft # units.ft**2 / units.ft - - return y, [dydx] # return z, grad(z), hess(z)... + dydx = dydx * units.ft # units.ft**2 / units.ft + + return y, [dydx] # return z, grad(z), hess(z)... + f.ConstraintList([ {'outputs':y, 'operators':'==', 'inputs':x, 'black_box':Parabola() },]) + cl = f.get_constraints() + self.assertTrue(len(cl)==1) - f.ConstraintList( - [{'outputs': y, 'operators': '==', 'inputs': x, 'black_box': Parabola()}] - ) - cl = f.get_constraints() - self.assertTrue(len(cl) == 1) def test_edi_formulation_runtimeconstraint_exceptions(self): "Tests the runtime constraint constructor in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel - f = Formulation() - x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') - y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') - z = f.Variable( - name='z', guess=1.0, units='m^2', description='The unit circle output' - ) - c = f.Constant( - name='c', value=1.0, units='', description='A constant c', size=2 - ) - f.Objective(x + y) - + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The unit circle output') + c = f.Constant(name = 'c', value = 1.0, units = '', description = 'A constant c', size = 2) + f.Objective( x + y ) class UnitCircle(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super(UnitCircle, self).__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append(name='x', units='ft', description='The x variable') - self.inputs.append(name='y', units='ft', description='The y variable') - self.outputs.append( - name='z', - units='ft**2', - description='Resultant of the unit circle evaluation', - ) + self.inputs.append(name = 'x', units = 'ft' , description = 'The x variable') + self.inputs.append(name = 'y', units = 'ft' , description = 'The y variable') + self.outputs.append(name = 'z', units = 'ft**2', description = 'Resultant of the unit circle evaluation') self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - - def BlackBox(self, x, y): # The actual function that does things - x = pyo.value( - units.convert(x, self.inputs[0].units) - ) # Converts to correct units then casts to float - y = pyo.value( - units.convert(y, self.inputs[1].units) - ) # Converts to correct units then casts to float - z = x**2 + y**2 # Compute z - dzdx = 2 * x # Compute dz/dx - dzdy = 2 * y # Compute dz/dy + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float + y = pyo.value(units.convert(y,self.inputs[1].units)) # Converts to correct units then casts to float + z = x**2 + y**2 # Compute z + dzdx = 2*x # Compute dz/dx + dzdy = 2*y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... - - f.Constraint(z <= 1 * units.m**2) + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + f.Constraint( z <= 1*units.m**2 ) # flaggs the input or as bad before assigning the incorrect black box - self.assertRaises( - ValueError, f.RuntimeConstraint, *(z, '==', 1.0, UnitCircle()) - ) - self.assertRaises( - ValueError, f.RuntimeConstraint, *(1.0, '==', [x, y], UnitCircle()) - ) + self.assertRaises(ValueError, f.RuntimeConstraint, *(z, '==', 1.0 , UnitCircle() )) + self.assertRaises(ValueError, f.RuntimeConstraint, *(1.0, '==', [x,y] , UnitCircle() )) - self.assertRaises( - ValueError, f.RuntimeConstraint, *(z, '==', [1.0, y], UnitCircle()) - ) - self.assertRaises( - ValueError, f.RuntimeConstraint, *(z, 1.0, [x, y], UnitCircle()) - ) - self.assertRaises( - ValueError, f.RuntimeConstraint, *(z, '=', [x, y], UnitCircle()) - ) + self.assertRaises(ValueError, f.RuntimeConstraint, *(z, '==', [1.0,y], UnitCircle() )) + self.assertRaises(ValueError, f.RuntimeConstraint, *(z, 1.0 , [x ,y], UnitCircle() )) + self.assertRaises(ValueError, f.RuntimeConstraint, *(z, '=', [x ,y], UnitCircle() )) def test_edi_formulation_getvariables(self): "Tests the get_variables function in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') - y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') vrs = f.get_variables() - self.assertListEqual(vrs, [x, y]) + self.assertListEqual(vrs, [x,y]) def test_edi_formulation_getconstants(self): "Tests the get_constants function in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') - y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') - - c1 = f.Constant( - name='c1', - value=1.0, - units='m', - description='A constant c1', - size=None, - within=None, - ) - c2 = f.Constant( - name='c2', - value=1.0, - units='m', - description='A constant c2', - size=None, - within=None, - ) + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + + c1 = f.Constant(name = 'c1', value = 1.0, units = 'm', description = 'A constant c1', size = None, within = None) + c2 = f.Constant(name = 'c2', value = 1.0, units = 'm', description = 'A constant c2', size = None, within = None) csts = f.get_constants() - self.assertListEqual(csts, [c1, c2]) + self.assertListEqual(csts, [c1,c2]) def test_edi_formulation_getobjectives(self): "Tests the get_objectives function in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') - y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') - f.Objective(x + y) + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + f.Objective(x+y) objList = f.get_objectives() - self.assertTrue(len(objList) == 1) + self.assertTrue(len(objList)==1) # not really sure how to check this, so I wont def test_edi_formulation_getconstraints(self): @@ -819,45 +452,40 @@ def test_edi_formulation_getconstraints(self): # ================= # Declare Variables # ================= - x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') - y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') - z = f.Variable( - name='z', guess=1.0, units='m^2', description='The unit circle output' - ) + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The unit circle output') # ================= # Declare Constants # ================= - c = f.Constant( - name='c', value=1.0, units='', description='A constant c', size=2 - ) + c = f.Constant(name = 'c', value = 1.0, units = '', description = 'A constant c', size = 2) # ===================== # Declare the Objective # ===================== - f.Objective(c[0] * x + c[1] * y) + f.Objective( + c[0]*x + c[1]*y + ) # =================== # Declare a Black Box # =================== class UnitCircle(BlackBoxFunctionModel): - def __init__(self): # The initialization function + def __init__(self): # The initialization function + # Initialize the black box model super(UnitCircle, self).__init__() # A brief description of the model self.description = 'This model evaluates the function: z = x**2 + y**2' - + # Declare the black box model inputs - self.inputs.append(name='x', units='ft', description='The x variable') - self.inputs.append(name='y', units='ft', description='The y variable') + self.inputs.append(name = 'x', units = 'ft' , description = 'The x variable') + self.inputs.append(name = 'y', units = 'ft' , description = 'The y variable') # Declare the black box model outputs - self.outputs.append( - name='z', - units='ft**2', - description='Resultant of the unit circle evaluation', - ) + self.outputs.append(name = 'z', units = 'ft**2', description = 'Resultant of the unit circle evaluation') # Declare the maximum available derivative self.availableDerivative = 1 @@ -865,36 +493,37 @@ def __init__(self): # The initialization function # Post-initialization setup self.post_init_setup(len(self.inputs)) - def BlackBox(self, x, y): # The actual function that does things - x = pyo.value( - units.convert(x, self.inputs[0].units) - ) # Converts to correct units then casts to float - y = pyo.value( - units.convert(y, self.inputs[1].units) - ) # Converts to correct units then casts to float + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float + y = pyo.value(units.convert(y,self.inputs[1].units)) # Converts to correct units then casts to float - z = x**2 + y**2 # Compute z - dzdx = 2 * x # Compute dz/dx - dzdy = 2 * y # Compute dz/dy + z = x**2 + y**2 # Compute z + dzdx = 2*x # Compute dz/dx + dzdy = 2*y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... # ======================= # Declare the Constraints # ======================= - f.ConstraintList([(z, '==', [x, y], UnitCircle()), z <= 1 * units.m**2]) + f.ConstraintList( + [ + (z, '==', [x,y], UnitCircle() ) , + z <= 1*units.m**2 + ] + ) - cl = f.get_constraints() + cl = f.get_constraints() ecl = f.get_explicitConstraints() rcl = f.get_runtimeConstraints() - self.assertTrue(len(cl) == 2) - self.assertTrue(len(ecl) == 1) - self.assertTrue(len(rcl) == 1) + self.assertTrue(len(cl)==2) + self.assertTrue(len(ecl)==1) + self.assertTrue(len(rcl)==1) def test_edi_formulation_checkunits(self): "Tests the check_units function in edi.formulation" @@ -902,28 +531,43 @@ def test_edi_formulation_checkunits(self): import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') - y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - f.Objective(x + y) - f.Constraint(x + y <= 1.0 * units.m) + f.Objective(x+y) + f.Constraint(x+y <= 1.0*units.m) f.check_units() - f.Constraint(2.0 * x + y <= 1.0) - self.assertRaises( - pyomo.core.base.units_container.UnitsError, f.check_units, *() - ) + f.Constraint(2.0*x+y <= 1.0) + self.assertRaises(pyomo.core.base.units_container.UnitsError, f.check_units, *()) + f2 = Formulation() - u = f2.Variable(name='u', guess=1.0, units='m', description='The u variable') - v = f2.Variable(name='v', guess=1.0, units='kg', description='The v variable') - f2.Objective(u + v) - self.assertRaises( - pyomo.core.base.units_container.UnitsError, f2.check_units, *() - ) + u = f2.Variable(name = 'u', guess = 1.0, units = 'm' , description = 'The u variable') + v = f2.Variable(name = 'v', guess = 1.0, units = 'kg' , description = 'The v variable') + f2.Objective(u+v) + self.assertRaises(pyomo.core.base.units_container.UnitsError, f2.check_units, *()) + + + if __name__ == '__main__': unittest.main() + + + + + + + + + + + + + + + + From 7542063c21c8cc5b4becf5af5116a6d5cfce4538 Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Tue, 8 Aug 2023 01:07:00 -0600 Subject: [PATCH 29/61] adding black, part 2 --- pyomo/contrib/edi/blackBoxFunctionModel.py | 638 +++++++---- pyomo/contrib/edi/examples/aircraft_gp.py | 94 +- pyomo/contrib/edi/examples/readme_example.py | 68 +- pyomo/contrib/edi/formulation.py | 320 ++++-- pyomo/contrib/edi/tests/test_blackbox.py | 1030 ++++++++++-------- pyomo/contrib/edi/tests/test_docSnippets.py | 980 ++++++++--------- pyomo/contrib/edi/tests/test_examples.py | 24 +- pyomo/contrib/edi/tests/test_formulation.py | 897 ++++++++++----- 8 files changed, 2387 insertions(+), 1664 deletions(-) diff --git a/pyomo/contrib/edi/blackBoxFunctionModel.py b/pyomo/contrib/edi/blackBoxFunctionModel.py index d8647df4625..0b1d593d7a6 100644 --- a/pyomo/contrib/edi/blackBoxFunctionModel.py +++ b/pyomo/contrib/edi/blackBoxFunctionModel.py @@ -23,48 +23,63 @@ try: import numpy as np except: - raise ImportError("pyomo.contrib.edi requires numpy to enable black box capability, fix with 'pip install numpy' ") + raise ImportError( + "pyomo.contrib.edi requires numpy to enable black box capability, fix with 'pip install numpy' " + ) try: import scipy.sparse as sps except: - raise ImportError("pyomo.contrib.edi requires scipy to enable black box capability, fix with 'pip install scipy' ") + raise ImportError( + "pyomo.contrib.edi requires scipy to enable black box capability, fix with 'pip install scipy' " + ) try: - from pyomo.contrib.pynumero.interfaces.external_grey_box import ExternalGreyBoxModel, ExternalGreyBoxBlock + from pyomo.contrib.pynumero.interfaces.external_grey_box import ( + ExternalGreyBoxModel, + ExternalGreyBoxBlock, + ) except: - raise ImportError("pyomo.contrib.edi requires pyomo.contrib.pynumero to be installed to enable black box capability, this should have installed with base pyomo") + raise ImportError( + "pyomo.contrib.edi requires pyomo.contrib.pynumero to be installed to enable black box capability, this should have installed with base pyomo" + ) + class BlackBoxFunctionModel_Variable(object): - def __init__(self, name, units, description = '', size = 0): + def __init__(self, name, units, description='', size=0): # Order matters self.name = name self.units = units self.size = size self.description = description -# ===================================================================================================================== -# The printing function -# ===================================================================================================================== + + # ===================================================================================================================== + # The printing function + # ===================================================================================================================== def __repr__(self): - return self.name -# ===================================================================================================================== -# Define the name -# ===================================================================================================================== + return self.name + + # ===================================================================================================================== + # Define the name + # ===================================================================================================================== @property def name(self): return self._name + @name.setter - def name(self,val): + def name(self, val): if isinstance(val, str): self._name = val else: raise ValueError('Invalid name. Must be a string.') -# ===================================================================================================================== -# Define the units -# ===================================================================================================================== + + # ===================================================================================================================== + # Define the units + # ===================================================================================================================== @property def units(self): return self._units + @units.setter def units(self, val): # set dimensionless if a null string is passed in @@ -73,87 +88,101 @@ def units(self, val): val = 'dimensionless' if val is None: val = 'dimensionless' - + if isinstance(val, str): self._units = pyomo_units.__getattr__(val) elif isinstance(val, pyomo.core.base.units_container._PyomoUnit): self._units = val else: - raise ValueError('Invalid units. Must be a string compatible with pint or a unit instance.') -# ===================================================================================================================== -# Define the size -# ===================================================================================================================== + raise ValueError( + 'Invalid units. Must be a string compatible with pint or a unit instance.' + ) + + # ===================================================================================================================== + # Define the size + # ===================================================================================================================== @property def size(self): return self._size + @size.setter def size(self, val): invalid = False - if isinstance(val,(list, tuple)): + if isinstance(val, (list, tuple)): sizeTemp = [] for x in val: if isinstance(x, str): # is a vector of unknown length, should be 'inf', but any string accepted x = -1 # pass - elif not isinstance(x,int): - raise ValueError('Invalid size. Must be an integer or list/tuple of integers') + elif not isinstance(x, int): + raise ValueError( + 'Invalid size. Must be an integer or list/tuple of integers' + ) if x == 1: - raise ValueError('A value of 1 is not valid for defining size. Use fewer dimensions.') + raise ValueError( + 'A value of 1 is not valid for defining size. Use fewer dimensions.' + ) sizeTemp.append(x) self._size = val else: if val is None: - self._size = 0 # set to scalar + self._size = 0 # set to scalar elif isinstance(val, str): # is a 1D vector of unknown length, should be 'inf', but any string accepted self._size = -1 # pass elif isinstance(val, int): if val == 1: - raise ValueError('A value of 1 is not valid for defining size. Use 0 to indicate a scalar value.') + raise ValueError( + 'A value of 1 is not valid for defining size. Use 0 to indicate a scalar value.' + ) else: self._size = val else: - raise ValueError('Invalid size. Must be an integer or list/tuple of integers') -# ===================================================================================================================== -# Define the description -# ===================================================================================================================== + raise ValueError( + 'Invalid size. Must be an integer or list/tuple of integers' + ) + + # ===================================================================================================================== + # Define the description + # ===================================================================================================================== @property def description(self): return self._description + @description.setter def description(self, val): - if isinstance(val,str): + if isinstance(val, str): self._description = val else: raise ValueError('Invalid description. Must be a string.') class TypeCheckedList(list): - def __init__(self, checkItem, itemList = None): + def __init__(self, checkItem, itemList=None): super(TypeCheckedList, self).__init__() self.checkItem = checkItem - + if itemList is not None: if isinstance(itemList, list) or isinstance(itemList, tuple): for itm in itemList: self.append(itm) else: raise ValueError('Input to itemList is not iterable') - + def __setitem__(self, key, val): if isinstance(val, self.checkItem): super(TypeCheckedList, self).__setitem__(key, val) - elif isinstance(val, (tuple,list)): - cks = [isinstance(vl,self.checkItem) for vl in val] + elif isinstance(val, (tuple, list)): + cks = [isinstance(vl, self.checkItem) for vl in val] if sum(cks) == len(cks): super(TypeCheckedList, self).__setitem__(key, val) else: - raise ValueError('Input must be an instance of the defined type') + raise ValueError('Input must be an instance of the defined type') else: raise ValueError('Input must be an instance of the defined type') - + def append(self, val): if isinstance(val, self.checkItem): super(TypeCheckedList, self).append(val) @@ -163,7 +192,7 @@ def append(self, val): class BBList(TypeCheckedList): def __init__(self): - super(BBList, self).__init__(BlackBoxFunctionModel_Variable,[]) + super(BBList, self).__init__(BlackBoxFunctionModel_Variable, []) self._lookupDict = {} self._counter = 0 @@ -175,64 +204,75 @@ def __getitem__(self, val): else: raise ValueError('Input must be an integer or a valid variable name') - - def append(*args, **kwargs): args = list(args) self = args.pop(0) - + if len(args) + len(kwargs.values()) == 1: if len(args) == 1: inputData = args[0] if len(kwargs.values()) == 1: inputData = list(kwargs.values())[0] - + if isinstance(inputData, self.checkItem): if inputData.name in self._lookupDict.keys(): - raise ValueError("Key '%s' already exists in the input list"%(inputData.name)) + raise ValueError( + "Key '%s' already exists in the input list" % (inputData.name) + ) self._lookupDict[inputData.name] = self._counter self._counter += 1 super(BBList, self).append(inputData) else: if isinstance(inputData, str): - raise ValueError("Key '%s' not passed in to the black box variable constructor"%('units')) + raise ValueError( + "Key '%s' not passed in to the black box variable constructor" + % ('units') + ) else: raise ValueError('Invalid (single) input type') - + elif len(args) + len(kwargs.values()) <= 4: - argKeys = ['name','units','description','size'] - ipd = dict(zip(argKeys[0:len(args)],args)) + argKeys = ['name', 'units', 'description', 'size'] + ipd = dict(zip(argKeys[0 : len(args)], args)) for ky, vl in kwargs.items(): if ky in ipd: - raise ValueError("Key '%s' declared after non-keyword arguments and is out of order"%(ky)) + raise ValueError( + "Key '%s' declared after non-keyword arguments and is out of order" + % (ky) + ) else: - ipd[ky]=vl - + ipd[ky] = vl + for ak in argKeys: if ak not in ipd.keys(): if ak == 'description': - ipd['description']='' + ipd['description'] = '' elif ak == 'size': ipd['size'] = 0 else: - raise ValueError("Key '%s' not passed in to the black box variable constructor"%(ak)) - + raise ValueError( + "Key '%s' not passed in to the black box variable constructor" + % (ak) + ) if ipd['name'] in self._lookupDict.keys(): - raise ValueError("Key '%s' already exists in the input list"%(ipd['name'])) + raise ValueError( + "Key '%s' already exists in the input list" % (ipd['name']) + ) self._lookupDict[ipd['name']] = self._counter self._counter += 1 super(BBList, self).append(BlackBoxFunctionModel_Variable(**ipd)) - + else: raise ValueError('Too many inputs to a black box variable') errorString = 'This function is calling to the base class and has not been defined.' + class BlackBoxFunctionModel(ExternalGreyBoxModel): -# --------------------------------------------------------------------------------------------------------------------- -# --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- def __init__(self): super(BlackBoxFunctionModel, self).__init__() @@ -240,9 +280,9 @@ def __init__(self): self.inputs = BBList() self.outputs = BBList() - self.inputVariables_optimization = None + self.inputVariables_optimization = None self.outputVariables_optimization = None - + # A simple description of the model self.description = None @@ -251,15 +291,17 @@ def __init__(self): self._cache = None self._NunwrappedOutputs = None - self._NunwrappedInputs = None + self._NunwrappedInputs = None - def setOptimizationVariables(self, inputVariables_optimization, outputVariables_optimization): - self.inputVariables_optimization = inputVariables_optimization + def setOptimizationVariables( + self, inputVariables_optimization, outputVariables_optimization + ): + self.inputVariables_optimization = inputVariables_optimization self.outputVariables_optimization = outputVariables_optimization -# --------------------------------------------------------------------------------------------------------------------- -# pyomo things -# --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- + # pyomo things + # --------------------------------------------------------------------------------------------------------------------- def input_names(self): inputs_unwrapped = [] for ivar in self.inputVariables_optimization: @@ -270,10 +312,9 @@ def input_names(self): for vi in validIndices: inputs_unwrapped.append(ivar[vi]) else: - raise ValueError("Invalid type for input variable") - - return [ip.__str__() for ip in inputs_unwrapped] + raise ValueError("Invalid type for input variable") + return [ip.__str__() for ip in inputs_unwrapped] def output_names(self): outputs_unwrapped = [] @@ -285,7 +326,7 @@ def output_names(self): for vi in validIndices: outputs_unwrapped.append(ovar[vi]) else: - raise ValueError("Invalid type for output variable") + raise ValueError("Invalid type for output variable") return [op.__str__() for op in outputs_unwrapped] @@ -297,16 +338,15 @@ def evaluate_outputs(self): self.fillCache() opts = self._cache['pyomo_outputs'] return opts - + def evaluate_jacobian_outputs(self): self.fillCache() jac = self._cache['pyomo_jacobian'] return jac - def post_init_setup(self, defaultVal = 1.0): + def post_init_setup(self, defaultVal=1.0): self._input_values = np.ones(self._NunwrappedInputs) * defaultVal - def fillCache(self): if self._cache is None: self._cache = {} @@ -316,95 +356,113 @@ def fillCache(self): ptr = 0 - for i in range(0,len(self.inputVariables_optimization)): + for i in range(0, len(self.inputVariables_optimization)): optimizationInput = self.inputVariables_optimization[i] - if not isinstance(optimizationInput, (pyomo.core.base.var.IndexedVar, pyomo.core.base.var.ScalarVar)): + if not isinstance( + optimizationInput, + (pyomo.core.base.var.IndexedVar, pyomo.core.base.var.ScalarVar), + ): raise ValueError("Invalid input variable type") - ipt = self.inputs[i] + ipt = self.inputs[i] - shape = [len(idx) for idx in optimizationInput.index_set().subsets()] - localShape = ipt.size + shape = [len(idx) for idx in optimizationInput.index_set().subsets()] + localShape = ipt.size optimizationUnits = self.inputVariables_optimization[i].get_units() - localUnits = ipt.units + localUnits = ipt.units if isinstance(optimizationInput, pyomo.core.base.var.IndexedVar): - value = np.zeros(shape) + value = np.zeros(shape) for vix in list(optimizationInput.index_set().data()): - raw_val = float(raw_inputs[ptr]) * optimizationUnits - raw_val_correctedUnits = pyomo_units.convert(raw_val, localUnits) + raw_val = float(raw_inputs[ptr]) * optimizationUnits + raw_val_correctedUnits = pyomo_units.convert( + raw_val, localUnits + ) value[vix] = pyo.value(raw_val_correctedUnits) ptr += 1 - self.sizeCheck(localShape, value*localUnits) - bb_inputs.append(value*localUnits) + self.sizeCheck(localShape, value * localUnits) + bb_inputs.append(value * localUnits) - else: # isinstance(optimizationInput, pyomo.core.base.var.ScalarVar):, just checked this + else: # isinstance(optimizationInput, pyomo.core.base.var.ScalarVar):, just checked this value = raw_inputs[ptr] * optimizationUnits ptr += 1 self.sizeCheck(localShape, value) value_correctedUnits = pyomo_units.convert(value, localUnits) bb_inputs.append(value_correctedUnits) - bbo = self.BlackBox(*bb_inputs) - self._cache['raw'] = bbo - self._cache['raw_value'] = bbo[0] - self._cache['raw_jacobian'] = bbo[1] + self._cache['raw'] = bbo + self._cache['raw_value'] = bbo[0] + self._cache['raw_jacobian'] = bbo[1] outputVector = [] - if not isinstance(bbo[0], (list,tuple)): + if not isinstance(bbo[0], (list, tuple)): valueList = [bbo[0]] jacobianList = [bbo[1]] else: valueList = bbo[0] jacobianList = bbo[1] - for i in range(0,len(valueList)): + for i in range(0, len(valueList)): optimizationOutput = self.outputVariables_optimization[i] - if not isinstance(optimizationOutput, (pyomo.core.base.var.IndexedVar, pyomo.core.base.var.ScalarVar)): + if not isinstance( + optimizationOutput, + (pyomo.core.base.var.IndexedVar, pyomo.core.base.var.ScalarVar), + ): raise ValueError("Invalid output variable type") - opt = self.outputs[i] + opt = self.outputs[i] - modelOutputUnits = opt.units + modelOutputUnits = opt.units outputOptimizationUnits = optimizationOutput.get_units() vl = valueList[i] if isinstance(vl, pyomo.core.expr.numeric_expr.NumericNDArray): validIndexList = optimizationOutput.index_set().data() - for j in range(0,len(validIndexList)): + for j in range(0, len(validIndexList)): vi = validIndexList[j] - corrected_value = pyo.value(pyomo_units.convert(vl[vi], outputOptimizationUnits)) # now unitless in correct units - outputVector.append(corrected_value) - - elif isinstance(vl, (pyomo.core.expr.numeric_expr.NPV_ProductExpression, - pyomo.core.base.units_container._PyomoUnit)): - corrected_value = pyo.value(pyomo_units.convert(vl, outputOptimizationUnits)) # now unitless in correct units - outputVector.append(corrected_value) + corrected_value = pyo.value( + pyomo_units.convert(vl[vi], outputOptimizationUnits) + ) # now unitless in correct units + outputVector.append(corrected_value) + + elif isinstance( + vl, + ( + pyomo.core.expr.numeric_expr.NPV_ProductExpression, + pyomo.core.base.units_container._PyomoUnit, + ), + ): + corrected_value = pyo.value( + pyomo_units.convert(vl, outputOptimizationUnits) + ) # now unitless in correct units + outputVector.append(corrected_value) else: raise ValueError("Invalid output variable type") self._cache['pyomo_outputs'] = outputVector - outputJacobian = np.ones([self._NunwrappedOutputs, self._NunwrappedInputs]) * -1 + outputJacobian = ( + np.ones([self._NunwrappedOutputs, self._NunwrappedInputs]) * -1 + ) ptr_row = 0 - ptr_col = 0 + ptr_col = 0 - for i in range(0,len(jacobianList)): + for i in range(0, len(jacobianList)): oopt = self.outputVariables_optimization[i] # Checked about 20 lines above # if not isinstance(oopt, (pyomo.core.base.var.ScalarVar,pyomo.core.base.var.IndexedVar)): - # raise ValueError("Invalid type for output variable") + # raise ValueError("Invalid type for output variable") lopt = self.outputs[i] oounits = oopt.get_units() lounits = lopt.units # oshape = [len(idx) for idx in oopt.index_set().subsets()] ptr_col = 0 - for j in range(0,len(self.inputs)): + for j in range(0, len(self.inputs)): oipt = self.inputVariables_optimization[j] # This is checked about 80 lines up # if not isinstance(oipt, (pyomo.core.base.var.ScalarVar,pyomo.core.base.var.IndexedVar)): - # raise ValueError("Invalid type for output variable") + # raise ValueError("Invalid type for output variable") lipt = self.inputs[j] oiunits = oipt.get_units() liunits = lipt.units @@ -412,31 +470,44 @@ def fillCache(self): jacobianValue_raw = jacobianList[i][j] - if isinstance(jacobianValue_raw, (pyomo.core.expr.numeric_expr.NPV_ProductExpression, - pyomo.core.base.units_container._PyomoUnit)): - corrected_value = pyo.value(pyomo_units.convert(jacobianValue_raw, oounits/oiunits)) # now unitless in correct units - outputJacobian[ptr_row,ptr_col] = corrected_value + if isinstance( + jacobianValue_raw, + ( + pyomo.core.expr.numeric_expr.NPV_ProductExpression, + pyomo.core.base.units_container._PyomoUnit, + ), + ): + corrected_value = pyo.value( + pyomo_units.convert(jacobianValue_raw, oounits / oiunits) + ) # now unitless in correct units + outputJacobian[ptr_row, ptr_col] = corrected_value ptr_col += 1 ptr_row_step = 1 - elif isinstance(jacobianValue_raw,pyomo.core.expr.numeric_expr.NumericNDArray): + elif isinstance( + jacobianValue_raw, pyomo.core.expr.numeric_expr.NumericNDArray + ): jshape = jacobianValue_raw.shape if isinstance(oopt, pyomo.core.base.var.ScalarVar): oshape = 0 - else: # isinstance(oopt, pyomo.core.base.var.IndexedVar), checked above - oshape = [len(idx) for idx in oopt.index_set().subsets()] + else: # isinstance(oopt, pyomo.core.base.var.IndexedVar), checked above + oshape = [len(idx) for idx in oopt.index_set().subsets()] if isinstance(oipt, pyomo.core.base.var.ScalarVar): ishape = 0 - else: # isinstance(oipt, pyomo.core.base.var.IndexedVar), checked above - ishape = [len(idx) for idx in oipt.index_set().subsets()] + else: # isinstance(oipt, pyomo.core.base.var.IndexedVar), checked above + ishape = [len(idx) for idx in oipt.index_set().subsets()] if oshape == 0: validIndices = list(oipt.index_set().data()) for vi in validIndices: - corrected_value = pyo.value(pyomo_units.convert(jacobianValue_raw[vi], oounits/oiunits)) # now unitless in correct units - outputJacobian[ptr_row,ptr_col] = corrected_value + corrected_value = pyo.value( + pyomo_units.convert( + jacobianValue_raw[vi], oounits / oiunits + ) + ) # now unitless in correct units + outputJacobian[ptr_row, ptr_col] = corrected_value ptr_col += 1 ptr_row_step = 1 @@ -444,30 +515,39 @@ def fillCache(self): ptr_row_cache = ptr_row validIndices = list(oopt.index_set().data()) for vi in validIndices: - corrected_value = pyo.value(pyomo_units.convert(jacobianValue_raw[vi], oounits/oiunits)) # now unitless in correct units - outputJacobian[ptr_row,ptr_col] = corrected_value + corrected_value = pyo.value( + pyomo_units.convert( + jacobianValue_raw[vi], oounits / oiunits + ) + ) # now unitless in correct units + outputJacobian[ptr_row, ptr_col] = corrected_value ptr_row += 1 ptr_row = ptr_row_cache ptr_row_step = len(validIndices) # elif ishape == 0 and oshape == 0: # Handled by the scalar case above - else: + else: # both are dimensioned vectors - #oshape, ishape, jshape + # oshape, ishape, jshape ptr_row_cache = ptr_row ptr_col_cache = ptr_col validIndices_o = list(oopt.index_set().data()) validIndices_i = list(oipt.index_set().data()) for vio in validIndices_o: - if isinstance(vio, (float,int)): + if isinstance(vio, (float, int)): vio = (vio,) for vii in validIndices_i: - if isinstance(vii, (float,int)): + if isinstance(vii, (float, int)): vii = (vii,) - corrected_value = pyo.value(pyomo_units.convert(jacobianValue_raw[vio + vii], oounits/oiunits)) # now unitless in correct units - outputJacobian[ptr_row,ptr_col] = corrected_value + corrected_value = pyo.value( + pyomo_units.convert( + jacobianValue_raw[vio + vii], + oounits / oiunits, + ) + ) # now unitless in correct units + outputJacobian[ptr_row, ptr_col] = corrected_value ptr_col += 1 ptr_col = ptr_col_cache ptr_row += 1 @@ -477,37 +557,41 @@ def fillCache(self): else: raise ValueError("Invalid jacobian type") - ptr_row += ptr_row_step + ptr_row += ptr_row_step self._cache['pyomo_jacobian'] = sps.coo_matrix(outputJacobian) -# --------------------------------------------------------------------------------------------------------------------- -# --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- # These models must be defined in each individual model, just placeholders here def BlackBox(*args, **kwargs): raise AttributeError(errorString) - def convert(self, val, unts): try: val = val * pyomo_units.dimensionless except: - pass ## will handle later - - if isinstance(val, (pyomo.core.base.units_container._PyomoUnit,pyomo.core.expr.numeric_expr.NPV_ProductExpression)): + pass ## will handle later + + if isinstance( + val, + ( + pyomo.core.base.units_container._PyomoUnit, + pyomo.core.expr.numeric_expr.NPV_ProductExpression, + ), + ): return pyomo_units.convert(val, unts) elif isinstance(val, pyomo.core.expr.numeric_expr.NumericNDArray): shp = val.shape ix = np.ndindex(*shp) opt = np.zeros(shp) - for i in range(0,np.prod(shp)): + for i in range(0, np.prod(shp)): ixt = next(ix) - opt[ixt] = pyo.value(pyomo_units.convert(val[ixt],unts)) - return opt*unts + opt[ixt] = pyo.value(pyomo_units.convert(val[ixt], unts)) + return opt * unts else: raise ValueError('Invalid type passed to unit conversion function') - def pyomo_value(self, val): try: return pyo.value(val) @@ -516,19 +600,19 @@ def pyomo_value(self, val): shp = val.shape ix = np.ndindex(*shp) opt = np.zeros(shp) - for i in range(0,np.prod(shp)): + for i in range(0, np.prod(shp)): ixt = next(ix) opt[ixt] = pyo.value(val[ixt]) return opt else: raise ValueError('Invalid type passed to pyomo_value function') -# --------------------------------------------------------------------------------------------------------------------- -# --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- def parseInputs(self, *args, **kwargs): - args = list(args) # convert tuple to list + args = list(args) # convert tuple to list - inputNames = [self.inputs[i].name for i in range(0,len(self.inputs))] + inputNames = [self.inputs[i].name for i in range(0, len(self.inputs))] # ------------------------------ # ------------------------------ @@ -541,32 +625,44 @@ def parseInputs(self, *args, **kwargs): if len(inputNames) == 1: try: rs = self.sanitizeInputs(inputData) - return [dict(zip(inputNames,[rs]))], -self.availableDerivative-1, {} # one input being passed in + return ( + [dict(zip(inputNames, [rs]))], + -self.availableDerivative - 1, + {}, + ) # one input being passed in except: - pass #otherwise, proceed - - if isinstance(inputData, (list,tuple)): + pass # otherwise, proceed + + if isinstance(inputData, (list, tuple)): dataRuns = [] for idc in inputData: if isinstance(idc, dict): sips = self.sanitizeInputs(**idc) if len(inputNames) == 1: sips = [sips] - runDictS = dict(zip(inputNames,sips)) - dataRuns.append(runDictS) # the BlackBox([{'x1':x1, 'x2':x2},{'x1':x1, 'x2':x2},...]) case - elif isinstance(idc,(list,tuple)): + runDictS = dict(zip(inputNames, sips)) + dataRuns.append( + runDictS + ) # the BlackBox([{'x1':x1, 'x2':x2},{'x1':x1, 'x2':x2},...]) case + elif isinstance(idc, (list, tuple)): if len(idc) == len(inputNames): sips = self.sanitizeInputs(*idc) if len(inputNames) == 1: sips = [sips] - runDictS = dict(zip(inputNames,sips)) - dataRuns.append(runDictS) # the BlackBox([ [x1, x2], [x1, x2],...]) case + runDictS = dict(zip(inputNames, sips)) + dataRuns.append( + runDictS + ) # the BlackBox([ [x1, x2], [x1, x2],...]) case else: - raise ValueError('Entry in input data list has improper length') + raise ValueError( + 'Entry in input data list has improper length' + ) else: - raise ValueError("Invalid data type in the input list. Note that BlackBox([x1,x2,y]) must be passed in as BlackBox([[x1,x2,y]]) or "+ - "BlackBox(*[x1,x2,y]) or BlackBox({'x1':x1,'x2':x2,'y':y}) or simply BlackBox(x1, x2, y) to avoid processing singularities. "+ - "Best practice is BlackBox({'x1':x1,'x2':x2,'y':y})") + raise ValueError( + "Invalid data type in the input list. Note that BlackBox([x1,x2,y]) must be passed in as BlackBox([[x1,x2,y]]) or " + + "BlackBox(*[x1,x2,y]) or BlackBox({'x1':x1,'x2':x2,'y':y}) or simply BlackBox(x1, x2, y) to avoid processing singularities. " + + "Best practice is BlackBox({'x1':x1,'x2':x2,'y':y})" + ) return dataRuns, self.availableDerivative, {} elif isinstance(inputData, dict): @@ -577,45 +673,73 @@ def parseInputs(self, *args, **kwargs): sips = self.sanitizeInputs(**inputData) if len(inputNames) == 1: sips = [sips] - return [dict(zip(inputNames,sips))], -self.availableDerivative-1, {} # the BlackBox(*{'x1':x1, 'x2':x2}) case, somewhat likely - - if not all([inputLengths[i] == inputLengths[0] for i in range(0,len(inputLengths))]): + return ( + [dict(zip(inputNames, sips))], + -self.availableDerivative - 1, + {}, + ) # the BlackBox(*{'x1':x1, 'x2':x2}) case, somewhat likely + + if not all( + [ + inputLengths[i] == inputLengths[0] + for i in range(0, len(inputLengths)) + ] + ): sips = self.sanitizeInputs(**inputData) - return [dict(zip(inputNames,sips))], -self.availableDerivative-1, {} # the BlackBox(*{'x1':x1, 'x2':x2}) case where vectors for x1... are passed in (likely to fail on previous line) + return ( + [dict(zip(inputNames, sips))], + -self.availableDerivative - 1, + {}, + ) # the BlackBox(*{'x1':x1, 'x2':x2}) case where vectors for x1... are passed in (likely to fail on previous line) else: try: sips = self.sanitizeInputs(**inputData) - return [dict(zip(inputNames,sips))], -self.availableDerivative-1, {} # the BlackBox(*{'x1':x1, 'x2':x2}) case where vectors all inputs have same length intentionally (likely to fail on previous line) + return ( + [dict(zip(inputNames, sips))], + -self.availableDerivative - 1, + {}, + ) # the BlackBox(*{'x1':x1, 'x2':x2}) case where vectors all inputs have same length intentionally (likely to fail on previous line) except: dataRuns = [] - for i in range(0,inputLengths[0]): + for i in range(0, inputLengths[0]): runDict = {} - for ky,vl in inputData.items(): + for ky, vl in inputData.items(): runDict[ky] = vl[i] sips = self.sanitizeInputs(**runDict) if len(inputNames) == 1: sips = [sips] - runDictS = dict(zip(inputNames,sips)) + runDictS = dict(zip(inputNames, sips)) dataRuns.append(runDictS) - return dataRuns, self.availableDerivative, {} # the BlackBox({'x1':x1_vec, 'x2':x2_vec}) case, most likely + return ( + dataRuns, + self.availableDerivative, + {}, + ) # the BlackBox({'x1':x1_vec, 'x2':x2_vec}) case, most likely else: raise ValueError('Keywords did not match the expected list') else: - raise ValueError('Got unexpected data type %s'%(str(type(inputData)))) + raise ValueError('Got unexpected data type %s' % (str(type(inputData)))) # ------------------------------ # ------------------------------ else: - if any([ list(kwargs.keys())[i] in inputNames for i in range(0,len(list(kwargs.keys()))) ]): + if any( + [ + list(kwargs.keys())[i] in inputNames + for i in range(0, len(list(kwargs.keys()))) + ] + ): # some of the inputs are defined in the kwargs if len(args) >= len(inputNames): - raise ValueError('A keyword input is defining an input, but there are too many unkeyed arguments for this to occur. Check the inputs.') + raise ValueError( + 'A keyword input is defining an input, but there are too many unkeyed arguments for this to occur. Check the inputs.' + ) else: if len(args) != 0: - availableKeywords = inputNames[-len(args):] - else: + availableKeywords = inputNames[-len(args) :] + else: availableKeywords = inputNames - valList = args + [None]*(len(inputNames)-len(args)) + valList = args + [None] * (len(inputNames) - len(args)) for ky in availableKeywords: ix = inputNames.index(ky) valList[ix] = kwargs[ky] @@ -624,7 +748,7 @@ def parseInputs(self, *args, **kwargs): # if any([valList[i]==None for i in range(0,len(valList))]): # raise ValueError('Keywords did not properly fill in the remaining arguments. Check the inputs.') - sips = self.sanitizeInputs(*valList) + sips = self.sanitizeInputs(*valList) if len(inputNames) == 1: sips = [sips] @@ -637,18 +761,26 @@ def parseInputs(self, *args, **kwargs): # was in args pass - return [dict(zip(inputNames,sips))], -self.availableDerivative-1, remainingKwargs # Mix of args and kwargs define inputs + return ( + [dict(zip(inputNames, sips))], + -self.availableDerivative - 1, + remainingKwargs, + ) # Mix of args and kwargs define inputs else: # all of the inputs are in the args try: - sips = self.sanitizeInputs(*args[0:len(inputNames)]) + sips = self.sanitizeInputs(*args[0 : len(inputNames)]) if len(inputNames) == 1: sips = [sips] remainingKwargs = copy.deepcopy(kwargs) - remainingKwargs['remainingArgs'] = args[len(inputNames):] - return [dict(zip(inputNames,sips))], -self.availableDerivative-1, remainingKwargs # all inputs are in args + remainingKwargs['remainingArgs'] = args[len(inputNames) :] + return ( + [dict(zip(inputNames, sips))], + -self.availableDerivative - 1, + remainingKwargs, + ) # all inputs are in args # except: except Exception as e: # not possible due to other checks @@ -656,46 +788,62 @@ def parseInputs(self, *args, **kwargs): # raise ValueError(e) if str(e) == 'Not enough inputs': raise ValueError(e) - else:#otherwise, proceed - runCases, returnMode, extra_singleInput = self.parseInputs(args[0]) + else: # otherwise, proceed + runCases, returnMode, extra_singleInput = self.parseInputs( + args[0] + ) remainingKwargs = copy.deepcopy(kwargs) - remainingKwargs['remainingArgs'] = args[len(inputNames):] + remainingKwargs['remainingArgs'] = args[len(inputNames) :] return runCases, returnMode, remainingKwargs -# --------------------------------------------------------------------------------------------------------------------- -# --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- def sizeCheck(self, size, ipval_correctUnits): if size is not None: szVal = ipval_correctUnits - if isinstance(szVal, (pyomo.core.expr.numeric_expr.NPV_ProductExpression, - pyomo.core.base.units_container._PyomoUnit)): - if size != 0 and size != 1 : - raise ValueError('Size did not match the expected size %s (ie: Scalar)'%(str(size))) + if isinstance( + szVal, + ( + pyomo.core.expr.numeric_expr.NPV_ProductExpression, + pyomo.core.base.units_container._PyomoUnit, + ), + ): + if size != 0 and size != 1: + raise ValueError( + 'Size did not match the expected size %s (ie: Scalar)' + % (str(size)) + ) elif isinstance(szVal, pyomo.core.expr.numeric_expr.NumericNDArray): shp = szVal.shape - if isinstance(size,(int,float)): + if isinstance(size, (int, float)): size = [size] # else: if len(shp) != len(size): - raise ValueError('Shapes/Sizes of %s does not match the expected %s'%(str(shp),str(size))) - for j in range(0,len(shp)): + raise ValueError( + 'Shapes/Sizes of %s does not match the expected %s' + % (str(shp), str(size)) + ) + for j in range(0, len(shp)): if size[j] != -1: # was declared of flexible length if size[j] != shp[j]: - raise ValueError('Shapes/Sizes of %s does not match the expected %s'%(str(shp),str(size))) + raise ValueError( + 'Shapes/Sizes of %s does not match the expected %s' + % (str(shp), str(size)) + ) else: raise ValueError('Invalid type detected when checking size') -# --------------------------------------------------------------------------------------------------------------------- -# --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- def sanitizeInputs(self, *args, **kwargs): - nameList = [self.inputs[i].name for i in range(0,len(self.inputs))] + nameList = [self.inputs[i].name for i in range(0, len(self.inputs))] if len(args) + len(kwargs.values()) > len(nameList): raise ValueError('Too many inputs') if len(args) + len(kwargs.values()) < len(nameList): raise ValueError('Not enough inputs') inputDict = {} - for i in range(0,len(args)): + for i in range(0, len(args)): rg = args[i] inputDict[nameList[i]] = rg @@ -703,34 +851,42 @@ def sanitizeInputs(self, *args, **kwargs): if ky in nameList: inputDict[ky] = vl else: - raise ValueError('Unexpected input keyword argument %s in the inputs'%(ky)) + raise ValueError( + 'Unexpected input keyword argument %s in the inputs' % (ky) + ) opts = [] - for i in range(0,len(nameList)): + for i in range(0, len(nameList)): name = nameList[i] nameCheck = self.inputs[i].name unts = self.inputs[i].units size = self.inputs[i].size - + # should be impossible # if name != nameCheck: # raise RuntimeError('Something went wrong and values are not consistent. Check your defined inputs.') ipval = inputDict[name] - if isinstance(ipval, pyomo.core.expr.numeric_expr.NumericNDArray): - for ii in range(0,len(ipval)): + if isinstance(ipval, pyomo.core.expr.numeric_expr.NumericNDArray): + for ii in range(0, len(ipval)): try: - ipval[ii] = self.convert(ipval[ii], unts)#ipval.to(unts) + ipval[ii] = self.convert(ipval[ii], unts) # ipval.to(unts) except: - raise ValueError('Could not convert %s of %s to %s'%(name, str(ipval),str(unts))) + raise ValueError( + 'Could not convert %s of %s to %s' + % (name, str(ipval), str(unts)) + ) ipval_correctUnits = ipval else: try: - ipval_correctUnits = self.convert(ipval, unts)#ipval.to(unts) + ipval_correctUnits = self.convert(ipval, unts) # ipval.to(unts) except: - raise ValueError('Could not convert %s of %s to %s'%(name, str(ipval),str(unts))) + raise ValueError( + 'Could not convert %s of %s to %s' + % (name, str(ipval), str(unts)) + ) # superseded by the custom convert function # if not isinstance(ipval_correctUnits, (pyomo.core.expr.numeric_expr.NPV_ProductExpression, @@ -746,8 +902,8 @@ def sanitizeInputs(self, *args, **kwargs): return opts -# --------------------------------------------------------------------------------------------------------------------- -# --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- def checkOutputs(self, *args, **kwargs): raise NotImplementedError('Contact developers to use this function') # nameList = [self.outputs[i].name for i in range(0,len(self.outputs))] @@ -773,7 +929,7 @@ def checkOutputs(self, *args, **kwargs): # nameCheck = self.outputs[i].name # unts = self.outputs[i].units # size = self.outputs[i].size - + # if name != nameCheck: # raise RuntimeError('Something went wrong and values are not consistent. Check your defined inputs.') @@ -800,15 +956,15 @@ def checkOutputs(self, *args, **kwargs): # return opts -# --------------------------------------------------------------------------------------------------------------------- -# --------------------------------------------------------------------------------------------------------------------- - def getSummary(self, whitespace = 6): + # --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- + def getSummary(self, whitespace=6): pstr = '\n' pstr += 'Model Description\n' pstr += '=================\n' descr_str = self.description.__repr__() pstr += descr_str[1:-1] + '\n\n' - + longestName = 0 longestUnits = 0 longestSize = 0 @@ -816,7 +972,7 @@ def getSummary(self, whitespace = 6): nml = len(ipt.name) if nml > longestName: longestName = nml - unts = ipt.units.__str__()#_repr_html_() + unts = ipt.units.__str__() # _repr_html_() # unts = unts.replace('','^') # unts = unts.replace('','') # unts = unts.replace('\[', '[') @@ -833,37 +989,37 @@ def getSummary(self, whitespace = 6): namespace = max([4, longestName]) + whitespace unitspace = max([5, longestUnits]) + whitespace sizespace = max([4, longestSize]) + whitespace - fulllength = namespace+unitspace+sizespace+11 + fulllength = namespace + unitspace + sizespace + 11 pstr += 'Inputs\n' - pstr += '='*fulllength + pstr += '=' * fulllength pstr += '\n' pstr += 'Name'.ljust(namespace) pstr += 'Units'.ljust(unitspace) pstr += 'Size'.ljust(sizespace) pstr += 'Description' pstr += '\n' - pstr += '-'*(namespace - whitespace) - pstr += ' '*whitespace - pstr += '-'*(unitspace - whitespace) - pstr += ' '*whitespace - pstr += '-'*(sizespace - whitespace) - pstr += ' '*whitespace + pstr += '-' * (namespace - whitespace) + pstr += ' ' * whitespace + pstr += '-' * (unitspace - whitespace) + pstr += ' ' * whitespace + pstr += '-' * (sizespace - whitespace) + pstr += ' ' * whitespace pstr += '-----------' pstr += '\n' for ipt in self.inputs: pstr += ipt.name.ljust(namespace) - unts = ipt.units.__str__()#_repr_html_() + unts = ipt.units.__str__() # _repr_html_() # unts = unts.replace('','^') # unts = unts.replace('','') # unts = unts.replace('\[', '[') # unts = unts.replace('\]', ']') pstr += unts.ljust(unitspace) - lnstr = '%s'%(ipt.size.__repr__()) + lnstr = '%s' % (ipt.size.__repr__()) pstr += lnstr.ljust(sizespace) pstr += ipt.description pstr += '\n' pstr += '\n' - + longestName = 0 longestUnits = 0 longestSize = 0 @@ -871,7 +1027,7 @@ def getSummary(self, whitespace = 6): nml = len(opt.name) if nml > longestName: longestName = nml - unts = opt.units.__str__()#_repr_html_() + unts = opt.units.__str__() # _repr_html_() # unts = unts.replace('','^') # unts = unts.replace('','') # unts = unts.replace('\[', '[') @@ -888,51 +1044,51 @@ def getSummary(self, whitespace = 6): namespace = max([4, longestName]) + whitespace unitspace = max([5, longestUnits]) + whitespace sizespace = max([4, longestSize]) + whitespace - fulllength = namespace+unitspace+sizespace+11 + fulllength = namespace + unitspace + sizespace + 11 pstr += 'Outputs\n' - pstr += '='*fulllength + pstr += '=' * fulllength pstr += '\n' pstr += 'Name'.ljust(namespace) pstr += 'Units'.ljust(unitspace) pstr += 'Size'.ljust(sizespace) pstr += 'Description' pstr += '\n' - pstr += '-'*(namespace - whitespace) - pstr += ' '*whitespace - pstr += '-'*(unitspace - whitespace) - pstr += ' '*whitespace - pstr += '-'*(sizespace - whitespace) - pstr += ' '*whitespace + pstr += '-' * (namespace - whitespace) + pstr += ' ' * whitespace + pstr += '-' * (unitspace - whitespace) + pstr += ' ' * whitespace + pstr += '-' * (sizespace - whitespace) + pstr += ' ' * whitespace pstr += '-----------' pstr += '\n' for opt in self.outputs: pstr += opt.name.ljust(namespace) - unts = opt.units.__str__()#_repr_html_() + unts = opt.units.__str__() # _repr_html_() # unts = unts.replace('','^') # unts = unts.replace('','') # unts = unts.replace('\[', '[') # unts = unts.replace('\]', ']') pstr += unts.ljust(unitspace) - lnstr = '%s'%(opt.size.__repr__()) + lnstr = '%s' % (opt.size.__repr__()) pstr += lnstr.ljust(sizespace) pstr += opt.description pstr += '\n' pstr += '\n' - + return pstr -# --------------------------------------------------------------------------------------------------------------------- -# --------------------------------------------------------------------------------------------------------------------- - + # --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- + @property def summary(self): return self.getSummary() -# --------------------------------------------------------------------------------------------------------------------- -# --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- def __repr__(self): pstr = 'AnalysisModel( [' - for i in range(0,len(self.outputs)): + for i in range(0, len(self.outputs)): pstr += self.outputs[i].name pstr += ',' pstr = pstr[0:-1] @@ -947,4 +1103,4 @@ def __repr__(self): pstr += ' , ' pstr = pstr[0:-2] pstr += '])' - return pstr \ No newline at end of file + return pstr diff --git a/pyomo/contrib/edi/examples/aircraft_gp.py b/pyomo/contrib/edi/examples/aircraft_gp.py index c20f06f5497..94e1db70083 100644 --- a/pyomo/contrib/edi/examples/aircraft_gp.py +++ b/pyomo/contrib/edi/examples/aircraft_gp.py @@ -25,47 +25,71 @@ # Declare Variables # ================= -A = f.Variable(name = "A", guess = 10.0, units = "-", description = "aspect ratio") -C_D = f.Variable(name = "C_D", guess = .025, units = "-", description = "Drag coefficient of wing") -C_f = f.Variable(name = "C_f", guess = .003, units = "-", description = "skin friction coefficient") -C_L = f.Variable(name = "C_L", guess = .5, units = "-", description = "Lift coefficient of wing") -D = f.Variable(name = "D", guess = 300, units = "N", description = "total drag force") -Re = f.Variable(name = "Re", guess = 3e6, units = "-", description = "Reynold's number") -S = f.Variable(name = "S", guess = 10.0, units = "m^2", description = "total wing area") -V = f.Variable(name = "V", guess = 30.0, units = "m/s", description = "cruising speed") -W = f.Variable(name = "W", guess = 10000.0, units = "N", description = "total aircraft weight") -W_w = f.Variable(name = "W_w", guess = 2500, units = "N", description = "wing weight") +A = f.Variable(name="A", guess=10.0, units="-", description="aspect ratio") +C_D = f.Variable( + name="C_D", guess=0.025, units="-", description="Drag coefficient of wing" +) +C_f = f.Variable( + name="C_f", guess=0.003, units="-", description="skin friction coefficient" +) +C_L = f.Variable( + name="C_L", guess=0.5, units="-", description="Lift coefficient of wing" +) +D = f.Variable(name="D", guess=300, units="N", description="total drag force") +Re = f.Variable(name="Re", guess=3e6, units="-", description="Reynold's number") +S = f.Variable(name="S", guess=10.0, units="m^2", description="total wing area") +V = f.Variable(name="V", guess=30.0, units="m/s", description="cruising speed") +W = f.Variable(name="W", guess=10000.0, units="N", description="total aircraft weight") +W_w = f.Variable(name="W_w", guess=2500, units="N", description="wing weight") # ================= # Declare Constants # ================= -C_Lmax = f.Constant(name = "C_Lmax", value = 2.0, units = "-", description = "max CL with flaps down") -CDA0 = f.Constant(name = "CDA0", value = 0.0306, units = "m^2", description = "fuselage drag area") -e = f.Constant(name = "e", value = 0.96, units = "-", description = "Oswald efficiency factor") -k = f.Constant(name = "k", value = 1.2, units = "-", description = "form factor") -mu = f.Constant(name = "mu", value = 1.78e-5, units = "kg/m/s", description = "viscosity of air") -N_ult = f.Constant(name = "N_ult", value = 2.5, units = "-", description = "ultimate load factor") -rho = f.Constant(name = "rho", value = 1.23, units = "kg/m^3", description = "density of air") -S_wetratio = f.Constant(name = "Srat", value = 2.05, units = "-", description = "wetted area ratio") -tau = f.Constant(name = "tau", value = 0.12, units = "-", description = "airfoil thickness to chord ratio") -V_min = f.Constant(name = "V_min", value = 22, units = "m/s", description = "takeoff speed") -W_0 = f.Constant(name = "W_0", value = 4940.0, units = "N", description = "aircraft weight excluding wing") -W_W_coeff1 = f.Constant(name = "W_c1", value = 8.71e-5, units = "1/m", description = "Wing Weight Coefficient 1") -W_W_coeff2 = f.Constant(name = "W_c2", value = 45.24, units = "Pa", description = "Wing Weight Coefficient 2") +C_Lmax = f.Constant( + name="C_Lmax", value=2.0, units="-", description="max CL with flaps down" +) +CDA0 = f.Constant( + name="CDA0", value=0.0306, units="m^2", description="fuselage drag area" +) +e = f.Constant(name="e", value=0.96, units="-", description="Oswald efficiency factor") +k = f.Constant(name="k", value=1.2, units="-", description="form factor") +mu = f.Constant( + name="mu", value=1.78e-5, units="kg/m/s", description="viscosity of air" +) +N_ult = f.Constant( + name="N_ult", value=2.5, units="-", description="ultimate load factor" +) +rho = f.Constant(name="rho", value=1.23, units="kg/m^3", description="density of air") +S_wetratio = f.Constant( + name="Srat", value=2.05, units="-", description="wetted area ratio" +) +tau = f.Constant( + name="tau", value=0.12, units="-", description="airfoil thickness to chord ratio" +) +V_min = f.Constant(name="V_min", value=22, units="m/s", description="takeoff speed") +W_0 = f.Constant( + name="W_0", value=4940.0, units="N", description="aircraft weight excluding wing" +) +W_W_coeff1 = f.Constant( + name="W_c1", value=8.71e-5, units="1/m", description="Wing Weight Coefficient 1" +) +W_W_coeff2 = f.Constant( + name="W_c2", value=45.24, units="Pa", description="Wing Weight Coefficient 2" +) # ===================== # Declare the Objective # ===================== -f.Objective( D ) +f.Objective(D) # =================================== # Declare some intermediate variables # =================================== pi = np.pi -C_D_fuse = CDA0/S -C_D_wpar = k*C_f*S_wetratio -C_D_ind = C_L**2/(pi*A*e) -W_w_strc = W_W_coeff1*(N_ult*A**1.5*(W_0*W*S)**0.5)/tau +C_D_fuse = CDA0 / S +C_D_wpar = k * C_f * S_wetratio +C_D_ind = C_L**2 / (pi * A * e) +W_w_strc = W_W_coeff1 * (N_ult * A**1.5 * (W_0 * W * S) ** 0.5) / tau W_w_surf = W_W_coeff2 * S # ======================= @@ -75,11 +99,11 @@ [ C_D >= C_D_fuse + C_D_wpar + C_D_ind, W_w >= W_w_surf + W_w_strc, - D >= 0.5*rho*S*C_D*V**2, - Re == (rho/mu)*V*(S/A)**0.5, - C_f == 0.074/Re**0.2, - W == 0.5*rho*S*C_L*V**2, - W == 0.5*rho*S*C_Lmax*V_min**2, + D >= 0.5 * rho * S * C_D * V**2, + Re == (rho / mu) * V * (S / A) ** 0.5, + C_f == 0.074 / Re**0.2, + W == 0.5 * rho * S * C_L * V**2, + W == 0.5 * rho * S * C_Lmax * V_min**2, W >= W_0 + W_w, - ] -) \ No newline at end of file + ] +) diff --git a/pyomo/contrib/edi/examples/readme_example.py b/pyomo/contrib/edi/examples/readme_example.py index d4c80eaefa0..d4f2d026cb1 100644 --- a/pyomo/contrib/edi/examples/readme_example.py +++ b/pyomo/contrib/edi/examples/readme_example.py @@ -14,81 +14,67 @@ # ================= # Declare Variables # ================= -x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') -y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') -z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'Model output') +x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') +y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') +z = f.Variable(name='z', guess=1.0, units='m^2', description='Model output') # ================= # Declare Constants # ================= -c = f.Constant( name = 'c', - value = 1.0, - units = '', - description = 'A constant c', - size = 2 ) +c = f.Constant(name='c', value=1.0, units='', description='A constant c', size=2) # ===================== # Declare the Objective # ===================== -f.Objective( - c[0]*x + c[1]*y -) +f.Objective(c[0] * x + c[1] * y) + # =================== # Declare a Black Box # =================== class UnitCircle(BlackBoxFunctionModel): - def __init__(self): # The initialization function - + def __init__(self): # The initialization function # Initialize the black box model super().__init__() # A brief description of the model self.description = 'This model evaluates the function: z = x**2 + y**2' - + # Declare the black box model inputs - self.inputs.append( name = 'x', - units = 'ft' , - description = 'The x variable') - self.inputs.append( name = 'y', - units = 'ft' , - description = 'The y variable') + self.inputs.append(name='x', units='ft', description='The x variable') + self.inputs.append(name='y', units='ft', description='The y variable') # Declare the black box model outputs - self.outputs.append( name = 'z', - units = 'ft**2', - description = 'Resultant of the unit circle') + self.outputs.append( + name='z', units='ft**2', description='Resultant of the unit circle' + ) # Declare the maximum available derivative self.availableDerivative = 1 - - def BlackBox(self, x, y): # The actual function that does things + + def BlackBox(self, x, y): # The actual function that does things # Converts to correct units then casts to float - x = pyo.value(units.convert(x,self.inputs['x'].units)) - y = pyo.value(units.convert(y,self.inputs['y'].units)) + x = pyo.value(units.convert(x, self.inputs['x'].units)) + y = pyo.value(units.convert(y, self.inputs['y'].units)) - z = x**2 + y**2 # Compute z - dzdx = 2*x # Compute dz/dx - dzdy = 2*y # Compute dz/dy + z = x**2 + y**2 # Compute z + dzdx = 2 * x # Compute dz/dx + dzdy = 2 * y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + # ======================= # Declare the Constraints # ======================= -f.ConstraintList( - [ - [ z, '==', [x,y], UnitCircle() ] , - z <= 1*units.m**2 - ] -) +f.ConstraintList([[z, '==', [x, y], UnitCircle()], z <= 1 * units.m**2]) # ============================================= # Run the black box (improves coverage metrics) # ============================================= uc = UnitCircle() -bbo = uc.BlackBox(0.5*units.m , 0.5*units.m) +bbo = uc.BlackBox(0.5 * units.m, 0.5 * units.m) diff --git a/pyomo/contrib/edi/formulation.py b/pyomo/contrib/edi/formulation.py index b57292ba540..f0652771ec2 100644 --- a/pyomo/contrib/edi/formulation.py +++ b/pyomo/contrib/edi/formulation.py @@ -16,13 +16,16 @@ # ___________________________________________________________________________ import pyomo -import pyomo.environ as pyo +import pyomo.environ as pyo from pyomo.util.check_units import assert_units_consistent from pyomo.environ import ConcreteModel from pyomo.environ import Var, Param, Objective, Constraint, Set from pyomo.environ import maximize, minimize from pyomo.environ import units as pyomo_units -from pyomo.contrib.pynumero.interfaces.external_grey_box import ExternalGreyBoxModel, ExternalGreyBoxBlock +from pyomo.contrib.pynumero.interfaces.external_grey_box import ( + ExternalGreyBoxModel, + ExternalGreyBoxBlock, +) from pyomo.environ import ( Reals, PositiveReals, @@ -43,7 +46,7 @@ PercentFraction, RealInterval, IntegerInterval, -) +) domainList = [ Reals, @@ -67,15 +70,17 @@ IntegerInterval, ] + def decodeUnits(u_val): - if isinstance(u_val,str): - if u_val in ['','-','None',' ','dimensionless']: + if isinstance(u_val, str): + if u_val in ['', '-', 'None', ' ', 'dimensionless']: return pyomo_units.__getattr__('dimensionless') else: return pyomo_units.__getattr__(u_val) else: return u_val - + + class Formulation(ConcreteModel): def __init__(self): super(Formulation, self).__init__() @@ -84,17 +89,18 @@ def __init__(self): self._objective_counter = 0 self._constraint_counter = 0 - self._variable_keys = [] - self._constant_keys = [] + self._variable_keys = [] + self._constant_keys = [] self._objective_keys = [] self._runtimeObjective_keys = [] self._objective_keys = [] self._runtimeConstraint_keys = [] self._constraint_keys = [] self._allConstraint_keys = [] - - def Variable(self, name, guess, units, description='', size = None, bounds=None, domain=None): + def Variable( + self, name, guess, units, description='', size=None, bounds=None, domain=None + ): if domain is None: domain = Reals else: @@ -102,47 +108,102 @@ def Variable(self, name, guess, units, description='', size = None, bounds=None, raise RuntimeError("Invalid domain") if bounds is not None: - if not isinstance(bounds,(list,tuple)): - raise ValueError('The keyword bounds must be a 2 length list or tuple of floats') - if len(bounds)!=2: - raise ValueError('The keyword bounds must be a 2 length list or tuple of floats') + if not isinstance(bounds, (list, tuple)): + raise ValueError( + 'The keyword bounds must be a 2 length list or tuple of floats' + ) + if len(bounds) != 2: + raise ValueError( + 'The keyword bounds must be a 2 length list or tuple of floats' + ) if not isinstance(bounds[0], (float, int)): - raise ValueError('The keyword bounds must be a 2 length list or tuple of floats') + raise ValueError( + 'The keyword bounds must be a 2 length list or tuple of floats' + ) if not isinstance(bounds[1], (float, int)): - raise ValueError('The keyword bounds must be a 2 length list or tuple of floats') + raise ValueError( + 'The keyword bounds must be a 2 length list or tuple of floats' + ) if bounds[0] > bounds[1]: raise ValueError("Lower bound is higher than upper bound") - if size is not None: - if isinstance(size,(list, tuple)): - for i in range(0,len(size)): - if not isinstance(size[i],int): - raise ValueError('Invalid size. Must be an integer or list/tuple of integers') - if size[i] == 1 or size[i] == 0 : - raise ValueError('A value of 0 or 1 is not valid for defining size. Use fewer dimensions.') + if isinstance(size, (list, tuple)): + for i in range(0, len(size)): + if not isinstance(size[i], int): + raise ValueError( + 'Invalid size. Must be an integer or list/tuple of integers' + ) + if size[i] == 1 or size[i] == 0: + raise ValueError( + 'A value of 0 or 1 is not valid for defining size. Use fewer dimensions.' + ) if i == 0: - st = pyo.Set(initialize=list(range(0,size[i]))) + st = pyo.Set(initialize=list(range(0, size[i]))) else: - st *= pyo.Set(initialize=list(range(0,size[i]))) + st *= pyo.Set(initialize=list(range(0, size[i]))) st.construct() - self.add_component(name, pyo.Var(st, name=name, initialize=guess, domain=domain, bounds=bounds, doc=description, units=decodeUnits(units))) + self.add_component( + name, + pyo.Var( + st, + name=name, + initialize=guess, + domain=domain, + bounds=bounds, + doc=description, + units=decodeUnits(units), + ), + ) else: if isinstance(size, int): if size == 1 or size == 0: - self.add_component(name, pyo.Var(name=name, initialize=guess, domain=domain, bounds=bounds, doc=description, units=decodeUnits(units))) + self.add_component( + name, + pyo.Var( + name=name, + initialize=guess, + domain=domain, + bounds=bounds, + doc=description, + units=decodeUnits(units), + ), + ) else: - st = pyo.Set(initialize=list(range(0,size))) + st = pyo.Set(initialize=list(range(0, size))) st.construct() - self.add_component(name, pyo.Var(st, name=name, initialize=guess, domain=domain, bounds=bounds, doc=description, units=decodeUnits(units))) + self.add_component( + name, + pyo.Var( + st, + name=name, + initialize=guess, + domain=domain, + bounds=bounds, + doc=description, + units=decodeUnits(units), + ), + ) else: - raise ValueError('Invalid size. Must be an integer or list/tuple of integers') + raise ValueError( + 'Invalid size. Must be an integer or list/tuple of integers' + ) else: - self.add_component(name, pyo.Var(name=name, initialize=guess, domain=domain, bounds=bounds, doc=description, units=decodeUnits(units))) + self.add_component( + name, + pyo.Var( + name=name, + initialize=guess, + domain=domain, + bounds=bounds, + doc=description, + units=decodeUnits(units), + ), + ) self.__dict__[name].construct() self._variable_keys.append(name) return self.__dict__[name] - + def Constant(self, name, value, units, description='', size=None, within=None): if within is None: within = Reals @@ -151,76 +212,130 @@ def Constant(self, name, value, units, description='', size=None, within=None): raise RuntimeError("Invalid within") if size is not None: - if isinstance(size,(list, tuple)): - for i in range(0,len(size)): - if not isinstance(size[i],int): - raise ValueError('Invalid size. Must be an integer or list/tuple of integers') + if isinstance(size, (list, tuple)): + for i in range(0, len(size)): + if not isinstance(size[i], int): + raise ValueError( + 'Invalid size. Must be an integer or list/tuple of integers' + ) if size[i] == 1: - raise ValueError('A value of 1 is not valid for defining size. Use fewer dimensions.') + raise ValueError( + 'A value of 1 is not valid for defining size. Use fewer dimensions.' + ) if i == 0: - st = pyo.Set(initialize=list(range(0,size[i]))) + st = pyo.Set(initialize=list(range(0, size[i]))) else: - st *= pyo.Set(initialize=list(range(0,size[i]))) + st *= pyo.Set(initialize=list(range(0, size[i]))) st.construct() - self.add_component(name, pyo.Param(st, name=name, initialize=value, within=within, doc=description, units=decodeUnits(units), mutable=True)) + self.add_component( + name, + pyo.Param( + st, + name=name, + initialize=value, + within=within, + doc=description, + units=decodeUnits(units), + mutable=True, + ), + ) else: if isinstance(size, int): if size == 1 or size == 0: - self.add_component(name, pyo.Param(name=name, initialize=value, within=within, doc=description, units=decodeUnits(units), mutable=True)) + self.add_component( + name, + pyo.Param( + name=name, + initialize=value, + within=within, + doc=description, + units=decodeUnits(units), + mutable=True, + ), + ) else: - st = pyo.Set(initialize=list(range(0,size))) + st = pyo.Set(initialize=list(range(0, size))) st.construct() - self.add_component(name, pyo.Param(st, name=name, initialize=value, within=within, doc=description, units=decodeUnits(units), mutable=True)) + self.add_component( + name, + pyo.Param( + st, + name=name, + initialize=value, + within=within, + doc=description, + units=decodeUnits(units), + mutable=True, + ), + ) else: - raise ValueError('Invalid size. Must be an integer or list/tuple of integers') + raise ValueError( + 'Invalid size. Must be an integer or list/tuple of integers' + ) else: - self.add_component(name, pyo.Param(name=name, initialize=value, within=within, doc=description, units=decodeUnits(units), mutable=True)) - + self.add_component( + name, + pyo.Param( + name=name, + initialize=value, + within=within, + doc=description, + units=decodeUnits(units), + mutable=True, + ), + ) + self.__dict__[name].construct() self._constant_keys.append(name) return self.__dict__[name] - + def Objective(self, expr, sense=minimize): self._objective_counter += 1 - self.add_component('objective_'+str(self._objective_counter) , pyo.Objective(expr=expr,sense=sense)) - self._objective_keys.append('objective_'+str(self._objective_counter)) - self.__dict__['objective_'+str(self._objective_counter)].construct() - + self.add_component( + 'objective_' + str(self._objective_counter), + pyo.Objective(expr=expr, sense=sense), + ) + self._objective_keys.append('objective_' + str(self._objective_counter)) + self.__dict__['objective_' + str(self._objective_counter)].construct() + # def RuntimeObjective(self): # pass - + def Constraint(self, expr): self._constraint_counter += 1 - conName = 'constraint_'+str(self._constraint_counter) + conName = 'constraint_' + str(self._constraint_counter) self.add_component(conName, pyo.Constraint(expr=expr)) self._constraint_keys.append(conName) self._allConstraint_keys.append(conName) self.__dict__[conName].construct() - def RuntimeConstraint(self, outputs, operators, inputs, black_box): self._constraint_counter += 1 - conName = 'constraint_'+str(self._constraint_counter) + conName = 'constraint_' + str(self._constraint_counter) self._runtimeConstraint_keys.append(conName) self._allConstraint_keys.append(conName) - self.add_component(conName, ExternalGreyBoxBlock() ) + self.add_component(conName, ExternalGreyBoxBlock()) self.__dict__[conName].construct() # TODO: Need to include operators after Michael fixes things - inputs_raw = inputs - outputs_raw = outputs + inputs_raw = inputs + outputs_raw = outputs operators_raw = operators - if isinstance(inputs_raw, (pyomo.core.base.var.IndexedVar,pyomo.core.base.var.ScalarVar)): + if isinstance( + inputs_raw, (pyomo.core.base.var.IndexedVar, pyomo.core.base.var.ScalarVar) + ): inputs_raw = [inputs_raw] elif isinstance(inputs_raw, (list, tuple)): inputs_raw = list(inputs_raw) else: raise ValueError("Invalid type for input variables") - if isinstance(outputs_raw, (pyomo.core.base.var.IndexedVar,pyomo.core.base.var.ScalarVar)): + if isinstance( + outputs_raw, (pyomo.core.base.var.IndexedVar, pyomo.core.base.var.ScalarVar) + ): outputs_raw = [outputs_raw] elif isinstance(outputs_raw, (list, tuple)): outputs_raw = list(outputs_raw) @@ -228,17 +343,19 @@ def RuntimeConstraint(self, outputs, operators, inputs, black_box): raise ValueError("Invalid type for output variables") for lst in [outputs_raw, inputs_raw]: for vr in lst: - if not isinstance(vr, (pyomo.core.base.var.IndexedVar,pyomo.core.base.var.ScalarVar)): - raise ValueError("Invalid type when checking inputs and outputs") + if not isinstance( + vr, (pyomo.core.base.var.IndexedVar, pyomo.core.base.var.ScalarVar) + ): + raise ValueError("Invalid type when checking inputs and outputs") if isinstance(operators_raw, (list, tuple)): operators_raw = list(operators_raw) - elif isinstance(operators_raw,str): + elif isinstance(operators_raw, str): operators_raw = [operators_raw] else: raise ValueError("Invalid type for operators") for opr in operators_raw: - if opr not in ["==",">=","<="]: + if opr not in ["==", ">=", "<="]: raise ValueError("Invalid operator") black_box.setOptimizationVariables(inputs_raw, outputs_raw) @@ -250,7 +367,7 @@ def RuntimeConstraint(self, outputs, operators, inputs, black_box): for ovar in outputs_raw: if isinstance(ovar, pyomo.core.base.var.ScalarVar): outputs_unwrapped.append(ovar) - else: # isinstance(ovar, pyomo.core.base.var.IndexedVar), validated above + else: # isinstance(ovar, pyomo.core.base.var.IndexedVar), validated above validIndices = list(ovar.index_set().data()) for vi in validIndices: outputs_unwrapped.append(ovar[vi]) @@ -259,24 +376,25 @@ def RuntimeConstraint(self, outputs, operators, inputs, black_box): for ivar in inputs_raw: if isinstance(ivar, pyomo.core.base.var.ScalarVar): inputs_unwrapped.append(ivar) - else: # isinstance(ivar, pyomo.core.base.var.IndexedVar), validated above + else: # isinstance(ivar, pyomo.core.base.var.IndexedVar), validated above validIndices = list(ivar.index_set().data()) for vi in validIndices: - inputs_unwrapped.append(ivar[vi]) + inputs_unwrapped.append(ivar[vi]) black_box._NunwrappedOutputs = len(outputs_unwrapped) - black_box._NunwrappedInputs = len(inputs_unwrapped) + black_box._NunwrappedInputs = len(inputs_unwrapped) black_box.post_init_setup() - # TODO: Need to unwrap operators - + # TODO: Need to unwrap operators - self.__dict__[conName].set_external_model(black_box, inputs=inputs_unwrapped, outputs=outputs_unwrapped)#,operators=operators_unwrapped) + self.__dict__[conName].set_external_model( + black_box, inputs=inputs_unwrapped, outputs=outputs_unwrapped + ) # ,operators=operators_unwrapped) def ConstraintList(self, conList): - for i in range(0,len(conList)): + for i in range(0, len(conList)): con = conList[i] - if isinstance(con, (tuple,list)): + if isinstance(con, (tuple, list)): self.RuntimeConstraint(*con) elif isinstance(con, dict): self.RuntimeConstraint(**con) @@ -284,30 +402,54 @@ def ConstraintList(self, conList): self.Constraint(con) def get_variables(self): - return [self.__dict__[nm] for nm in self.__dict__.keys() if nm in self._variable_keys] + return [ + self.__dict__[nm] + for nm in self.__dict__.keys() + if nm in self._variable_keys + ] def get_constants(self): - return [self.__dict__[nm] for nm in self.__dict__.keys() if nm in self._constant_keys] + return [ + self.__dict__[nm] + for nm in self.__dict__.keys() + if nm in self._constant_keys + ] def get_objectives(self): - return [self.__dict__[nm] for nm in self.__dict__.keys() if nm in self._objective_keys] - + return [ + self.__dict__[nm] + for nm in self.__dict__.keys() + if nm in self._objective_keys + ] + def get_constraints(self): - return [self.__dict__[nm] for nm in self.__dict__.keys() if nm in self._allConstraint_keys] + return [ + self.__dict__[nm] + for nm in self.__dict__.keys() + if nm in self._allConstraint_keys + ] def get_explicitConstraints(self): - return [self.__dict__[nm] for nm in self.__dict__.keys() if nm in self._constraint_keys] + return [ + self.__dict__[nm] + for nm in self.__dict__.keys() + if nm in self._constraint_keys + ] def get_runtimeConstraints(self): - return [self.__dict__[nm] for nm in self.__dict__.keys() if nm in self._runtimeConstraint_keys] + return [ + self.__dict__[nm] + for nm in self.__dict__.keys() + if nm in self._runtimeConstraint_keys + ] def check_units(self): - for i in range(1,self._objective_counter+1): - assert_units_consistent(self.__dict__['objective_'+str(i)]) - - for i in range(1,self._constraint_counter+1): - if not isinstance(self.__dict__['constraint_'+str(i)],pyomo.contrib.pynumero.interfaces.external_grey_box.ExternalGreyBoxBlock): - assert_units_consistent(self.__dict__['constraint_'+str(i)]) - - - \ No newline at end of file + for i in range(1, self._objective_counter + 1): + assert_units_consistent(self.__dict__['objective_' + str(i)]) + + for i in range(1, self._constraint_counter + 1): + if not isinstance( + self.__dict__['constraint_' + str(i)], + pyomo.contrib.pynumero.interfaces.external_grey_box.ExternalGreyBoxBlock, + ): + assert_units_consistent(self.__dict__['constraint_' + str(i)]) diff --git a/pyomo/contrib/edi/tests/test_blackbox.py b/pyomo/contrib/edi/tests/test_blackbox.py index 771cad7175d..7671fb11f3e 100644 --- a/pyomo/contrib/edi/tests/test_blackbox.py +++ b/pyomo/contrib/edi/tests/test_blackbox.py @@ -19,9 +19,7 @@ import pyomo.environ as pyo from pyomo.common.dependencies import attempt_import -from pyomo.core.base.units_container import ( - pint_available, -) +from pyomo.core.base.units_container import pint_available from pyomo.common.dependencies import numpy, numpy_available from pyomo.common.dependencies import scipy, scipy_available @@ -29,113 +27,133 @@ if numpy_available: import numpy as np + @unittest.skipIf(not numpy_available, 'Testing pyomo.contrib.edi requires numpy') @unittest.skipIf(not scipy_available, 'Testing pyomo.contrib.edi requires scipy') -@unittest.skipIf(not pint_available, 'Testing units requires pint') +@unittest.skipIf(not pint_available, 'Testing units requires pint') class TestEDIBlackBox(unittest.TestCase): def test_edi_blackbox_variable(self): "Tests the black box variable class" from pyomo.environ import units from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + from pyomo.contrib.edi.blackBoxFunctionModel import ( + BlackBoxFunctionModel_Variable, + TypeCheckedList, + BBList, + BlackBoxFunctionModel, + ) - x = BlackBoxFunctionModel_Variable('x','') + x = BlackBoxFunctionModel_Variable('x', '') x_print = x.__repr__() x_name = x.name - x_units = x.units - x_size = x.size + x_units = x.units + x_size = x.size x_desc = x.description - self.assertRaises(ValueError,x.__init__,*(1.0, '')) + self.assertRaises(ValueError, x.__init__, *(1.0, '')) x.__init__('x', units.dimensionless) - self.assertRaises(ValueError,x.__init__,*('x', 1.0)) + self.assertRaises(ValueError, x.__init__, *('x', 1.0)) x.__init__('x', units.dimensionless, '', 'flex') - x.__init__('x', units.dimensionless, '', ['flex',2]) + x.__init__('x', units.dimensionless, '', ['flex', 2]) x.__init__('x', units.dimensionless, '', 2) - x.__init__('x', units.dimensionless, '', [2,2]) + x.__init__('x', units.dimensionless, '', [2, 2]) - self.assertRaises(ValueError,x.__init__,*('x', '','',[[],2])) - self.assertRaises(ValueError,x.__init__,*('x', '','',[2,1])) + self.assertRaises(ValueError, x.__init__, *('x', '', '', [[], 2])) + self.assertRaises(ValueError, x.__init__, *('x', '', '', [2, 1])) x.__init__('x', units.dimensionless, '', None) x.__init__('x', None, '', None) - self.assertRaises(ValueError,x.__init__,*('x', '','', 1 )) - self.assertRaises(ValueError,x.__init__,*('x', '','', {} )) - self.assertRaises(ValueError,x.__init__,*('x', '', 1.0 )) - - - + self.assertRaises(ValueError, x.__init__, *('x', '', '', 1)) + self.assertRaises(ValueError, x.__init__, *('x', '', '', {})) + self.assertRaises(ValueError, x.__init__, *('x', '', 1.0)) def test_edi_blackbox_tcl(self): "Tests the black box type checked list class" from pyomo.environ import units from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + from pyomo.contrib.edi.blackBoxFunctionModel import ( + BlackBoxFunctionModel_Variable, + TypeCheckedList, + BBList, + BlackBoxFunctionModel, + ) - tcl = TypeCheckedList(int, [1,2,3]) + tcl = TypeCheckedList(int, [1, 2, 3]) tcl[1] = 1 - tcl[0:2] = [1,2] - - self.assertRaises(ValueError,tcl.__init__,*( int, 1 )) - self.assertRaises(ValueError,tcl.__setitem__,*( 1, 3.333 )) - self.assertRaises(ValueError,tcl.__setitem__,*( 1, [1, 2.222] )) - self.assertRaises(ValueError,tcl.append,*( 2.222 ,)) - + tcl[0:2] = [1, 2] + self.assertRaises(ValueError, tcl.__init__, *(int, 1)) + self.assertRaises(ValueError, tcl.__setitem__, *(1, 3.333)) + self.assertRaises(ValueError, tcl.__setitem__, *(1, [1, 2.222])) + self.assertRaises(ValueError, tcl.append, *(2.222,)) def test_edi_blackbox_bbl(self): "Tests the black box BBList class" from pyomo.environ import units from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + from pyomo.contrib.edi.blackBoxFunctionModel import ( + BlackBoxFunctionModel_Variable, + TypeCheckedList, + BBList, + BlackBoxFunctionModel, + ) bbl = BBList() - bbl.append('x','') - bbl.append('y','') - bbl.append(BlackBoxFunctionModel_Variable('z','')) - bbl.append(var = BlackBoxFunctionModel_Variable('u','')) - self.assertRaises(ValueError,bbl.append,*( BlackBoxFunctionModel_Variable('u',''),)) - self.assertRaises(ValueError,bbl.append,*( 'badvar',)) - self.assertRaises(ValueError,bbl.append,*( 2.222,)) - - self.assertRaises(ValueError,bbl.append,*( 'bv','',''),**{'units':'m'}) - self.assertRaises(ValueError,bbl.append,**{'units':'m','description':'hi'}) - self.assertRaises(ValueError,bbl.append,**{'name':'x', 'units':''}) - self.assertRaises(ValueError,bbl.append,*( 'bv','','',0,'extra')) + bbl.append('x', '') + bbl.append('y', '') + bbl.append(BlackBoxFunctionModel_Variable('z', '')) + bbl.append(var=BlackBoxFunctionModel_Variable('u', '')) + self.assertRaises( + ValueError, bbl.append, *(BlackBoxFunctionModel_Variable('u', ''),) + ) + self.assertRaises(ValueError, bbl.append, *('badvar',)) + self.assertRaises(ValueError, bbl.append, *(2.222,)) + + self.assertRaises(ValueError, bbl.append, *('bv', '', ''), **{'units': 'm'}) + self.assertRaises(ValueError, bbl.append, **{'units': 'm', 'description': 'hi'}) + self.assertRaises(ValueError, bbl.append, **{'name': 'x', 'units': ''}) + self.assertRaises(ValueError, bbl.append, *('bv', '', '', 0, 'extra')) xv = bbl['x'] xv2 = bbl[0] - self.assertRaises(ValueError,bbl.__getitem__,*( 2.22, )) - + self.assertRaises(ValueError, bbl.__getitem__, *(2.22,)) def test_edi_blackbox_someexceptions(self): "Tests some of the exceptions in the black box model class" import numpy as np from pyomo.environ import units from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + from pyomo.contrib.edi.blackBoxFunctionModel import ( + BlackBoxFunctionModel_Variable, + TypeCheckedList, + BBList, + BlackBoxFunctionModel, + ) bb = BlackBoxFunctionModel() - bb.inputVariables_optimization = [1,2,3] + bb.inputVariables_optimization = [1, 2, 3] # bb.set_input_values(np.array([1,2,3])) - self.assertRaises(ValueError,bb.input_names,*( )) + self.assertRaises(ValueError, bb.input_names, *()) # self.assertRaises(ValueError,bb.fillCache,*( )) - bb = BlackBoxFunctionModel() - bb.outputVariables_optimization = [1,2,3] - self.assertRaises(ValueError,bb.output_names,*( )) - + bb.outputVariables_optimization = [1, 2, 3] + self.assertRaises(ValueError, bb.output_names, *()) def test_edi_blackbox_etc_1(self): "Tests a black box assertion issue" - from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel - bbfm = BlackBoxFunctionModel() - self.assertRaises(AttributeError,bbfm.BlackBox,()) + from pyomo.contrib.edi.blackBoxFunctionModel import ( + BlackBoxFunctionModel_Variable, + TypeCheckedList, + BBList, + BlackBoxFunctionModel, + ) + bbfm = BlackBoxFunctionModel() + self.assertRaises(AttributeError, bbfm.BlackBox, ()) def test_edi_blackbox_etc_2(self): "Tests a black box assertion issue" @@ -143,53 +161,56 @@ def test_edi_blackbox_etc_2(self): from pyomo.environ import units import pyomo.environ as pyo from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + from pyomo.contrib.edi.blackBoxFunctionModel import ( + BlackBoxFunctionModel_Variable, + TypeCheckedList, + BBList, + BlackBoxFunctionModel, + ) f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'y variable') - z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'Output var') - f.Objective( x + y ) + x = f.Variable(name='x', guess=1.0, units='m', description='x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='y variable') + z = f.Variable(name='z', guess=1.0, units='m^2', description='Output var') + f.Objective(x + y) + class UnitCircle(BlackBoxFunctionModel): def __init__(self): super().__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append( name = 'x', - units = 'ft' , - description = 'The x variable' ) - self.inputs.append( name = 'y', - units = 'ft' , - description = 'The y variable' ) - self.outputs.append(name = 'z', - units = 'ft**2', - description = 'Output variable' ) + self.inputs.append(name='x', units='ft', description='The x variable') + self.inputs.append(name='y', units='ft', description='The y variable') + self.outputs.append( + name='z', units='ft**2', description='Output variable' + ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, x, y): # The actual function that does things + + def BlackBox(self, x, y): # The actual function that does things # Converts to correct units then casts to float - x = pyo.value(units.convert(x,self.inputs[0].units)) - y = pyo.value(units.convert(y,self.inputs[1].units)) - z = x**2 + y**2 # Compute z - dzdx = 2*x # Compute dz/dx - dzdy = 2*y # Compute dz/dy + x = pyo.value(units.convert(x, self.inputs[0].units)) + y = pyo.value(units.convert(y, self.inputs[1].units)) + z = x**2 + y**2 # Compute z + dzdx = 2 * x # Compute dz/dx + dzdy = 2 * y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + f.ConstraintList([z <= 1 * units.m**2, [z, '==', [x, y], UnitCircle()]]) - f.ConstraintList( - [ - z <= 1*units.m**2 , - [ z, '==', [x,y], UnitCircle() ] , - ] + f.__dict__['constraint_2'].get_external_model().set_input_values( + np.array([2.0, 2.0]) ) - - f.__dict__['constraint_2'].get_external_model().set_input_values(np.array([2.0, 2.0])) - f.__dict__['constraint_2'].get_external_model().inputVariables_optimization = [1,2] + f.__dict__['constraint_2'].get_external_model().inputVariables_optimization = [ + 1, + 2, + ] # f.__dict__['constraint_2'].get_external_model().fillCache() - self.assertRaises(ValueError,f.__dict__['constraint_2'].get_external_model().fillCache,*()) - + self.assertRaises( + ValueError, f.__dict__['constraint_2'].get_external_model().fillCache, *() + ) def test_edi_blackbox_etc_3(self): "Tests a black box assertion issue" @@ -197,215 +218,239 @@ def test_edi_blackbox_etc_3(self): from pyomo.environ import units import pyomo.environ as pyo from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + from pyomo.contrib.edi.blackBoxFunctionModel import ( + BlackBoxFunctionModel_Variable, + TypeCheckedList, + BBList, + BlackBoxFunctionModel, + ) f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'y variable') - z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'Output var') - f.Objective( x + y ) + x = f.Variable(name='x', guess=1.0, units='m', description='x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='y variable') + z = f.Variable(name='z', guess=1.0, units='m^2', description='Output var') + f.Objective(x + y) + class UnitCircle(BlackBoxFunctionModel): def __init__(self): super().__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append( name = 'x', - units = 'ft' , - description = 'The x variable' ) - self.inputs.append( name = 'y', - units = 'ft' , - description = 'The y variable' ) - self.outputs.append(name = 'z', - units = 'ft**2', - description = 'Output variable' ) + self.inputs.append(name='x', units='ft', description='The x variable') + self.inputs.append(name='y', units='ft', description='The y variable') + self.outputs.append( + name='z', units='ft**2', description='Output variable' + ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, x, y): # The actual function that does things + + def BlackBox(self, x, y): # The actual function that does things # Converts to correct units then casts to float - x = pyo.value(units.convert(x,self.inputs[0].units)) - y = pyo.value(units.convert(y,self.inputs[1].units)) - z = x**2 + y**2 # Compute z - dzdx = 2*x # Compute dz/dx - dzdy = 2*y # Compute dz/dy + x = pyo.value(units.convert(x, self.inputs[0].units)) + y = pyo.value(units.convert(y, self.inputs[1].units)) + z = x**2 + y**2 # Compute z + dzdx = 2 * x # Compute dz/dx + dzdy = 2 * y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return ['err'], [[dzdx, dzdy]] # return z, grad(z), hess(z)... + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return ['err'], [[dzdx, dzdy]] # return z, grad(z), hess(z)... + f.ConstraintList([z <= 1 * units.m**2, [z, '==', [x, y], UnitCircle()]]) - f.ConstraintList( - [ - z <= 1*units.m**2 , - [ z, '==', [x,y], UnitCircle() ] , - ] + self.assertRaises( + ValueError, f.__dict__['constraint_2'].get_external_model().fillCache, *() ) - self.assertRaises(ValueError,f.__dict__['constraint_2'].get_external_model().fillCache,*()) - def test_edi_blackbox_etc_4(self): "Tests a black box assertion issue" import numpy as np from pyomo.environ import units import pyomo.environ as pyo from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + from pyomo.contrib.edi.blackBoxFunctionModel import ( + BlackBoxFunctionModel_Variable, + TypeCheckedList, + BBList, + BlackBoxFunctionModel, + ) f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'y variable') - z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'Output var') - f.Objective( x + y ) + x = f.Variable(name='x', guess=1.0, units='m', description='x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='y variable') + z = f.Variable(name='z', guess=1.0, units='m^2', description='Output var') + f.Objective(x + y) + class UnitCircle(BlackBoxFunctionModel): def __init__(self): super().__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append( name = 'x', - units = 'ft' , - description = 'The x variable' ) - self.inputs.append( name = 'y', - units = 'ft' , - description = 'The y variable' ) - self.outputs.append(name = 'z', - units = 'ft**2', - description = 'Output variable' ) + self.inputs.append(name='x', units='ft', description='The x variable') + self.inputs.append(name='y', units='ft', description='The y variable') + self.outputs.append( + name='z', units='ft**2', description='Output variable' + ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, x, y): # The actual function that does things + + def BlackBox(self, x, y): # The actual function that does things # Converts to correct units then casts to float - x = pyo.value(units.convert(x,self.inputs[0].units)) - y = pyo.value(units.convert(y,self.inputs[1].units)) - z = x**2 + y**2 # Compute z - dzdx = 2*x # Compute dz/dx - dzdy = 2*y # Compute dz/dy + x = pyo.value(units.convert(x, self.inputs[0].units)) + y = pyo.value(units.convert(y, self.inputs[1].units)) + z = x**2 + y**2 # Compute z + dzdx = 2 * x # Compute dz/dx + dzdy = 2 * y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + f.ConstraintList([z <= 1 * units.m**2, [z, '==', [x, y], UnitCircle()]]) - f.ConstraintList( - [ - z <= 1*units.m**2 , - [ z, '==', [x,y], UnitCircle() ] , - ] + f.__dict__['constraint_2'].get_external_model().set_input_values( + np.array([2.0, 2.0]) ) - - f.__dict__['constraint_2'].get_external_model().set_input_values(np.array([2.0, 2.0])) - f.__dict__['constraint_2'].get_external_model().outputVariables_optimization = [1] + f.__dict__['constraint_2'].get_external_model().outputVariables_optimization = [ + 1 + ] # f.__dict__['constraint_2'].get_external_model().fillCache() - self.assertRaises(ValueError,f.__dict__['constraint_2'].get_external_model().fillCache,*()) - + self.assertRaises( + ValueError, f.__dict__['constraint_2'].get_external_model().fillCache, *() + ) def test_edi_blackbox_example_1(self): "Tests a black box example construction" from pyomo.environ import units import pyomo.environ as pyo from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + from pyomo.contrib.edi.blackBoxFunctionModel import ( + BlackBoxFunctionModel_Variable, + TypeCheckedList, + BBList, + BlackBoxFunctionModel, + ) f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'y variable') - z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'Output var') - f.Objective( x + y ) + x = f.Variable(name='x', guess=1.0, units='m', description='x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='y variable') + z = f.Variable(name='z', guess=1.0, units='m^2', description='Output var') + f.Objective(x + y) + class UnitCircle(BlackBoxFunctionModel): def __init__(self): super().__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append( name = 'x', - units = 'ft' , - description = 'The x variable' ) - self.inputs.append( name = 'y', - units = 'ft' , - description = 'The y variable' ) - self.outputs.append(name = 'z', - units = 'ft**2', - description = 'Output variable' ) + self.inputs.append(name='x', units='ft', description='The x variable') + self.inputs.append(name='y', units='ft', description='The y variable') + self.outputs.append( + name='z', units='ft**2', description='Output variable' + ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, x, y): # The actual function that does things + + def BlackBox(self, x, y): # The actual function that does things # Converts to correct units then casts to float - x = pyo.value(units.convert(x,self.inputs[0].units)) - y = pyo.value(units.convert(y,self.inputs[1].units)) - z = x**2 + y**2 # Compute z - dzdx = 2*x # Compute dz/dx - dzdy = 2*y # Compute dz/dy + x = pyo.value(units.convert(x, self.inputs[0].units)) + y = pyo.value(units.convert(y, self.inputs[1].units)) + z = x**2 + y**2 # Compute z + dzdx = 2 * x # Compute dz/dx + dzdy = 2 * y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + f.ConstraintList([z <= 1 * units.m**2, [z, '==', [x, y], UnitCircle()]]) - f.ConstraintList( - [ - z <= 1*units.m**2 , - [ z, '==', [x,y], UnitCircle() ] , - ] + f.__dict__['constraint_2'].get_external_model().set_input_values( + np.array([2.0, 2.0]) + ) + opt = f.__dict__['constraint_2'].get_external_model().evaluate_outputs() + jac = ( + f.__dict__['constraint_2'] + .get_external_model() + .evaluate_jacobian_outputs() + .todense() ) - f.__dict__['constraint_2'].get_external_model().set_input_values(np.array([2.0, 2.0])) - opt = f.__dict__['constraint_2'].get_external_model().evaluate_outputs() - jac = f.__dict__['constraint_2'].get_external_model().evaluate_jacobian_outputs().todense() - - self.assertAlmostEqual(opt[0],8) - self.assertAlmostEqual(jac[0,0],4) - self.assertAlmostEqual(jac[0,1],4) + self.assertAlmostEqual(opt[0], 8) + self.assertAlmostEqual(jac[0, 0], 4) + self.assertAlmostEqual(jac[0, 1], 4) - sm = f.__dict__['constraint_2'].get_external_model().summary + sm = f.__dict__['constraint_2'].get_external_model().summary e_print = f.__dict__['constraint_2'].get_external_model().__repr__() - def test_edi_blackbox_example_2(self): "Tests a black box example construction" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable', size=3) - y = f.Variable(name = 'y', guess = 1.0, units = 'm**2' , description = 'The y variable', size=3) - f.Objective( y[0] + y[1] + y[2] ) + x = f.Variable( + name='x', guess=1.0, units='m', description='The x variable', size=3 + ) + y = f.Variable( + name='y', guess=1.0, units='m**2', description='The y variable', size=3 + ) + f.Objective(y[0] + y[1] + y[2]) + class Parabola(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super(Parabola, self).__init__() self.description = 'This model evaluates the function: y = x**2' - self.inputs.append(name = 'x', size = 3, units = 'ft' , description = 'The x variable') - self.outputs.append(name = 'y', size = 3, units = 'ft**2' , description = 'The y variable') + self.inputs.append( + name='x', size=3, units='ft', description='The x variable' + ) + self.outputs.append( + name='y', size=3, units='ft**2', description='The y variable' + ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, *args, **kwargs):# The actual function that does things - runCases, returnMode, remainingKwargs = self.parseInputs(*args, **kwargs) - + def BlackBox(self, *args, **kwargs): # The actual function that does things + runCases, returnMode, remainingKwargs = self.parseInputs( + *args, **kwargs + ) + x = self.sanitizeInputs(runCases[0]['x']) x = np.array([pyo.value(xval) for xval in x], dtype=np.float64) - y = x**2 # Compute y - dydx = 2*x # Compute dy/dx - + y = x**2 # Compute y + dydx = 2 * x # Compute dy/dx + y = y * units.ft**2 dydx = np.diag(dydx) - dydx = dydx * units.ft # units.ft**2 / units.ft - - return y, [dydx] # return z, grad(z), hess(z)... - f.ConstraintList([ {'outputs':y, 'operators':'==', 'inputs':x, 'black_box':Parabola() },]) + dydx = dydx * units.ft # units.ft**2 / units.ft + + return y, [dydx] # return z, grad(z), hess(z)... - f.__dict__['constraint_1'].get_external_model().set_input_values(np.ones(3)*2) + f.ConstraintList( + [{'outputs': y, 'operators': '==', 'inputs': x, 'black_box': Parabola()}] + ) + + f.__dict__['constraint_1'].get_external_model().set_input_values(np.ones(3) * 2) opt = f.__dict__['constraint_1'].get_external_model().evaluate_outputs() - jac = f.__dict__['constraint_1'].get_external_model().evaluate_jacobian_outputs().todense() - - self.assertAlmostEqual(opt[0],4) - self.assertAlmostEqual(opt[1],4) - self.assertAlmostEqual(opt[2],4) - self.assertAlmostEqual(jac[0,0],4) - self.assertAlmostEqual(jac[0,1],0) - self.assertAlmostEqual(jac[0,2],0) - self.assertAlmostEqual(jac[0,1],0) - self.assertAlmostEqual(jac[1,1],4) - self.assertAlmostEqual(jac[2,1],0) - self.assertAlmostEqual(jac[2,0],0) - self.assertAlmostEqual(jac[2,1],0) - self.assertAlmostEqual(jac[2,2],4) - - sm = f.__dict__['constraint_1'].get_external_model().summary + jac = ( + f.__dict__['constraint_1'] + .get_external_model() + .evaluate_jacobian_outputs() + .todense() + ) + + self.assertAlmostEqual(opt[0], 4) + self.assertAlmostEqual(opt[1], 4) + self.assertAlmostEqual(opt[2], 4) + self.assertAlmostEqual(jac[0, 0], 4) + self.assertAlmostEqual(jac[0, 1], 0) + self.assertAlmostEqual(jac[0, 2], 0) + self.assertAlmostEqual(jac[0, 1], 0) + self.assertAlmostEqual(jac[1, 1], 4) + self.assertAlmostEqual(jac[2, 1], 0) + self.assertAlmostEqual(jac[2, 0], 0) + self.assertAlmostEqual(jac[2, 1], 0) + self.assertAlmostEqual(jac[2, 2], 4) + + sm = f.__dict__['constraint_1'].get_external_model().summary e_print = f.__dict__['constraint_1'].get_external_model().__repr__() def test_edi_blackbox_example_3(self): @@ -414,109 +459,133 @@ def test_edi_blackbox_example_3(self): from pyomo.environ import units import pyomo.environ as pyo from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + from pyomo.contrib.edi.blackBoxFunctionModel import ( + BlackBoxFunctionModel_Variable, + TypeCheckedList, + BBList, + BlackBoxFunctionModel, + ) f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = '' , description = 'x variable', size = 3) - y = f.Variable(name = 'y', guess = 1.0, units = '' , description = 'y variable') - f.Objective( y ) + x = f.Variable(name='x', guess=1.0, units='', description='x variable', size=3) + y = f.Variable(name='y', guess=1.0, units='', description='y variable') + f.Objective(y) + class Norm_2(BlackBoxFunctionModel): def __init__(self): super().__init__() self.description = 'This model evaluates the two norm' - self.inputs.append( name = 'x', - units = '' , - description = 'The x variable' , - size = 3) - self.outputs.append(name = 'y', - units = '' , - description = 'The y variable' ) + self.inputs.append( + name='x', units='', description='The x variable', size=3 + ) + self.outputs.append(name='y', units='', description='The y variable') self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, *args, **kwargs):# The actual function that does things - runCases, returnMode, remainingKwargs = self.parseInputs(*args, **kwargs) + + def BlackBox(self, *args, **kwargs): # The actual function that does things + runCases, returnMode, remainingKwargs = self.parseInputs( + *args, **kwargs + ) x = self.sanitizeInputs(runCases[0]['x']) x = np.array([pyo.value(xval) for xval in x], dtype=np.float64) - y = x[0]**2 + x[1]**2 + x[2]**2 # Compute y - dydx0 = 2*x[0] # Compute dy/dx0 - dydx1 = 2*x[1] # Compute dy/dx1 - dydx2 = 2*x[2] # Compute dy/dx2 + y = x[0] ** 2 + x[1] ** 2 + x[2] ** 2 # Compute y + dydx0 = 2 * x[0] # Compute dy/dx0 + dydx1 = 2 * x[1] # Compute dy/dx1 + dydx2 = 2 * x[2] # Compute dy/dx2 y = y * units.dimensionless - dydx = np.array([dydx0,dydx1,dydx2]) * units.dimensionless + dydx = np.array([dydx0, dydx1, dydx2]) * units.dimensionless # dydx0 = dydx0 * units.dimensionless # dydx1 = dydx1 * units.dimensionless # dydx2 = dydx2 * units.dimensionless - return y, [dydx] # return z, grad(z), hess(z)... - - f.ConstraintList([ {'outputs':y, 'operators':'==', 'inputs':x, 'black_box':Norm_2() },]) + return y, [dydx] # return z, grad(z), hess(z)... + + f.ConstraintList( + [{'outputs': y, 'operators': '==', 'inputs': x, 'black_box': Norm_2()}] + ) - f.__dict__['constraint_1'].get_external_model().set_input_values(np.ones(3)*2) + f.__dict__['constraint_1'].get_external_model().set_input_values(np.ones(3) * 2) opt = f.__dict__['constraint_1'].get_external_model().evaluate_outputs() - jac = f.__dict__['constraint_1'].get_external_model().evaluate_jacobian_outputs().todense() + jac = ( + f.__dict__['constraint_1'] + .get_external_model() + .evaluate_jacobian_outputs() + .todense() + ) - self.assertAlmostEqual(opt[0],12) - self.assertAlmostEqual(jac[0,0],4) - self.assertAlmostEqual(jac[0,1],4) - self.assertAlmostEqual(jac[0,2],4) + self.assertAlmostEqual(opt[0], 12) + self.assertAlmostEqual(jac[0, 0], 4) + self.assertAlmostEqual(jac[0, 1], 4) + self.assertAlmostEqual(jac[0, 2], 4) - sm = f.__dict__['constraint_1'].get_external_model().summary + sm = f.__dict__['constraint_1'].get_external_model().summary e_print = f.__dict__['constraint_1'].get_external_model().__repr__() - def test_edi_blackbox_example_4(self): "Tests a black box example construction" import numpy as np from pyomo.environ import units import pyomo.environ as pyo from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + from pyomo.contrib.edi.blackBoxFunctionModel import ( + BlackBoxFunctionModel_Variable, + TypeCheckedList, + BBList, + BlackBoxFunctionModel, + ) f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = '' , description = 'x variable') - y = f.Variable(name = 'y', guess = 1.0, units = '' , description = 'y variable', size=3) - f.Objective( y[0]**2 + y[1]**2 + y[2]**2 ) + x = f.Variable(name='x', guess=1.0, units='', description='x variable') + y = f.Variable(name='y', guess=1.0, units='', description='y variable', size=3) + f.Objective(y[0] ** 2 + y[1] ** 2 + y[2] ** 2) + class VectorCast(BlackBoxFunctionModel): def __init__(self): super().__init__() self.description = 'This model evaluates the two norm' - self.inputs.append( name = 'x', - units = '' , - description = 'The x variable' ) - self.outputs.append(name = 'y', - units = '' , - description = 'The y variable' , - size = 3) + self.inputs.append(name='x', units='', description='The x variable') + self.outputs.append( + name='y', units='', description='The y variable', size=3 + ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, *args, **kwargs):# The actual function that does things - runCases, returnMode, remainingKwargs = self.parseInputs(*args, **kwargs) + def BlackBox(self, *args, **kwargs): # The actual function that does things + runCases, returnMode, remainingKwargs = self.parseInputs( + *args, **kwargs + ) x = pyo.value(self.sanitizeInputs(runCases[0]['x'])) - y = np.array([x,x,x]) * units.dimensionless - dydx = np.array([1.0,1.0,1.0]) * units.dimensionless + y = np.array([x, x, x]) * units.dimensionless + dydx = np.array([1.0, 1.0, 1.0]) * units.dimensionless - return y, [dydx] # return z, grad(z), hess(z)... + return y, [dydx] # return z, grad(z), hess(z)... - f.ConstraintList([ {'outputs':y, 'operators':'==', 'inputs':x, 'black_box':VectorCast() },]) + f.ConstraintList( + [{'outputs': y, 'operators': '==', 'inputs': x, 'black_box': VectorCast()}] + ) - f.__dict__['constraint_1'].get_external_model().set_input_values(np.ones(3)*2) + f.__dict__['constraint_1'].get_external_model().set_input_values(np.ones(3) * 2) opt = f.__dict__['constraint_1'].get_external_model().evaluate_outputs() - jac = f.__dict__['constraint_1'].get_external_model().evaluate_jacobian_outputs().todense() + jac = ( + f.__dict__['constraint_1'] + .get_external_model() + .evaluate_jacobian_outputs() + .todense() + ) - self.assertAlmostEqual(opt[0],2.0) - self.assertAlmostEqual(opt[1],2.0) - self.assertAlmostEqual(opt[2],2.0) - self.assertAlmostEqual(jac[0,0],1.0) - self.assertAlmostEqual(jac[1,0],1.0) - self.assertAlmostEqual(jac[2,0],1.0) + self.assertAlmostEqual(opt[0], 2.0) + self.assertAlmostEqual(opt[1], 2.0) + self.assertAlmostEqual(opt[2], 2.0) + self.assertAlmostEqual(jac[0, 0], 1.0) + self.assertAlmostEqual(jac[1, 0], 1.0) + self.assertAlmostEqual(jac[2, 0], 1.0) - sm = f.__dict__['constraint_1'].get_external_model().summary + sm = f.__dict__['constraint_1'].get_external_model().summary e_print = f.__dict__['constraint_1'].get_external_model().__repr__() def test_edi_blackbox_badexample_1(self): @@ -525,35 +594,41 @@ def test_edi_blackbox_badexample_1(self): from pyomo.environ import units import pyomo.environ as pyo from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + from pyomo.contrib.edi.blackBoxFunctionModel import ( + BlackBoxFunctionModel_Variable, + TypeCheckedList, + BBList, + BlackBoxFunctionModel, + ) f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = '' , description = 'x variable', size = 3) - y = f.Variable(name = 'y', guess = 1.0, units = '' , description = 'y variable') - f.Objective( y ) + x = f.Variable(name='x', guess=1.0, units='', description='x variable', size=3) + y = f.Variable(name='y', guess=1.0, units='', description='y variable') + f.Objective(y) + class Norm_2(BlackBoxFunctionModel): def __init__(self): super().__init__() self.description = 'This model evaluates the two norm' - self.inputs.append( name = 'x', - units = '' , - description = 'The x variable' , - size = 3) - self.outputs.append(name = 'y', - units = '' , - description = 'The y variable' ) + self.inputs.append( + name='x', units='', description='The x variable', size=3 + ) + self.outputs.append(name='y', units='', description='The y variable') self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, *args, **kwargs):# The actual function that does things - runCases, returnMode, remainingKwargs = self.parseInputs(*args, **kwargs) + + def BlackBox(self, *args, **kwargs): # The actual function that does things + runCases, returnMode, remainingKwargs = self.parseInputs( + *args, **kwargs + ) x = self.sanitizeInputs(runCases[0]['x']) x = np.array([pyo.value(xval) for xval in x], dtype=np.float64) - y = x[0]**2 + x[1]**2 + x[2]**2 # Compute y - dydx0 = 2*x[0] # Compute dy/dx0 - dydx1 = 2*x[1] # Compute dy/dx1 - dydx2 = 2*x[2] # Compute dy/dx2 + y = x[0] ** 2 + x[1] ** 2 + x[2] ** 2 # Compute y + dydx0 = 2 * x[0] # Compute dy/dx0 + dydx1 = 2 * x[1] # Compute dy/dx1 + dydx2 = 2 * x[2] # Compute dy/dx2 y = y * units.dimensionless # dydx = np.array([dydx0,dydx1,dydx2]) * units.dimensionless @@ -561,13 +636,18 @@ def BlackBox(self, *args, **kwargs):# The actual function that does things dydx1 = dydx1 * units.dimensionless dydx2 = dydx2 * units.dimensionless - return y, [[dydx0, dydx1, dydx2]] # return z, grad(z), hess(z)... - - f.ConstraintList([ {'outputs':y, 'operators':'==', 'inputs':x, 'black_box':Norm_2() },]) + return y, [[dydx0, dydx1, dydx2]] # return z, grad(z), hess(z)... - f.__dict__['constraint_1'].get_external_model().set_input_values(np.ones(3)*2) - self.assertRaises(ValueError,f.__dict__['constraint_1'].get_external_model().evaluate_outputs,*()) + f.ConstraintList( + [{'outputs': y, 'operators': '==', 'inputs': x, 'black_box': Norm_2()}] + ) + f.__dict__['constraint_1'].get_external_model().set_input_values(np.ones(3) * 2) + self.assertRaises( + ValueError, + f.__dict__['constraint_1'].get_external_model().evaluate_outputs, + *() + ) def test_edi_blackbox_smallfunctions(self): "Tests the more general value and convert functions" @@ -575,22 +655,31 @@ def test_edi_blackbox_smallfunctions(self): from pyomo.environ import units import pyomo.environ as pyo from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + from pyomo.contrib.edi.blackBoxFunctionModel import ( + BlackBoxFunctionModel_Variable, + TypeCheckedList, + BBList, + BlackBoxFunctionModel, + ) bb = BlackBoxFunctionModel() - t1 = bb.convert(2*units.m, units.ft) - t2 = bb.convert(np.ones([2,2])*units.m, units.ft) - self.assertRaises(ValueError,bb.convert,*('err',units.ft)) - - t3 = bb.pyomo_value(2*units.m) - t3 = bb.pyomo_value(np.ones([2,2])*units.m) - self.assertRaises(ValueError,bb.pyomo_value,*(np.ones([2,2]),)) - - bb.sizeCheck([2,2],np.ones([2,2])*units.m) - self.assertRaises(ValueError,bb.sizeCheck,*(2,3*units.m,)) - self.assertRaises(ValueError,bb.sizeCheck,*([10,10,10],np.ones([2,2])*units.m,)) - self.assertRaises(ValueError,bb.sizeCheck,*([10,10],np.ones([2,2])*units.m,)) - self.assertRaises(ValueError,bb.sizeCheck,*([10,10],[],)) + t1 = bb.convert(2 * units.m, units.ft) + t2 = bb.convert(np.ones([2, 2]) * units.m, units.ft) + self.assertRaises(ValueError, bb.convert, *('err', units.ft)) + + t3 = bb.pyomo_value(2 * units.m) + t3 = bb.pyomo_value(np.ones([2, 2]) * units.m) + self.assertRaises(ValueError, bb.pyomo_value, *(np.ones([2, 2]),)) + + bb.sizeCheck([2, 2], np.ones([2, 2]) * units.m) + self.assertRaises(ValueError, bb.sizeCheck, *(2, 3 * units.m)) + self.assertRaises( + ValueError, bb.sizeCheck, *([10, 10, 10], np.ones([2, 2]) * units.m) + ) + self.assertRaises( + ValueError, bb.sizeCheck, *([10, 10], np.ones([2, 2]) * units.m) + ) + self.assertRaises(ValueError, bb.sizeCheck, *([10, 10], [])) def test_edi_blackbox_bare_example_1(self): "Tests a black box example construction without an optimization problem" @@ -604,34 +693,38 @@ def __init__(self): # Set up all the attributes by calling Model.__init__ super().__init__() - #Setup Inputs - self.inputs.append( 'x', '', 'Independent Variable') + # Setup Inputs + self.inputs.append('x', '', 'Independent Variable') - #Setup Outputs - self.outputs.append( 'y', '', 'Dependent Variable') + # Setup Outputs + self.outputs.append('y', '', 'Dependent Variable') - #Simple model description - self.description = 'This model evaluates the function: max([-6*x-6, x**4-3*x**2])' + # Simple model description + self.description = ( + 'This model evaluates the function: max([-6*x-6, x**4-3*x**2])' + ) self.availableDerivative = 1 - #standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) + # standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) def BlackBox(self, *args, **kwargs): runCases, returnMode, extras = self.parseInputs(*args, **kwargs) - x = np.array([ pyo.value(runCases[i]['x']) for i in range(0,len(runCases)) ]) - - y = np.maximum(-6*x-6, x**4-3*x**2) - dydx = 4*x**3 - 6*x - ddy_ddx = 12*x**2 - 6 - gradientSwitch = -6*x-6 > x**4-3*x**2 + x = np.array( + [pyo.value(runCases[i]['x']) for i in range(0, len(runCases))] + ) + + y = np.maximum(-6 * x - 6, x**4 - 3 * x**2) + dydx = 4 * x**3 - 6 * x + ddy_ddx = 12 * x**2 - 6 + gradientSwitch = -6 * x - 6 > x**4 - 3 * x**2 dydx[gradientSwitch] = -6 ddy_ddx[gradientSwitch] = 0 - y = [ yval * units.dimensionless for yval in y] - dydx = [dydx[i] * units.dimensionless for i in range(0,len(dydx))] + y = [yval * units.dimensionless for yval in y] + dydx = [dydx[i] * units.dimensionless for i in range(0, len(dydx))] if returnMode < 0: - returnMode = -1*(returnMode + 1) + returnMode = -1 * (returnMode + 1) if returnMode == 0: return y[0] if returnMode == 1: @@ -639,43 +732,44 @@ def BlackBox(self, *args, **kwargs): else: if returnMode == 0: opt = [] - for i in range(0,len(y)): - opt.append([ y[i] ]) + for i in range(0, len(y)): + opt.append([y[i]]) return opt if returnMode == 1: opt = [] - for i in range(0,len(y)): - opt.append([ [y[i]], [ [[dydx[i]]] ] ]) + for i in range(0, len(y)): + opt.append([[y[i]], [[[dydx[i]]]]]) return opt s = SignomialTest() - ivals = [[x] for x in np.linspace(-2,2,11)] + ivals = [[x] for x in np.linspace(-2, 2, 11)] # How the black box may be called using EDI - bbo = s.BlackBox(**{'x':0.5}) - bbo = s.BlackBox({'x':0.5}) - bbo = s.BlackBox(**{'x':0.5, 'optn':True}) + bbo = s.BlackBox(**{'x': 0.5}) + bbo = s.BlackBox({'x': 0.5}) + bbo = s.BlackBox(**{'x': 0.5, 'optn': True}) # Additional options available with parseInputs bbo = s.BlackBox(*[0.5], **{'optn1': True, 'optn2': False}) - bbo = s.BlackBox(*[0.5,True], **{'optn': False}) - bbo = s.BlackBox({'x':[x for x in np.linspace(-2,2,11)]}) - bbo = s.BlackBox([{'x':x} for x in np.linspace(-2,2,11)]) - bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)]) - bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)], True, optn=False) - bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)], optn1=True, optn2=False) + bbo = s.BlackBox(*[0.5, True], **{'optn': False}) + bbo = s.BlackBox({'x': [x for x in np.linspace(-2, 2, 11)]}) + bbo = s.BlackBox([{'x': x} for x in np.linspace(-2, 2, 11)]) + bbo = s.BlackBox([[x] for x in np.linspace(-2, 2, 11)]) + bbo = s.BlackBox([[x] for x in np.linspace(-2, 2, 11)], True, optn=False) + bbo = s.BlackBox([[x] for x in np.linspace(-2, 2, 11)], optn1=True, optn2=False) sm = s.summary - self.assertRaises(ValueError,s.BlackBox,*( [ [[1,2],2,3] , [1,2,3]] )) - self.assertRaises(ValueError,s.BlackBox,*( [ [np.ones(2),2,3] , [1,2,3]] )) - - self.assertRaises(ValueError,s.sanitizeInputs,*( )) - self.assertRaises(ValueError,s.sanitizeInputs,*( 1 , 2, 3 )) - self.assertRaises(ValueError,s.sanitizeInputs,**{'z':2.0*units.dimensionless}) - self.assertRaises(ValueError,s.sanitizeInputs,*(2.0*units.ft,)) - self.assertRaises(NotImplementedError,s.checkOutputs,*()) + self.assertRaises(ValueError, s.BlackBox, *([[[1, 2], 2, 3], [1, 2, 3]])) + self.assertRaises(ValueError, s.BlackBox, *([[np.ones(2), 2, 3], [1, 2, 3]])) + self.assertRaises(ValueError, s.sanitizeInputs, *()) + self.assertRaises(ValueError, s.sanitizeInputs, *(1, 2, 3)) + self.assertRaises( + ValueError, s.sanitizeInputs, **{'z': 2.0 * units.dimensionless} + ) + self.assertRaises(ValueError, s.sanitizeInputs, *(2.0 * units.ft,)) + self.assertRaises(NotImplementedError, s.checkOutputs, *()) def test_edi_blackbox_bare_example_2(self): "Tests a black box example construction without an optimization problem" @@ -690,37 +784,39 @@ def __init__(self): # Set up all the attributes by calling Model.__init__ super().__init__() - #Setup Inputs - self.inputs.append( 'x', '', 'Independent Variable', size = [2,2]) + # Setup Inputs + self.inputs.append('x', '', 'Independent Variable', size=[2, 2]) - #Setup Outputs - self.outputs.append( 'y', '', 'Dependent Variable', size = [2,2]) + # Setup Outputs + self.outputs.append('y', '', 'Dependent Variable', size=[2, 2]) - #Simple model description + # Simple model description self.description = 'This model is a pass through)' self.availableDerivative = 1 - #standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) + # standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) def BlackBox(self, *args, **kwargs): runCases, returnMode, extras = self.parseInputs(*args, **kwargs) - x = [ self.pyomo_value(runCases[i]['x']) for i in range(0,len(runCases)) ] + x = [ + self.pyomo_value(runCases[i]['x']) for i in range(0, len(runCases)) + ] - y = [] + y = [] dydx = [] - + for xval in x: - y.append(xval*units.dimensionless) - dydx_temp = np.zeros([2,2,2,2]) - dydx_temp[0,0,0,0] = 1.0 - dydx_temp[0,1,0,1] = 1.0 - dydx_temp[1,0,1,0] = 1.0 - dydx_temp[1,1,1,1] = 1.0 - - dydx.append(dydx_temp*units.dimensionless) - + y.append(xval * units.dimensionless) + dydx_temp = np.zeros([2, 2, 2, 2]) + dydx_temp[0, 0, 0, 0] = 1.0 + dydx_temp[0, 1, 0, 1] = 1.0 + dydx_temp[1, 0, 1, 0] = 1.0 + dydx_temp[1, 1, 1, 1] = 1.0 + + dydx.append(dydx_temp * units.dimensionless) + if returnMode < 0: - returnMode = -1*(returnMode + 1) + returnMode = -1 * (returnMode + 1) if returnMode == 0: return y[0] if returnMode == 1: @@ -728,38 +824,41 @@ def BlackBox(self, *args, **kwargs): else: if returnMode == 0: opt = [] - for i in range(0,len(y)): - opt.append([ y[i] ]) + for i in range(0, len(y)): + opt.append([y[i]]) return opt if returnMode == 1: opt = [] - for i in range(0,len(y)): - opt.append([ [y[i]], [ [[dydx[i]]] ] ]) + for i in range(0, len(y)): + opt.append([[y[i]], [[[dydx[i]]]]]) return opt bb = PassThrough() - ivals = [[np.eye(2)*units.dimensionless], [np.ones([2,2])*units.dimensionless], [np.zeros([2,2])*units.dimensionless] ] + ivals = [ + [np.eye(2) * units.dimensionless], + [np.ones([2, 2]) * units.dimensionless], + [np.zeros([2, 2]) * units.dimensionless], + ] - xv = np.eye(2)*units.dimensionless + xv = np.eye(2) * units.dimensionless # How the black box may be called using EDI - bbo = bb.BlackBox(**{'x':xv}) - bbo = bb.BlackBox({'x':xv}) - bbo = bb.BlackBox(**{'x':xv, 'optn':True}) + bbo = bb.BlackBox(**{'x': xv}) + bbo = bb.BlackBox({'x': xv}) + bbo = bb.BlackBox(**{'x': xv, 'optn': True}) # # Additional options available with parseInputs bbo = bb.BlackBox(*[xv], **{'optn1': True, 'optn2': False}) - bbo = bb.BlackBox(*[xv,True], **{'optn': False}) - bbo = bb.BlackBox({'x':[x[0] for x in ivals]}) - bbo = bb.BlackBox([{'x':x[0]} for x in ivals]) - bbo = bb.BlackBox([ [x[0]] for x in ivals]) - bbo = bb.BlackBox([ [x[0]] for x in ivals], True, optn=False) - bbo = bb.BlackBox([ [x[0]] for x in ivals], optn1=True, optn2=False) + bbo = bb.BlackBox(*[xv, True], **{'optn': False}) + bbo = bb.BlackBox({'x': [x[0] for x in ivals]}) + bbo = bb.BlackBox([{'x': x[0]} for x in ivals]) + bbo = bb.BlackBox([[x[0]] for x in ivals]) + bbo = bb.BlackBox([[x[0]] for x in ivals], True, optn=False) + bbo = bb.BlackBox([[x[0]] for x in ivals], optn1=True, optn2=False) sm = bb.summary - self.assertRaises(ValueError,bb.sanitizeInputs,*(np.ones([2,2])*units.ft,)) - + self.assertRaises(ValueError, bb.sanitizeInputs, *(np.ones([2, 2]) * units.ft,)) def test_edi_blackbox_bare_example_3(self): "Tests a black box example construction" @@ -767,58 +866,91 @@ def test_edi_blackbox_bare_example_3(self): from pyomo.environ import units import pyomo.environ as pyo from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + from pyomo.contrib.edi.blackBoxFunctionModel import ( + BlackBoxFunctionModel_Variable, + TypeCheckedList, + BBList, + BlackBoxFunctionModel, + ) class Norm_2(BlackBoxFunctionModel): def __init__(self): super().__init__() self.description = 'This model evaluates the two norm' - self.inputs.append( name = 'x', - units = '' , - description = 'The x variable' , - size = 3) - self.inputs.append( name = 'y', - units = '' , - description = 'The y variable' , - size = 2) - self.outputs.append(name = 'z', - units = '' , - description = 'The z variable') + self.inputs.append( + name='x', units='', description='The x variable', size=3 + ) + self.inputs.append( + name='y', units='', description='The y variable', size=2 + ) + self.outputs.append(name='z', units='', description='The z variable') self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, *args, **kwargs):# The actual function that does things - runCases, returnMode, remainingKwargs = self.parseInputs(*args, **kwargs) - x = [ rc['x'] for rc in runCases][0] + def BlackBox(self, *args, **kwargs): # The actual function that does things + runCases, returnMode, remainingKwargs = self.parseInputs( + *args, **kwargs + ) + + x = [rc['x'] for rc in runCases][0] x = np.array([self.pyomo_value(xval) for xval in x], dtype=np.float64) - y = [ rc['y'] for rc in runCases][0] + y = [rc['y'] for rc in runCases][0] y = np.array([self.pyomo_value(yval) for yval in y], dtype=np.float64) - z = x[0]**2 + x[1]**2 + x[2]**2 + y[0]**2 + y[1]**2 - dzdx0 = 2*x[0] # Compute dy/dx0 - dzdx1 = 2*x[1] # Compute dy/dx1 - dzdx2 = 2*x[2] # Compute dy/dx2 - dzdy0 = 2*y[0] - dzdy1 = 2*y[1] + z = x[0] ** 2 + x[1] ** 2 + x[2] ** 2 + y[0] ** 2 + y[1] ** 2 + dzdx0 = 2 * x[0] # Compute dy/dx0 + dzdx1 = 2 * x[1] # Compute dy/dx1 + dzdx2 = 2 * x[2] # Compute dy/dx2 + dzdy0 = 2 * y[0] + dzdy1 = 2 * y[1] z = z * units.dimensionless - dz = np.array([dzdx0,dzdx1,dzdx2,dzdy0,dzdy1]) * units.dimensionless + dz = np.array([dzdx0, dzdx1, dzdx2, dzdy0, dzdy1]) * units.dimensionless # dydx0 = dydx0 * units.dimensionless # dydx1 = dydx1 * units.dimensionless # dydx2 = dydx2 * units.dimensionless - return z, [dz] # return z, grad(z), hess(z)... + return z, [dz] # return z, grad(z), hess(z)... bb = Norm_2() - bbo = bb.BlackBox({'x':np.array([0,0,0])*units.dimensionless, 'y':np.array([0,0])*units.dimensionless}) - bbo = bb.BlackBox(np.array([0,0,0])*units.dimensionless, y=np.array([0,0])*units.dimensionless) - - self.assertRaises(ValueError,bb.BlackBox,*( {'er':np.array([0,0,0])*units.dimensionless, 'y':np.array([0,0])*units.dimensionless}, )) - self.assertRaises(ValueError,bb.BlackBox,*( 'err', )) - self.assertRaises(ValueError,bb.BlackBox,*( np.array([0,0,0])*units.dimensionless, np.array([0,0])*units.dimensionless ), **{'x':'err too many'}) - self.assertRaises(ValueError,bb.BlackBox,*( np.array([0,0,0])*units.dimensionless, ), **{'notY':np.array([0,0])*units.dimensionless}) + bbo = bb.BlackBox( + { + 'x': np.array([0, 0, 0]) * units.dimensionless, + 'y': np.array([0, 0]) * units.dimensionless, + } + ) + bbo = bb.BlackBox( + np.array([0, 0, 0]) * units.dimensionless, + y=np.array([0, 0]) * units.dimensionless, + ) + self.assertRaises( + ValueError, + bb.BlackBox, + *( + { + 'er': np.array([0, 0, 0]) * units.dimensionless, + 'y': np.array([0, 0]) * units.dimensionless, + }, + ) + ) + self.assertRaises(ValueError, bb.BlackBox, *('err',)) + self.assertRaises( + ValueError, + bb.BlackBox, + *( + np.array([0, 0, 0]) * units.dimensionless, + np.array([0, 0]) * units.dimensionless, + ), + **{'x': 'err too many'} + ) + self.assertRaises( + ValueError, + bb.BlackBox, + *(np.array([0, 0, 0]) * units.dimensionless,), + **{'notY': np.array([0, 0]) * units.dimensionless} + ) def test_edi_blackbox_bare_example_4(self): "Tests a black box example construction without an optimization problem" @@ -833,64 +965,70 @@ def __init__(self): # Set up all the attributes by calling Model.__init__ super().__init__() - #Setup Inputs - self.inputs.append( 'x', '', 'X Variable') - self.inputs.append( 'y', '', 'Y Variable') + # Setup Inputs + self.inputs.append('x', '', 'X Variable') + self.inputs.append('y', '', 'Y Variable') - #Setup Outputs - self.outputs.append( 'u', '', 'U Variable') - self.outputs.append( 'v', '', 'V Variable') + # Setup Outputs + self.outputs.append('u', '', 'U Variable') + self.outputs.append('v', '', 'V Variable') - #Simple model description + # Simple model description self.description = 'This model is a pass through)' self.availableDerivative = 1 - #standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) + # standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) def BlackBox(self, *args, **kwargs): runCases, returnMode, extras = self.parseInputs(*args, **kwargs) - x = [ self.pyomo_value(runCases[i]['x']) for i in range(0,len(runCases)) ] - y = [ self.pyomo_value(runCases[i]['x']) for i in range(0,len(runCases)) ] - - u = [] + x = [ + self.pyomo_value(runCases[i]['x']) for i in range(0, len(runCases)) + ] + y = [ + self.pyomo_value(runCases[i]['x']) for i in range(0, len(runCases)) + ] + + u = [] dudx = [] dudy = [] - v = [] + v = [] dvdx = [] dvdy = [] for xval in x: - u.append(xval*units.dimensionless) - dudx.append(1.0*units.dimensionless) - dudy.append(0.0*units.dimensionless) - + u.append(xval * units.dimensionless) + dudx.append(1.0 * units.dimensionless) + dudy.append(0.0 * units.dimensionless) + for yval in y: - v.append(yval*units.dimensionless) - dvdx.append(0.0*units.dimensionless) - dvdy.append(1.0*units.dimensionless) - + v.append(yval * units.dimensionless) + dvdx.append(0.0 * units.dimensionless) + dvdy.append(1.0 * units.dimensionless) + if returnMode < 0: - returnMode = -1*(returnMode + 1) + returnMode = -1 * (returnMode + 1) if returnMode == 0: return [u[0], v[0]] if returnMode == 1: - return [u[0], v[0]] , [[dudx[0], dudy[0]], [dvdx[0], dvdy[0]]] + return [u[0], v[0]], [[dudx[0], dudy[0]], [dvdx[0], dvdy[0]]] else: if returnMode == 0: opt = [] - for i in range(0,len(y)): - opt.append([ u[i], v[i] ]) + for i in range(0, len(y)): + opt.append([u[i], v[i]]) return opt if returnMode == 1: opt = [] - for i in range(0,len(y)): - opt.append([ [u[i], v[i]], [ [ dudx[i], dudy[i]], [dvdx[i], dvdy[i]] ] ]) + for i in range(0, len(y)): + opt.append( + [[u[i], v[i]], [[dudx[i], dudy[i]], [dvdx[i], dvdy[i]]]] + ) return opt bb = PassThrough() - bbo = bb.BlackBox(1.0,1.0) - bbo = bb.BlackBox({'x':np.linspace(0,10,11),'y':np.linspace(0,10,11)}) + bbo = bb.BlackBox(1.0, 1.0) + bbo = bb.BlackBox({'x': np.linspace(0, 10, 11), 'y': np.linspace(0, 10, 11)}) if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/pyomo/contrib/edi/tests/test_docSnippets.py b/pyomo/contrib/edi/tests/test_docSnippets.py index 71caa2fda77..7bb97ca6227 100644 --- a/pyomo/contrib/edi/tests/test_docSnippets.py +++ b/pyomo/contrib/edi/tests/test_docSnippets.py @@ -19,9 +19,7 @@ import pyomo.environ as pyo from pyomo.common.dependencies import attempt_import -from pyomo.core.base.units_container import ( - pint_available, -) +from pyomo.core.base.units_container import pint_available from pyomo.common.dependencies import numpy, numpy_available from pyomo.common.dependencies import scipy, scipy_available @@ -29,496 +27,488 @@ if numpy_available: import numpy as np + @unittest.skipIf(not numpy_available, 'Testing pyomo.contrib.edi requires numpy') @unittest.skipIf(not scipy_available, 'Testing pyomo.contrib.edi requires scipy') -@unittest.skipIf(not pint_available, 'Testing units requires pint') +@unittest.skipIf(not pint_available, 'Testing units requires pint') class TestEDISnippets(unittest.TestCase): def test_edi_snippet_formuation_01(self): # BEGIN: Formulation_Snippet_01 from pyomo.contrib.edi import Formulation + f = Formulation() # END: Formulation_Snippet_01 def test_edi_snippet_formuation_02(self): # BEGIN: Formulation_Snippet_02 from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = '') + x = f.Variable(name='x', guess=1.0, units='') # END: Formulation_Snippet_02 def test_edi_snippet_formuation_03(self): # BEGIN: Formulation_Snippet_03 from pyomo.contrib.edi import Formulation + f = Formulation() - c = f.Constant(name = 'c', value = 1.0, units = '') + c = f.Constant(name='c', value=1.0, units='') # END: Formulation_Snippet_03 def test_edi_snippet_formuation_04(self): # BEGIN: Formulation_Snippet_04 from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = '') - y = f.Variable(name = 'y', guess = 1.0, units = '') - c = f.Constant(name = 'c', value = 1.0, units = '') - f.Objective( c*x + y ) + x = f.Variable(name='x', guess=1.0, units='') + y = f.Variable(name='y', guess=1.0, units='') + c = f.Constant(name='c', value=1.0, units='') + f.Objective(c * x + y) # END: Formulation_Snippet_04 def test_edi_snippet_formuation_05(self): # BEGIN: Formulation_Snippet_05 from pyomo.contrib.edi import Formulation from pyomo.environ import maximize, minimize + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = '') - y = f.Variable(name = 'y', guess = 1.0, units = '') - c = f.Constant(name = 'c', value = 1.0, units = '') - f.Objective( c*x + y , sense = maximize) + x = f.Variable(name='x', guess=1.0, units='') + y = f.Variable(name='y', guess=1.0, units='') + c = f.Constant(name='c', value=1.0, units='') + f.Objective(c * x + y, sense=maximize) # END: Formulation_Snippet_05 - + def test_edi_snippet_formuation_06(self): # BEGIN: Formulation_Snippet_06 from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = '') - y = f.Variable(name = 'y', guess = 1.0, units = '') - c = f.Constant(name = 'c', value = 1.0, units = '') - f.Objective( c*x + y ) - f.Constraint( x**2 + y**2 <= 1.0 ) - f.Constraint( x >= 0) - f.Constraint( y <= 0) + x = f.Variable(name='x', guess=1.0, units='') + y = f.Variable(name='y', guess=1.0, units='') + c = f.Constant(name='c', value=1.0, units='') + f.Objective(c * x + y) + f.Constraint(x**2 + y**2 <= 1.0) + f.Constraint(x >= 0) + f.Constraint(y <= 0) # END: Formulation_Snippet_06 - + def test_edi_snippet_formuation_07(self): # BEGIN: Formulation_Snippet_07 from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = '') - y = f.Variable(name = 'y', guess = 1.0, units = '') - c = f.Constant(name = 'c', value = 1.0, units = '') - f.Objective( c*x + y ) - f.ConstraintList( - [ - x**2 + y**2 <= 1.0 , - x >= 0, - y <= 0, - ] - ) + x = f.Variable(name='x', guess=1.0, units='') + y = f.Variable(name='y', guess=1.0, units='') + c = f.Constant(name='c', value=1.0, units='') + f.Objective(c * x + y) + f.ConstraintList([x**2 + y**2 <= 1.0, x >= 0, y <= 0]) # END: Formulation_Snippet_07 - + def test_edi_snippet_formuation_08(self): # BEGIN: Formulation_Snippet_08 from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = '') - y = f.Variable(name = 'y', guess = 1.0, units = '') - c = f.Constant(name = 'c', value = 1.0, units = '') - f.Objective( c*x + y ) - - constraintList = [ - x**2 + y**2 <= 1.0 , - x >= 0, - y <= 0, - ] - - f.ConstraintList( constraintList ) + x = f.Variable(name='x', guess=1.0, units='') + y = f.Variable(name='y', guess=1.0, units='') + c = f.Constant(name='c', value=1.0, units='') + f.Objective(c * x + y) + + constraintList = [x**2 + y**2 <= 1.0, x >= 0, y <= 0] + + f.ConstraintList(constraintList) # END: Formulation_Snippet_08 - + def test_edi_snippet_formuation_09(self): # BEGIN: Formulation_Snippet_09 import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'y variable') - z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'Output var') - f.Objective( x + y ) + x = f.Variable(name='x', guess=1.0, units='m', description='x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='y variable') + z = f.Variable(name='z', guess=1.0, units='m^2', description='Output var') + f.Objective(x + y) + class UnitCircle(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super().__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append( name = 'x', - units = 'ft' , - description = 'The x variable' ) - self.inputs.append( name = 'y', - units = 'ft' , - description = 'The y variable' ) - self.outputs.append( name = 'z', - units = 'ft**2', - description = 'Output variable' ) + self.inputs.append(name='x', units='ft', description='The x variable') + self.inputs.append(name='y', units='ft', description='The y variable') + self.outputs.append( + name='z', units='ft**2', description='Output variable' + ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, x, y): # The actual function that does things + + def BlackBox(self, x, y): # The actual function that does things # Converts to correct units then casts to float - x = pyo.value(units.convert(x,self.inputs['x'].units)) - y = pyo.value(units.convert(y,self.inputs['y'].units)) - z = x**2 + y**2 # Compute z - dzdx = 2*x # Compute dz/dx - dzdy = 2*y # Compute dz/dy + x = pyo.value(units.convert(x, self.inputs['x'].units)) + y = pyo.value(units.convert(y, self.inputs['y'].units)) + z = x**2 + y**2 # Compute z + dzdx = 2 * x # Compute dz/dx + dzdy = 2 * y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... - f.Constraint( z <= 1*units.m**2 ) + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + + f.Constraint(z <= 1 * units.m**2) - f.RuntimeConstraint( z, '==', [x,y], UnitCircle() ) + f.RuntimeConstraint(z, '==', [x, y], UnitCircle()) # END: Formulation_Snippet_09 - + ### This will fail validatation, but should construct appropriately # BEGIN: Formulation_Snippet_10 - f.RuntimeConstraint( *( z, '==', [x,y], UnitCircle() ) ) + f.RuntimeConstraint(*(z, '==', [x, y], UnitCircle())) # END: Formulation_Snippet_10 - + # BEGIN: Formulation_Snippet_11 - f.RuntimeConstraint( *[ z, '==', [x,y], UnitCircle() ] ) + f.RuntimeConstraint(*[z, '==', [x, y], UnitCircle()]) # END: Formulation_Snippet_11 - + # BEGIN: Formulation_Snippet_12 - f.RuntimeConstraint( **{ 'outputs' : z, - 'operators' : '==', - 'inputs' : [x,y], - 'black_box' : UnitCircle() } ) + f.RuntimeConstraint( + **{ + 'outputs': z, + 'operators': '==', + 'inputs': [x, y], + 'black_box': UnitCircle(), + } + ) # END: Formulation_Snippet_12 - + # BEGIN: Formulation_Snippet_13 - f.RuntimeConstraint( *( [z], ['=='], [x,y], UnitCircle() ) ) + f.RuntimeConstraint(*([z], ['=='], [x, y], UnitCircle())) # END: Formulation_Snippet_13 - + def test_edi_snippet_formuation_14(self): # BEGIN: Formulation_Snippet_14 import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'y variable') - z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'Output var') - f.Objective( x + y ) + x = f.Variable(name='x', guess=1.0, units='m', description='x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='y variable') + z = f.Variable(name='z', guess=1.0, units='m^2', description='Output var') + f.Objective(x + y) + class UnitCircle(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super().__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append( name = 'x', - units = 'ft' , - description = 'The x variable' ) - self.inputs.append( name = 'y', - units = 'ft' , - description = 'The y variable' ) - self.outputs.append( name = 'z', - units = 'ft**2', - description = 'Output variable' ) + self.inputs.append(name='x', units='ft', description='The x variable') + self.inputs.append(name='y', units='ft', description='The y variable') + self.outputs.append( + name='z', units='ft**2', description='Output variable' + ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, x, y): # The actual function that does things + + def BlackBox(self, x, y): # The actual function that does things # Converts to correct units then casts to float - x = pyo.value(units.convert(x,self.inputs[0].units)) - y = pyo.value(units.convert(y,self.inputs[1].units)) - z = x**2 + y**2 # Compute z - dzdx = 2*x # Compute dz/dx - dzdy = 2*y # Compute dz/dy + x = pyo.value(units.convert(x, self.inputs[0].units)) + y = pyo.value(units.convert(y, self.inputs[1].units)) + z = x**2 + y**2 # Compute z + dzdx = 2 * x # Compute dz/dx + dzdy = 2 * y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... - f.ConstraintList( - [ - z <= 1*units.m**2 , - [ z, '==', [x,y], UnitCircle() ] , - ] - ) + f.ConstraintList([z <= 1 * units.m**2, [z, '==', [x, y], UnitCircle()]]) # END: Formulation_Snippet_14 ### This will fail validatation, but should construct appropriately # BEGIN: Formulation_Snippet_15 - f.ConstraintList( - [ - z <= 1*units.m**2 , - ( z, '==', [x,y], UnitCircle() ) , - ] - ) + f.ConstraintList([z <= 1 * units.m**2, (z, '==', [x, y], UnitCircle())]) # END: Formulation_Snippet_15 - + # BEGIN: Formulation_Snippet_16 - f.ConstraintList( - [ - z <= 1*units.m**2 , - [ z, '==', [x,y], UnitCircle() ] , - ] - ) + f.ConstraintList([z <= 1 * units.m**2, [z, '==', [x, y], UnitCircle()]]) # END: Formulation_Snippet_16 - + # BEGIN: Formulation_Snippet_17 f.ConstraintList( [ - z <= 1*units.m**2 , - { 'outputs' : z, - 'operators' : '==', - 'inputs' : [x,y], - 'black_box' : UnitCircle() } , + z <= 1 * units.m**2, + { + 'outputs': z, + 'operators': '==', + 'inputs': [x, y], + 'black_box': UnitCircle(), + }, ] ) # END: Formulation_Snippet_17 - + # BEGIN: Formulation_Snippet_18 - f.ConstraintList( - [ - z <= 1*units.m**2 , - ( [z], ['=='], [x,y], UnitCircle() ) , - ] - ) + f.ConstraintList([z <= 1 * units.m**2, ([z], ['=='], [x, y], UnitCircle())]) # END: Formulation_Snippet_18 def test_edi_snippet_variables_01(self): # BEGIN: Variables_Snippet_01 from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') # END: Variables_Snippet_01 - + def test_edi_snippet_variables_02(self): # BEGIN: Variables_Snippet_02 from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Variable('x', 1.0, 'm' ) + x = f.Variable('x', 1.0, 'm') # END: Variables_Snippet_02 - + def test_edi_snippet_variables_03(self): # BEGIN: Variables_Snippet_03 from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Variable( name = 'x', - guess = 1.0, - units = 'm' , - description = 'The x variable', - bounds = [-10,10] ) + x = f.Variable( + name='x', + guess=1.0, + units='m', + description='The x variable', + bounds=[-10, 10], + ) # END: Variables_Snippet_03 - + def test_edi_snippet_variables_04(self): # BEGIN: Variables_Snippet_04 from pyomo.contrib.edi import Formulation from pyomo.environ import Integers + f = Formulation() - x = f.Variable( name = 'x', - guess = 1.0, - units = 'm' , - description = 'The x variable', - domain = Integers ) + x = f.Variable( + name='x', + guess=1.0, + units='m', + description='The x variable', + domain=Integers, + ) # END: Variables_Snippet_04 - + def test_edi_snippet_variables_05(self): # BEGIN: Variables_Snippet_05 from pyomo.environ import units from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Variable( name = 'x', - guess = 1.0, - units = units.m , - description = 'The x variable' ) + x = f.Variable(name='x', guess=1.0, units=units.m, description='The x variable') # END: Variables_Snippet_05 - + def test_edi_snippet_variables_06(self): # BEGIN: Variables_Snippet_06 from pyomo.environ import units from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Variable( name = 'x', - guess = 1.0, - units = 'm' , - description = 'The x variable', - size = 5 ) + x = f.Variable( + name='x', guess=1.0, units='m', description='The x variable', size=5 + ) # END: Variables_Snippet_06 - + def test_edi_snippet_variables_07(self): # BEGIN: Variables_Snippet_07 from pyomo.environ import units from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Variable( name = 'x', - guess = 1.0, - units = 'm' , - description = 'The x variable', - size = [10,2] ) + x = f.Variable( + name='x', guess=1.0, units='m', description='The x variable', size=[10, 2] + ) # END: Variables_Snippet_07 - + def test_edi_snippet_variables_08(self): # BEGIN: Variables_Snippet_08 from pyomo.environ import units from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Variable( name = 'x', - guess = 1.0, - units = 'kg*m/s**2' , - description = 'The x variable' ) + x = f.Variable( + name='x', guess=1.0, units='kg*m/s**2', description='The x variable' + ) # END: Variables_Snippet_08 def test_edi_snippet_constants_01(self): # BEGIN: Constants_Snippet_01 from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Constant(name = 'c', value = 1.0, units = 'm' , description = 'A constant c') + x = f.Constant(name='c', value=1.0, units='m', description='A constant c') # END: Constants_Snippet_01 def test_edi_snippet_constants_02(self): # BEGIN: Constants_Snippet_02 from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Constant('c', 1.0, 'm' ) + x = f.Constant('c', 1.0, 'm') # END: Constants_Snippet_02 def test_edi_snippet_constants_03(self): # BEGIN: Constants_Snippet_03 from pyomo.environ import units from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Constant( name = 'c', - value = 1.0, - units = units.m , - description = 'A constant c' ) + x = f.Constant(name='c', value=1.0, units=units.m, description='A constant c') # END: Constants_Snippet_03 def test_edi_snippet_constants_04(self): # BEGIN: Constants_Snippet_04 from pyomo.environ import units from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Constant( name = 'c', - value = 1.0, - units = 'm' , - description = 'A constant c', - size = 5 ) + x = f.Constant( + name='c', value=1.0, units='m', description='A constant c', size=5 + ) # END: Constants_Snippet_04 def test_edi_snippet_constants_05(self): # BEGIN: Constants_Snippet_05 from pyomo.environ import units from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Constant( name = 'c', - value = 1.0, - units = 'm' , - description = 'A constant c', - size = [10,2] ) + x = f.Constant( + name='c', value=1.0, units='m', description='A constant c', size=[10, 2] + ) # END: Constants_Snippet_05 def test_edi_snippet_constants_06(self): # BEGIN: Constants_Snippet_06 from pyomo.environ import units from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Constant( name = 'c', - value = 1.0, - units = 'kg*m/s**2' , - description = 'A constant c' ) + x = f.Constant( + name='c', value=1.0, units='kg*m/s**2', description='A constant c' + ) # END: Constants_Snippet_06 def test_edi_snippet_objectives_01(self): # BEGIN: Objectives_Snippet_01 from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) - f.Objective( c*x + y ) # Default is minimize + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + c = f.Constant(name='c', value=1.0, units='', description='A constant c') + f.Objective(c * x + y) # Default is minimize # END: Objectives_Snippet_01 def test_edi_snippet_objectives_02(self): # BEGIN: Objectives_Snippet_02 from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) - f.Objective( c*x**4 + y**4 ) # Default is minimize + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + c = f.Constant(name='c', value=1.0, units='', description='A constant c') + f.Objective(c * x**4 + y**4) # Default is minimize # END: Objectives_Snippet_02 def test_edi_snippet_objectives_03(self): # BEGIN: Objectives_Snippet_03 from pyomo.contrib.edi import Formulation from pyomo.environ import minimize, maximize + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) - f.Objective( c*x**4 + y**4 , sense = minimize) + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + c = f.Constant(name='c', value=1.0, units='', description='A constant c') + f.Objective(c * x**4 + y**4, sense=minimize) # END: Objectives_Snippet_03 def test_edi_snippet_objectives_04(self): # BEGIN: Objectives_Snippet_04 from pyomo.contrib.edi import Formulation from pyomo.environ import minimize, maximize + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) - f.Objective( c*x**4 + y**4 , sense = 1 ) # 1 corresponds to minimize + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + c = f.Constant(name='c', value=1.0, units='', description='A constant c') + f.Objective(c * x**4 + y**4, sense=1) # 1 corresponds to minimize # END: Objectives_Snippet_04 def test_edi_snippet_objectives_05(self): # BEGIN: Objectives_Snippet_05 from pyomo.contrib.edi import Formulation from pyomo.environ import minimize, maximize + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) - f.Objective( -c*x**4 - y**4 , sense = maximize) + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + c = f.Constant(name='c', value=1.0, units='', description='A constant c') + f.Objective(-c * x**4 - y**4, sense=maximize) # END: Objectives_Snippet_05 def test_edi_snippet_objectives_06(self): # BEGIN: Objectives_Snippet_06 from pyomo.contrib.edi import Formulation from pyomo.environ import minimize, maximize + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) - f.Objective( -c*x**4 - y**4 , sense = -1 ) # -1 corresponds to maximize + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + c = f.Constant(name='c', value=1.0, units='', description='A constant c') + f.Objective(-c * x**4 - y**4, sense=-1) # -1 corresponds to maximize # END: Objectives_Snippet_06 def test_edi_snippet_objectives_07(self): # BEGIN: Objectives_Snippet_07 from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Variable( name = 'x', - guess = 1.0, - units = 'm' , - description = 'The x variable', - bounds = [0,100] , - size = 3 ) - y = f.Variable( name = 'y', - guess = 1.0, - units = 'm' , - description = 'The y variable', - size = [2,2]) - c = f.Constant( name = 'c', - value = 1.0, - units = '' , - description = 'A constant c' , - size = 3 ) - f.Objective( c[0]*x[0] + c[1]*x[1] + c[2]*x[2] - + y[0,0]**4 - + y[0,1]**4 - + y[1,0]**4 - + y[1,1]**4 ) # Default is minimize + x = f.Variable( + name='x', + guess=1.0, + units='m', + description='The x variable', + bounds=[0, 100], + size=3, + ) + y = f.Variable( + name='y', guess=1.0, units='m', description='The y variable', size=[2, 2] + ) + c = f.Constant( + name='c', value=1.0, units='', description='A constant c', size=3 + ) + f.Objective( + c[0] * x[0] + + c[1] * x[1] + + c[2] * x[2] + + y[0, 0] ** 4 + + y[0, 1] ** 4 + + y[1, 0] ** 4 + + y[1, 1] ** 4 + ) # Default is minimize # END: Objectives_Snippet_07 def test_edi_snippet_constraints_01(self): # BEGIN: Constraints_Snippet_01 from pyomo.environ import units from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) - f.Objective( c*x + y ) + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + c = f.Constant(name='c', value=1.0, units='', description='A constant c') + f.Objective(c * x + y) f.ConstraintList( - [ - x**2 + y**2 <= 1.0*units.m**2 , - x <= 0.75*units.m, - x >= y , - ] + [x**2 + y**2 <= 1.0 * units.m**2, x <= 0.75 * units.m, x >= y] ) # END: Constraints_Snippet_01 @@ -526,68 +516,58 @@ def test_edi_snippet_constraints_02(self): # BEGIN: Constraints_Snippet_02 from pyomo.environ import units from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) - f.Objective( c*x + y ) - f.Constraint( x**2 + y**2 <= 1.0*units.m**2 ) - f.Constraint( x <= 0.75*units.m ) - f.Constraint( x >= y ) + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + c = f.Constant(name='c', value=1.0, units='', description='A constant c') + f.Objective(c * x + y) + f.Constraint(x**2 + y**2 <= 1.0 * units.m**2) + f.Constraint(x <= 0.75 * units.m) + f.Constraint(x >= y) # END: Constraints_Snippet_02 def test_edi_snippet_constraints_03(self): # BEGIN: Constraints_Snippet_03 from pyomo.environ import units from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Variable( name = 'x', - guess = 1.0, - units = 'm' , - description = 'The x variable', - bounds = [0,100] , - size = 3 ) - y = f.Variable( name = 'y', - guess = 1.0, - units = 'm' , - description = 'The y variable', - size = [2,2]) - c = f.Constant( name = 'c', - value = 1.0, - units = '' , - description = 'A constant c' , - size = 3 ) - f.Objective( c[0]*x[0] + c[1]*x[1] + c[2]*x[2] - + y[0,0]**4 - + y[0,1]**4 - + y[1,0]**4 - + y[1,1]**4 ) # Default is minimize + x = f.Variable( + name='x', + guess=1.0, + units='m', + description='The x variable', + bounds=[0, 100], + size=3, + ) + y = f.Variable( + name='y', guess=1.0, units='m', description='The y variable', size=[2, 2] + ) + c = f.Constant( + name='c', value=1.0, units='', description='A constant c', size=3 + ) + f.Objective( + c[0] * x[0] + + c[1] * x[1] + + c[2] * x[2] + + y[0, 0] ** 4 + + y[0, 1] ** 4 + + y[1, 0] ** 4 + + y[1, 1] ** 4 + ) # Default is minimize f.ConstraintList( [ - x[0]**2 + x[1]**2 + x[2]**2 <= 1.0*units.m, - y[0,0] >= 1.0*units.m, - y[0,1] >= 1.0*units.m, - y[1,0] >= 1.0*units.m, - y[1,1] >= 1.0*units.m, - x[0] >= y[0,0] , + x[0] ** 2 + x[1] ** 2 + x[2] ** 2 <= 1.0 * units.m, + y[0, 0] >= 1.0 * units.m, + y[0, 1] >= 1.0 * units.m, + y[1, 0] >= 1.0 * units.m, + y[1, 1] >= 1.0 * units.m, + x[0] >= y[0, 0], ] ) # END: Constraints_Snippet_03 - - - - - - - - - - - - - - def test_edi_snippet_runtimeconstraints_01(self): # BEGIN: RuntimeConstraints_Snippet_01 import pyomo.environ as pyo @@ -595,8 +575,7 @@ def test_edi_snippet_runtimeconstraints_01(self): from pyomo.contrib.edi import BlackBoxFunctionModel class Parabola(BlackBoxFunctionModel): - def __init__(self): - + def __init__(self): # Call parent init super().__init__() @@ -604,53 +583,48 @@ def __init__(self): self.description = 'This model evaluates the function: y = x**2' # Append the model inputs - self.inputs.append( - name='x', units='ft' , description='The x variable' - ) + self.inputs.append(name='x', units='ft', description='The x variable') # Append the model outputs self.outputs.append( - name='y', units='ft**2' , description='The y variable' + name='y', units='ft**2', description='The y variable' ) - + # Set the highest available derivative # Should be 1 for most cases but defaults to 0 self.availableDerivative = 1 - def BlackBox(self, x): # The actual function that does things - + def BlackBox(self, x): # The actual function that does things # Convert to correct units and cast to a float - x = pyo.value(units.convert(x,self.inputs['x'].units)) + x = pyo.value(units.convert(x, self.inputs['x'].units)) # Compute y y = x**2 # Compute dy/dx - dydx = 2*x - + dydx = 2 * x + # Add the units to the output y = y * self.outputs['y'].units - + # Add the units to the derivative for output dydx = dydx * self.outputs['y'].units / self.inputs['x'].units - + # Return using the output packing guidelines described in the documentation: # returnVal[0] = output_value # returnVal[1] = jacobian # returnVal[1][0] = derivative_scalarOutput_wrt_0th_input - return y, [dydx] - # END: RuntimeConstraints_Snippet_01 + return y, [dydx] + # END: RuntimeConstraints_Snippet_01 def test_edi_snippet_runtimeconstraints_02(self): - import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import BlackBoxFunctionModel class Parabola(BlackBoxFunctionModel): - def __init__(self): - + def __init__(self): # BEGIN: RuntimeConstraints_Snippet_02 # Call parent init super().__init__() @@ -660,104 +634,95 @@ def __init__(self): self.description = 'This model evaluates the function: y = x**2' # Append the model inputs - self.inputs.append( - name='x', units='ft' , description='The x variable' - ) + self.inputs.append(name='x', units='ft', description='The x variable') # Append the model outputs self.outputs.append( - name='y', units='ft**2' , description='The y variable' + name='y', units='ft**2', description='The y variable' ) - + # BEGIN: RuntimeConstraints_Snippet_05 # Set the highest available derivative # Should be 1 for most cases but defaults to 0 self.availableDerivative = 1 # END: RuntimeConstraints_Snippet_05 - def BlackBox(self, x): # The actual function that does things - + def BlackBox(self, x): # The actual function that does things storeX = x # BEGIN: RuntimeConstraints_Snippet_06 - x = units.convert(x,self.inputs['x'].units) + x = units.convert(x, self.inputs['x'].units) # END: RuntimeConstraints_Snippet_06 x = storeX # BEGIN: RuntimeConstraints_Snippet_07 # Convert to correct units and cast to a float - x = pyo.value(units.convert(x,self.inputs['x'].units)) + x = pyo.value(units.convert(x, self.inputs['x'].units)) # END: RuntimeConstraints_Snippet_07 # Compute y y = x**2 # Compute dy/dx - dydx = 2*x - + dydx = 2 * x + # BEGIN: RuntimeConstraints_Snippet_08 # Add the units to the output y = y * self.outputs['y'].units - + # Add the units to the derivative for output dydx = dydx * self.outputs['y'].units / self.inputs['x'].units # END: RuntimeConstraints_Snippet_08 - + # BEGIN: RuntimeConstraints_Snippet_09 # Return using the output packing guidelines described in the documentation: # returnVal[0] = output_value # returnVal[1] = jacobian # returnVal[1][0] = derivative_scalarOutput_wrt_0th_input - return y, [dydx] + return y, [dydx] # END: RuntimeConstraints_Snippet_09 def test_edi_snippet_runtimeconstraints_03(self): import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The z variable') + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + z = f.Variable(name='z', guess=1.0, units='m^2', description='The z variable') - f.Objective( x + y ) + f.Objective(x + y) class UnitCircle(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super().__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' # BEGIN: RuntimeConstraints_Snippet_03 - self.inputs.append( name = 'x', - units = 'ft' , - description = 'The x variable' ) - self.inputs.append( name = 'y', - units = 'ft' , - description = 'The y variable' ) + self.inputs.append(name='x', units='ft', description='The x variable') + self.inputs.append(name='y', units='ft', description='The y variable') # END: RuntimeConstraints_Snippet_03 - self.outputs.append( name = 'z', - units = 'ft**2', - description = 'Resultant of the unit circle evaluation' ) + self.outputs.append( + name='z', + units='ft**2', + description='Resultant of the unit circle evaluation', + ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, x, y): # The actual function that does things - x = pyo.value(units.convert(x,self.inputs['x'].units)) - y = pyo.value(units.convert(y,self.inputs['y'].units)) + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value(units.convert(x, self.inputs['x'].units)) + y = pyo.value(units.convert(y, self.inputs['y'].units)) z = x**2 + y**2 - dzdx = 2*x - dzdy = 2*y + dzdx = 2 * x + dzdy = 2 * y z = z * self.outputs['z'].units dzdx = dzdx * self.outputs['z'].units / self.inputs['x'].units dzdy = dzdy * self.outputs['z'].units / self.inputs['y'].units - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... - f.ConstraintList( - [ - (z, '==', [x,y], UnitCircle() ) , - z <= 1*units.m**2 - ] - ) + f.ConstraintList([(z, '==', [x, y], UnitCircle()), z <= 1 * units.m**2]) def test_edi_snippet_runtimeconstraints_04(self): import numpy as np @@ -771,139 +736,113 @@ def __init__(self): # Set up all the attributes by calling Model.__init__ super().__init__() - #Setup Inputs - self.inputs.append( 'x', '', 'X Variable') - self.inputs.append( 'y', '', 'Y Variable') + # Setup Inputs + self.inputs.append('x', '', 'X Variable') + self.inputs.append('y', '', 'Y Variable') # BEGIN: RuntimeConstraints_Snippet_04 - #Setup Outputs - self.outputs.append( 'u', '', 'U Variable') - self.outputs.append( 'v', '', 'V Variable') + # Setup Outputs + self.outputs.append('u', '', 'U Variable') + self.outputs.append('v', '', 'V Variable') # END: RuntimeConstraints_Snippet_04 - #Simple model description + # Simple model description self.description = 'This model is a pass through)' self.availableDerivative = 1 - #standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) + # standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) def BlackBox(self, *args, **kwargs): runCases, returnMode, extras = self.parseInputs(*args, **kwargs) - x = [ self.pyomo_value(runCases[i]['x']) for i in range(0,len(runCases)) ] - y = [ self.pyomo_value(runCases[i]['x']) for i in range(0,len(runCases)) ] - - u = [] + x = [ + self.pyomo_value(runCases[i]['x']) for i in range(0, len(runCases)) + ] + y = [ + self.pyomo_value(runCases[i]['x']) for i in range(0, len(runCases)) + ] + + u = [] dudx = [] dudy = [] - v = [] + v = [] dvdx = [] dvdy = [] for xval in x: - u.append(xval*units.dimensionless) - dudx.append(1.0*units.dimensionless) - dudy.append(0.0*units.dimensionless) - + u.append(xval * units.dimensionless) + dudx.append(1.0 * units.dimensionless) + dudy.append(0.0 * units.dimensionless) + for yval in y: - v.append(yval*units.dimensionless) - dvdx.append(0.0*units.dimensionless) - dvdy.append(1.0*units.dimensionless) - + v.append(yval * units.dimensionless) + dvdx.append(0.0 * units.dimensionless) + dvdy.append(1.0 * units.dimensionless) + if returnMode < 0: - returnMode = -1*(returnMode + 1) + returnMode = -1 * (returnMode + 1) if returnMode == 0: return [u[0], v[0]] if returnMode == 1: - return [u[0], v[0]] , [[dudx[0], dudy[0]], [dvdx[0], dvdy[0]]] + return [u[0], v[0]], [[dudx[0], dudy[0]], [dvdx[0], dvdy[0]]] else: if returnMode == 0: opt = [] - for i in range(0,len(y)): - opt.append([ u[i], v[i] ]) + for i in range(0, len(y)): + opt.append([u[i], v[i]]) return opt if returnMode == 1: opt = [] - for i in range(0,len(y)): - opt.append([ [u[i], v[i]], [ [ dudx[i], dudy[i]], [dvdx[i], dvdy[i]] ] ]) + for i in range(0, len(y)): + opt.append( + [[u[i], v[i]], [[dudx[i], dudy[i]], [dvdx[i], dvdy[i]]]] + ) return opt bb = PassThrough() - bbo = bb.BlackBox(1.0,1.0) - bbo = bb.BlackBox({'x':np.linspace(0,10,11),'y':np.linspace(0,10,11)}) + bbo = bb.BlackBox(1.0, 1.0) + bbo = bb.BlackBox({'x': np.linspace(0, 10, 11), 'y': np.linspace(0, 10, 11)}) def test_edi_snippet_runtimeconstraints_10(self): # BEGIN: RuntimeConstraints_Snippet_10 import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The z variable') + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + z = f.Variable(name='z', guess=1.0, units='m^2', description='The z variable') - f.Objective( x + y ) + f.Objective(x + y) class UnitCircle(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super().__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append( name = 'x', - units = 'ft' , - description = 'The x variable' ) - self.inputs.append( name = 'y', - units = 'ft' , - description = 'The y variable' ) - self.outputs.append( name = 'z', - units = 'ft**2', - description = 'Resultant of the unit circle evaluation' ) + self.inputs.append(name='x', units='ft', description='The x variable') + self.inputs.append(name='y', units='ft', description='The y variable') + self.outputs.append( + name='z', + units='ft**2', + description='Resultant of the unit circle evaluation', + ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, x, y): # The actual function that does things - x = pyo.value(units.convert(x,self.inputs['x'].units)) - y = pyo.value(units.convert(y,self.inputs['y'].units)) + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value(units.convert(x, self.inputs['x'].units)) + y = pyo.value(units.convert(y, self.inputs['y'].units)) z = x**2 + y**2 - dzdx = 2*x - dzdy = 2*y + dzdx = 2 * x + dzdy = 2 * y z = z * self.outputs['z'].units dzdx = dzdx * self.outputs['z'].units / self.inputs['x'].units dzdy = dzdy * self.outputs['z'].units / self.inputs['y'].units - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... - f.ConstraintList( - [ - (z, '==', [x,y], UnitCircle() ) , - z <= 1*units.m**2 - ] - ) + f.ConstraintList([(z, '==', [x, y], UnitCircle()), z <= 1 * units.m**2]) # END: RuntimeConstraints_Snippet_10 - - - - - - - - - - - - - - - - - - - - - - - - - - def test_edi_snippet_advancedRTC_01(self): # BEGIN: AdvancedRTC_Snippet_01 import numpy as np @@ -915,36 +854,39 @@ class SignomialTest(BlackBoxFunctionModel): def __init__(self): # Set up all the attributes by calling Model.__init__ super().__init__() - - #Setup Inputs - self.inputs.append( 'x', '', 'Independent Variable') - - #Setup Outputs - self.outputs.append( 'y', '', 'Dependent Variable') - - #Simple model description - self.description = ( 'This model evaluates the '+ - 'function: max([-6*x-6, x**4-3*x**2])' ) - + + # Setup Inputs + self.inputs.append('x', '', 'Independent Variable') + + # Setup Outputs + self.outputs.append('y', '', 'Dependent Variable') + + # Simple model description + self.description = ( + 'This model evaluates the ' + 'function: max([-6*x-6, x**4-3*x**2])' + ) + self.availableDerivative = 1 - + def BlackBox(self, *args, **kwargs): runCases, returnMode, extras = self.parseInputs(*args, **kwargs) - x = np.array([ pyo.value(runCases[i]['x']) for i in range(0,len(runCases)) ]) - - y = np.maximum(-6*x-6, x**4-3*x**2) - dydx = 4*x**3 - 6*x - ddy_ddx = 12*x**2 - 6 - gradientSwitch = -6*x-6 > x**4-3*x**2 + x = np.array( + [pyo.value(runCases[i]['x']) for i in range(0, len(runCases))] + ) + + y = np.maximum(-6 * x - 6, x**4 - 3 * x**2) + dydx = 4 * x**3 - 6 * x + ddy_ddx = 12 * x**2 - 6 + gradientSwitch = -6 * x - 6 > x**4 - 3 * x**2 dydx[gradientSwitch] = -6 ddy_ddx[gradientSwitch] = 0 - - y = [ yval*units.dimensionless for yval in y ] - dydx = [dydx[i] * units.dimensionless for i in range(0,len(dydx))] - + + y = [yval * units.dimensionless for yval in y] + dydx = [dydx[i] * units.dimensionless for i in range(0, len(dydx))] + if returnMode < 0: - returnMode = -1*(returnMode + 1) + returnMode = -1 * (returnMode + 1) if returnMode == 0: return y[0] if returnMode == 1: @@ -952,34 +894,33 @@ def BlackBox(self, *args, **kwargs): else: if returnMode == 0: opt = [] - for i in range(0,len(y)): - opt.append([ y[i] ]) + for i in range(0, len(y)): + opt.append([y[i]]) return opt if returnMode == 1: opt = [] - for i in range(0,len(y)): - opt.append([ [y[i]], [ [[dydx[i]]] ] ]) + for i in range(0, len(y)): + opt.append([[y[i]], [[[dydx[i]]]]]) return opt s = SignomialTest() - ivals = [[x] for x in np.linspace(-2,2,11)] + ivals = [[x] for x in np.linspace(-2, 2, 11)] # How the black box may be called using EDI - bbo = s.BlackBox(**{'x':0.5}) - bbo = s.BlackBox({'x':0.5}) - bbo = s.BlackBox(**{'x':0.5, 'optn':True}) + bbo = s.BlackBox(**{'x': 0.5}) + bbo = s.BlackBox({'x': 0.5}) + bbo = s.BlackBox(**{'x': 0.5, 'optn': True}) # Additional options available with parseInputs bbo = s.BlackBox(*[0.5], **{'optn1': True, 'optn2': False}) - bbo = s.BlackBox(*[0.5,True], **{'optn': False}) - bbo = s.BlackBox({'x':[x for x in np.linspace(-2,2,11)]}) - bbo = s.BlackBox([{'x':x} for x in np.linspace(-2,2,11)]) - bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)]) - bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)], True, optn=False) - bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)], optn1=True, optn2=False) + bbo = s.BlackBox(*[0.5, True], **{'optn': False}) + bbo = s.BlackBox({'x': [x for x in np.linspace(-2, 2, 11)]}) + bbo = s.BlackBox([{'x': x} for x in np.linspace(-2, 2, 11)]) + bbo = s.BlackBox([[x] for x in np.linspace(-2, 2, 11)]) + bbo = s.BlackBox([[x] for x in np.linspace(-2, 2, 11)], True, optn=False) + bbo = s.BlackBox([[x] for x in np.linspace(-2, 2, 11)], optn1=True, optn2=False) # END: AdvancedRTC_Snippet_01 - def test_edi_snippet_advancedRTC_02(self): import numpy as np import pyomo.environ as pyo @@ -990,38 +931,41 @@ class SignomialTest(BlackBoxFunctionModel): def __init__(self): # Set up all the attributes by calling Model.__init__ super().__init__() - - #Setup Inputs - self.inputs.append( 'x', '', 'Independent Variable') - - #Setup Outputs - self.outputs.append( 'y', '', 'Dependent Variable') - - #Simple model description - self.description = ( 'This model evaluates the '+ - 'function: max([-6*x-6, x**4-3*x**2])' ) - + + # Setup Inputs + self.inputs.append('x', '', 'Independent Variable') + + # Setup Outputs + self.outputs.append('y', '', 'Dependent Variable') + + # Simple model description + self.description = ( + 'This model evaluates the ' + 'function: max([-6*x-6, x**4-3*x**2])' + ) + self.availableDerivative = 1 # BEGIN: AdvancedRTC_Snippet_02 def BlackBox(self, *args, **kwargs): runCases, returnMode, extras = self.parseInputs(*args, **kwargs) - # END: AdvancedRTC_Snippet_02 - - x = np.array([ pyo.value(runCases[i]['x']) for i in range(0,len(runCases)) ]) - - y = np.maximum(-6*x-6, x**4-3*x**2) - dydx = 4*x**3 - 6*x - ddy_ddx = 12*x**2 - 6 - gradientSwitch = -6*x-6 > x**4-3*x**2 + # END: AdvancedRTC_Snippet_02 + + x = np.array( + [pyo.value(runCases[i]['x']) for i in range(0, len(runCases))] + ) + + y = np.maximum(-6 * x - 6, x**4 - 3 * x**2) + dydx = 4 * x**3 - 6 * x + ddy_ddx = 12 * x**2 - 6 + gradientSwitch = -6 * x - 6 > x**4 - 3 * x**2 dydx[gradientSwitch] = -6 ddy_ddx[gradientSwitch] = 0 - - y = [ yval*units.dimensionless for yval in y ] - dydx = [dydx[i] * units.dimensionless for i in range(0,len(dydx))] - + + y = [yval * units.dimensionless for yval in y] + dydx = [dydx[i] * units.dimensionless for i in range(0, len(dydx))] + if returnMode < 0: - returnMode = -1*(returnMode + 1) + returnMode = -1 * (returnMode + 1) if returnMode == 0: return y[0] if returnMode == 1: @@ -1029,36 +973,32 @@ def BlackBox(self, *args, **kwargs): else: if returnMode == 0: opt = [] - for i in range(0,len(y)): - opt.append([ y[i] ]) + for i in range(0, len(y)): + opt.append([y[i]]) return opt if returnMode == 1: opt = [] - for i in range(0,len(y)): - opt.append([ [y[i]], [ [[dydx[i]]] ] ]) + for i in range(0, len(y)): + opt.append([[y[i]], [[[dydx[i]]]]]) return opt s = SignomialTest() - ivals = [[x] for x in np.linspace(-2,2,11)] + ivals = [[x] for x in np.linspace(-2, 2, 11)] # How the black box may be called using EDI - bbo = s.BlackBox(**{'x':0.5}) - bbo = s.BlackBox({'x':0.5}) - bbo = s.BlackBox(**{'x':0.5, 'optn':True}) + bbo = s.BlackBox(**{'x': 0.5}) + bbo = s.BlackBox({'x': 0.5}) + bbo = s.BlackBox(**{'x': 0.5, 'optn': True}) # Additional options available with parseInputs bbo = s.BlackBox(*[0.5], **{'optn1': True, 'optn2': False}) - bbo = s.BlackBox(*[0.5,True], **{'optn': False}) - bbo = s.BlackBox({'x':[x for x in np.linspace(-2,2,11)]}) - bbo = s.BlackBox([{'x':x} for x in np.linspace(-2,2,11)]) - bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)]) - bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)], True, optn=False) - bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)], optn1=True, optn2=False) - - - - + bbo = s.BlackBox(*[0.5, True], **{'optn': False}) + bbo = s.BlackBox({'x': [x for x in np.linspace(-2, 2, 11)]}) + bbo = s.BlackBox([{'x': x} for x in np.linspace(-2, 2, 11)]) + bbo = s.BlackBox([[x] for x in np.linspace(-2, 2, 11)]) + bbo = s.BlackBox([[x] for x in np.linspace(-2, 2, 11)], True, optn=False) + bbo = s.BlackBox([[x] for x in np.linspace(-2, 2, 11)], optn1=True, optn2=False) if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/pyomo/contrib/edi/tests/test_examples.py b/pyomo/contrib/edi/tests/test_examples.py index 4f8d11f1c76..bb59eae7851 100644 --- a/pyomo/contrib/edi/tests/test_examples.py +++ b/pyomo/contrib/edi/tests/test_examples.py @@ -19,42 +19,40 @@ import pyomo.environ as pyo from pyomo.common.dependencies import attempt_import import importlib + testIndex = 0 -from pyomo.core.base.units_container import ( - pint_available, -) +from pyomo.core.base.units_container import pint_available from pyomo.common.dependencies import numpy, numpy_available from pyomo.common.dependencies import scipy, scipy_available + @unittest.skipIf(not numpy_available, 'Testing pyomo.contrib.edi requires numpy') @unittest.skipIf(not scipy_available, 'Testing pyomo.contrib.edi requires scipy') -@unittest.skipIf(not pint_available, 'Testing units requires pint') +@unittest.skipIf(not pint_available, 'Testing units requires pint') class EDIExamples(unittest.TestCase): def test_edi_example_placeholder(self): "A placeholder" pass + def create_new(filename): def t_function(self): importName = filename[0:-3] # filename = ".."+filename try: - importlib.import_module("pyomo.contrib.edi.examples."+importName) + importlib.import_module("pyomo.contrib.edi.examples." + importName) except: - self.fail("This example is failing: %s"%(filename)) - return t_function + self.fail("This example is failing: %s" % (filename)) + return t_function -pythonFileList = [ - "readme_example.py", - "aircraft_gp.py" -] +pythonFileList = ["readme_example.py", "aircraft_gp.py"] for filename in pythonFileList: - testName = 'test_DocumentationExample_%d'%(testIndex) + testName = 'test_DocumentationExample_%d' % (testIndex) testIndex += 1 t_Function = create_new(filename) if pint_available: @@ -63,5 +61,3 @@ def t_function(self): if __name__ == '__main__': unittest.main() - - diff --git a/pyomo/contrib/edi/tests/test_formulation.py b/pyomo/contrib/edi/tests/test_formulation.py index 5b85c22aeb5..91346d722c3 100644 --- a/pyomo/contrib/edi/tests/test_formulation.py +++ b/pyomo/contrib/edi/tests/test_formulation.py @@ -19,9 +19,8 @@ import pyomo.environ as pyo from pyomo.common.dependencies import attempt_import -from pyomo.core.base.units_container import ( - pint_available, -) +from pyomo.core.base.units_container import pint_available + @unittest.skipIf(not pint_available, 'Testing pyomo.contrib.edi requires pint') class TestEDIFormulation(unittest.TestCase): @@ -29,22 +28,22 @@ def test_edi_formulation_init(self): "Tests that a formulation initializes to the correct type and has proper data" from pyomo.environ import ConcreteModel from pyomo.contrib.edi import Formulation + f = Formulation() self.assertIsInstance(f, Formulation) self.assertIsInstance(f, ConcreteModel) - self.assertEqual(f._objective_counter , 0 ) - self.assertEqual(f._constraint_counter , 0 ) - self.assertEqual(f._variable_keys , [] ) - self.assertEqual(f._constant_keys , [] ) - self.assertEqual(f._objective_keys , [] ) - self.assertEqual(f._runtimeObjective_keys , [] ) - self.assertEqual(f._objective_keys , [] ) - self.assertEqual(f._runtimeConstraint_keys , [] ) - self.assertEqual(f._constraint_keys , [] ) - self.assertEqual(f._allConstraint_keys , [] ) - + self.assertEqual(f._objective_counter, 0) + self.assertEqual(f._constraint_counter, 0) + self.assertEqual(f._variable_keys, []) + self.assertEqual(f._constant_keys, []) + self.assertEqual(f._objective_keys, []) + self.assertEqual(f._runtimeObjective_keys, []) + self.assertEqual(f._objective_keys, []) + self.assertEqual(f._runtimeConstraint_keys, []) + self.assertEqual(f._constraint_keys, []) + self.assertEqual(f._allConstraint_keys, []) def test_edi_formulation_variable(self): "Tests the variable constructor in edi.formulation" @@ -54,26 +53,182 @@ def test_edi_formulation_variable(self): f = Formulation() - x1 = f.Variable(name = 'x1', guess = 1.0, units = 'm' , description = 'The x variable', size = None, bounds=None, domain=None ) - self.assertRaises(RuntimeError,f.Variable,*('x1', 1.0, 'm')) + x1 = f.Variable( + name='x1', + guess=1.0, + units='m', + description='The x variable', + size=None, + bounds=None, + domain=None, + ) + self.assertRaises(RuntimeError, f.Variable, *('x1', 1.0, 'm')) x2 = f.Variable('x2', 1.0, 'm') - x3 = f.Variable('x3', 1.0, 'm' , 'The x variable', None, None, None ) - x4 = f.Variable(name = 'x4', guess = 1.0, units = 'm' , description = 'The x variable', size = None, bounds=None, domain=PositiveReals ) - self.assertRaises(RuntimeError, f.Variable, **{'name':'x5', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': None, 'bounds':None, 'domain':"error" }) - - x6 = f.Variable(name = 'x6', guess = 1.0, units = 'm' , description = 'The x variable', size = 0, bounds=None, domain=None ) - x7 = f.Variable(name = 'x7', guess = 1.0, units = 'm' , description = 'The x variable', size = 5, bounds=None, domain=None ) - self.assertRaises(ValueError, f.Variable, **{'name':'x8', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': 'error', 'bounds':None, 'domain':None }) - x9 = f.Variable(name = 'x9', guess = 1.0, units = 'm' , description = 'The x variable', size = [2,2], bounds=None, domain=None ) - self.assertRaises(ValueError, f.Variable, **{'name':'x10', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': ['2','2'], 'bounds':None, 'domain':None }) - self.assertRaises(ValueError, f.Variable, **{'name':'x11', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': [2,1], 'bounds':None, 'domain':None }) - - x12 = f.Variable(name = 'x12', guess = 1.0, units = 'm' , description = 'The x variable', size = None, bounds=[-10,10], domain=None ) - self.assertRaises(ValueError, f.Variable, **{'name':'x13', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': None, 'bounds':[10,-10], 'domain':None }) - self.assertRaises(ValueError, f.Variable, **{'name':'x14', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': None, 'bounds':["-10","10"], 'domain':None }) - self.assertRaises(ValueError, f.Variable, **{'name':'x15', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': None, 'bounds':[1,2,3], 'domain':None }) - self.assertRaises(ValueError, f.Variable, **{'name':'x16', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': None, 'bounds':"error", 'domain':None }) - self.assertRaises(ValueError, f.Variable, **{'name':'x17', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': None, 'bounds':[0,"10"], 'domain':None }) + x3 = f.Variable('x3', 1.0, 'm', 'The x variable', None, None, None) + x4 = f.Variable( + name='x4', + guess=1.0, + units='m', + description='The x variable', + size=None, + bounds=None, + domain=PositiveReals, + ) + self.assertRaises( + RuntimeError, + f.Variable, + **{ + 'name': 'x5', + 'guess': 1.0, + 'units': 'm', + 'description': 'The x variable', + 'size': None, + 'bounds': None, + 'domain': "error", + } + ) + + x6 = f.Variable( + name='x6', + guess=1.0, + units='m', + description='The x variable', + size=0, + bounds=None, + domain=None, + ) + x7 = f.Variable( + name='x7', + guess=1.0, + units='m', + description='The x variable', + size=5, + bounds=None, + domain=None, + ) + self.assertRaises( + ValueError, + f.Variable, + **{ + 'name': 'x8', + 'guess': 1.0, + 'units': 'm', + 'description': 'The x variable', + 'size': 'error', + 'bounds': None, + 'domain': None, + } + ) + x9 = f.Variable( + name='x9', + guess=1.0, + units='m', + description='The x variable', + size=[2, 2], + bounds=None, + domain=None, + ) + self.assertRaises( + ValueError, + f.Variable, + **{ + 'name': 'x10', + 'guess': 1.0, + 'units': 'm', + 'description': 'The x variable', + 'size': ['2', '2'], + 'bounds': None, + 'domain': None, + } + ) + self.assertRaises( + ValueError, + f.Variable, + **{ + 'name': 'x11', + 'guess': 1.0, + 'units': 'm', + 'description': 'The x variable', + 'size': [2, 1], + 'bounds': None, + 'domain': None, + } + ) + + x12 = f.Variable( + name='x12', + guess=1.0, + units='m', + description='The x variable', + size=None, + bounds=[-10, 10], + domain=None, + ) + self.assertRaises( + ValueError, + f.Variable, + **{ + 'name': 'x13', + 'guess': 1.0, + 'units': 'm', + 'description': 'The x variable', + 'size': None, + 'bounds': [10, -10], + 'domain': None, + } + ) + self.assertRaises( + ValueError, + f.Variable, + **{ + 'name': 'x14', + 'guess': 1.0, + 'units': 'm', + 'description': 'The x variable', + 'size': None, + 'bounds': ["-10", "10"], + 'domain': None, + } + ) + self.assertRaises( + ValueError, + f.Variable, + **{ + 'name': 'x15', + 'guess': 1.0, + 'units': 'm', + 'description': 'The x variable', + 'size': None, + 'bounds': [1, 2, 3], + 'domain': None, + } + ) + self.assertRaises( + ValueError, + f.Variable, + **{ + 'name': 'x16', + 'guess': 1.0, + 'units': 'm', + 'description': 'The x variable', + 'size': None, + 'bounds': "error", + 'domain': None, + } + ) + self.assertRaises( + ValueError, + f.Variable, + **{ + 'name': 'x17', + 'guess': 1.0, + 'units': 'm', + 'description': 'The x variable', + 'size': None, + 'bounds': [0, "10"], + 'domain': None, + } + ) # verifies alternate unit construction x18 = f.Variable('x18', 1.0, pyo.units.m) @@ -86,20 +241,98 @@ def test_edi_formulation_constant(self): f = Formulation() - c1 = f.Constant(name = 'c1', value = 1.0, units = 'm', description = 'A constant c', size = None, within = None) - self.assertRaises(RuntimeError,f.Constant,*('c1', 1.0, 'm')) + c1 = f.Constant( + name='c1', + value=1.0, + units='m', + description='A constant c', + size=None, + within=None, + ) + self.assertRaises(RuntimeError, f.Constant, *('c1', 1.0, 'm')) c2 = f.Constant('c2', 1.0, 'm') c3 = f.Constant('c3', 1.0, 'm', 'A constant c', None, None) - c4 = f.Constant(name = 'c4', value = 1.0, units = 'm' , description = 'A constant c', size = None, within=PositiveReals ) - self.assertRaises(RuntimeError, f.Constant, **{'name':'c5', 'value':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': None, 'within':"error" }) - - c6 = f.Constant(name = 'c6', value = 1.0, units = 'm' , description = 'A constant c', size = 0, within=None ) - c7 = f.Constant(name = 'c7', value = 1.0, units = 'm' , description = 'A constant c', size = 5, within=None ) - self.assertRaises(ValueError, f.Constant, **{'name':'c8', 'value':1.0 , 'units' :'m' , 'description': 'A constant c', 'size': 'error', 'within':None }) - c9 = f.Constant(name = 'c9', value = 1.0, units = 'm' , description = 'A constant c', size = [2,2], within=None ) - self.assertRaises(ValueError, f.Constant, **{'name':'c10', 'value':1.0 , 'units' :'m' , 'description': 'A constant c', 'size': ['2','2'], 'within':None }) - self.assertRaises(ValueError, f.Constant, **{'name':'c11', 'value':1.0 , 'units' :'m' , 'description': 'A constant c', 'size': [2,1], 'within':None }) + c4 = f.Constant( + name='c4', + value=1.0, + units='m', + description='A constant c', + size=None, + within=PositiveReals, + ) + self.assertRaises( + RuntimeError, + f.Constant, + **{ + 'name': 'c5', + 'value': 1.0, + 'units': 'm', + 'description': 'The x variable', + 'size': None, + 'within': "error", + } + ) + c6 = f.Constant( + name='c6', + value=1.0, + units='m', + description='A constant c', + size=0, + within=None, + ) + c7 = f.Constant( + name='c7', + value=1.0, + units='m', + description='A constant c', + size=5, + within=None, + ) + self.assertRaises( + ValueError, + f.Constant, + **{ + 'name': 'c8', + 'value': 1.0, + 'units': 'm', + 'description': 'A constant c', + 'size': 'error', + 'within': None, + } + ) + c9 = f.Constant( + name='c9', + value=1.0, + units='m', + description='A constant c', + size=[2, 2], + within=None, + ) + self.assertRaises( + ValueError, + f.Constant, + **{ + 'name': 'c10', + 'value': 1.0, + 'units': 'm', + 'description': 'A constant c', + 'size': ['2', '2'], + 'within': None, + } + ) + self.assertRaises( + ValueError, + f.Constant, + **{ + 'name': 'c11', + 'value': 1.0, + 'units': 'm', + 'description': 'A constant c', + 'size': [2, 1], + 'within': None, + } + ) def test_edi_formulation_objective(self): "Tests the objective constructor in edi.formulation" @@ -108,9 +341,9 @@ def test_edi_formulation_objective(self): from pyomo.contrib.edi import Formulation f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - f.Objective( x + y ) + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + f.Objective(x + y) def test_edi_formulation_runtimeobjective(self): "Tests the runtime objective constructor in edi.formulation" @@ -122,317 +355,436 @@ def test_edi_formulation_constraint(self): import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - f.Objective(x+y) - f.Constraint(x+y <= 1.0*units.m) + f = Formulation() + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + f.Objective(x + y) + f.Constraint(x + y <= 1.0 * units.m) def test_edi_formulation_runtimeconstraint_tuple(self): "Tests the runtime constraint constructor in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The unit circle output') - c = f.Constant(name = 'c', value = 1.0, units = '', description = 'A constant c', size = 2) - f.Objective( x + y ) + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + z = f.Variable( + name='z', guess=1.0, units='m^2', description='The unit circle output' + ) + c = f.Constant( + name='c', value=1.0, units='', description='A constant c', size=2 + ) + f.Objective(x + y) + class UnitCircle(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super(UnitCircle, self).__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append(name = 'x', units = 'ft' , description = 'The x variable') - self.inputs.append(name = 'y', units = 'ft' , description = 'The y variable') - self.outputs.append(name = 'z', units = 'ft**2', description = 'Resultant of the unit circle evaluation') + self.inputs.append(name='x', units='ft', description='The x variable') + self.inputs.append(name='y', units='ft', description='The y variable') + self.outputs.append( + name='z', + units='ft**2', + description='Resultant of the unit circle evaluation', + ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, x, y): # The actual function that does things - x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float - y = pyo.value(units.convert(y,self.inputs[1].units)) # Converts to correct units then casts to float - z = x**2 + y**2 # Compute z - dzdx = 2*x # Compute dz/dx - dzdy = 2*y # Compute dz/dy + + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value( + units.convert(x, self.inputs[0].units) + ) # Converts to correct units then casts to float + y = pyo.value( + units.convert(y, self.inputs[1].units) + ) # Converts to correct units then casts to float + z = x**2 + y**2 # Compute z + dzdx = 2 * x # Compute dz/dx + dzdy = 2 * y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... - f.Constraint( z <= 1*units.m**2 ) + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + + f.Constraint(z <= 1 * units.m**2) - f.RuntimeConstraint( *( z, '==', [x,y], UnitCircle() ) ) + f.RuntimeConstraint(*(z, '==', [x, y], UnitCircle())) def test_edi_formulation_runtimeconstraint_list(self): "Tests the runtime constraint constructor in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The unit circle output') - c = f.Constant(name = 'c', value = 1.0, units = '', description = 'A constant c', size = 2) - f.Objective( x + y ) + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + z = f.Variable( + name='z', guess=1.0, units='m^2', description='The unit circle output' + ) + c = f.Constant( + name='c', value=1.0, units='', description='A constant c', size=2 + ) + f.Objective(x + y) + class UnitCircle(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super(UnitCircle, self).__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append(name = 'x', units = 'ft' , description = 'The x variable') - self.inputs.append(name = 'y', units = 'ft' , description = 'The y variable') - self.outputs.append(name = 'z', units = 'ft**2', description = 'Resultant of the unit circle evaluation') + self.inputs.append(name='x', units='ft', description='The x variable') + self.inputs.append(name='y', units='ft', description='The y variable') + self.outputs.append( + name='z', + units='ft**2', + description='Resultant of the unit circle evaluation', + ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, x, y): # The actual function that does things - x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float - y = pyo.value(units.convert(y,self.inputs[1].units)) # Converts to correct units then casts to float - z = x**2 + y**2 # Compute z - dzdx = 2*x # Compute dz/dx - dzdy = 2*y # Compute dz/dy + + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value( + units.convert(x, self.inputs[0].units) + ) # Converts to correct units then casts to float + y = pyo.value( + units.convert(y, self.inputs[1].units) + ) # Converts to correct units then casts to float + z = x**2 + y**2 # Compute z + dzdx = 2 * x # Compute dz/dx + dzdy = 2 * y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... - f.Constraint( z <= 1*units.m**2 ) - - f.RuntimeConstraint( *[ [z], ['=='], [x,y], UnitCircle() ] ) + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + f.Constraint(z <= 1 * units.m**2) + + f.RuntimeConstraint(*[[z], ['=='], [x, y], UnitCircle()]) def test_edi_formulation_runtimeconstraint_dict(self): "Tests the runtime constraint constructor in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The unit circle output') - c = f.Constant(name = 'c', value = 1.0, units = '', description = 'A constant c', size = 2) - f.Objective( x + y ) + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + z = f.Variable( + name='z', guess=1.0, units='m^2', description='The unit circle output' + ) + c = f.Constant( + name='c', value=1.0, units='', description='A constant c', size=2 + ) + f.Objective(x + y) + class UnitCircle(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super(UnitCircle, self).__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append(name = 'x', units = 'ft' , description = 'The x variable') - self.inputs.append(name = 'y', units = 'ft' , description = 'The y variable') - self.outputs.append(name = 'z', units = 'ft**2', description = 'Resultant of the unit circle evaluation') + self.inputs.append(name='x', units='ft', description='The x variable') + self.inputs.append(name='y', units='ft', description='The y variable') + self.outputs.append( + name='z', + units='ft**2', + description='Resultant of the unit circle evaluation', + ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, x, y): # The actual function that does things - x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float - y = pyo.value(units.convert(y,self.inputs[1].units)) # Converts to correct units then casts to float - z = x**2 + y**2 # Compute z - dzdx = 2*x # Compute dz/dx - dzdy = 2*y # Compute dz/dy + + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value( + units.convert(x, self.inputs[0].units) + ) # Converts to correct units then casts to float + y = pyo.value( + units.convert(y, self.inputs[1].units) + ) # Converts to correct units then casts to float + z = x**2 + y**2 # Compute z + dzdx = 2 * x # Compute dz/dx + dzdy = 2 * y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... - f.Constraint( z <= 1*units.m**2 ) - - f.RuntimeConstraint( **{ 'outputs':z, 'operators':'==', 'inputs':[x,y], 'black_box':UnitCircle() } ) - + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + + f.Constraint(z <= 1 * units.m**2) + + f.RuntimeConstraint( + **{ + 'outputs': z, + 'operators': '==', + 'inputs': [x, y], + 'black_box': UnitCircle(), + } + ) def test_edi_formulation_constraintlist_1(self): "Tests the constraint list constructor in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The unit circle output') - c = f.Constant(name = 'c', value = 1.0, units = '', description = 'A constant c', size = 2) - f.Objective( x + y ) + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + z = f.Variable( + name='z', guess=1.0, units='m^2', description='The unit circle output' + ) + c = f.Constant( + name='c', value=1.0, units='', description='A constant c', size=2 + ) + f.Objective(x + y) class UnitCircle(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super(UnitCircle, self).__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append(name = 'x', units = 'ft' , description = 'The x variable') - self.inputs.append(name = 'y', units = 'ft' , description = 'The y variable') - self.outputs.append(name = 'z', units = 'ft**2', description = 'Resultant of the unit circle evaluation') + self.inputs.append(name='x', units='ft', description='The x variable') + self.inputs.append(name='y', units='ft', description='The y variable') + self.outputs.append( + name='z', + units='ft**2', + description='Resultant of the unit circle evaluation', + ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, x, y): # The actual function that does things - x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float - y = pyo.value(units.convert(y,self.inputs[1].units)) # Converts to correct units then casts to float - z = x**2 + y**2 # Compute z - dzdx = 2*x # Compute dz/dx - dzdy = 2*y # Compute dz/dy + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value( + units.convert(x, self.inputs[0].units) + ) # Converts to correct units then casts to float + y = pyo.value( + units.convert(y, self.inputs[1].units) + ) # Converts to correct units then casts to float + z = x**2 + y**2 # Compute z + dzdx = 2 * x # Compute dz/dx + dzdy = 2 * y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... - f.ConstraintList( - [ - (z, '==', [x,y], UnitCircle() ) , - z <= 1*units.m**2 - ] - ) - - cl = f.get_constraints() + f.ConstraintList([(z, '==', [x, y], UnitCircle()), z <= 1 * units.m**2]) - self.assertTrue(len(cl)==2) + cl = f.get_constraints() + self.assertTrue(len(cl) == 2) def test_edi_formulation_constraintlist_2(self): "Tests the constraint list constructor in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm**2' , description = 'The y variable') - f.Objective( y ) + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m**2', description='The y variable') + f.Objective(y) class Parabola(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super(Parabola, self).__init__() self.description = 'This model evaluates the function: y = x**2' - self.inputs.append(name = 'x', units = 'ft' , description = 'The x variable') - self.outputs.append(name = 'y', units = 'ft**2' , description = 'The y variable') + self.inputs.append(name='x', units='ft', description='The x variable') + self.outputs.append( + name='y', units='ft**2', description='The y variable' + ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, x): # The actual function that does things - x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float - y = x**2 # Compute y - dydx = 2*x # Compute dy/dx + def BlackBox(self, x): # The actual function that does things + x = pyo.value( + units.convert(x, self.inputs[0].units) + ) # Converts to correct units then casts to float + y = x**2 # Compute y + dydx = 2 * x # Compute dy/dx y *= units.ft**2 - dydx *= units.ft # units.ft**2 / units.ft - return y, [dydx] # return z, grad(z), hess(z)... + dydx *= units.ft # units.ft**2 / units.ft + return y, [dydx] # return z, grad(z), hess(z)... f.ConstraintList( - [ - {'outputs':y, 'operators':'==', 'inputs':x, 'black_box':Parabola() }, - ] + [{'outputs': y, 'operators': '==', 'inputs': x, 'black_box': Parabola()}] ) - cl = f.get_constraints() - - self.assertTrue(len(cl)==1) + cl = f.get_constraints() + self.assertTrue(len(cl) == 1) def test_edi_formulation_constraintlist_3(self): "Tests the constraint list constructor in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable', size=3) - y = f.Variable(name = 'y', guess = 1.0, units = 'm**2' , description = 'The y variable', size=3) - f.Objective( y[0] + y[1] + y[2] ) + x = f.Variable( + name='x', guess=1.0, units='m', description='The x variable', size=3 + ) + y = f.Variable( + name='y', guess=1.0, units='m**2', description='The y variable', size=3 + ) + f.Objective(y[0] + y[1] + y[2]) + class Parabola(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super(Parabola, self).__init__() self.description = 'This model evaluates the function: y = x**2' - self.inputs.append(name = 'x', size = 3, units = 'ft' , description = 'The x variable') - self.outputs.append(name = 'y', size = 3, units = 'ft**2' , description = 'The y variable') + self.inputs.append( + name='x', size=3, units='ft', description='The x variable' + ) + self.outputs.append( + name='y', size=3, units='ft**2', description='The y variable' + ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(*args, **kwargs):# The actual function that does things + def BlackBox(*args, **kwargs): # The actual function that does things args = list(args) self = args.pop(0) - runCases, returnMode, remainingKwargs = self.parseInputs(*args, **kwargs) - + runCases, returnMode, remainingKwargs = self.parseInputs( + *args, **kwargs + ) + x = self.sanitizeInputs(runCases[0]['x']) x = np.array([pyo.value(xval) for xval in x], dtype=np.float64) - y = x**2 # Compute y - dydx = 2*x # Compute dy/dx - + y = x**2 # Compute y + dydx = 2 * x # Compute dy/dx + y = y * units.ft**2 dydx = np.diag(dydx) - dydx = dydx * units.ft # units.ft**2 / units.ft - - return y, [dydx] # return z, grad(z), hess(z)... - f.ConstraintList([ {'outputs':y, 'operators':'==', 'inputs':x, 'black_box':Parabola() },]) - cl = f.get_constraints() - self.assertTrue(len(cl)==1) + dydx = dydx * units.ft # units.ft**2 / units.ft + return y, [dydx] # return z, grad(z), hess(z)... + + f.ConstraintList( + [{'outputs': y, 'operators': '==', 'inputs': x, 'black_box': Parabola()}] + ) + cl = f.get_constraints() + self.assertTrue(len(cl) == 1) def test_edi_formulation_runtimeconstraint_exceptions(self): "Tests the runtime constraint constructor in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The unit circle output') - c = f.Constant(name = 'c', value = 1.0, units = '', description = 'A constant c', size = 2) - f.Objective( x + y ) + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + z = f.Variable( + name='z', guess=1.0, units='m^2', description='The unit circle output' + ) + c = f.Constant( + name='c', value=1.0, units='', description='A constant c', size=2 + ) + f.Objective(x + y) + class UnitCircle(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super(UnitCircle, self).__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append(name = 'x', units = 'ft' , description = 'The x variable') - self.inputs.append(name = 'y', units = 'ft' , description = 'The y variable') - self.outputs.append(name = 'z', units = 'ft**2', description = 'Resultant of the unit circle evaluation') + self.inputs.append(name='x', units='ft', description='The x variable') + self.inputs.append(name='y', units='ft', description='The y variable') + self.outputs.append( + name='z', + units='ft**2', + description='Resultant of the unit circle evaluation', + ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, x, y): # The actual function that does things - x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float - y = pyo.value(units.convert(y,self.inputs[1].units)) # Converts to correct units then casts to float - z = x**2 + y**2 # Compute z - dzdx = 2*x # Compute dz/dx - dzdy = 2*y # Compute dz/dy + + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value( + units.convert(x, self.inputs[0].units) + ) # Converts to correct units then casts to float + y = pyo.value( + units.convert(y, self.inputs[1].units) + ) # Converts to correct units then casts to float + z = x**2 + y**2 # Compute z + dzdx = 2 * x # Compute dz/dx + dzdy = 2 * y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... - f.Constraint( z <= 1*units.m**2 ) + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + + f.Constraint(z <= 1 * units.m**2) # flaggs the input or as bad before assigning the incorrect black box - self.assertRaises(ValueError, f.RuntimeConstraint, *(z, '==', 1.0 , UnitCircle() )) - self.assertRaises(ValueError, f.RuntimeConstraint, *(1.0, '==', [x,y] , UnitCircle() )) + self.assertRaises( + ValueError, f.RuntimeConstraint, *(z, '==', 1.0, UnitCircle()) + ) + self.assertRaises( + ValueError, f.RuntimeConstraint, *(1.0, '==', [x, y], UnitCircle()) + ) - self.assertRaises(ValueError, f.RuntimeConstraint, *(z, '==', [1.0,y], UnitCircle() )) - self.assertRaises(ValueError, f.RuntimeConstraint, *(z, 1.0 , [x ,y], UnitCircle() )) - self.assertRaises(ValueError, f.RuntimeConstraint, *(z, '=', [x ,y], UnitCircle() )) + self.assertRaises( + ValueError, f.RuntimeConstraint, *(z, '==', [1.0, y], UnitCircle()) + ) + self.assertRaises( + ValueError, f.RuntimeConstraint, *(z, 1.0, [x, y], UnitCircle()) + ) + self.assertRaises( + ValueError, f.RuntimeConstraint, *(z, '=', [x, y], UnitCircle()) + ) def test_edi_formulation_getvariables(self): "Tests the get_variables function in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') vrs = f.get_variables() - self.assertListEqual(vrs, [x,y]) + self.assertListEqual(vrs, [x, y]) def test_edi_formulation_getconstants(self): "Tests the get_constants function in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - c1 = f.Constant(name = 'c1', value = 1.0, units = 'm', description = 'A constant c1', size = None, within = None) - c2 = f.Constant(name = 'c2', value = 1.0, units = 'm', description = 'A constant c2', size = None, within = None) + f = Formulation() + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + + c1 = f.Constant( + name='c1', + value=1.0, + units='m', + description='A constant c1', + size=None, + within=None, + ) + c2 = f.Constant( + name='c2', + value=1.0, + units='m', + description='A constant c2', + size=None, + within=None, + ) csts = f.get_constants() - self.assertListEqual(csts, [c1,c2]) + self.assertListEqual(csts, [c1, c2]) def test_edi_formulation_getobjectives(self): "Tests the get_objectives function in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - f.Objective(x+y) + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + f.Objective(x + y) objList = f.get_objectives() - self.assertTrue(len(objList)==1) + self.assertTrue(len(objList) == 1) # not really sure how to check this, so I wont def test_edi_formulation_getconstraints(self): @@ -452,40 +804,45 @@ def test_edi_formulation_getconstraints(self): # ================= # Declare Variables # ================= - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The unit circle output') + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + z = f.Variable( + name='z', guess=1.0, units='m^2', description='The unit circle output' + ) # ================= # Declare Constants # ================= - c = f.Constant(name = 'c', value = 1.0, units = '', description = 'A constant c', size = 2) + c = f.Constant( + name='c', value=1.0, units='', description='A constant c', size=2 + ) # ===================== # Declare the Objective # ===================== - f.Objective( - c[0]*x + c[1]*y - ) + f.Objective(c[0] * x + c[1] * y) # =================== # Declare a Black Box # =================== class UnitCircle(BlackBoxFunctionModel): - def __init__(self): # The initialization function - + def __init__(self): # The initialization function # Initialize the black box model super(UnitCircle, self).__init__() # A brief description of the model self.description = 'This model evaluates the function: z = x**2 + y**2' - + # Declare the black box model inputs - self.inputs.append(name = 'x', units = 'ft' , description = 'The x variable') - self.inputs.append(name = 'y', units = 'ft' , description = 'The y variable') + self.inputs.append(name='x', units='ft', description='The x variable') + self.inputs.append(name='y', units='ft', description='The y variable') # Declare the black box model outputs - self.outputs.append(name = 'z', units = 'ft**2', description = 'Resultant of the unit circle evaluation') + self.outputs.append( + name='z', + units='ft**2', + description='Resultant of the unit circle evaluation', + ) # Declare the maximum available derivative self.availableDerivative = 1 @@ -493,37 +850,36 @@ def __init__(self): # The initialization function # Post-initialization setup self.post_init_setup(len(self.inputs)) - def BlackBox(self, x, y): # The actual function that does things - x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float - y = pyo.value(units.convert(y,self.inputs[1].units)) # Converts to correct units then casts to float + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value( + units.convert(x, self.inputs[0].units) + ) # Converts to correct units then casts to float + y = pyo.value( + units.convert(y, self.inputs[1].units) + ) # Converts to correct units then casts to float - z = x**2 + y**2 # Compute z - dzdx = 2*x # Compute dz/dx - dzdy = 2*y # Compute dz/dy + z = x**2 + y**2 # Compute z + dzdx = 2 * x # Compute dz/dx + dzdy = 2 * y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... # ======================= # Declare the Constraints # ======================= - f.ConstraintList( - [ - (z, '==', [x,y], UnitCircle() ) , - z <= 1*units.m**2 - ] - ) + f.ConstraintList([(z, '==', [x, y], UnitCircle()), z <= 1 * units.m**2]) - cl = f.get_constraints() + cl = f.get_constraints() ecl = f.get_explicitConstraints() rcl = f.get_runtimeConstraints() - self.assertTrue(len(cl)==2) - self.assertTrue(len(ecl)==1) - self.assertTrue(len(rcl)==1) + self.assertTrue(len(cl) == 2) + self.assertTrue(len(ecl) == 1) + self.assertTrue(len(rcl) == 1) def test_edi_formulation_checkunits(self): "Tests the check_units function in edi.formulation" @@ -531,43 +887,28 @@ def test_edi_formulation_checkunits(self): import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') - f.Objective(x+y) - f.Constraint(x+y <= 1.0*units.m) + f.Objective(x + y) + f.Constraint(x + y <= 1.0 * units.m) f.check_units() - f.Constraint(2.0*x+y <= 1.0) - self.assertRaises(pyomo.core.base.units_container.UnitsError, f.check_units, *()) - + f.Constraint(2.0 * x + y <= 1.0) + self.assertRaises( + pyomo.core.base.units_container.UnitsError, f.check_units, *() + ) f2 = Formulation() - u = f2.Variable(name = 'u', guess = 1.0, units = 'm' , description = 'The u variable') - v = f2.Variable(name = 'v', guess = 1.0, units = 'kg' , description = 'The v variable') - f2.Objective(u+v) - self.assertRaises(pyomo.core.base.units_container.UnitsError, f2.check_units, *()) - - - + u = f2.Variable(name='u', guess=1.0, units='m', description='The u variable') + v = f2.Variable(name='v', guess=1.0, units='kg', description='The v variable') + f2.Objective(u + v) + self.assertRaises( + pyomo.core.base.units_container.UnitsError, f2.check_units, *() + ) if __name__ == '__main__': unittest.main() - - - - - - - - - - - - - - - - From b75d75f48e6d166dd2bbcb801ba2c367ba0c7d2b Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Tue, 8 Aug 2023 10:18:39 -0600 Subject: [PATCH 30/61] undoing black --- pyomo/contrib/edi/README.md | 16 ++++++ pyomo/contrib/edi/blackBoxFunctionModel.py | 9 +++ pyomo/contrib/edi/examples/aircraft_gp.py | 49 ++++++++++++++++ pyomo/contrib/edi/examples/readme_example.py | 3 + pyomo/contrib/edi/formulation.py | 60 ++++++++++++++++++++ pyomo/contrib/edi/tests/test_blackbox.py | 17 ++++-- pyomo/contrib/edi/tests/test_examples.py | 10 ++++ pyomo/contrib/edi/tests/test_formulation.py | 21 ++++++- 8 files changed, 180 insertions(+), 5 deletions(-) diff --git a/pyomo/contrib/edi/README.md b/pyomo/contrib/edi/README.md index 90f469c4000..8a6b592baa0 100644 --- a/pyomo/contrib/edi/README.md +++ b/pyomo/contrib/edi/README.md @@ -101,3 +101,19 @@ f.ConstraintList( ] ) ``` + +## Acknowledgement + +Pyomo: Python Optimization Modeling Objects +Copyright (c) 2008-2023 +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. + +Development of this module was conducted as part of the Institute for +the Design of Advanced Energy Systems (IDAES) with support through the +Simulation-Based Engineering, Crosscutting Research Program within the +U.S. Department of Energy’s Office of Fossil Energy and Carbon Management. + +This software is distributed under the 3-clause BSD License. \ No newline at end of file diff --git a/pyomo/contrib/edi/blackBoxFunctionModel.py b/pyomo/contrib/edi/blackBoxFunctionModel.py index 0b1d593d7a6..b7d5c0bb55c 100644 --- a/pyomo/contrib/edi/blackBoxFunctionModel.py +++ b/pyomo/contrib/edi/blackBoxFunctionModel.py @@ -16,10 +16,17 @@ # ___________________________________________________________________________ import copy +import numpy as np import pyomo import pyomo.environ as pyo from pyomo.environ import units as pyomo_units +import scipy.sparse as sps +from pyomo.contrib.pynumero.interfaces.external_grey_box import ( + ExternalGreyBoxModel, + ExternalGreyBoxBlock, +) +<<<<<<< HEAD try: import numpy as np except: @@ -44,6 +51,8 @@ "pyomo.contrib.edi requires pyomo.contrib.pynumero to be installed to enable black box capability, this should have installed with base pyomo" ) +======= +>>>>>>> parent of f76f4974b (addressing concerns in PR) class BlackBoxFunctionModel_Variable(object): def __init__(self, name, units, description='', size=0): diff --git a/pyomo/contrib/edi/examples/aircraft_gp.py b/pyomo/contrib/edi/examples/aircraft_gp.py index 94e1db70083..de2af641afc 100644 --- a/pyomo/contrib/edi/examples/aircraft_gp.py +++ b/pyomo/contrib/edi/examples/aircraft_gp.py @@ -24,6 +24,7 @@ # ================= # Declare Variables # ================= +<<<<<<< HEAD A = f.Variable(name="A", guess=10.0, units="-", description="aspect ratio") C_D = f.Variable( @@ -40,11 +41,29 @@ S = f.Variable(name="S", guess=10.0, units="m^2", description="total wing area") V = f.Variable(name="V", guess=30.0, units="m/s", description="cruising speed") W = f.Variable(name="W", guess=10000.0, units="N", description="total aircraft weight") +======= +D = f.Variable(name="D", guess=300, units="N", description="total drag force") +A = f.Variable(name="A", guess=10.0, units="-", description="aspect ratio") +S = f.Variable(name="S", guess=10.0, units="m^2", description="total wing area") +V = f.Variable(name="V", guess=30.0, units="m/s", description="cruising speed") +W = f.Variable(name="W", guess=10000.0, units="N", description="total aircraft weight") +Re = f.Variable(name="Re", guess=3e6, units="-", description="Reynold's number") +C_D = f.Variable( + name="C_D", guess=0.025, units="-", description="Drag coefficient of wing" +) +C_L = f.Variable( + name="C_L", guess=0.5, units="-", description="Lift coefficient of wing" +) +C_f = f.Variable( + name="C_f", guess=0.003, units="-", description="skin friction coefficient" +) +>>>>>>> parent of f76f4974b (addressing concerns in PR) W_w = f.Variable(name="W_w", guess=2500, units="N", description="wing weight") # ================= # Declare Constants # ================= +<<<<<<< HEAD C_Lmax = f.Constant( name="C_Lmax", value=2.0, units="-", description="max CL with flaps down" ) @@ -70,12 +89,42 @@ W_0 = f.Constant( name="W_0", value=4940.0, units="N", description="aircraft weight excluding wing" ) +======= +k = f.Constant(name="k", value=1.2, units="-", description="form factor") +e = f.Constant(name="e", value=0.96, units="-", description="Oswald efficiency factor") +mu = f.Constant( + name="mu", value=1.78e-5, units="kg/m/s", description="viscosity of air" +) +rho = f.Constant(name="rho", value=1.23, units="kg/m^3", description="density of air") +tau = f.Constant( + name="tau", value=0.12, units="-", description="airfoil thickness to chord ratio" +) +N_ult = f.Constant( + name="N_ult", value=2.5, units="-", description="ultimate load factor" +) +V_min = f.Constant(name="V_min", value=22, units="m/s", description="takeoff speed") +C_Lmax = f.Constant( + name="C_Lmax", value=2.0, units="-", description="max CL with flaps down" +) +S_wetratio = f.Constant( + name="Srat", value=2.05, units="-", description="wetted area ratio" +) +>>>>>>> parent of f76f4974b (addressing concerns in PR) W_W_coeff1 = f.Constant( name="W_c1", value=8.71e-5, units="1/m", description="Wing Weight Coefficient 1" ) W_W_coeff2 = f.Constant( name="W_c2", value=45.24, units="Pa", description="Wing Weight Coefficient 2" ) +<<<<<<< HEAD +======= +CDA0 = f.Constant( + name="CDA0", value=0.0306, units="m^2", description="fuselage drag area" +) +W_0 = f.Constant( + name="W_0", value=4940.0, units="N", description="aircraft weight excluding wing" +) +>>>>>>> parent of f76f4974b (addressing concerns in PR) # ===================== # Declare the Objective diff --git a/pyomo/contrib/edi/examples/readme_example.py b/pyomo/contrib/edi/examples/readme_example.py index d4f2d026cb1..b9b6e12918c 100644 --- a/pyomo/contrib/edi/examples/readme_example.py +++ b/pyomo/contrib/edi/examples/readme_example.py @@ -72,9 +72,12 @@ def BlackBox(self, x, y): # The actual function that does things # Declare the Constraints # ======================= f.ConstraintList([[z, '==', [x, y], UnitCircle()], z <= 1 * units.m**2]) +<<<<<<< HEAD # ============================================= # Run the black box (improves coverage metrics) # ============================================= uc = UnitCircle() bbo = uc.BlackBox(0.5 * units.m, 0.5 * units.m) +======= +>>>>>>> parent of f76f4974b (addressing concerns in PR) diff --git a/pyomo/contrib/edi/formulation.py b/pyomo/contrib/edi/formulation.py index f0652771ec2..5f8b636b47f 100644 --- a/pyomo/contrib/edi/formulation.py +++ b/pyomo/contrib/edi/formulation.py @@ -139,6 +139,7 @@ def Variable( 'A value of 0 or 1 is not valid for defining size. Use fewer dimensions.' ) if i == 0: +<<<<<<< HEAD st = pyo.Set(initialize=list(range(0, size[i]))) else: st *= pyo.Set(initialize=list(range(0, size[i]))) @@ -146,6 +147,15 @@ def Variable( self.add_component( name, pyo.Var( +======= + st = Set(initialize=list(range(0, size[i]))) + else: + st *= Set(initialize=list(range(0, size[i]))) + st.construct() + self.add_component( + name, + Var( +>>>>>>> parent of f76f4974b (addressing concerns in PR) st, name=name, initialize=guess, @@ -160,7 +170,11 @@ def Variable( if size == 1 or size == 0: self.add_component( name, +<<<<<<< HEAD pyo.Var( +======= + Var( +>>>>>>> parent of f76f4974b (addressing concerns in PR) name=name, initialize=guess, domain=domain, @@ -170,11 +184,19 @@ def Variable( ), ) else: +<<<<<<< HEAD st = pyo.Set(initialize=list(range(0, size))) st.construct() self.add_component( name, pyo.Var( +======= + st = Set(initialize=list(range(0, size))) + st.construct() + self.add_component( + name, + Var( +>>>>>>> parent of f76f4974b (addressing concerns in PR) st, name=name, initialize=guess, @@ -191,7 +213,11 @@ def Variable( else: self.add_component( name, +<<<<<<< HEAD pyo.Var( +======= + Var( +>>>>>>> parent of f76f4974b (addressing concerns in PR) name=name, initialize=guess, domain=domain, @@ -223,6 +249,7 @@ def Constant(self, name, value, units, description='', size=None, within=None): 'A value of 1 is not valid for defining size. Use fewer dimensions.' ) if i == 0: +<<<<<<< HEAD st = pyo.Set(initialize=list(range(0, size[i]))) else: st *= pyo.Set(initialize=list(range(0, size[i]))) @@ -230,6 +257,15 @@ def Constant(self, name, value, units, description='', size=None, within=None): self.add_component( name, pyo.Param( +======= + st = Set(initialize=list(range(0, size[i]))) + else: + st *= Set(initialize=list(range(0, size[i]))) + st.construct() + self.add_component( + name, + Param( +>>>>>>> parent of f76f4974b (addressing concerns in PR) st, name=name, initialize=value, @@ -244,7 +280,11 @@ def Constant(self, name, value, units, description='', size=None, within=None): if size == 1 or size == 0: self.add_component( name, +<<<<<<< HEAD pyo.Param( +======= + Param( +>>>>>>> parent of f76f4974b (addressing concerns in PR) name=name, initialize=value, within=within, @@ -254,11 +294,19 @@ def Constant(self, name, value, units, description='', size=None, within=None): ), ) else: +<<<<<<< HEAD st = pyo.Set(initialize=list(range(0, size))) st.construct() self.add_component( name, pyo.Param( +======= + st = Set(initialize=list(range(0, size))) + st.construct() + self.add_component( + name, + Param( +>>>>>>> parent of f76f4974b (addressing concerns in PR) st, name=name, initialize=value, @@ -275,7 +323,11 @@ def Constant(self, name, value, units, description='', size=None, within=None): else: self.add_component( name, +<<<<<<< HEAD pyo.Param( +======= + Param( +>>>>>>> parent of f76f4974b (addressing concerns in PR) name=name, initialize=value, within=within, @@ -293,7 +345,11 @@ def Objective(self, expr, sense=minimize): self._objective_counter += 1 self.add_component( 'objective_' + str(self._objective_counter), +<<<<<<< HEAD pyo.Objective(expr=expr, sense=sense), +======= + Objective(expr=expr, sense=sense), +>>>>>>> parent of f76f4974b (addressing concerns in PR) ) self._objective_keys.append('objective_' + str(self._objective_counter)) self.__dict__['objective_' + str(self._objective_counter)].construct() @@ -304,7 +360,11 @@ def Objective(self, expr, sense=minimize): def Constraint(self, expr): self._constraint_counter += 1 conName = 'constraint_' + str(self._constraint_counter) +<<<<<<< HEAD self.add_component(conName, pyo.Constraint(expr=expr)) +======= + self.add_component(conName, Constraint(expr=expr)) +>>>>>>> parent of f76f4974b (addressing concerns in PR) self._constraint_keys.append(conName) self._allConstraint_keys.append(conName) self.__dict__[conName].construct() diff --git a/pyomo/contrib/edi/tests/test_blackbox.py b/pyomo/contrib/edi/tests/test_blackbox.py index 7671fb11f3e..d8831f1a9cc 100644 --- a/pyomo/contrib/edi/tests/test_blackbox.py +++ b/pyomo/contrib/edi/tests/test_blackbox.py @@ -21,15 +21,21 @@ from pyomo.core.base.units_container import pint_available -from pyomo.common.dependencies import numpy, numpy_available -from pyomo.common.dependencies import scipy, scipy_available +np, numpy_available = attempt_import('numpy', 'edi requires numpy') +# scipy, scipy_available = attempt_import( +# 'scipy', 'inverse_reduced_hessian requires scipy' +# ) -if numpy_available: - import numpy as np +# if not (numpy_available and scipy_available): +if not numpy_available: + raise unittest.SkipTest('edi.formulation tests require numpy') +<<<<<<< HEAD @unittest.skipIf(not numpy_available, 'Testing pyomo.contrib.edi requires numpy') @unittest.skipIf(not scipy_available, 'Testing pyomo.contrib.edi requires scipy') +======= +>>>>>>> parent of f76f4974b (addressing concerns in PR) @unittest.skipIf(not pint_available, 'Testing units requires pint') class TestEDIBlackBox(unittest.TestCase): def test_edi_blackbox_variable(self): @@ -951,6 +957,7 @@ def BlackBox(self, *args, **kwargs): # The actual function that does things *(np.array([0, 0, 0]) * units.dimensionless,), **{'notY': np.array([0, 0]) * units.dimensionless} ) +<<<<<<< HEAD def test_edi_blackbox_bare_example_4(self): "Tests a black box example construction without an optimization problem" @@ -1028,6 +1035,8 @@ def BlackBox(self, *args, **kwargs): bb = PassThrough() bbo = bb.BlackBox(1.0, 1.0) bbo = bb.BlackBox({'x': np.linspace(0, 10, 11), 'y': np.linspace(0, 10, 11)}) +======= +>>>>>>> parent of f76f4974b (addressing concerns in PR) if __name__ == '__main__': diff --git a/pyomo/contrib/edi/tests/test_examples.py b/pyomo/contrib/edi/tests/test_examples.py index bb59eae7851..2bbad68bb08 100644 --- a/pyomo/contrib/edi/tests/test_examples.py +++ b/pyomo/contrib/edi/tests/test_examples.py @@ -18,8 +18,12 @@ import pyomo.common.unittest as unittest import pyomo.environ as pyo from pyomo.common.dependencies import attempt_import + +from pyomo.core.base.units_container import pint_available + import importlib +<<<<<<< HEAD testIndex = 0 from pyomo.core.base.units_container import pint_available @@ -31,6 +35,12 @@ @unittest.skipIf(not numpy_available, 'Testing pyomo.contrib.edi requires numpy') @unittest.skipIf(not scipy_available, 'Testing pyomo.contrib.edi requires scipy') @unittest.skipIf(not pint_available, 'Testing units requires pint') +======= + +testIndex = 0 + + +>>>>>>> parent of f76f4974b (addressing concerns in PR) class EDIExamples(unittest.TestCase): def test_edi_example_placeholder(self): "A placeholder" diff --git a/pyomo/contrib/edi/tests/test_formulation.py b/pyomo/contrib/edi/tests/test_formulation.py index 91346d722c3..b9d5414e525 100644 --- a/pyomo/contrib/edi/tests/test_formulation.py +++ b/pyomo/contrib/edi/tests/test_formulation.py @@ -17,12 +17,31 @@ import pyomo.common.unittest as unittest import pyomo.environ as pyo + +# from pyomo.opt import check_optimal_termination from pyomo.common.dependencies import attempt_import from pyomo.core.base.units_container import pint_available +<<<<<<< HEAD + +======= +>>>>>>> parent of f76f4974b (addressing concerns in PR) + +# np, numpy_available = attempt_import( +# 'numpy', 'edi requires numpy' +# ) +# scipy, scipy_available = attempt_import( +# 'scipy', 'inverse_reduced_hessian requires scipy' +# ) + +# if not (numpy_available and scipy_available): +# if not numpy_available: +# raise unittest.SkipTest( +# 'edi.formulation tests require numpy' +# ) -@unittest.skipIf(not pint_available, 'Testing pyomo.contrib.edi requires pint') +@unittest.skipIf(not pint_available, 'Testing units requires pint') class TestEDIFormulation(unittest.TestCase): def test_edi_formulation_init(self): "Tests that a formulation initializes to the correct type and has proper data" From e5fbc0e0d40c6a55e456d714233d37558ecfed11 Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Tue, 8 Aug 2023 10:20:07 -0600 Subject: [PATCH 31/61] fixing imports --- pyomo/contrib/edi/blackBoxFunctionModel.py | 53 ++++++++------------- pyomo/contrib/edi/tests/test_blackbox.py | 23 ++++----- pyomo/contrib/edi/tests/test_docSnippets.py | 9 ++-- 3 files changed, 37 insertions(+), 48 deletions(-) diff --git a/pyomo/contrib/edi/blackBoxFunctionModel.py b/pyomo/contrib/edi/blackBoxFunctionModel.py index b7d5c0bb55c..c7d33b631e3 100644 --- a/pyomo/contrib/edi/blackBoxFunctionModel.py +++ b/pyomo/contrib/edi/blackBoxFunctionModel.py @@ -16,46 +16,35 @@ # ___________________________________________________________________________ import copy -import numpy as np import pyomo import pyomo.environ as pyo from pyomo.environ import units as pyomo_units -import scipy.sparse as sps -from pyomo.contrib.pynumero.interfaces.external_grey_box import ( - ExternalGreyBoxModel, - ExternalGreyBoxBlock, -) - -<<<<<<< HEAD -try: +from pyomo.common.dependencies import attempt_import + + +egb, egb_available = attempt_import("pyomo.contrib.pynumero.interfaces.external_grey_box") + +from pyomo.common.dependencies import numpy, numpy_available +from pyomo.common.dependencies import scipy, scipy_available + + +if numpy_available: import numpy as np -except: - raise ImportError( - "pyomo.contrib.edi requires numpy to enable black box capability, fix with 'pip install numpy' " - ) +else: + raise ImportError("pyomo.contrib.edi requires numpy to enable black box capability, fix with 'pip install numpy' ") -try: +if scipy_available import scipy.sparse as sps -except: - raise ImportError( - "pyomo.contrib.edi requires scipy to enable black box capability, fix with 'pip install scipy' " - ) - -try: - from pyomo.contrib.pynumero.interfaces.external_grey_box import ( - ExternalGreyBoxModel, - ExternalGreyBoxBlock, - ) -except: - raise ImportError( - "pyomo.contrib.edi requires pyomo.contrib.pynumero to be installed to enable black box capability, this should have installed with base pyomo" - ) - -======= ->>>>>>> parent of f76f4974b (addressing concerns in PR) +else: + raise ImportError("pyomo.contrib.edi requires scipy to enable black box capability, fix with 'pip install scipy' ") + +if egb_available: + from pyomo.contrib.pynumero.interfaces.external_grey_box import ExternalGreyBoxModel, ExternalGreyBoxBlock +else: + raise ImportError("pyomo.contrib.edi requires pyomo.contrib.pynumero to be installed to enable black box capability, this should have installed with base pyomo") class BlackBoxFunctionModel_Variable(object): - def __init__(self, name, units, description='', size=0): + def __init__(self, name, units, description = '', size = 0): # Order matters self.name = name self.units = units diff --git a/pyomo/contrib/edi/tests/test_blackbox.py b/pyomo/contrib/edi/tests/test_blackbox.py index d8831f1a9cc..faced47b207 100644 --- a/pyomo/contrib/edi/tests/test_blackbox.py +++ b/pyomo/contrib/edi/tests/test_blackbox.py @@ -19,24 +19,21 @@ import pyomo.environ as pyo from pyomo.common.dependencies import attempt_import -from pyomo.core.base.units_container import pint_available +from pyomo.core.base.units_container import ( + pint_available, +) -np, numpy_available = attempt_import('numpy', 'edi requires numpy') -# scipy, scipy_available = attempt_import( -# 'scipy', 'inverse_reduced_hessian requires scipy' -# ) +from pyomo.common.dependencies import numpy, numpy_available +from pyomo.common.dependencies import scipy, scipy_available +egb, egb_available = attempt_import("pyomo.contrib.pynumero.interfaces.external_grey_box") -# if not (numpy_available and scipy_available): -if not numpy_available: - raise unittest.SkipTest('edi.formulation tests require numpy') +if numpy_available: + import numpy as np - -<<<<<<< HEAD +@unittest.skipIf(not egb_available, 'Testing pyomo.contrib.edi requires pynumero external grey boxes') @unittest.skipIf(not numpy_available, 'Testing pyomo.contrib.edi requires numpy') @unittest.skipIf(not scipy_available, 'Testing pyomo.contrib.edi requires scipy') -======= ->>>>>>> parent of f76f4974b (addressing concerns in PR) -@unittest.skipIf(not pint_available, 'Testing units requires pint') +@unittest.skipIf(not pint_available, 'Testing units requires pint') class TestEDIBlackBox(unittest.TestCase): def test_edi_blackbox_variable(self): "Tests the black box variable class" diff --git a/pyomo/contrib/edi/tests/test_docSnippets.py b/pyomo/contrib/edi/tests/test_docSnippets.py index 7bb97ca6227..38b98aa046a 100644 --- a/pyomo/contrib/edi/tests/test_docSnippets.py +++ b/pyomo/contrib/edi/tests/test_docSnippets.py @@ -19,18 +19,21 @@ import pyomo.environ as pyo from pyomo.common.dependencies import attempt_import -from pyomo.core.base.units_container import pint_available +from pyomo.core.base.units_container import ( + pint_available, +) from pyomo.common.dependencies import numpy, numpy_available from pyomo.common.dependencies import scipy, scipy_available +egb, egb_available = attempt_import("pyomo.contrib.pynumero.interfaces.external_grey_box") if numpy_available: import numpy as np - +@unittest.skipIf(not egb_available, 'Testing pyomo.contrib.edi requires pynumero external grey boxes') @unittest.skipIf(not numpy_available, 'Testing pyomo.contrib.edi requires numpy') @unittest.skipIf(not scipy_available, 'Testing pyomo.contrib.edi requires scipy') -@unittest.skipIf(not pint_available, 'Testing units requires pint') +@unittest.skipIf(not pint_available, 'Testing units requires pint') class TestEDISnippets(unittest.TestCase): def test_edi_snippet_formuation_01(self): # BEGIN: Formulation_Snippet_01 From 10d68453770296de44bc1f2460dcaf84a1637e7a Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Tue, 8 Aug 2023 10:37:27 -0600 Subject: [PATCH 32/61] fixing headers and staging for PR --- pyomo/contrib/edi/blackBoxFunctionModel.py | 618 +++++------ pyomo/contrib/edi/formulation.py | 378 ++----- pyomo/contrib/edi/tests/test_blackbox.py | 1026 ++++++++----------- pyomo/contrib/edi/tests/test_docSnippets.py | 973 +++++++++--------- pyomo/contrib/edi/tests/test_examples.py | 38 +- pyomo/contrib/edi/tests/test_formulation.py | 917 +++++------------ 6 files changed, 1576 insertions(+), 2374 deletions(-) diff --git a/pyomo/contrib/edi/blackBoxFunctionModel.py b/pyomo/contrib/edi/blackBoxFunctionModel.py index c7d33b631e3..4ec0cbfc901 100644 --- a/pyomo/contrib/edi/blackBoxFunctionModel.py +++ b/pyomo/contrib/edi/blackBoxFunctionModel.py @@ -50,34 +50,29 @@ def __init__(self, name, units, description = '', size = 0): self.units = units self.size = size self.description = description - - # ===================================================================================================================== - # The printing function - # ===================================================================================================================== +# ===================================================================================================================== +# The printing function +# ===================================================================================================================== def __repr__(self): - return self.name - - # ===================================================================================================================== - # Define the name - # ===================================================================================================================== + return self.name +# ===================================================================================================================== +# Define the name +# ===================================================================================================================== @property def name(self): return self._name - @name.setter - def name(self, val): + def name(self,val): if isinstance(val, str): self._name = val else: raise ValueError('Invalid name. Must be a string.') - - # ===================================================================================================================== - # Define the units - # ===================================================================================================================== +# ===================================================================================================================== +# Define the units +# ===================================================================================================================== @property def units(self): return self._units - @units.setter def units(self, val): # set dimensionless if a null string is passed in @@ -86,101 +81,87 @@ def units(self, val): val = 'dimensionless' if val is None: val = 'dimensionless' - + if isinstance(val, str): self._units = pyomo_units.__getattr__(val) elif isinstance(val, pyomo.core.base.units_container._PyomoUnit): self._units = val else: - raise ValueError( - 'Invalid units. Must be a string compatible with pint or a unit instance.' - ) - - # ===================================================================================================================== - # Define the size - # ===================================================================================================================== + raise ValueError('Invalid units. Must be a string compatible with pint or a unit instance.') +# ===================================================================================================================== +# Define the size +# ===================================================================================================================== @property def size(self): return self._size - @size.setter def size(self, val): invalid = False - if isinstance(val, (list, tuple)): + if isinstance(val,(list, tuple)): sizeTemp = [] for x in val: if isinstance(x, str): # is a vector of unknown length, should be 'inf', but any string accepted x = -1 # pass - elif not isinstance(x, int): - raise ValueError( - 'Invalid size. Must be an integer or list/tuple of integers' - ) + elif not isinstance(x,int): + raise ValueError('Invalid size. Must be an integer or list/tuple of integers') if x == 1: - raise ValueError( - 'A value of 1 is not valid for defining size. Use fewer dimensions.' - ) + raise ValueError('A value of 1 is not valid for defining size. Use fewer dimensions.') sizeTemp.append(x) self._size = val else: if val is None: - self._size = 0 # set to scalar + self._size = 0 # set to scalar elif isinstance(val, str): # is a 1D vector of unknown length, should be 'inf', but any string accepted self._size = -1 # pass elif isinstance(val, int): if val == 1: - raise ValueError( - 'A value of 1 is not valid for defining size. Use 0 to indicate a scalar value.' - ) + raise ValueError('A value of 1 is not valid for defining size. Use 0 to indicate a scalar value.') else: self._size = val else: - raise ValueError( - 'Invalid size. Must be an integer or list/tuple of integers' - ) - - # ===================================================================================================================== - # Define the description - # ===================================================================================================================== + raise ValueError('Invalid size. Must be an integer or list/tuple of integers') +# ===================================================================================================================== +# Define the description +# ===================================================================================================================== @property def description(self): return self._description - @description.setter def description(self, val): - if isinstance(val, str): + if isinstance(val,str): self._description = val else: raise ValueError('Invalid description. Must be a string.') class TypeCheckedList(list): - def __init__(self, checkItem, itemList=None): + def __init__(self, checkItem, itemList = None): super(TypeCheckedList, self).__init__() self.checkItem = checkItem - + if itemList is not None: if isinstance(itemList, list) or isinstance(itemList, tuple): for itm in itemList: self.append(itm) else: raise ValueError('Input to itemList is not iterable') - + def __setitem__(self, key, val): if isinstance(val, self.checkItem): super(TypeCheckedList, self).__setitem__(key, val) - elif isinstance(val, (tuple, list)): - cks = [isinstance(vl, self.checkItem) for vl in val] + elif isinstance(val, (tuple,list)): + cks = [isinstance(vl,self.checkItem) for vl in val] if sum(cks) == len(cks): super(TypeCheckedList, self).__setitem__(key, val) else: - raise ValueError('Input must be an instance of the defined type') + raise ValueError('Input must be an instance of the defined type') else: raise ValueError('Input must be an instance of the defined type') - + def append(self, val): if isinstance(val, self.checkItem): super(TypeCheckedList, self).append(val) @@ -190,7 +171,7 @@ def append(self, val): class BBList(TypeCheckedList): def __init__(self): - super(BBList, self).__init__(BlackBoxFunctionModel_Variable, []) + super(BBList, self).__init__(BlackBoxFunctionModel_Variable,[]) self._lookupDict = {} self._counter = 0 @@ -202,75 +183,64 @@ def __getitem__(self, val): else: raise ValueError('Input must be an integer or a valid variable name') + + def append(*args, **kwargs): args = list(args) self = args.pop(0) - + if len(args) + len(kwargs.values()) == 1: if len(args) == 1: inputData = args[0] if len(kwargs.values()) == 1: inputData = list(kwargs.values())[0] - + if isinstance(inputData, self.checkItem): if inputData.name in self._lookupDict.keys(): - raise ValueError( - "Key '%s' already exists in the input list" % (inputData.name) - ) + raise ValueError("Key '%s' already exists in the input list"%(inputData.name)) self._lookupDict[inputData.name] = self._counter self._counter += 1 super(BBList, self).append(inputData) else: if isinstance(inputData, str): - raise ValueError( - "Key '%s' not passed in to the black box variable constructor" - % ('units') - ) + raise ValueError("Key '%s' not passed in to the black box variable constructor"%('units')) else: raise ValueError('Invalid (single) input type') - + elif len(args) + len(kwargs.values()) <= 4: - argKeys = ['name', 'units', 'description', 'size'] - ipd = dict(zip(argKeys[0 : len(args)], args)) + argKeys = ['name','units','description','size'] + ipd = dict(zip(argKeys[0:len(args)],args)) for ky, vl in kwargs.items(): if ky in ipd: - raise ValueError( - "Key '%s' declared after non-keyword arguments and is out of order" - % (ky) - ) + raise ValueError("Key '%s' declared after non-keyword arguments and is out of order"%(ky)) else: - ipd[ky] = vl - + ipd[ky]=vl + for ak in argKeys: if ak not in ipd.keys(): if ak == 'description': - ipd['description'] = '' + ipd['description']='' elif ak == 'size': ipd['size'] = 0 else: - raise ValueError( - "Key '%s' not passed in to the black box variable constructor" - % (ak) - ) + raise ValueError("Key '%s' not passed in to the black box variable constructor"%(ak)) + if ipd['name'] in self._lookupDict.keys(): - raise ValueError( - "Key '%s' already exists in the input list" % (ipd['name']) - ) + raise ValueError("Key '%s' already exists in the input list"%(ipd['name'])) self._lookupDict[ipd['name']] = self._counter self._counter += 1 super(BBList, self).append(BlackBoxFunctionModel_Variable(**ipd)) - + else: raise ValueError('Too many inputs to a black box variable') errorString = 'This function is calling to the base class and has not been defined.' - class BlackBoxFunctionModel(ExternalGreyBoxModel): - # --------------------------------------------------------------------------------------------------------------------- - # --------------------------------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------------------------------- def __init__(self): super(BlackBoxFunctionModel, self).__init__() @@ -278,9 +248,9 @@ def __init__(self): self.inputs = BBList() self.outputs = BBList() - self.inputVariables_optimization = None + self.inputVariables_optimization = None self.outputVariables_optimization = None - + # A simple description of the model self.description = None @@ -289,17 +259,15 @@ def __init__(self): self._cache = None self._NunwrappedOutputs = None - self._NunwrappedInputs = None + self._NunwrappedInputs = None - def setOptimizationVariables( - self, inputVariables_optimization, outputVariables_optimization - ): - self.inputVariables_optimization = inputVariables_optimization + def setOptimizationVariables(self, inputVariables_optimization, outputVariables_optimization): + self.inputVariables_optimization = inputVariables_optimization self.outputVariables_optimization = outputVariables_optimization - # --------------------------------------------------------------------------------------------------------------------- - # pyomo things - # --------------------------------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------------------------------- +# pyomo things +# --------------------------------------------------------------------------------------------------------------------- def input_names(self): inputs_unwrapped = [] for ivar in self.inputVariables_optimization: @@ -310,9 +278,10 @@ def input_names(self): for vi in validIndices: inputs_unwrapped.append(ivar[vi]) else: - raise ValueError("Invalid type for input variable") + raise ValueError("Invalid type for input variable") + + return [ip.__str__() for ip in inputs_unwrapped] - return [ip.__str__() for ip in inputs_unwrapped] def output_names(self): outputs_unwrapped = [] @@ -324,7 +293,7 @@ def output_names(self): for vi in validIndices: outputs_unwrapped.append(ovar[vi]) else: - raise ValueError("Invalid type for output variable") + raise ValueError("Invalid type for output variable") return [op.__str__() for op in outputs_unwrapped] @@ -336,15 +305,16 @@ def evaluate_outputs(self): self.fillCache() opts = self._cache['pyomo_outputs'] return opts - + def evaluate_jacobian_outputs(self): self.fillCache() jac = self._cache['pyomo_jacobian'] return jac - def post_init_setup(self, defaultVal=1.0): + def post_init_setup(self, defaultVal = 1.0): self._input_values = np.ones(self._NunwrappedInputs) * defaultVal + def fillCache(self): if self._cache is None: self._cache = {} @@ -354,113 +324,95 @@ def fillCache(self): ptr = 0 - for i in range(0, len(self.inputVariables_optimization)): + for i in range(0,len(self.inputVariables_optimization)): optimizationInput = self.inputVariables_optimization[i] - if not isinstance( - optimizationInput, - (pyomo.core.base.var.IndexedVar, pyomo.core.base.var.ScalarVar), - ): + if not isinstance(optimizationInput, (pyomo.core.base.var.IndexedVar, pyomo.core.base.var.ScalarVar)): raise ValueError("Invalid input variable type") - ipt = self.inputs[i] + ipt = self.inputs[i] - shape = [len(idx) for idx in optimizationInput.index_set().subsets()] - localShape = ipt.size + shape = [len(idx) for idx in optimizationInput.index_set().subsets()] + localShape = ipt.size optimizationUnits = self.inputVariables_optimization[i].get_units() - localUnits = ipt.units + localUnits = ipt.units if isinstance(optimizationInput, pyomo.core.base.var.IndexedVar): - value = np.zeros(shape) + value = np.zeros(shape) for vix in list(optimizationInput.index_set().data()): - raw_val = float(raw_inputs[ptr]) * optimizationUnits - raw_val_correctedUnits = pyomo_units.convert( - raw_val, localUnits - ) + raw_val = float(raw_inputs[ptr]) * optimizationUnits + raw_val_correctedUnits = pyomo_units.convert(raw_val, localUnits) value[vix] = pyo.value(raw_val_correctedUnits) ptr += 1 - self.sizeCheck(localShape, value * localUnits) - bb_inputs.append(value * localUnits) + self.sizeCheck(localShape, value*localUnits) + bb_inputs.append(value*localUnits) - else: # isinstance(optimizationInput, pyomo.core.base.var.ScalarVar):, just checked this + else: # isinstance(optimizationInput, pyomo.core.base.var.ScalarVar):, just checked this value = raw_inputs[ptr] * optimizationUnits ptr += 1 self.sizeCheck(localShape, value) value_correctedUnits = pyomo_units.convert(value, localUnits) bb_inputs.append(value_correctedUnits) + bbo = self.BlackBox(*bb_inputs) - self._cache['raw'] = bbo - self._cache['raw_value'] = bbo[0] - self._cache['raw_jacobian'] = bbo[1] + self._cache['raw'] = bbo + self._cache['raw_value'] = bbo[0] + self._cache['raw_jacobian'] = bbo[1] outputVector = [] - if not isinstance(bbo[0], (list, tuple)): + if not isinstance(bbo[0], (list,tuple)): valueList = [bbo[0]] jacobianList = [bbo[1]] else: valueList = bbo[0] jacobianList = bbo[1] - for i in range(0, len(valueList)): + for i in range(0,len(valueList)): optimizationOutput = self.outputVariables_optimization[i] - if not isinstance( - optimizationOutput, - (pyomo.core.base.var.IndexedVar, pyomo.core.base.var.ScalarVar), - ): + if not isinstance(optimizationOutput, (pyomo.core.base.var.IndexedVar, pyomo.core.base.var.ScalarVar)): raise ValueError("Invalid output variable type") - opt = self.outputs[i] + opt = self.outputs[i] - modelOutputUnits = opt.units + modelOutputUnits = opt.units outputOptimizationUnits = optimizationOutput.get_units() vl = valueList[i] if isinstance(vl, pyomo.core.expr.numeric_expr.NumericNDArray): validIndexList = optimizationOutput.index_set().data() - for j in range(0, len(validIndexList)): + for j in range(0,len(validIndexList)): vi = validIndexList[j] - corrected_value = pyo.value( - pyomo_units.convert(vl[vi], outputOptimizationUnits) - ) # now unitless in correct units - outputVector.append(corrected_value) - - elif isinstance( - vl, - ( - pyomo.core.expr.numeric_expr.NPV_ProductExpression, - pyomo.core.base.units_container._PyomoUnit, - ), - ): - corrected_value = pyo.value( - pyomo_units.convert(vl, outputOptimizationUnits) - ) # now unitless in correct units - outputVector.append(corrected_value) + corrected_value = pyo.value(pyomo_units.convert(vl[vi], outputOptimizationUnits)) # now unitless in correct units + outputVector.append(corrected_value) + + elif isinstance(vl, (pyomo.core.expr.numeric_expr.NPV_ProductExpression, + pyomo.core.base.units_container._PyomoUnit)): + corrected_value = pyo.value(pyomo_units.convert(vl, outputOptimizationUnits)) # now unitless in correct units + outputVector.append(corrected_value) else: raise ValueError("Invalid output variable type") self._cache['pyomo_outputs'] = outputVector - outputJacobian = ( - np.ones([self._NunwrappedOutputs, self._NunwrappedInputs]) * -1 - ) + outputJacobian = np.ones([self._NunwrappedOutputs, self._NunwrappedInputs]) * -1 ptr_row = 0 - ptr_col = 0 + ptr_col = 0 - for i in range(0, len(jacobianList)): + for i in range(0,len(jacobianList)): oopt = self.outputVariables_optimization[i] # Checked about 20 lines above # if not isinstance(oopt, (pyomo.core.base.var.ScalarVar,pyomo.core.base.var.IndexedVar)): - # raise ValueError("Invalid type for output variable") + # raise ValueError("Invalid type for output variable") lopt = self.outputs[i] oounits = oopt.get_units() lounits = lopt.units # oshape = [len(idx) for idx in oopt.index_set().subsets()] ptr_col = 0 - for j in range(0, len(self.inputs)): + for j in range(0,len(self.inputs)): oipt = self.inputVariables_optimization[j] # This is checked about 80 lines up # if not isinstance(oipt, (pyomo.core.base.var.ScalarVar,pyomo.core.base.var.IndexedVar)): - # raise ValueError("Invalid type for output variable") + # raise ValueError("Invalid type for output variable") lipt = self.inputs[j] oiunits = oipt.get_units() liunits = lipt.units @@ -468,44 +420,31 @@ def fillCache(self): jacobianValue_raw = jacobianList[i][j] - if isinstance( - jacobianValue_raw, - ( - pyomo.core.expr.numeric_expr.NPV_ProductExpression, - pyomo.core.base.units_container._PyomoUnit, - ), - ): - corrected_value = pyo.value( - pyomo_units.convert(jacobianValue_raw, oounits / oiunits) - ) # now unitless in correct units - outputJacobian[ptr_row, ptr_col] = corrected_value + if isinstance(jacobianValue_raw, (pyomo.core.expr.numeric_expr.NPV_ProductExpression, + pyomo.core.base.units_container._PyomoUnit)): + corrected_value = pyo.value(pyomo_units.convert(jacobianValue_raw, oounits/oiunits)) # now unitless in correct units + outputJacobian[ptr_row,ptr_col] = corrected_value ptr_col += 1 ptr_row_step = 1 - elif isinstance( - jacobianValue_raw, pyomo.core.expr.numeric_expr.NumericNDArray - ): + elif isinstance(jacobianValue_raw,pyomo.core.expr.numeric_expr.NumericNDArray): jshape = jacobianValue_raw.shape if isinstance(oopt, pyomo.core.base.var.ScalarVar): oshape = 0 - else: # isinstance(oopt, pyomo.core.base.var.IndexedVar), checked above - oshape = [len(idx) for idx in oopt.index_set().subsets()] + else: # isinstance(oopt, pyomo.core.base.var.IndexedVar), checked above + oshape = [len(idx) for idx in oopt.index_set().subsets()] if isinstance(oipt, pyomo.core.base.var.ScalarVar): ishape = 0 - else: # isinstance(oipt, pyomo.core.base.var.IndexedVar), checked above - ishape = [len(idx) for idx in oipt.index_set().subsets()] + else: # isinstance(oipt, pyomo.core.base.var.IndexedVar), checked above + ishape = [len(idx) for idx in oipt.index_set().subsets()] if oshape == 0: validIndices = list(oipt.index_set().data()) for vi in validIndices: - corrected_value = pyo.value( - pyomo_units.convert( - jacobianValue_raw[vi], oounits / oiunits - ) - ) # now unitless in correct units - outputJacobian[ptr_row, ptr_col] = corrected_value + corrected_value = pyo.value(pyomo_units.convert(jacobianValue_raw[vi], oounits/oiunits)) # now unitless in correct units + outputJacobian[ptr_row,ptr_col] = corrected_value ptr_col += 1 ptr_row_step = 1 @@ -513,39 +452,30 @@ def fillCache(self): ptr_row_cache = ptr_row validIndices = list(oopt.index_set().data()) for vi in validIndices: - corrected_value = pyo.value( - pyomo_units.convert( - jacobianValue_raw[vi], oounits / oiunits - ) - ) # now unitless in correct units - outputJacobian[ptr_row, ptr_col] = corrected_value + corrected_value = pyo.value(pyomo_units.convert(jacobianValue_raw[vi], oounits/oiunits)) # now unitless in correct units + outputJacobian[ptr_row,ptr_col] = corrected_value ptr_row += 1 ptr_row = ptr_row_cache ptr_row_step = len(validIndices) # elif ishape == 0 and oshape == 0: # Handled by the scalar case above - else: + else: # both are dimensioned vectors - # oshape, ishape, jshape + #oshape, ishape, jshape ptr_row_cache = ptr_row ptr_col_cache = ptr_col validIndices_o = list(oopt.index_set().data()) validIndices_i = list(oipt.index_set().data()) for vio in validIndices_o: - if isinstance(vio, (float, int)): + if isinstance(vio, (float,int)): vio = (vio,) for vii in validIndices_i: - if isinstance(vii, (float, int)): + if isinstance(vii, (float,int)): vii = (vii,) - corrected_value = pyo.value( - pyomo_units.convert( - jacobianValue_raw[vio + vii], - oounits / oiunits, - ) - ) # now unitless in correct units - outputJacobian[ptr_row, ptr_col] = corrected_value + corrected_value = pyo.value(pyomo_units.convert(jacobianValue_raw[vio + vii], oounits/oiunits)) # now unitless in correct units + outputJacobian[ptr_row,ptr_col] = corrected_value ptr_col += 1 ptr_col = ptr_col_cache ptr_row += 1 @@ -555,41 +485,37 @@ def fillCache(self): else: raise ValueError("Invalid jacobian type") - ptr_row += ptr_row_step + ptr_row += ptr_row_step self._cache['pyomo_jacobian'] = sps.coo_matrix(outputJacobian) - # --------------------------------------------------------------------------------------------------------------------- - # --------------------------------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------------------------------- # These models must be defined in each individual model, just placeholders here def BlackBox(*args, **kwargs): raise AttributeError(errorString) + def convert(self, val, unts): try: val = val * pyomo_units.dimensionless except: - pass ## will handle later - - if isinstance( - val, - ( - pyomo.core.base.units_container._PyomoUnit, - pyomo.core.expr.numeric_expr.NPV_ProductExpression, - ), - ): + pass ## will handle later + + if isinstance(val, (pyomo.core.base.units_container._PyomoUnit,pyomo.core.expr.numeric_expr.NPV_ProductExpression)): return pyomo_units.convert(val, unts) elif isinstance(val, pyomo.core.expr.numeric_expr.NumericNDArray): shp = val.shape ix = np.ndindex(*shp) opt = np.zeros(shp) - for i in range(0, np.prod(shp)): + for i in range(0,np.prod(shp)): ixt = next(ix) - opt[ixt] = pyo.value(pyomo_units.convert(val[ixt], unts)) - return opt * unts + opt[ixt] = pyo.value(pyomo_units.convert(val[ixt],unts)) + return opt*unts else: raise ValueError('Invalid type passed to unit conversion function') + def pyomo_value(self, val): try: return pyo.value(val) @@ -598,19 +524,19 @@ def pyomo_value(self, val): shp = val.shape ix = np.ndindex(*shp) opt = np.zeros(shp) - for i in range(0, np.prod(shp)): + for i in range(0,np.prod(shp)): ixt = next(ix) opt[ixt] = pyo.value(val[ixt]) return opt else: raise ValueError('Invalid type passed to pyomo_value function') - # --------------------------------------------------------------------------------------------------------------------- - # --------------------------------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------------------------------- def parseInputs(self, *args, **kwargs): - args = list(args) # convert tuple to list + args = list(args) # convert tuple to list - inputNames = [self.inputs[i].name for i in range(0, len(self.inputs))] + inputNames = [self.inputs[i].name for i in range(0,len(self.inputs))] # ------------------------------ # ------------------------------ @@ -623,44 +549,32 @@ def parseInputs(self, *args, **kwargs): if len(inputNames) == 1: try: rs = self.sanitizeInputs(inputData) - return ( - [dict(zip(inputNames, [rs]))], - -self.availableDerivative - 1, - {}, - ) # one input being passed in + return [dict(zip(inputNames,[rs]))], -self.availableDerivative-1, {} # one input being passed in except: - pass # otherwise, proceed - - if isinstance(inputData, (list, tuple)): + pass #otherwise, proceed + + if isinstance(inputData, (list,tuple)): dataRuns = [] for idc in inputData: if isinstance(idc, dict): sips = self.sanitizeInputs(**idc) if len(inputNames) == 1: sips = [sips] - runDictS = dict(zip(inputNames, sips)) - dataRuns.append( - runDictS - ) # the BlackBox([{'x1':x1, 'x2':x2},{'x1':x1, 'x2':x2},...]) case - elif isinstance(idc, (list, tuple)): + runDictS = dict(zip(inputNames,sips)) + dataRuns.append(runDictS) # the BlackBox([{'x1':x1, 'x2':x2},{'x1':x1, 'x2':x2},...]) case + elif isinstance(idc,(list,tuple)): if len(idc) == len(inputNames): sips = self.sanitizeInputs(*idc) if len(inputNames) == 1: sips = [sips] - runDictS = dict(zip(inputNames, sips)) - dataRuns.append( - runDictS - ) # the BlackBox([ [x1, x2], [x1, x2],...]) case + runDictS = dict(zip(inputNames,sips)) + dataRuns.append(runDictS) # the BlackBox([ [x1, x2], [x1, x2],...]) case else: - raise ValueError( - 'Entry in input data list has improper length' - ) + raise ValueError('Entry in input data list has improper length') else: - raise ValueError( - "Invalid data type in the input list. Note that BlackBox([x1,x2,y]) must be passed in as BlackBox([[x1,x2,y]]) or " - + "BlackBox(*[x1,x2,y]) or BlackBox({'x1':x1,'x2':x2,'y':y}) or simply BlackBox(x1, x2, y) to avoid processing singularities. " - + "Best practice is BlackBox({'x1':x1,'x2':x2,'y':y})" - ) + raise ValueError("Invalid data type in the input list. Note that BlackBox([x1,x2,y]) must be passed in as BlackBox([[x1,x2,y]]) or "+ + "BlackBox(*[x1,x2,y]) or BlackBox({'x1':x1,'x2':x2,'y':y}) or simply BlackBox(x1, x2, y) to avoid processing singularities. "+ + "Best practice is BlackBox({'x1':x1,'x2':x2,'y':y})") return dataRuns, self.availableDerivative, {} elif isinstance(inputData, dict): @@ -671,73 +585,45 @@ def parseInputs(self, *args, **kwargs): sips = self.sanitizeInputs(**inputData) if len(inputNames) == 1: sips = [sips] - return ( - [dict(zip(inputNames, sips))], - -self.availableDerivative - 1, - {}, - ) # the BlackBox(*{'x1':x1, 'x2':x2}) case, somewhat likely - - if not all( - [ - inputLengths[i] == inputLengths[0] - for i in range(0, len(inputLengths)) - ] - ): + return [dict(zip(inputNames,sips))], -self.availableDerivative-1, {} # the BlackBox(*{'x1':x1, 'x2':x2}) case, somewhat likely + + if not all([inputLengths[i] == inputLengths[0] for i in range(0,len(inputLengths))]): sips = self.sanitizeInputs(**inputData) - return ( - [dict(zip(inputNames, sips))], - -self.availableDerivative - 1, - {}, - ) # the BlackBox(*{'x1':x1, 'x2':x2}) case where vectors for x1... are passed in (likely to fail on previous line) + return [dict(zip(inputNames,sips))], -self.availableDerivative-1, {} # the BlackBox(*{'x1':x1, 'x2':x2}) case where vectors for x1... are passed in (likely to fail on previous line) else: try: sips = self.sanitizeInputs(**inputData) - return ( - [dict(zip(inputNames, sips))], - -self.availableDerivative - 1, - {}, - ) # the BlackBox(*{'x1':x1, 'x2':x2}) case where vectors all inputs have same length intentionally (likely to fail on previous line) + return [dict(zip(inputNames,sips))], -self.availableDerivative-1, {} # the BlackBox(*{'x1':x1, 'x2':x2}) case where vectors all inputs have same length intentionally (likely to fail on previous line) except: dataRuns = [] - for i in range(0, inputLengths[0]): + for i in range(0,inputLengths[0]): runDict = {} - for ky, vl in inputData.items(): + for ky,vl in inputData.items(): runDict[ky] = vl[i] sips = self.sanitizeInputs(**runDict) if len(inputNames) == 1: sips = [sips] - runDictS = dict(zip(inputNames, sips)) + runDictS = dict(zip(inputNames,sips)) dataRuns.append(runDictS) - return ( - dataRuns, - self.availableDerivative, - {}, - ) # the BlackBox({'x1':x1_vec, 'x2':x2_vec}) case, most likely + return dataRuns, self.availableDerivative, {} # the BlackBox({'x1':x1_vec, 'x2':x2_vec}) case, most likely else: raise ValueError('Keywords did not match the expected list') else: - raise ValueError('Got unexpected data type %s' % (str(type(inputData)))) + raise ValueError('Got unexpected data type %s'%(str(type(inputData)))) # ------------------------------ # ------------------------------ else: - if any( - [ - list(kwargs.keys())[i] in inputNames - for i in range(0, len(list(kwargs.keys()))) - ] - ): + if any([ list(kwargs.keys())[i] in inputNames for i in range(0,len(list(kwargs.keys()))) ]): # some of the inputs are defined in the kwargs if len(args) >= len(inputNames): - raise ValueError( - 'A keyword input is defining an input, but there are too many unkeyed arguments for this to occur. Check the inputs.' - ) + raise ValueError('A keyword input is defining an input, but there are too many unkeyed arguments for this to occur. Check the inputs.') else: if len(args) != 0: - availableKeywords = inputNames[-len(args) :] - else: + availableKeywords = inputNames[-len(args):] + else: availableKeywords = inputNames - valList = args + [None] * (len(inputNames) - len(args)) + valList = args + [None]*(len(inputNames)-len(args)) for ky in availableKeywords: ix = inputNames.index(ky) valList[ix] = kwargs[ky] @@ -746,7 +632,7 @@ def parseInputs(self, *args, **kwargs): # if any([valList[i]==None for i in range(0,len(valList))]): # raise ValueError('Keywords did not properly fill in the remaining arguments. Check the inputs.') - sips = self.sanitizeInputs(*valList) + sips = self.sanitizeInputs(*valList) if len(inputNames) == 1: sips = [sips] @@ -759,26 +645,18 @@ def parseInputs(self, *args, **kwargs): # was in args pass - return ( - [dict(zip(inputNames, sips))], - -self.availableDerivative - 1, - remainingKwargs, - ) # Mix of args and kwargs define inputs + return [dict(zip(inputNames,sips))], -self.availableDerivative-1, remainingKwargs # Mix of args and kwargs define inputs else: # all of the inputs are in the args try: - sips = self.sanitizeInputs(*args[0 : len(inputNames)]) + sips = self.sanitizeInputs(*args[0:len(inputNames)]) if len(inputNames) == 1: sips = [sips] remainingKwargs = copy.deepcopy(kwargs) - remainingKwargs['remainingArgs'] = args[len(inputNames) :] - return ( - [dict(zip(inputNames, sips))], - -self.availableDerivative - 1, - remainingKwargs, - ) # all inputs are in args + remainingKwargs['remainingArgs'] = args[len(inputNames):] + return [dict(zip(inputNames,sips))], -self.availableDerivative-1, remainingKwargs # all inputs are in args # except: except Exception as e: # not possible due to other checks @@ -786,62 +664,46 @@ def parseInputs(self, *args, **kwargs): # raise ValueError(e) if str(e) == 'Not enough inputs': raise ValueError(e) - else: # otherwise, proceed - runCases, returnMode, extra_singleInput = self.parseInputs( - args[0] - ) + else:#otherwise, proceed + runCases, returnMode, extra_singleInput = self.parseInputs(args[0]) remainingKwargs = copy.deepcopy(kwargs) - remainingKwargs['remainingArgs'] = args[len(inputNames) :] + remainingKwargs['remainingArgs'] = args[len(inputNames):] return runCases, returnMode, remainingKwargs - # --------------------------------------------------------------------------------------------------------------------- - # --------------------------------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------------------------------- def sizeCheck(self, size, ipval_correctUnits): if size is not None: szVal = ipval_correctUnits - if isinstance( - szVal, - ( - pyomo.core.expr.numeric_expr.NPV_ProductExpression, - pyomo.core.base.units_container._PyomoUnit, - ), - ): - if size != 0 and size != 1: - raise ValueError( - 'Size did not match the expected size %s (ie: Scalar)' - % (str(size)) - ) + if isinstance(szVal, (pyomo.core.expr.numeric_expr.NPV_ProductExpression, + pyomo.core.base.units_container._PyomoUnit)): + if size != 0 and size != 1 : + raise ValueError('Size did not match the expected size %s (ie: Scalar)'%(str(size))) elif isinstance(szVal, pyomo.core.expr.numeric_expr.NumericNDArray): shp = szVal.shape - if isinstance(size, (int, float)): + if isinstance(size,(int,float)): size = [size] # else: if len(shp) != len(size): - raise ValueError( - 'Shapes/Sizes of %s does not match the expected %s' - % (str(shp), str(size)) - ) - for j in range(0, len(shp)): + raise ValueError('Shapes/Sizes of %s does not match the expected %s'%(str(shp),str(size))) + for j in range(0,len(shp)): if size[j] != -1: # was declared of flexible length if size[j] != shp[j]: - raise ValueError( - 'Shapes/Sizes of %s does not match the expected %s' - % (str(shp), str(size)) - ) + raise ValueError('Shapes/Sizes of %s does not match the expected %s'%(str(shp),str(size))) else: raise ValueError('Invalid type detected when checking size') - # --------------------------------------------------------------------------------------------------------------------- - # --------------------------------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------------------------------- def sanitizeInputs(self, *args, **kwargs): - nameList = [self.inputs[i].name for i in range(0, len(self.inputs))] + nameList = [self.inputs[i].name for i in range(0,len(self.inputs))] if len(args) + len(kwargs.values()) > len(nameList): raise ValueError('Too many inputs') if len(args) + len(kwargs.values()) < len(nameList): raise ValueError('Not enough inputs') inputDict = {} - for i in range(0, len(args)): + for i in range(0,len(args)): rg = args[i] inputDict[nameList[i]] = rg @@ -849,42 +711,34 @@ def sanitizeInputs(self, *args, **kwargs): if ky in nameList: inputDict[ky] = vl else: - raise ValueError( - 'Unexpected input keyword argument %s in the inputs' % (ky) - ) + raise ValueError('Unexpected input keyword argument %s in the inputs'%(ky)) opts = [] - for i in range(0, len(nameList)): + for i in range(0,len(nameList)): name = nameList[i] nameCheck = self.inputs[i].name unts = self.inputs[i].units size = self.inputs[i].size - + # should be impossible # if name != nameCheck: # raise RuntimeError('Something went wrong and values are not consistent. Check your defined inputs.') ipval = inputDict[name] - if isinstance(ipval, pyomo.core.expr.numeric_expr.NumericNDArray): - for ii in range(0, len(ipval)): + if isinstance(ipval, pyomo.core.expr.numeric_expr.NumericNDArray): + for ii in range(0,len(ipval)): try: - ipval[ii] = self.convert(ipval[ii], unts) # ipval.to(unts) + ipval[ii] = self.convert(ipval[ii], unts)#ipval.to(unts) except: - raise ValueError( - 'Could not convert %s of %s to %s' - % (name, str(ipval), str(unts)) - ) + raise ValueError('Could not convert %s of %s to %s'%(name, str(ipval),str(unts))) ipval_correctUnits = ipval else: try: - ipval_correctUnits = self.convert(ipval, unts) # ipval.to(unts) + ipval_correctUnits = self.convert(ipval, unts)#ipval.to(unts) except: - raise ValueError( - 'Could not convert %s of %s to %s' - % (name, str(ipval), str(unts)) - ) + raise ValueError('Could not convert %s of %s to %s'%(name, str(ipval),str(unts))) # superseded by the custom convert function # if not isinstance(ipval_correctUnits, (pyomo.core.expr.numeric_expr.NPV_ProductExpression, @@ -900,8 +754,8 @@ def sanitizeInputs(self, *args, **kwargs): return opts - # --------------------------------------------------------------------------------------------------------------------- - # --------------------------------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------------------------------- def checkOutputs(self, *args, **kwargs): raise NotImplementedError('Contact developers to use this function') # nameList = [self.outputs[i].name for i in range(0,len(self.outputs))] @@ -927,7 +781,7 @@ def checkOutputs(self, *args, **kwargs): # nameCheck = self.outputs[i].name # unts = self.outputs[i].units # size = self.outputs[i].size - + # if name != nameCheck: # raise RuntimeError('Something went wrong and values are not consistent. Check your defined inputs.') @@ -954,15 +808,15 @@ def checkOutputs(self, *args, **kwargs): # return opts - # --------------------------------------------------------------------------------------------------------------------- - # --------------------------------------------------------------------------------------------------------------------- - def getSummary(self, whitespace=6): +# --------------------------------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------------------------------- + def getSummary(self, whitespace = 6): pstr = '\n' pstr += 'Model Description\n' pstr += '=================\n' descr_str = self.description.__repr__() pstr += descr_str[1:-1] + '\n\n' - + longestName = 0 longestUnits = 0 longestSize = 0 @@ -970,7 +824,7 @@ def getSummary(self, whitespace=6): nml = len(ipt.name) if nml > longestName: longestName = nml - unts = ipt.units.__str__() # _repr_html_() + unts = ipt.units.__str__()#_repr_html_() # unts = unts.replace('','^') # unts = unts.replace('','') # unts = unts.replace('\[', '[') @@ -987,37 +841,37 @@ def getSummary(self, whitespace=6): namespace = max([4, longestName]) + whitespace unitspace = max([5, longestUnits]) + whitespace sizespace = max([4, longestSize]) + whitespace - fulllength = namespace + unitspace + sizespace + 11 + fulllength = namespace+unitspace+sizespace+11 pstr += 'Inputs\n' - pstr += '=' * fulllength + pstr += '='*fulllength pstr += '\n' pstr += 'Name'.ljust(namespace) pstr += 'Units'.ljust(unitspace) pstr += 'Size'.ljust(sizespace) pstr += 'Description' pstr += '\n' - pstr += '-' * (namespace - whitespace) - pstr += ' ' * whitespace - pstr += '-' * (unitspace - whitespace) - pstr += ' ' * whitespace - pstr += '-' * (sizespace - whitespace) - pstr += ' ' * whitespace + pstr += '-'*(namespace - whitespace) + pstr += ' '*whitespace + pstr += '-'*(unitspace - whitespace) + pstr += ' '*whitespace + pstr += '-'*(sizespace - whitespace) + pstr += ' '*whitespace pstr += '-----------' pstr += '\n' for ipt in self.inputs: pstr += ipt.name.ljust(namespace) - unts = ipt.units.__str__() # _repr_html_() + unts = ipt.units.__str__()#_repr_html_() # unts = unts.replace('','^') # unts = unts.replace('','') # unts = unts.replace('\[', '[') # unts = unts.replace('\]', ']') pstr += unts.ljust(unitspace) - lnstr = '%s' % (ipt.size.__repr__()) + lnstr = '%s'%(ipt.size.__repr__()) pstr += lnstr.ljust(sizespace) pstr += ipt.description pstr += '\n' pstr += '\n' - + longestName = 0 longestUnits = 0 longestSize = 0 @@ -1025,7 +879,7 @@ def getSummary(self, whitespace=6): nml = len(opt.name) if nml > longestName: longestName = nml - unts = opt.units.__str__() # _repr_html_() + unts = opt.units.__str__()#_repr_html_() # unts = unts.replace('','^') # unts = unts.replace('','') # unts = unts.replace('\[', '[') @@ -1042,51 +896,51 @@ def getSummary(self, whitespace=6): namespace = max([4, longestName]) + whitespace unitspace = max([5, longestUnits]) + whitespace sizespace = max([4, longestSize]) + whitespace - fulllength = namespace + unitspace + sizespace + 11 + fulllength = namespace+unitspace+sizespace+11 pstr += 'Outputs\n' - pstr += '=' * fulllength + pstr += '='*fulllength pstr += '\n' pstr += 'Name'.ljust(namespace) pstr += 'Units'.ljust(unitspace) pstr += 'Size'.ljust(sizespace) pstr += 'Description' pstr += '\n' - pstr += '-' * (namespace - whitespace) - pstr += ' ' * whitespace - pstr += '-' * (unitspace - whitespace) - pstr += ' ' * whitespace - pstr += '-' * (sizespace - whitespace) - pstr += ' ' * whitespace + pstr += '-'*(namespace - whitespace) + pstr += ' '*whitespace + pstr += '-'*(unitspace - whitespace) + pstr += ' '*whitespace + pstr += '-'*(sizespace - whitespace) + pstr += ' '*whitespace pstr += '-----------' pstr += '\n' for opt in self.outputs: pstr += opt.name.ljust(namespace) - unts = opt.units.__str__() # _repr_html_() + unts = opt.units.__str__()#_repr_html_() # unts = unts.replace('','^') # unts = unts.replace('','') # unts = unts.replace('\[', '[') # unts = unts.replace('\]', ']') pstr += unts.ljust(unitspace) - lnstr = '%s' % (opt.size.__repr__()) + lnstr = '%s'%(opt.size.__repr__()) pstr += lnstr.ljust(sizespace) pstr += opt.description pstr += '\n' pstr += '\n' - + return pstr - # --------------------------------------------------------------------------------------------------------------------- - # --------------------------------------------------------------------------------------------------------------------- - +# --------------------------------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------------------------------- + @property def summary(self): return self.getSummary() - # --------------------------------------------------------------------------------------------------------------------- - # --------------------------------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------------------------------- def __repr__(self): pstr = 'AnalysisModel( [' - for i in range(0, len(self.outputs)): + for i in range(0,len(self.outputs)): pstr += self.outputs[i].name pstr += ',' pstr = pstr[0:-1] @@ -1101,4 +955,4 @@ def __repr__(self): pstr += ' , ' pstr = pstr[0:-2] pstr += '])' - return pstr + return pstr \ No newline at end of file diff --git a/pyomo/contrib/edi/formulation.py b/pyomo/contrib/edi/formulation.py index 5f8b636b47f..65080707e6e 100644 --- a/pyomo/contrib/edi/formulation.py +++ b/pyomo/contrib/edi/formulation.py @@ -16,16 +16,13 @@ # ___________________________________________________________________________ import pyomo -import pyomo.environ as pyo +import pyomo.environ as pyo from pyomo.util.check_units import assert_units_consistent from pyomo.environ import ConcreteModel from pyomo.environ import Var, Param, Objective, Constraint, Set from pyomo.environ import maximize, minimize from pyomo.environ import units as pyomo_units -from pyomo.contrib.pynumero.interfaces.external_grey_box import ( - ExternalGreyBoxModel, - ExternalGreyBoxBlock, -) +from pyomo.contrib.pynumero.interfaces.external_grey_box import ExternalGreyBoxModel, ExternalGreyBoxBlock from pyomo.environ import ( Reals, PositiveReals, @@ -46,7 +43,7 @@ PercentFraction, RealInterval, IntegerInterval, -) +) domainList = [ Reals, @@ -70,17 +67,15 @@ IntegerInterval, ] - def decodeUnits(u_val): - if isinstance(u_val, str): - if u_val in ['', '-', 'None', ' ', 'dimensionless']: + if isinstance(u_val,str): + if u_val in ['','-','None',' ','dimensionless']: return pyomo_units.__getattr__('dimensionless') else: return pyomo_units.__getattr__(u_val) else: return u_val - - + class Formulation(ConcreteModel): def __init__(self): super(Formulation, self).__init__() @@ -89,18 +84,17 @@ def __init__(self): self._objective_counter = 0 self._constraint_counter = 0 - self._variable_keys = [] - self._constant_keys = [] + self._variable_keys = [] + self._constant_keys = [] self._objective_keys = [] self._runtimeObjective_keys = [] self._objective_keys = [] self._runtimeConstraint_keys = [] self._constraint_keys = [] self._allConstraint_keys = [] + + def Variable(self, name, guess, units, description='', size = None, bounds=None, domain=None): - def Variable( - self, name, guess, units, description='', size=None, bounds=None, domain=None - ): if domain is None: domain = Reals else: @@ -108,128 +102,47 @@ def Variable( raise RuntimeError("Invalid domain") if bounds is not None: - if not isinstance(bounds, (list, tuple)): - raise ValueError( - 'The keyword bounds must be a 2 length list or tuple of floats' - ) - if len(bounds) != 2: - raise ValueError( - 'The keyword bounds must be a 2 length list or tuple of floats' - ) + if not isinstance(bounds,(list,tuple)): + raise ValueError('The keyword bounds must be a 2 length list or tuple of floats') + if len(bounds)!=2: + raise ValueError('The keyword bounds must be a 2 length list or tuple of floats') if not isinstance(bounds[0], (float, int)): - raise ValueError( - 'The keyword bounds must be a 2 length list or tuple of floats' - ) + raise ValueError('The keyword bounds must be a 2 length list or tuple of floats') if not isinstance(bounds[1], (float, int)): - raise ValueError( - 'The keyword bounds must be a 2 length list or tuple of floats' - ) + raise ValueError('The keyword bounds must be a 2 length list or tuple of floats') if bounds[0] > bounds[1]: raise ValueError("Lower bound is higher than upper bound") + if size is not None: - if isinstance(size, (list, tuple)): - for i in range(0, len(size)): - if not isinstance(size[i], int): - raise ValueError( - 'Invalid size. Must be an integer or list/tuple of integers' - ) - if size[i] == 1 or size[i] == 0: - raise ValueError( - 'A value of 0 or 1 is not valid for defining size. Use fewer dimensions.' - ) + if isinstance(size,(list, tuple)): + for i in range(0,len(size)): + if not isinstance(size[i],int): + raise ValueError('Invalid size. Must be an integer or list/tuple of integers') + if size[i] == 1 or size[i] == 0 : + raise ValueError('A value of 0 or 1 is not valid for defining size. Use fewer dimensions.') if i == 0: -<<<<<<< HEAD - st = pyo.Set(initialize=list(range(0, size[i]))) + st = pyo.Set(initialize=list(range(0,size[i]))) else: - st *= pyo.Set(initialize=list(range(0, size[i]))) + st *= pyo.Set(initialize=list(range(0,size[i]))) st.construct() - self.add_component( - name, - pyo.Var( -======= - st = Set(initialize=list(range(0, size[i]))) - else: - st *= Set(initialize=list(range(0, size[i]))) - st.construct() - self.add_component( - name, - Var( ->>>>>>> parent of f76f4974b (addressing concerns in PR) - st, - name=name, - initialize=guess, - domain=domain, - bounds=bounds, - doc=description, - units=decodeUnits(units), - ), - ) + self.add_component(name, pyo.Var(st, name=name, initialize=guess, domain=domain, bounds=bounds, doc=description, units=decodeUnits(units))) else: if isinstance(size, int): if size == 1 or size == 0: - self.add_component( - name, -<<<<<<< HEAD - pyo.Var( -======= - Var( ->>>>>>> parent of f76f4974b (addressing concerns in PR) - name=name, - initialize=guess, - domain=domain, - bounds=bounds, - doc=description, - units=decodeUnits(units), - ), - ) + self.add_component(name, pyo.Var(name=name, initialize=guess, domain=domain, bounds=bounds, doc=description, units=decodeUnits(units))) else: -<<<<<<< HEAD - st = pyo.Set(initialize=list(range(0, size))) - st.construct() - self.add_component( - name, - pyo.Var( -======= - st = Set(initialize=list(range(0, size))) + st = pyo.Set(initialize=list(range(0,size))) st.construct() - self.add_component( - name, - Var( ->>>>>>> parent of f76f4974b (addressing concerns in PR) - st, - name=name, - initialize=guess, - domain=domain, - bounds=bounds, - doc=description, - units=decodeUnits(units), - ), - ) + self.add_component(name, pyo.Var(st, name=name, initialize=guess, domain=domain, bounds=bounds, doc=description, units=decodeUnits(units))) else: - raise ValueError( - 'Invalid size. Must be an integer or list/tuple of integers' - ) + raise ValueError('Invalid size. Must be an integer or list/tuple of integers') else: - self.add_component( - name, -<<<<<<< HEAD - pyo.Var( -======= - Var( ->>>>>>> parent of f76f4974b (addressing concerns in PR) - name=name, - initialize=guess, - domain=domain, - bounds=bounds, - doc=description, - units=decodeUnits(units), - ), - ) + self.add_component(name, pyo.Var(name=name, initialize=guess, domain=domain, bounds=bounds, doc=description, units=decodeUnits(units))) self.__dict__[name].construct() self._variable_keys.append(name) return self.__dict__[name] - + def Constant(self, name, value, units, description='', size=None, within=None): if within is None: within = Reals @@ -238,164 +151,76 @@ def Constant(self, name, value, units, description='', size=None, within=None): raise RuntimeError("Invalid within") if size is not None: - if isinstance(size, (list, tuple)): - for i in range(0, len(size)): - if not isinstance(size[i], int): - raise ValueError( - 'Invalid size. Must be an integer or list/tuple of integers' - ) + if isinstance(size,(list, tuple)): + for i in range(0,len(size)): + if not isinstance(size[i],int): + raise ValueError('Invalid size. Must be an integer or list/tuple of integers') if size[i] == 1: - raise ValueError( - 'A value of 1 is not valid for defining size. Use fewer dimensions.' - ) + raise ValueError('A value of 1 is not valid for defining size. Use fewer dimensions.') if i == 0: -<<<<<<< HEAD - st = pyo.Set(initialize=list(range(0, size[i]))) - else: - st *= pyo.Set(initialize=list(range(0, size[i]))) - st.construct() - self.add_component( - name, - pyo.Param( -======= - st = Set(initialize=list(range(0, size[i]))) + st = pyo.Set(initialize=list(range(0,size[i]))) else: - st *= Set(initialize=list(range(0, size[i]))) + st *= pyo.Set(initialize=list(range(0,size[i]))) st.construct() - self.add_component( - name, - Param( ->>>>>>> parent of f76f4974b (addressing concerns in PR) - st, - name=name, - initialize=value, - within=within, - doc=description, - units=decodeUnits(units), - mutable=True, - ), - ) + self.add_component(name, pyo.Param(st, name=name, initialize=value, within=within, doc=description, units=decodeUnits(units), mutable=True)) else: if isinstance(size, int): if size == 1 or size == 0: - self.add_component( - name, -<<<<<<< HEAD - pyo.Param( -======= - Param( ->>>>>>> parent of f76f4974b (addressing concerns in PR) - name=name, - initialize=value, - within=within, - doc=description, - units=decodeUnits(units), - mutable=True, - ), - ) + self.add_component(name, pyo.Param(name=name, initialize=value, within=within, doc=description, units=decodeUnits(units), mutable=True)) else: -<<<<<<< HEAD - st = pyo.Set(initialize=list(range(0, size))) - st.construct() - self.add_component( - name, - pyo.Param( -======= - st = Set(initialize=list(range(0, size))) + st = pyo.Set(initialize=list(range(0,size))) st.construct() - self.add_component( - name, - Param( ->>>>>>> parent of f76f4974b (addressing concerns in PR) - st, - name=name, - initialize=value, - within=within, - doc=description, - units=decodeUnits(units), - mutable=True, - ), - ) + self.add_component(name, pyo.Param(st, name=name, initialize=value, within=within, doc=description, units=decodeUnits(units), mutable=True)) else: - raise ValueError( - 'Invalid size. Must be an integer or list/tuple of integers' - ) + raise ValueError('Invalid size. Must be an integer or list/tuple of integers') else: - self.add_component( - name, -<<<<<<< HEAD - pyo.Param( -======= - Param( ->>>>>>> parent of f76f4974b (addressing concerns in PR) - name=name, - initialize=value, - within=within, - doc=description, - units=decodeUnits(units), - mutable=True, - ), - ) - + self.add_component(name, pyo.Param(name=name, initialize=value, within=within, doc=description, units=decodeUnits(units), mutable=True)) + self.__dict__[name].construct() self._constant_keys.append(name) return self.__dict__[name] - + def Objective(self, expr, sense=minimize): self._objective_counter += 1 - self.add_component( - 'objective_' + str(self._objective_counter), -<<<<<<< HEAD - pyo.Objective(expr=expr, sense=sense), -======= - Objective(expr=expr, sense=sense), ->>>>>>> parent of f76f4974b (addressing concerns in PR) - ) - self._objective_keys.append('objective_' + str(self._objective_counter)) - self.__dict__['objective_' + str(self._objective_counter)].construct() - + self.add_component('objective_'+str(self._objective_counter) , pyo.Objective(expr=expr,sense=sense)) + self._objective_keys.append('objective_'+str(self._objective_counter)) + self.__dict__['objective_'+str(self._objective_counter)].construct() + # def RuntimeObjective(self): # pass - + def Constraint(self, expr): self._constraint_counter += 1 - conName = 'constraint_' + str(self._constraint_counter) -<<<<<<< HEAD + conName = 'constraint_'+str(self._constraint_counter) self.add_component(conName, pyo.Constraint(expr=expr)) -======= - self.add_component(conName, Constraint(expr=expr)) ->>>>>>> parent of f76f4974b (addressing concerns in PR) self._constraint_keys.append(conName) self._allConstraint_keys.append(conName) self.__dict__[conName].construct() + def RuntimeConstraint(self, outputs, operators, inputs, black_box): self._constraint_counter += 1 - conName = 'constraint_' + str(self._constraint_counter) + conName = 'constraint_'+str(self._constraint_counter) self._runtimeConstraint_keys.append(conName) self._allConstraint_keys.append(conName) - self.add_component(conName, ExternalGreyBoxBlock()) + self.add_component(conName, ExternalGreyBoxBlock() ) self.__dict__[conName].construct() # TODO: Need to include operators after Michael fixes things - inputs_raw = inputs - outputs_raw = outputs + inputs_raw = inputs + outputs_raw = outputs operators_raw = operators - if isinstance( - inputs_raw, (pyomo.core.base.var.IndexedVar, pyomo.core.base.var.ScalarVar) - ): + if isinstance(inputs_raw, (pyomo.core.base.var.IndexedVar,pyomo.core.base.var.ScalarVar)): inputs_raw = [inputs_raw] elif isinstance(inputs_raw, (list, tuple)): inputs_raw = list(inputs_raw) else: raise ValueError("Invalid type for input variables") - if isinstance( - outputs_raw, (pyomo.core.base.var.IndexedVar, pyomo.core.base.var.ScalarVar) - ): + if isinstance(outputs_raw, (pyomo.core.base.var.IndexedVar,pyomo.core.base.var.ScalarVar)): outputs_raw = [outputs_raw] elif isinstance(outputs_raw, (list, tuple)): outputs_raw = list(outputs_raw) @@ -403,19 +228,17 @@ def RuntimeConstraint(self, outputs, operators, inputs, black_box): raise ValueError("Invalid type for output variables") for lst in [outputs_raw, inputs_raw]: for vr in lst: - if not isinstance( - vr, (pyomo.core.base.var.IndexedVar, pyomo.core.base.var.ScalarVar) - ): - raise ValueError("Invalid type when checking inputs and outputs") + if not isinstance(vr, (pyomo.core.base.var.IndexedVar,pyomo.core.base.var.ScalarVar)): + raise ValueError("Invalid type when checking inputs and outputs") if isinstance(operators_raw, (list, tuple)): operators_raw = list(operators_raw) - elif isinstance(operators_raw, str): + elif isinstance(operators_raw,str): operators_raw = [operators_raw] else: raise ValueError("Invalid type for operators") for opr in operators_raw: - if opr not in ["==", ">=", "<="]: + if opr not in ["==",">=","<="]: raise ValueError("Invalid operator") black_box.setOptimizationVariables(inputs_raw, outputs_raw) @@ -427,7 +250,7 @@ def RuntimeConstraint(self, outputs, operators, inputs, black_box): for ovar in outputs_raw: if isinstance(ovar, pyomo.core.base.var.ScalarVar): outputs_unwrapped.append(ovar) - else: # isinstance(ovar, pyomo.core.base.var.IndexedVar), validated above + else: # isinstance(ovar, pyomo.core.base.var.IndexedVar), validated above validIndices = list(ovar.index_set().data()) for vi in validIndices: outputs_unwrapped.append(ovar[vi]) @@ -436,25 +259,24 @@ def RuntimeConstraint(self, outputs, operators, inputs, black_box): for ivar in inputs_raw: if isinstance(ivar, pyomo.core.base.var.ScalarVar): inputs_unwrapped.append(ivar) - else: # isinstance(ivar, pyomo.core.base.var.IndexedVar), validated above + else: # isinstance(ivar, pyomo.core.base.var.IndexedVar), validated above validIndices = list(ivar.index_set().data()) for vi in validIndices: - inputs_unwrapped.append(ivar[vi]) + inputs_unwrapped.append(ivar[vi]) black_box._NunwrappedOutputs = len(outputs_unwrapped) - black_box._NunwrappedInputs = len(inputs_unwrapped) + black_box._NunwrappedInputs = len(inputs_unwrapped) black_box.post_init_setup() - # TODO: Need to unwrap operators + # TODO: Need to unwrap operators - self.__dict__[conName].set_external_model( - black_box, inputs=inputs_unwrapped, outputs=outputs_unwrapped - ) # ,operators=operators_unwrapped) + + self.__dict__[conName].set_external_model(black_box, inputs=inputs_unwrapped, outputs=outputs_unwrapped)#,operators=operators_unwrapped) def ConstraintList(self, conList): - for i in range(0, len(conList)): + for i in range(0,len(conList)): con = conList[i] - if isinstance(con, (tuple, list)): + if isinstance(con, (tuple,list)): self.RuntimeConstraint(*con) elif isinstance(con, dict): self.RuntimeConstraint(**con) @@ -462,54 +284,28 @@ def ConstraintList(self, conList): self.Constraint(con) def get_variables(self): - return [ - self.__dict__[nm] - for nm in self.__dict__.keys() - if nm in self._variable_keys - ] + return [self.__dict__[nm] for nm in self.__dict__.keys() if nm in self._variable_keys] def get_constants(self): - return [ - self.__dict__[nm] - for nm in self.__dict__.keys() - if nm in self._constant_keys - ] + return [self.__dict__[nm] for nm in self.__dict__.keys() if nm in self._constant_keys] def get_objectives(self): - return [ - self.__dict__[nm] - for nm in self.__dict__.keys() - if nm in self._objective_keys - ] - + return [self.__dict__[nm] for nm in self.__dict__.keys() if nm in self._objective_keys] + def get_constraints(self): - return [ - self.__dict__[nm] - for nm in self.__dict__.keys() - if nm in self._allConstraint_keys - ] + return [self.__dict__[nm] for nm in self.__dict__.keys() if nm in self._allConstraint_keys] def get_explicitConstraints(self): - return [ - self.__dict__[nm] - for nm in self.__dict__.keys() - if nm in self._constraint_keys - ] + return [self.__dict__[nm] for nm in self.__dict__.keys() if nm in self._constraint_keys] def get_runtimeConstraints(self): - return [ - self.__dict__[nm] - for nm in self.__dict__.keys() - if nm in self._runtimeConstraint_keys - ] + return [self.__dict__[nm] for nm in self.__dict__.keys() if nm in self._runtimeConstraint_keys] def check_units(self): - for i in range(1, self._objective_counter + 1): - assert_units_consistent(self.__dict__['objective_' + str(i)]) - - for i in range(1, self._constraint_counter + 1): - if not isinstance( - self.__dict__['constraint_' + str(i)], - pyomo.contrib.pynumero.interfaces.external_grey_box.ExternalGreyBoxBlock, - ): - assert_units_consistent(self.__dict__['constraint_' + str(i)]) + for i in range(1,self._objective_counter+1): + assert_units_consistent(self.__dict__['objective_'+str(i)]) + + for i in range(1,self._constraint_counter+1): + if not isinstance(self.__dict__['constraint_'+str(i)],pyomo.contrib.pynumero.interfaces.external_grey_box.ExternalGreyBoxBlock): + assert_units_consistent(self.__dict__['constraint_'+str(i)]) + \ No newline at end of file diff --git a/pyomo/contrib/edi/tests/test_blackbox.py b/pyomo/contrib/edi/tests/test_blackbox.py index faced47b207..383e2e54dec 100644 --- a/pyomo/contrib/edi/tests/test_blackbox.py +++ b/pyomo/contrib/edi/tests/test_blackbox.py @@ -39,124 +39,105 @@ def test_edi_blackbox_variable(self): "Tests the black box variable class" from pyomo.environ import units from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import ( - BlackBoxFunctionModel_Variable, - TypeCheckedList, - BBList, - BlackBoxFunctionModel, - ) + from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel - x = BlackBoxFunctionModel_Variable('x', '') + x = BlackBoxFunctionModel_Variable('x','') x_print = x.__repr__() x_name = x.name - x_units = x.units - x_size = x.size + x_units = x.units + x_size = x.size x_desc = x.description - self.assertRaises(ValueError, x.__init__, *(1.0, '')) + self.assertRaises(ValueError,x.__init__,*(1.0, '')) x.__init__('x', units.dimensionless) - self.assertRaises(ValueError, x.__init__, *('x', 1.0)) + self.assertRaises(ValueError,x.__init__,*('x', 1.0)) x.__init__('x', units.dimensionless, '', 'flex') - x.__init__('x', units.dimensionless, '', ['flex', 2]) + x.__init__('x', units.dimensionless, '', ['flex',2]) x.__init__('x', units.dimensionless, '', 2) - x.__init__('x', units.dimensionless, '', [2, 2]) + x.__init__('x', units.dimensionless, '', [2,2]) - self.assertRaises(ValueError, x.__init__, *('x', '', '', [[], 2])) - self.assertRaises(ValueError, x.__init__, *('x', '', '', [2, 1])) + self.assertRaises(ValueError,x.__init__,*('x', '','',[[],2])) + self.assertRaises(ValueError,x.__init__,*('x', '','',[2,1])) x.__init__('x', units.dimensionless, '', None) x.__init__('x', None, '', None) - self.assertRaises(ValueError, x.__init__, *('x', '', '', 1)) - self.assertRaises(ValueError, x.__init__, *('x', '', '', {})) - self.assertRaises(ValueError, x.__init__, *('x', '', 1.0)) + self.assertRaises(ValueError,x.__init__,*('x', '','', 1 )) + self.assertRaises(ValueError,x.__init__,*('x', '','', {} )) + self.assertRaises(ValueError,x.__init__,*('x', '', 1.0 )) + + + def test_edi_blackbox_tcl(self): "Tests the black box type checked list class" from pyomo.environ import units from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import ( - BlackBoxFunctionModel_Variable, - TypeCheckedList, - BBList, - BlackBoxFunctionModel, - ) + from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel - tcl = TypeCheckedList(int, [1, 2, 3]) + tcl = TypeCheckedList(int, [1,2,3]) tcl[1] = 1 - tcl[0:2] = [1, 2] + tcl[0:2] = [1,2] + + self.assertRaises(ValueError,tcl.__init__,*( int, 1 )) + self.assertRaises(ValueError,tcl.__setitem__,*( 1, 3.333 )) + self.assertRaises(ValueError,tcl.__setitem__,*( 1, [1, 2.222] )) + self.assertRaises(ValueError,tcl.append,*( 2.222 ,)) + - self.assertRaises(ValueError, tcl.__init__, *(int, 1)) - self.assertRaises(ValueError, tcl.__setitem__, *(1, 3.333)) - self.assertRaises(ValueError, tcl.__setitem__, *(1, [1, 2.222])) - self.assertRaises(ValueError, tcl.append, *(2.222,)) def test_edi_blackbox_bbl(self): "Tests the black box BBList class" from pyomo.environ import units from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import ( - BlackBoxFunctionModel_Variable, - TypeCheckedList, - BBList, - BlackBoxFunctionModel, - ) + from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel bbl = BBList() - bbl.append('x', '') - bbl.append('y', '') - bbl.append(BlackBoxFunctionModel_Variable('z', '')) - bbl.append(var=BlackBoxFunctionModel_Variable('u', '')) - self.assertRaises( - ValueError, bbl.append, *(BlackBoxFunctionModel_Variable('u', ''),) - ) - self.assertRaises(ValueError, bbl.append, *('badvar',)) - self.assertRaises(ValueError, bbl.append, *(2.222,)) - - self.assertRaises(ValueError, bbl.append, *('bv', '', ''), **{'units': 'm'}) - self.assertRaises(ValueError, bbl.append, **{'units': 'm', 'description': 'hi'}) - self.assertRaises(ValueError, bbl.append, **{'name': 'x', 'units': ''}) - self.assertRaises(ValueError, bbl.append, *('bv', '', '', 0, 'extra')) + bbl.append('x','') + bbl.append('y','') + bbl.append(BlackBoxFunctionModel_Variable('z','')) + bbl.append(var = BlackBoxFunctionModel_Variable('u','')) + self.assertRaises(ValueError,bbl.append,*( BlackBoxFunctionModel_Variable('u',''),)) + self.assertRaises(ValueError,bbl.append,*( 'badvar',)) + self.assertRaises(ValueError,bbl.append,*( 2.222,)) + + self.assertRaises(ValueError,bbl.append,*( 'bv','',''),**{'units':'m'}) + self.assertRaises(ValueError,bbl.append,**{'units':'m','description':'hi'}) + self.assertRaises(ValueError,bbl.append,**{'name':'x', 'units':''}) + self.assertRaises(ValueError,bbl.append,*( 'bv','','',0,'extra')) xv = bbl['x'] xv2 = bbl[0] - self.assertRaises(ValueError, bbl.__getitem__, *(2.22,)) + self.assertRaises(ValueError,bbl.__getitem__,*( 2.22, )) + def test_edi_blackbox_someexceptions(self): "Tests some of the exceptions in the black box model class" import numpy as np from pyomo.environ import units from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import ( - BlackBoxFunctionModel_Variable, - TypeCheckedList, - BBList, - BlackBoxFunctionModel, - ) + from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel bb = BlackBoxFunctionModel() - bb.inputVariables_optimization = [1, 2, 3] + bb.inputVariables_optimization = [1,2,3] # bb.set_input_values(np.array([1,2,3])) - self.assertRaises(ValueError, bb.input_names, *()) + self.assertRaises(ValueError,bb.input_names,*( )) # self.assertRaises(ValueError,bb.fillCache,*( )) + bb = BlackBoxFunctionModel() - bb.outputVariables_optimization = [1, 2, 3] - self.assertRaises(ValueError, bb.output_names, *()) + bb.outputVariables_optimization = [1,2,3] + self.assertRaises(ValueError,bb.output_names,*( )) + def test_edi_blackbox_etc_1(self): "Tests a black box assertion issue" - from pyomo.contrib.edi.blackBoxFunctionModel import ( - BlackBoxFunctionModel_Variable, - TypeCheckedList, - BBList, - BlackBoxFunctionModel, - ) - + from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel bbfm = BlackBoxFunctionModel() - self.assertRaises(AttributeError, bbfm.BlackBox, ()) + self.assertRaises(AttributeError,bbfm.BlackBox,()) + def test_edi_blackbox_etc_2(self): "Tests a black box assertion issue" @@ -164,56 +145,53 @@ def test_edi_blackbox_etc_2(self): from pyomo.environ import units import pyomo.environ as pyo from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import ( - BlackBoxFunctionModel_Variable, - TypeCheckedList, - BBList, - BlackBoxFunctionModel, - ) + from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel f = Formulation() - x = f.Variable(name='x', guess=1.0, units='m', description='x variable') - y = f.Variable(name='y', guess=1.0, units='m', description='y variable') - z = f.Variable(name='z', guess=1.0, units='m^2', description='Output var') - f.Objective(x + y) - + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'y variable') + z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'Output var') + f.Objective( x + y ) class UnitCircle(BlackBoxFunctionModel): def __init__(self): super().__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append(name='x', units='ft', description='The x variable') - self.inputs.append(name='y', units='ft', description='The y variable') - self.outputs.append( - name='z', units='ft**2', description='Output variable' - ) + self.inputs.append( name = 'x', + units = 'ft' , + description = 'The x variable' ) + self.inputs.append( name = 'y', + units = 'ft' , + description = 'The y variable' ) + self.outputs.append(name = 'z', + units = 'ft**2', + description = 'Output variable' ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - - def BlackBox(self, x, y): # The actual function that does things + def BlackBox(self, x, y): # The actual function that does things # Converts to correct units then casts to float - x = pyo.value(units.convert(x, self.inputs[0].units)) - y = pyo.value(units.convert(y, self.inputs[1].units)) - z = x**2 + y**2 # Compute z - dzdx = 2 * x # Compute dz/dx - dzdy = 2 * y # Compute dz/dy + x = pyo.value(units.convert(x,self.inputs[0].units)) + y = pyo.value(units.convert(y,self.inputs[1].units)) + z = x**2 + y**2 # Compute z + dzdx = 2*x # Compute dz/dx + dzdy = 2*y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... - f.ConstraintList([z <= 1 * units.m**2, [z, '==', [x, y], UnitCircle()]]) - f.__dict__['constraint_2'].get_external_model().set_input_values( - np.array([2.0, 2.0]) + f.ConstraintList( + [ + z <= 1*units.m**2 , + [ z, '==', [x,y], UnitCircle() ] , + ] ) - f.__dict__['constraint_2'].get_external_model().inputVariables_optimization = [ - 1, - 2, - ] + + f.__dict__['constraint_2'].get_external_model().set_input_values(np.array([2.0, 2.0])) + f.__dict__['constraint_2'].get_external_model().inputVariables_optimization = [1,2] # f.__dict__['constraint_2'].get_external_model().fillCache() - self.assertRaises( - ValueError, f.__dict__['constraint_2'].get_external_model().fillCache, *() - ) + self.assertRaises(ValueError,f.__dict__['constraint_2'].get_external_model().fillCache,*()) + def test_edi_blackbox_etc_3(self): "Tests a black box assertion issue" @@ -221,239 +199,215 @@ def test_edi_blackbox_etc_3(self): from pyomo.environ import units import pyomo.environ as pyo from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import ( - BlackBoxFunctionModel_Variable, - TypeCheckedList, - BBList, - BlackBoxFunctionModel, - ) + from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel f = Formulation() - x = f.Variable(name='x', guess=1.0, units='m', description='x variable') - y = f.Variable(name='y', guess=1.0, units='m', description='y variable') - z = f.Variable(name='z', guess=1.0, units='m^2', description='Output var') - f.Objective(x + y) - + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'y variable') + z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'Output var') + f.Objective( x + y ) class UnitCircle(BlackBoxFunctionModel): def __init__(self): super().__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append(name='x', units='ft', description='The x variable') - self.inputs.append(name='y', units='ft', description='The y variable') - self.outputs.append( - name='z', units='ft**2', description='Output variable' - ) + self.inputs.append( name = 'x', + units = 'ft' , + description = 'The x variable' ) + self.inputs.append( name = 'y', + units = 'ft' , + description = 'The y variable' ) + self.outputs.append(name = 'z', + units = 'ft**2', + description = 'Output variable' ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - - def BlackBox(self, x, y): # The actual function that does things + def BlackBox(self, x, y): # The actual function that does things # Converts to correct units then casts to float - x = pyo.value(units.convert(x, self.inputs[0].units)) - y = pyo.value(units.convert(y, self.inputs[1].units)) - z = x**2 + y**2 # Compute z - dzdx = 2 * x # Compute dz/dx - dzdy = 2 * y # Compute dz/dy + x = pyo.value(units.convert(x,self.inputs[0].units)) + y = pyo.value(units.convert(y,self.inputs[1].units)) + z = x**2 + y**2 # Compute z + dzdx = 2*x # Compute dz/dx + dzdy = 2*y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return ['err'], [[dzdx, dzdy]] # return z, grad(z), hess(z)... + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return ['err'], [[dzdx, dzdy]] # return z, grad(z), hess(z)... - f.ConstraintList([z <= 1 * units.m**2, [z, '==', [x, y], UnitCircle()]]) - self.assertRaises( - ValueError, f.__dict__['constraint_2'].get_external_model().fillCache, *() + f.ConstraintList( + [ + z <= 1*units.m**2 , + [ z, '==', [x,y], UnitCircle() ] , + ] ) + self.assertRaises(ValueError,f.__dict__['constraint_2'].get_external_model().fillCache,*()) + def test_edi_blackbox_etc_4(self): "Tests a black box assertion issue" import numpy as np from pyomo.environ import units import pyomo.environ as pyo from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import ( - BlackBoxFunctionModel_Variable, - TypeCheckedList, - BBList, - BlackBoxFunctionModel, - ) + from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel f = Formulation() - x = f.Variable(name='x', guess=1.0, units='m', description='x variable') - y = f.Variable(name='y', guess=1.0, units='m', description='y variable') - z = f.Variable(name='z', guess=1.0, units='m^2', description='Output var') - f.Objective(x + y) - + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'y variable') + z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'Output var') + f.Objective( x + y ) class UnitCircle(BlackBoxFunctionModel): def __init__(self): super().__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append(name='x', units='ft', description='The x variable') - self.inputs.append(name='y', units='ft', description='The y variable') - self.outputs.append( - name='z', units='ft**2', description='Output variable' - ) + self.inputs.append( name = 'x', + units = 'ft' , + description = 'The x variable' ) + self.inputs.append( name = 'y', + units = 'ft' , + description = 'The y variable' ) + self.outputs.append(name = 'z', + units = 'ft**2', + description = 'Output variable' ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - - def BlackBox(self, x, y): # The actual function that does things + def BlackBox(self, x, y): # The actual function that does things # Converts to correct units then casts to float - x = pyo.value(units.convert(x, self.inputs[0].units)) - y = pyo.value(units.convert(y, self.inputs[1].units)) - z = x**2 + y**2 # Compute z - dzdx = 2 * x # Compute dz/dx - dzdy = 2 * y # Compute dz/dy + x = pyo.value(units.convert(x,self.inputs[0].units)) + y = pyo.value(units.convert(y,self.inputs[1].units)) + z = x**2 + y**2 # Compute z + dzdx = 2*x # Compute dz/dx + dzdy = 2*y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... - f.ConstraintList([z <= 1 * units.m**2, [z, '==', [x, y], UnitCircle()]]) - f.__dict__['constraint_2'].get_external_model().set_input_values( - np.array([2.0, 2.0]) + f.ConstraintList( + [ + z <= 1*units.m**2 , + [ z, '==', [x,y], UnitCircle() ] , + ] ) - f.__dict__['constraint_2'].get_external_model().outputVariables_optimization = [ - 1 - ] + + f.__dict__['constraint_2'].get_external_model().set_input_values(np.array([2.0, 2.0])) + f.__dict__['constraint_2'].get_external_model().outputVariables_optimization = [1] # f.__dict__['constraint_2'].get_external_model().fillCache() - self.assertRaises( - ValueError, f.__dict__['constraint_2'].get_external_model().fillCache, *() - ) + self.assertRaises(ValueError,f.__dict__['constraint_2'].get_external_model().fillCache,*()) + def test_edi_blackbox_example_1(self): "Tests a black box example construction" from pyomo.environ import units import pyomo.environ as pyo from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import ( - BlackBoxFunctionModel_Variable, - TypeCheckedList, - BBList, - BlackBoxFunctionModel, - ) + from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel f = Formulation() - x = f.Variable(name='x', guess=1.0, units='m', description='x variable') - y = f.Variable(name='y', guess=1.0, units='m', description='y variable') - z = f.Variable(name='z', guess=1.0, units='m^2', description='Output var') - f.Objective(x + y) - + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'y variable') + z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'Output var') + f.Objective( x + y ) class UnitCircle(BlackBoxFunctionModel): def __init__(self): super().__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append(name='x', units='ft', description='The x variable') - self.inputs.append(name='y', units='ft', description='The y variable') - self.outputs.append( - name='z', units='ft**2', description='Output variable' - ) + self.inputs.append( name = 'x', + units = 'ft' , + description = 'The x variable' ) + self.inputs.append( name = 'y', + units = 'ft' , + description = 'The y variable' ) + self.outputs.append(name = 'z', + units = 'ft**2', + description = 'Output variable' ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - - def BlackBox(self, x, y): # The actual function that does things + def BlackBox(self, x, y): # The actual function that does things # Converts to correct units then casts to float - x = pyo.value(units.convert(x, self.inputs[0].units)) - y = pyo.value(units.convert(y, self.inputs[1].units)) - z = x**2 + y**2 # Compute z - dzdx = 2 * x # Compute dz/dx - dzdy = 2 * y # Compute dz/dy + x = pyo.value(units.convert(x,self.inputs[0].units)) + y = pyo.value(units.convert(y,self.inputs[1].units)) + z = x**2 + y**2 # Compute z + dzdx = 2*x # Compute dz/dx + dzdy = 2*y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... - f.ConstraintList([z <= 1 * units.m**2, [z, '==', [x, y], UnitCircle()]]) - f.__dict__['constraint_2'].get_external_model().set_input_values( - np.array([2.0, 2.0]) - ) - opt = f.__dict__['constraint_2'].get_external_model().evaluate_outputs() - jac = ( - f.__dict__['constraint_2'] - .get_external_model() - .evaluate_jacobian_outputs() - .todense() + f.ConstraintList( + [ + z <= 1*units.m**2 , + [ z, '==', [x,y], UnitCircle() ] , + ] ) - self.assertAlmostEqual(opt[0], 8) - self.assertAlmostEqual(jac[0, 0], 4) - self.assertAlmostEqual(jac[0, 1], 4) + f.__dict__['constraint_2'].get_external_model().set_input_values(np.array([2.0, 2.0])) + opt = f.__dict__['constraint_2'].get_external_model().evaluate_outputs() + jac = f.__dict__['constraint_2'].get_external_model().evaluate_jacobian_outputs().todense() - sm = f.__dict__['constraint_2'].get_external_model().summary + self.assertAlmostEqual(opt[0],8) + self.assertAlmostEqual(jac[0,0],4) + self.assertAlmostEqual(jac[0,1],4) + + sm = f.__dict__['constraint_2'].get_external_model().summary e_print = f.__dict__['constraint_2'].get_external_model().__repr__() + def test_edi_blackbox_example_2(self): "Tests a black box example construction" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel - f = Formulation() - x = f.Variable( - name='x', guess=1.0, units='m', description='The x variable', size=3 - ) - y = f.Variable( - name='y', guess=1.0, units='m**2', description='The y variable', size=3 - ) - f.Objective(y[0] + y[1] + y[2]) - + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable', size=3) + y = f.Variable(name = 'y', guess = 1.0, units = 'm**2' , description = 'The y variable', size=3) + f.Objective( y[0] + y[1] + y[2] ) class Parabola(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super(Parabola, self).__init__() self.description = 'This model evaluates the function: y = x**2' - self.inputs.append( - name='x', size=3, units='ft', description='The x variable' - ) - self.outputs.append( - name='y', size=3, units='ft**2', description='The y variable' - ) + self.inputs.append(name = 'x', size = 3, units = 'ft' , description = 'The x variable') + self.outputs.append(name = 'y', size = 3, units = 'ft**2' , description = 'The y variable') self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, *args, **kwargs): # The actual function that does things - runCases, returnMode, remainingKwargs = self.parseInputs( - *args, **kwargs - ) - + def BlackBox(self, *args, **kwargs):# The actual function that does things + runCases, returnMode, remainingKwargs = self.parseInputs(*args, **kwargs) + x = self.sanitizeInputs(runCases[0]['x']) x = np.array([pyo.value(xval) for xval in x], dtype=np.float64) - y = x**2 # Compute y - dydx = 2 * x # Compute dy/dx - + y = x**2 # Compute y + dydx = 2*x # Compute dy/dx + y = y * units.ft**2 dydx = np.diag(dydx) - dydx = dydx * units.ft # units.ft**2 / units.ft + dydx = dydx * units.ft # units.ft**2 / units.ft + + return y, [dydx] # return z, grad(z), hess(z)... + f.ConstraintList([ {'outputs':y, 'operators':'==', 'inputs':x, 'black_box':Parabola() },]) - return y, [dydx] # return z, grad(z), hess(z)... - - f.ConstraintList( - [{'outputs': y, 'operators': '==', 'inputs': x, 'black_box': Parabola()}] - ) - - f.__dict__['constraint_1'].get_external_model().set_input_values(np.ones(3) * 2) + f.__dict__['constraint_1'].get_external_model().set_input_values(np.ones(3)*2) opt = f.__dict__['constraint_1'].get_external_model().evaluate_outputs() - jac = ( - f.__dict__['constraint_1'] - .get_external_model() - .evaluate_jacobian_outputs() - .todense() - ) - - self.assertAlmostEqual(opt[0], 4) - self.assertAlmostEqual(opt[1], 4) - self.assertAlmostEqual(opt[2], 4) - self.assertAlmostEqual(jac[0, 0], 4) - self.assertAlmostEqual(jac[0, 1], 0) - self.assertAlmostEqual(jac[0, 2], 0) - self.assertAlmostEqual(jac[0, 1], 0) - self.assertAlmostEqual(jac[1, 1], 4) - self.assertAlmostEqual(jac[2, 1], 0) - self.assertAlmostEqual(jac[2, 0], 0) - self.assertAlmostEqual(jac[2, 1], 0) - self.assertAlmostEqual(jac[2, 2], 4) - - sm = f.__dict__['constraint_1'].get_external_model().summary + jac = f.__dict__['constraint_1'].get_external_model().evaluate_jacobian_outputs().todense() + + self.assertAlmostEqual(opt[0],4) + self.assertAlmostEqual(opt[1],4) + self.assertAlmostEqual(opt[2],4) + self.assertAlmostEqual(jac[0,0],4) + self.assertAlmostEqual(jac[0,1],0) + self.assertAlmostEqual(jac[0,2],0) + self.assertAlmostEqual(jac[0,1],0) + self.assertAlmostEqual(jac[1,1],4) + self.assertAlmostEqual(jac[2,1],0) + self.assertAlmostEqual(jac[2,0],0) + self.assertAlmostEqual(jac[2,1],0) + self.assertAlmostEqual(jac[2,2],4) + + sm = f.__dict__['constraint_1'].get_external_model().summary e_print = f.__dict__['constraint_1'].get_external_model().__repr__() def test_edi_blackbox_example_3(self): @@ -462,133 +416,109 @@ def test_edi_blackbox_example_3(self): from pyomo.environ import units import pyomo.environ as pyo from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import ( - BlackBoxFunctionModel_Variable, - TypeCheckedList, - BBList, - BlackBoxFunctionModel, - ) + from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel f = Formulation() - x = f.Variable(name='x', guess=1.0, units='', description='x variable', size=3) - y = f.Variable(name='y', guess=1.0, units='', description='y variable') - f.Objective(y) - + x = f.Variable(name = 'x', guess = 1.0, units = '' , description = 'x variable', size = 3) + y = f.Variable(name = 'y', guess = 1.0, units = '' , description = 'y variable') + f.Objective( y ) class Norm_2(BlackBoxFunctionModel): def __init__(self): super().__init__() self.description = 'This model evaluates the two norm' - self.inputs.append( - name='x', units='', description='The x variable', size=3 - ) - self.outputs.append(name='y', units='', description='The y variable') + self.inputs.append( name = 'x', + units = '' , + description = 'The x variable' , + size = 3) + self.outputs.append(name = 'y', + units = '' , + description = 'The y variable' ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - - def BlackBox(self, *args, **kwargs): # The actual function that does things - runCases, returnMode, remainingKwargs = self.parseInputs( - *args, **kwargs - ) + def BlackBox(self, *args, **kwargs):# The actual function that does things + runCases, returnMode, remainingKwargs = self.parseInputs(*args, **kwargs) x = self.sanitizeInputs(runCases[0]['x']) x = np.array([pyo.value(xval) for xval in x], dtype=np.float64) - y = x[0] ** 2 + x[1] ** 2 + x[2] ** 2 # Compute y - dydx0 = 2 * x[0] # Compute dy/dx0 - dydx1 = 2 * x[1] # Compute dy/dx1 - dydx2 = 2 * x[2] # Compute dy/dx2 + y = x[0]**2 + x[1]**2 + x[2]**2 # Compute y + dydx0 = 2*x[0] # Compute dy/dx0 + dydx1 = 2*x[1] # Compute dy/dx1 + dydx2 = 2*x[2] # Compute dy/dx2 y = y * units.dimensionless - dydx = np.array([dydx0, dydx1, dydx2]) * units.dimensionless + dydx = np.array([dydx0,dydx1,dydx2]) * units.dimensionless # dydx0 = dydx0 * units.dimensionless # dydx1 = dydx1 * units.dimensionless # dydx2 = dydx2 * units.dimensionless - return y, [dydx] # return z, grad(z), hess(z)... - - f.ConstraintList( - [{'outputs': y, 'operators': '==', 'inputs': x, 'black_box': Norm_2()}] - ) + return y, [dydx] # return z, grad(z), hess(z)... + + f.ConstraintList([ {'outputs':y, 'operators':'==', 'inputs':x, 'black_box':Norm_2() },]) - f.__dict__['constraint_1'].get_external_model().set_input_values(np.ones(3) * 2) + f.__dict__['constraint_1'].get_external_model().set_input_values(np.ones(3)*2) opt = f.__dict__['constraint_1'].get_external_model().evaluate_outputs() - jac = ( - f.__dict__['constraint_1'] - .get_external_model() - .evaluate_jacobian_outputs() - .todense() - ) + jac = f.__dict__['constraint_1'].get_external_model().evaluate_jacobian_outputs().todense() - self.assertAlmostEqual(opt[0], 12) - self.assertAlmostEqual(jac[0, 0], 4) - self.assertAlmostEqual(jac[0, 1], 4) - self.assertAlmostEqual(jac[0, 2], 4) + self.assertAlmostEqual(opt[0],12) + self.assertAlmostEqual(jac[0,0],4) + self.assertAlmostEqual(jac[0,1],4) + self.assertAlmostEqual(jac[0,2],4) - sm = f.__dict__['constraint_1'].get_external_model().summary + sm = f.__dict__['constraint_1'].get_external_model().summary e_print = f.__dict__['constraint_1'].get_external_model().__repr__() + def test_edi_blackbox_example_4(self): "Tests a black box example construction" import numpy as np from pyomo.environ import units import pyomo.environ as pyo from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import ( - BlackBoxFunctionModel_Variable, - TypeCheckedList, - BBList, - BlackBoxFunctionModel, - ) + from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel f = Formulation() - x = f.Variable(name='x', guess=1.0, units='', description='x variable') - y = f.Variable(name='y', guess=1.0, units='', description='y variable', size=3) - f.Objective(y[0] ** 2 + y[1] ** 2 + y[2] ** 2) - + x = f.Variable(name = 'x', guess = 1.0, units = '' , description = 'x variable') + y = f.Variable(name = 'y', guess = 1.0, units = '' , description = 'y variable', size=3) + f.Objective( y[0]**2 + y[1]**2 + y[2]**2 ) class VectorCast(BlackBoxFunctionModel): def __init__(self): super().__init__() self.description = 'This model evaluates the two norm' - self.inputs.append(name='x', units='', description='The x variable') - self.outputs.append( - name='y', units='', description='The y variable', size=3 - ) + self.inputs.append( name = 'x', + units = '' , + description = 'The x variable' ) + self.outputs.append(name = 'y', + units = '' , + description = 'The y variable' , + size = 3) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, *args, **kwargs): # The actual function that does things - runCases, returnMode, remainingKwargs = self.parseInputs( - *args, **kwargs - ) + def BlackBox(self, *args, **kwargs):# The actual function that does things + runCases, returnMode, remainingKwargs = self.parseInputs(*args, **kwargs) x = pyo.value(self.sanitizeInputs(runCases[0]['x'])) - y = np.array([x, x, x]) * units.dimensionless - dydx = np.array([1.0, 1.0, 1.0]) * units.dimensionless + y = np.array([x,x,x]) * units.dimensionless + dydx = np.array([1.0,1.0,1.0]) * units.dimensionless - return y, [dydx] # return z, grad(z), hess(z)... + return y, [dydx] # return z, grad(z), hess(z)... - f.ConstraintList( - [{'outputs': y, 'operators': '==', 'inputs': x, 'black_box': VectorCast()}] - ) + f.ConstraintList([ {'outputs':y, 'operators':'==', 'inputs':x, 'black_box':VectorCast() },]) - f.__dict__['constraint_1'].get_external_model().set_input_values(np.ones(3) * 2) + f.__dict__['constraint_1'].get_external_model().set_input_values(np.ones(3)*2) opt = f.__dict__['constraint_1'].get_external_model().evaluate_outputs() - jac = ( - f.__dict__['constraint_1'] - .get_external_model() - .evaluate_jacobian_outputs() - .todense() - ) + jac = f.__dict__['constraint_1'].get_external_model().evaluate_jacobian_outputs().todense() - self.assertAlmostEqual(opt[0], 2.0) - self.assertAlmostEqual(opt[1], 2.0) - self.assertAlmostEqual(opt[2], 2.0) - self.assertAlmostEqual(jac[0, 0], 1.0) - self.assertAlmostEqual(jac[1, 0], 1.0) - self.assertAlmostEqual(jac[2, 0], 1.0) + self.assertAlmostEqual(opt[0],2.0) + self.assertAlmostEqual(opt[1],2.0) + self.assertAlmostEqual(opt[2],2.0) + self.assertAlmostEqual(jac[0,0],1.0) + self.assertAlmostEqual(jac[1,0],1.0) + self.assertAlmostEqual(jac[2,0],1.0) - sm = f.__dict__['constraint_1'].get_external_model().summary + sm = f.__dict__['constraint_1'].get_external_model().summary e_print = f.__dict__['constraint_1'].get_external_model().__repr__() def test_edi_blackbox_badexample_1(self): @@ -597,41 +527,35 @@ def test_edi_blackbox_badexample_1(self): from pyomo.environ import units import pyomo.environ as pyo from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import ( - BlackBoxFunctionModel_Variable, - TypeCheckedList, - BBList, - BlackBoxFunctionModel, - ) + from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel f = Formulation() - x = f.Variable(name='x', guess=1.0, units='', description='x variable', size=3) - y = f.Variable(name='y', guess=1.0, units='', description='y variable') - f.Objective(y) - + x = f.Variable(name = 'x', guess = 1.0, units = '' , description = 'x variable', size = 3) + y = f.Variable(name = 'y', guess = 1.0, units = '' , description = 'y variable') + f.Objective( y ) class Norm_2(BlackBoxFunctionModel): def __init__(self): super().__init__() self.description = 'This model evaluates the two norm' - self.inputs.append( - name='x', units='', description='The x variable', size=3 - ) - self.outputs.append(name='y', units='', description='The y variable') + self.inputs.append( name = 'x', + units = '' , + description = 'The x variable' , + size = 3) + self.outputs.append(name = 'y', + units = '' , + description = 'The y variable' ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - - def BlackBox(self, *args, **kwargs): # The actual function that does things - runCases, returnMode, remainingKwargs = self.parseInputs( - *args, **kwargs - ) + def BlackBox(self, *args, **kwargs):# The actual function that does things + runCases, returnMode, remainingKwargs = self.parseInputs(*args, **kwargs) x = self.sanitizeInputs(runCases[0]['x']) x = np.array([pyo.value(xval) for xval in x], dtype=np.float64) - y = x[0] ** 2 + x[1] ** 2 + x[2] ** 2 # Compute y - dydx0 = 2 * x[0] # Compute dy/dx0 - dydx1 = 2 * x[1] # Compute dy/dx1 - dydx2 = 2 * x[2] # Compute dy/dx2 + y = x[0]**2 + x[1]**2 + x[2]**2 # Compute y + dydx0 = 2*x[0] # Compute dy/dx0 + dydx1 = 2*x[1] # Compute dy/dx1 + dydx2 = 2*x[2] # Compute dy/dx2 y = y * units.dimensionless # dydx = np.array([dydx0,dydx1,dydx2]) * units.dimensionless @@ -639,18 +563,13 @@ def BlackBox(self, *args, **kwargs): # The actual function that does things dydx1 = dydx1 * units.dimensionless dydx2 = dydx2 * units.dimensionless - return y, [[dydx0, dydx1, dydx2]] # return z, grad(z), hess(z)... + return y, [[dydx0, dydx1, dydx2]] # return z, grad(z), hess(z)... + + f.ConstraintList([ {'outputs':y, 'operators':'==', 'inputs':x, 'black_box':Norm_2() },]) - f.ConstraintList( - [{'outputs': y, 'operators': '==', 'inputs': x, 'black_box': Norm_2()}] - ) + f.__dict__['constraint_1'].get_external_model().set_input_values(np.ones(3)*2) + self.assertRaises(ValueError,f.__dict__['constraint_1'].get_external_model().evaluate_outputs,*()) - f.__dict__['constraint_1'].get_external_model().set_input_values(np.ones(3) * 2) - self.assertRaises( - ValueError, - f.__dict__['constraint_1'].get_external_model().evaluate_outputs, - *() - ) def test_edi_blackbox_smallfunctions(self): "Tests the more general value and convert functions" @@ -658,31 +577,22 @@ def test_edi_blackbox_smallfunctions(self): from pyomo.environ import units import pyomo.environ as pyo from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import ( - BlackBoxFunctionModel_Variable, - TypeCheckedList, - BBList, - BlackBoxFunctionModel, - ) + from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel bb = BlackBoxFunctionModel() - t1 = bb.convert(2 * units.m, units.ft) - t2 = bb.convert(np.ones([2, 2]) * units.m, units.ft) - self.assertRaises(ValueError, bb.convert, *('err', units.ft)) - - t3 = bb.pyomo_value(2 * units.m) - t3 = bb.pyomo_value(np.ones([2, 2]) * units.m) - self.assertRaises(ValueError, bb.pyomo_value, *(np.ones([2, 2]),)) - - bb.sizeCheck([2, 2], np.ones([2, 2]) * units.m) - self.assertRaises(ValueError, bb.sizeCheck, *(2, 3 * units.m)) - self.assertRaises( - ValueError, bb.sizeCheck, *([10, 10, 10], np.ones([2, 2]) * units.m) - ) - self.assertRaises( - ValueError, bb.sizeCheck, *([10, 10], np.ones([2, 2]) * units.m) - ) - self.assertRaises(ValueError, bb.sizeCheck, *([10, 10], [])) + t1 = bb.convert(2*units.m, units.ft) + t2 = bb.convert(np.ones([2,2])*units.m, units.ft) + self.assertRaises(ValueError,bb.convert,*('err',units.ft)) + + t3 = bb.pyomo_value(2*units.m) + t3 = bb.pyomo_value(np.ones([2,2])*units.m) + self.assertRaises(ValueError,bb.pyomo_value,*(np.ones([2,2]),)) + + bb.sizeCheck([2,2],np.ones([2,2])*units.m) + self.assertRaises(ValueError,bb.sizeCheck,*(2,3*units.m,)) + self.assertRaises(ValueError,bb.sizeCheck,*([10,10,10],np.ones([2,2])*units.m,)) + self.assertRaises(ValueError,bb.sizeCheck,*([10,10],np.ones([2,2])*units.m,)) + self.assertRaises(ValueError,bb.sizeCheck,*([10,10],[],)) def test_edi_blackbox_bare_example_1(self): "Tests a black box example construction without an optimization problem" @@ -696,38 +606,34 @@ def __init__(self): # Set up all the attributes by calling Model.__init__ super().__init__() - # Setup Inputs - self.inputs.append('x', '', 'Independent Variable') + #Setup Inputs + self.inputs.append( 'x', '', 'Independent Variable') - # Setup Outputs - self.outputs.append('y', '', 'Dependent Variable') + #Setup Outputs + self.outputs.append( 'y', '', 'Dependent Variable') - # Simple model description - self.description = ( - 'This model evaluates the function: max([-6*x-6, x**4-3*x**2])' - ) + #Simple model description + self.description = 'This model evaluates the function: max([-6*x-6, x**4-3*x**2])' self.availableDerivative = 1 - # standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) + #standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) def BlackBox(self, *args, **kwargs): runCases, returnMode, extras = self.parseInputs(*args, **kwargs) - x = np.array( - [pyo.value(runCases[i]['x']) for i in range(0, len(runCases))] - ) - - y = np.maximum(-6 * x - 6, x**4 - 3 * x**2) - dydx = 4 * x**3 - 6 * x - ddy_ddx = 12 * x**2 - 6 - gradientSwitch = -6 * x - 6 > x**4 - 3 * x**2 + x = np.array([ pyo.value(runCases[i]['x']) for i in range(0,len(runCases)) ]) + + y = np.maximum(-6*x-6, x**4-3*x**2) + dydx = 4*x**3 - 6*x + ddy_ddx = 12*x**2 - 6 + gradientSwitch = -6*x-6 > x**4-3*x**2 dydx[gradientSwitch] = -6 ddy_ddx[gradientSwitch] = 0 - y = [yval * units.dimensionless for yval in y] - dydx = [dydx[i] * units.dimensionless for i in range(0, len(dydx))] + y = [ yval * units.dimensionless for yval in y] + dydx = [dydx[i] * units.dimensionless for i in range(0,len(dydx))] if returnMode < 0: - returnMode = -1 * (returnMode + 1) + returnMode = -1*(returnMode + 1) if returnMode == 0: return y[0] if returnMode == 1: @@ -735,44 +641,43 @@ def BlackBox(self, *args, **kwargs): else: if returnMode == 0: opt = [] - for i in range(0, len(y)): - opt.append([y[i]]) + for i in range(0,len(y)): + opt.append([ y[i] ]) return opt if returnMode == 1: opt = [] - for i in range(0, len(y)): - opt.append([[y[i]], [[[dydx[i]]]]]) + for i in range(0,len(y)): + opt.append([ [y[i]], [ [[dydx[i]]] ] ]) return opt s = SignomialTest() - ivals = [[x] for x in np.linspace(-2, 2, 11)] + ivals = [[x] for x in np.linspace(-2,2,11)] # How the black box may be called using EDI - bbo = s.BlackBox(**{'x': 0.5}) - bbo = s.BlackBox({'x': 0.5}) - bbo = s.BlackBox(**{'x': 0.5, 'optn': True}) + bbo = s.BlackBox(**{'x':0.5}) + bbo = s.BlackBox({'x':0.5}) + bbo = s.BlackBox(**{'x':0.5, 'optn':True}) # Additional options available with parseInputs bbo = s.BlackBox(*[0.5], **{'optn1': True, 'optn2': False}) - bbo = s.BlackBox(*[0.5, True], **{'optn': False}) - bbo = s.BlackBox({'x': [x for x in np.linspace(-2, 2, 11)]}) - bbo = s.BlackBox([{'x': x} for x in np.linspace(-2, 2, 11)]) - bbo = s.BlackBox([[x] for x in np.linspace(-2, 2, 11)]) - bbo = s.BlackBox([[x] for x in np.linspace(-2, 2, 11)], True, optn=False) - bbo = s.BlackBox([[x] for x in np.linspace(-2, 2, 11)], optn1=True, optn2=False) + bbo = s.BlackBox(*[0.5,True], **{'optn': False}) + bbo = s.BlackBox({'x':[x for x in np.linspace(-2,2,11)]}) + bbo = s.BlackBox([{'x':x} for x in np.linspace(-2,2,11)]) + bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)]) + bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)], True, optn=False) + bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)], optn1=True, optn2=False) sm = s.summary - self.assertRaises(ValueError, s.BlackBox, *([[[1, 2], 2, 3], [1, 2, 3]])) - self.assertRaises(ValueError, s.BlackBox, *([[np.ones(2), 2, 3], [1, 2, 3]])) + self.assertRaises(ValueError,s.BlackBox,*( [ [[1,2],2,3] , [1,2,3]] )) + self.assertRaises(ValueError,s.BlackBox,*( [ [np.ones(2),2,3] , [1,2,3]] )) + + self.assertRaises(ValueError,s.sanitizeInputs,*( )) + self.assertRaises(ValueError,s.sanitizeInputs,*( 1 , 2, 3 )) + self.assertRaises(ValueError,s.sanitizeInputs,**{'z':2.0*units.dimensionless}) + self.assertRaises(ValueError,s.sanitizeInputs,*(2.0*units.ft,)) + self.assertRaises(NotImplementedError,s.checkOutputs,*()) - self.assertRaises(ValueError, s.sanitizeInputs, *()) - self.assertRaises(ValueError, s.sanitizeInputs, *(1, 2, 3)) - self.assertRaises( - ValueError, s.sanitizeInputs, **{'z': 2.0 * units.dimensionless} - ) - self.assertRaises(ValueError, s.sanitizeInputs, *(2.0 * units.ft,)) - self.assertRaises(NotImplementedError, s.checkOutputs, *()) def test_edi_blackbox_bare_example_2(self): "Tests a black box example construction without an optimization problem" @@ -787,39 +692,37 @@ def __init__(self): # Set up all the attributes by calling Model.__init__ super().__init__() - # Setup Inputs - self.inputs.append('x', '', 'Independent Variable', size=[2, 2]) + #Setup Inputs + self.inputs.append( 'x', '', 'Independent Variable', size = [2,2]) - # Setup Outputs - self.outputs.append('y', '', 'Dependent Variable', size=[2, 2]) + #Setup Outputs + self.outputs.append( 'y', '', 'Dependent Variable', size = [2,2]) - # Simple model description + #Simple model description self.description = 'This model is a pass through)' self.availableDerivative = 1 - # standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) + #standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) def BlackBox(self, *args, **kwargs): runCases, returnMode, extras = self.parseInputs(*args, **kwargs) - x = [ - self.pyomo_value(runCases[i]['x']) for i in range(0, len(runCases)) - ] + x = [ self.pyomo_value(runCases[i]['x']) for i in range(0,len(runCases)) ] - y = [] + y = [] dydx = [] - + for xval in x: - y.append(xval * units.dimensionless) - dydx_temp = np.zeros([2, 2, 2, 2]) - dydx_temp[0, 0, 0, 0] = 1.0 - dydx_temp[0, 1, 0, 1] = 1.0 - dydx_temp[1, 0, 1, 0] = 1.0 - dydx_temp[1, 1, 1, 1] = 1.0 - - dydx.append(dydx_temp * units.dimensionless) - + y.append(xval*units.dimensionless) + dydx_temp = np.zeros([2,2,2,2]) + dydx_temp[0,0,0,0] = 1.0 + dydx_temp[0,1,0,1] = 1.0 + dydx_temp[1,0,1,0] = 1.0 + dydx_temp[1,1,1,1] = 1.0 + + dydx.append(dydx_temp*units.dimensionless) + if returnMode < 0: - returnMode = -1 * (returnMode + 1) + returnMode = -1*(returnMode + 1) if returnMode == 0: return y[0] if returnMode == 1: @@ -827,41 +730,38 @@ def BlackBox(self, *args, **kwargs): else: if returnMode == 0: opt = [] - for i in range(0, len(y)): - opt.append([y[i]]) + for i in range(0,len(y)): + opt.append([ y[i] ]) return opt if returnMode == 1: opt = [] - for i in range(0, len(y)): - opt.append([[y[i]], [[[dydx[i]]]]]) + for i in range(0,len(y)): + opt.append([ [y[i]], [ [[dydx[i]]] ] ]) return opt bb = PassThrough() - ivals = [ - [np.eye(2) * units.dimensionless], - [np.ones([2, 2]) * units.dimensionless], - [np.zeros([2, 2]) * units.dimensionless], - ] + ivals = [[np.eye(2)*units.dimensionless], [np.ones([2,2])*units.dimensionless], [np.zeros([2,2])*units.dimensionless] ] - xv = np.eye(2) * units.dimensionless + xv = np.eye(2)*units.dimensionless # How the black box may be called using EDI - bbo = bb.BlackBox(**{'x': xv}) - bbo = bb.BlackBox({'x': xv}) - bbo = bb.BlackBox(**{'x': xv, 'optn': True}) + bbo = bb.BlackBox(**{'x':xv}) + bbo = bb.BlackBox({'x':xv}) + bbo = bb.BlackBox(**{'x':xv, 'optn':True}) # # Additional options available with parseInputs bbo = bb.BlackBox(*[xv], **{'optn1': True, 'optn2': False}) - bbo = bb.BlackBox(*[xv, True], **{'optn': False}) - bbo = bb.BlackBox({'x': [x[0] for x in ivals]}) - bbo = bb.BlackBox([{'x': x[0]} for x in ivals]) - bbo = bb.BlackBox([[x[0]] for x in ivals]) - bbo = bb.BlackBox([[x[0]] for x in ivals], True, optn=False) - bbo = bb.BlackBox([[x[0]] for x in ivals], optn1=True, optn2=False) + bbo = bb.BlackBox(*[xv,True], **{'optn': False}) + bbo = bb.BlackBox({'x':[x[0] for x in ivals]}) + bbo = bb.BlackBox([{'x':x[0]} for x in ivals]) + bbo = bb.BlackBox([ [x[0]] for x in ivals]) + bbo = bb.BlackBox([ [x[0]] for x in ivals], True, optn=False) + bbo = bb.BlackBox([ [x[0]] for x in ivals], optn1=True, optn2=False) sm = bb.summary - self.assertRaises(ValueError, bb.sanitizeInputs, *(np.ones([2, 2]) * units.ft,)) + self.assertRaises(ValueError,bb.sanitizeInputs,*(np.ones([2,2])*units.ft,)) + def test_edi_blackbox_bare_example_3(self): "Tests a black box example construction" @@ -869,92 +769,58 @@ def test_edi_blackbox_bare_example_3(self): from pyomo.environ import units import pyomo.environ as pyo from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import ( - BlackBoxFunctionModel_Variable, - TypeCheckedList, - BBList, - BlackBoxFunctionModel, - ) + from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel class Norm_2(BlackBoxFunctionModel): def __init__(self): super().__init__() self.description = 'This model evaluates the two norm' - self.inputs.append( - name='x', units='', description='The x variable', size=3 - ) - self.inputs.append( - name='y', units='', description='The y variable', size=2 - ) - self.outputs.append(name='z', units='', description='The z variable') + self.inputs.append( name = 'x', + units = '' , + description = 'The x variable' , + size = 3) + self.inputs.append( name = 'y', + units = '' , + description = 'The y variable' , + size = 2) + self.outputs.append(name = 'z', + units = '' , + description = 'The z variable') self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) + def BlackBox(self, *args, **kwargs):# The actual function that does things + runCases, returnMode, remainingKwargs = self.parseInputs(*args, **kwargs) - def BlackBox(self, *args, **kwargs): # The actual function that does things - runCases, returnMode, remainingKwargs = self.parseInputs( - *args, **kwargs - ) - - x = [rc['x'] for rc in runCases][0] + x = [ rc['x'] for rc in runCases][0] x = np.array([self.pyomo_value(xval) for xval in x], dtype=np.float64) - y = [rc['y'] for rc in runCases][0] + y = [ rc['y'] for rc in runCases][0] y = np.array([self.pyomo_value(yval) for yval in y], dtype=np.float64) - z = x[0] ** 2 + x[1] ** 2 + x[2] ** 2 + y[0] ** 2 + y[1] ** 2 - dzdx0 = 2 * x[0] # Compute dy/dx0 - dzdx1 = 2 * x[1] # Compute dy/dx1 - dzdx2 = 2 * x[2] # Compute dy/dx2 - dzdy0 = 2 * y[0] - dzdy1 = 2 * y[1] + z = x[0]**2 + x[1]**2 + x[2]**2 + y[0]**2 + y[1]**2 + dzdx0 = 2*x[0] # Compute dy/dx0 + dzdx1 = 2*x[1] # Compute dy/dx1 + dzdx2 = 2*x[2] # Compute dy/dx2 + dzdy0 = 2*y[0] + dzdy1 = 2*y[1] z = z * units.dimensionless - dz = np.array([dzdx0, dzdx1, dzdx2, dzdy0, dzdy1]) * units.dimensionless + dz = np.array([dzdx0,dzdx1,dzdx2,dzdy0,dzdy1]) * units.dimensionless # dydx0 = dydx0 * units.dimensionless # dydx1 = dydx1 * units.dimensionless # dydx2 = dydx2 * units.dimensionless - return z, [dz] # return z, grad(z), hess(z)... + return z, [dz] # return z, grad(z), hess(z)... bb = Norm_2() - bbo = bb.BlackBox( - { - 'x': np.array([0, 0, 0]) * units.dimensionless, - 'y': np.array([0, 0]) * units.dimensionless, - } - ) - bbo = bb.BlackBox( - np.array([0, 0, 0]) * units.dimensionless, - y=np.array([0, 0]) * units.dimensionless, - ) + bbo = bb.BlackBox({'x':np.array([0,0,0])*units.dimensionless, 'y':np.array([0,0])*units.dimensionless}) + bbo = bb.BlackBox(np.array([0,0,0])*units.dimensionless, y=np.array([0,0])*units.dimensionless) + + self.assertRaises(ValueError,bb.BlackBox,*( {'er':np.array([0,0,0])*units.dimensionless, 'y':np.array([0,0])*units.dimensionless}, )) + self.assertRaises(ValueError,bb.BlackBox,*( 'err', )) + self.assertRaises(ValueError,bb.BlackBox,*( np.array([0,0,0])*units.dimensionless, np.array([0,0])*units.dimensionless ), **{'x':'err too many'}) + self.assertRaises(ValueError,bb.BlackBox,*( np.array([0,0,0])*units.dimensionless, ), **{'notY':np.array([0,0])*units.dimensionless}) - self.assertRaises( - ValueError, - bb.BlackBox, - *( - { - 'er': np.array([0, 0, 0]) * units.dimensionless, - 'y': np.array([0, 0]) * units.dimensionless, - }, - ) - ) - self.assertRaises(ValueError, bb.BlackBox, *('err',)) - self.assertRaises( - ValueError, - bb.BlackBox, - *( - np.array([0, 0, 0]) * units.dimensionless, - np.array([0, 0]) * units.dimensionless, - ), - **{'x': 'err too many'} - ) - self.assertRaises( - ValueError, - bb.BlackBox, - *(np.array([0, 0, 0]) * units.dimensionless,), - **{'notY': np.array([0, 0]) * units.dimensionless} - ) -<<<<<<< HEAD def test_edi_blackbox_bare_example_4(self): "Tests a black box example construction without an optimization problem" @@ -969,72 +835,64 @@ def __init__(self): # Set up all the attributes by calling Model.__init__ super().__init__() - # Setup Inputs - self.inputs.append('x', '', 'X Variable') - self.inputs.append('y', '', 'Y Variable') + #Setup Inputs + self.inputs.append( 'x', '', 'X Variable') + self.inputs.append( 'y', '', 'Y Variable') - # Setup Outputs - self.outputs.append('u', '', 'U Variable') - self.outputs.append('v', '', 'V Variable') + #Setup Outputs + self.outputs.append( 'u', '', 'U Variable') + self.outputs.append( 'v', '', 'V Variable') - # Simple model description + #Simple model description self.description = 'This model is a pass through)' self.availableDerivative = 1 - # standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) + #standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) def BlackBox(self, *args, **kwargs): runCases, returnMode, extras = self.parseInputs(*args, **kwargs) - x = [ - self.pyomo_value(runCases[i]['x']) for i in range(0, len(runCases)) - ] - y = [ - self.pyomo_value(runCases[i]['x']) for i in range(0, len(runCases)) - ] - - u = [] + x = [ self.pyomo_value(runCases[i]['x']) for i in range(0,len(runCases)) ] + y = [ self.pyomo_value(runCases[i]['x']) for i in range(0,len(runCases)) ] + + u = [] dudx = [] dudy = [] - v = [] + v = [] dvdx = [] dvdy = [] for xval in x: - u.append(xval * units.dimensionless) - dudx.append(1.0 * units.dimensionless) - dudy.append(0.0 * units.dimensionless) - + u.append(xval*units.dimensionless) + dudx.append(1.0*units.dimensionless) + dudy.append(0.0*units.dimensionless) + for yval in y: - v.append(yval * units.dimensionless) - dvdx.append(0.0 * units.dimensionless) - dvdy.append(1.0 * units.dimensionless) - + v.append(yval*units.dimensionless) + dvdx.append(0.0*units.dimensionless) + dvdy.append(1.0*units.dimensionless) + if returnMode < 0: - returnMode = -1 * (returnMode + 1) + returnMode = -1*(returnMode + 1) if returnMode == 0: return [u[0], v[0]] if returnMode == 1: - return [u[0], v[0]], [[dudx[0], dudy[0]], [dvdx[0], dvdy[0]]] + return [u[0], v[0]] , [[dudx[0], dudy[0]], [dvdx[0], dvdy[0]]] else: if returnMode == 0: opt = [] - for i in range(0, len(y)): - opt.append([u[i], v[i]]) + for i in range(0,len(y)): + opt.append([ u[i], v[i] ]) return opt if returnMode == 1: opt = [] - for i in range(0, len(y)): - opt.append( - [[u[i], v[i]], [[dudx[i], dudy[i]], [dvdx[i], dvdy[i]]]] - ) + for i in range(0,len(y)): + opt.append([ [u[i], v[i]], [ [ dudx[i], dudy[i]], [dvdx[i], dvdy[i]] ] ]) return opt bb = PassThrough() - bbo = bb.BlackBox(1.0, 1.0) - bbo = bb.BlackBox({'x': np.linspace(0, 10, 11), 'y': np.linspace(0, 10, 11)}) -======= ->>>>>>> parent of f76f4974b (addressing concerns in PR) + bbo = bb.BlackBox(1.0,1.0) + bbo = bb.BlackBox({'x':np.linspace(0,10,11),'y':np.linspace(0,10,11)}) if __name__ == '__main__': - unittest.main() + unittest.main() \ No newline at end of file diff --git a/pyomo/contrib/edi/tests/test_docSnippets.py b/pyomo/contrib/edi/tests/test_docSnippets.py index 38b98aa046a..b125d524e14 100644 --- a/pyomo/contrib/edi/tests/test_docSnippets.py +++ b/pyomo/contrib/edi/tests/test_docSnippets.py @@ -38,480 +38,489 @@ class TestEDISnippets(unittest.TestCase): def test_edi_snippet_formuation_01(self): # BEGIN: Formulation_Snippet_01 from pyomo.contrib.edi import Formulation - f = Formulation() # END: Formulation_Snippet_01 def test_edi_snippet_formuation_02(self): # BEGIN: Formulation_Snippet_02 from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable(name='x', guess=1.0, units='') + x = f.Variable(name = 'x', guess = 1.0, units = '') # END: Formulation_Snippet_02 def test_edi_snippet_formuation_03(self): # BEGIN: Formulation_Snippet_03 from pyomo.contrib.edi import Formulation - f = Formulation() - c = f.Constant(name='c', value=1.0, units='') + c = f.Constant(name = 'c', value = 1.0, units = '') # END: Formulation_Snippet_03 def test_edi_snippet_formuation_04(self): # BEGIN: Formulation_Snippet_04 from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable(name='x', guess=1.0, units='') - y = f.Variable(name='y', guess=1.0, units='') - c = f.Constant(name='c', value=1.0, units='') - f.Objective(c * x + y) + x = f.Variable(name = 'x', guess = 1.0, units = '') + y = f.Variable(name = 'y', guess = 1.0, units = '') + c = f.Constant(name = 'c', value = 1.0, units = '') + f.Objective( c*x + y ) # END: Formulation_Snippet_04 def test_edi_snippet_formuation_05(self): # BEGIN: Formulation_Snippet_05 from pyomo.contrib.edi import Formulation from pyomo.environ import maximize, minimize - f = Formulation() - x = f.Variable(name='x', guess=1.0, units='') - y = f.Variable(name='y', guess=1.0, units='') - c = f.Constant(name='c', value=1.0, units='') - f.Objective(c * x + y, sense=maximize) + x = f.Variable(name = 'x', guess = 1.0, units = '') + y = f.Variable(name = 'y', guess = 1.0, units = '') + c = f.Constant(name = 'c', value = 1.0, units = '') + f.Objective( c*x + y , sense = maximize) # END: Formulation_Snippet_05 - + def test_edi_snippet_formuation_06(self): # BEGIN: Formulation_Snippet_06 from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable(name='x', guess=1.0, units='') - y = f.Variable(name='y', guess=1.0, units='') - c = f.Constant(name='c', value=1.0, units='') - f.Objective(c * x + y) - f.Constraint(x**2 + y**2 <= 1.0) - f.Constraint(x >= 0) - f.Constraint(y <= 0) + x = f.Variable(name = 'x', guess = 1.0, units = '') + y = f.Variable(name = 'y', guess = 1.0, units = '') + c = f.Constant(name = 'c', value = 1.0, units = '') + f.Objective( c*x + y ) + f.Constraint( x**2 + y**2 <= 1.0 ) + f.Constraint( x >= 0) + f.Constraint( y <= 0) # END: Formulation_Snippet_06 - + def test_edi_snippet_formuation_07(self): # BEGIN: Formulation_Snippet_07 from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable(name='x', guess=1.0, units='') - y = f.Variable(name='y', guess=1.0, units='') - c = f.Constant(name='c', value=1.0, units='') - f.Objective(c * x + y) - f.ConstraintList([x**2 + y**2 <= 1.0, x >= 0, y <= 0]) + x = f.Variable(name = 'x', guess = 1.0, units = '') + y = f.Variable(name = 'y', guess = 1.0, units = '') + c = f.Constant(name = 'c', value = 1.0, units = '') + f.Objective( c*x + y ) + f.ConstraintList( + [ + x**2 + y**2 <= 1.0 , + x >= 0, + y <= 0, + ] + ) # END: Formulation_Snippet_07 - + def test_edi_snippet_formuation_08(self): # BEGIN: Formulation_Snippet_08 from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable(name='x', guess=1.0, units='') - y = f.Variable(name='y', guess=1.0, units='') - c = f.Constant(name='c', value=1.0, units='') - f.Objective(c * x + y) - - constraintList = [x**2 + y**2 <= 1.0, x >= 0, y <= 0] - - f.ConstraintList(constraintList) + x = f.Variable(name = 'x', guess = 1.0, units = '') + y = f.Variable(name = 'y', guess = 1.0, units = '') + c = f.Constant(name = 'c', value = 1.0, units = '') + f.Objective( c*x + y ) + + constraintList = [ + x**2 + y**2 <= 1.0 , + x >= 0, + y <= 0, + ] + + f.ConstraintList( constraintList ) # END: Formulation_Snippet_08 - + def test_edi_snippet_formuation_09(self): # BEGIN: Formulation_Snippet_09 import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel - f = Formulation() - x = f.Variable(name='x', guess=1.0, units='m', description='x variable') - y = f.Variable(name='y', guess=1.0, units='m', description='y variable') - z = f.Variable(name='z', guess=1.0, units='m^2', description='Output var') - f.Objective(x + y) - + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'y variable') + z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'Output var') + f.Objective( x + y ) class UnitCircle(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super().__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append(name='x', units='ft', description='The x variable') - self.inputs.append(name='y', units='ft', description='The y variable') - self.outputs.append( - name='z', units='ft**2', description='Output variable' - ) + self.inputs.append( name = 'x', + units = 'ft' , + description = 'The x variable' ) + self.inputs.append( name = 'y', + units = 'ft' , + description = 'The y variable' ) + self.outputs.append( name = 'z', + units = 'ft**2', + description = 'Output variable' ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - - def BlackBox(self, x, y): # The actual function that does things + def BlackBox(self, x, y): # The actual function that does things # Converts to correct units then casts to float - x = pyo.value(units.convert(x, self.inputs['x'].units)) - y = pyo.value(units.convert(y, self.inputs['y'].units)) - z = x**2 + y**2 # Compute z - dzdx = 2 * x # Compute dz/dx - dzdy = 2 * y # Compute dz/dy + x = pyo.value(units.convert(x,self.inputs['x'].units)) + y = pyo.value(units.convert(y,self.inputs['y'].units)) + z = x**2 + y**2 # Compute z + dzdx = 2*x # Compute dz/dx + dzdy = 2*y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... - - f.Constraint(z <= 1 * units.m**2) + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + f.Constraint( z <= 1*units.m**2 ) - f.RuntimeConstraint(z, '==', [x, y], UnitCircle()) + f.RuntimeConstraint( z, '==', [x,y], UnitCircle() ) # END: Formulation_Snippet_09 - + ### This will fail validatation, but should construct appropriately # BEGIN: Formulation_Snippet_10 - f.RuntimeConstraint(*(z, '==', [x, y], UnitCircle())) + f.RuntimeConstraint( *( z, '==', [x,y], UnitCircle() ) ) # END: Formulation_Snippet_10 - + # BEGIN: Formulation_Snippet_11 - f.RuntimeConstraint(*[z, '==', [x, y], UnitCircle()]) + f.RuntimeConstraint( *[ z, '==', [x,y], UnitCircle() ] ) # END: Formulation_Snippet_11 - + # BEGIN: Formulation_Snippet_12 - f.RuntimeConstraint( - **{ - 'outputs': z, - 'operators': '==', - 'inputs': [x, y], - 'black_box': UnitCircle(), - } - ) + f.RuntimeConstraint( **{ 'outputs' : z, + 'operators' : '==', + 'inputs' : [x,y], + 'black_box' : UnitCircle() } ) # END: Formulation_Snippet_12 - + # BEGIN: Formulation_Snippet_13 - f.RuntimeConstraint(*([z], ['=='], [x, y], UnitCircle())) + f.RuntimeConstraint( *( [z], ['=='], [x,y], UnitCircle() ) ) # END: Formulation_Snippet_13 - + def test_edi_snippet_formuation_14(self): # BEGIN: Formulation_Snippet_14 import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel - f = Formulation() - x = f.Variable(name='x', guess=1.0, units='m', description='x variable') - y = f.Variable(name='y', guess=1.0, units='m', description='y variable') - z = f.Variable(name='z', guess=1.0, units='m^2', description='Output var') - f.Objective(x + y) - + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'y variable') + z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'Output var') + f.Objective( x + y ) class UnitCircle(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super().__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append(name='x', units='ft', description='The x variable') - self.inputs.append(name='y', units='ft', description='The y variable') - self.outputs.append( - name='z', units='ft**2', description='Output variable' - ) + self.inputs.append( name = 'x', + units = 'ft' , + description = 'The x variable' ) + self.inputs.append( name = 'y', + units = 'ft' , + description = 'The y variable' ) + self.outputs.append( name = 'z', + units = 'ft**2', + description = 'Output variable' ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - - def BlackBox(self, x, y): # The actual function that does things + def BlackBox(self, x, y): # The actual function that does things # Converts to correct units then casts to float - x = pyo.value(units.convert(x, self.inputs[0].units)) - y = pyo.value(units.convert(y, self.inputs[1].units)) - z = x**2 + y**2 # Compute z - dzdx = 2 * x # Compute dz/dx - dzdy = 2 * y # Compute dz/dy + x = pyo.value(units.convert(x,self.inputs[0].units)) + y = pyo.value(units.convert(y,self.inputs[1].units)) + z = x**2 + y**2 # Compute z + dzdx = 2*x # Compute dz/dx + dzdy = 2*y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... - f.ConstraintList([z <= 1 * units.m**2, [z, '==', [x, y], UnitCircle()]]) + f.ConstraintList( + [ + z <= 1*units.m**2 , + [ z, '==', [x,y], UnitCircle() ] , + ] + ) # END: Formulation_Snippet_14 ### This will fail validatation, but should construct appropriately # BEGIN: Formulation_Snippet_15 - f.ConstraintList([z <= 1 * units.m**2, (z, '==', [x, y], UnitCircle())]) + f.ConstraintList( + [ + z <= 1*units.m**2 , + ( z, '==', [x,y], UnitCircle() ) , + ] + ) # END: Formulation_Snippet_15 - + # BEGIN: Formulation_Snippet_16 - f.ConstraintList([z <= 1 * units.m**2, [z, '==', [x, y], UnitCircle()]]) + f.ConstraintList( + [ + z <= 1*units.m**2 , + [ z, '==', [x,y], UnitCircle() ] , + ] + ) # END: Formulation_Snippet_16 - + # BEGIN: Formulation_Snippet_17 f.ConstraintList( [ - z <= 1 * units.m**2, - { - 'outputs': z, - 'operators': '==', - 'inputs': [x, y], - 'black_box': UnitCircle(), - }, + z <= 1*units.m**2 , + { 'outputs' : z, + 'operators' : '==', + 'inputs' : [x,y], + 'black_box' : UnitCircle() } , ] ) # END: Formulation_Snippet_17 - + # BEGIN: Formulation_Snippet_18 - f.ConstraintList([z <= 1 * units.m**2, ([z], ['=='], [x, y], UnitCircle())]) + f.ConstraintList( + [ + z <= 1*units.m**2 , + ( [z], ['=='], [x,y], UnitCircle() ) , + ] + ) # END: Formulation_Snippet_18 def test_edi_snippet_variables_01(self): # BEGIN: Variables_Snippet_01 from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') # END: Variables_Snippet_01 - + def test_edi_snippet_variables_02(self): # BEGIN: Variables_Snippet_02 from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable('x', 1.0, 'm') + x = f.Variable('x', 1.0, 'm' ) # END: Variables_Snippet_02 - + def test_edi_snippet_variables_03(self): # BEGIN: Variables_Snippet_03 from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable( - name='x', - guess=1.0, - units='m', - description='The x variable', - bounds=[-10, 10], - ) + x = f.Variable( name = 'x', + guess = 1.0, + units = 'm' , + description = 'The x variable', + bounds = [-10,10] ) # END: Variables_Snippet_03 - + def test_edi_snippet_variables_04(self): # BEGIN: Variables_Snippet_04 from pyomo.contrib.edi import Formulation from pyomo.environ import Integers - f = Formulation() - x = f.Variable( - name='x', - guess=1.0, - units='m', - description='The x variable', - domain=Integers, - ) + x = f.Variable( name = 'x', + guess = 1.0, + units = 'm' , + description = 'The x variable', + domain = Integers ) # END: Variables_Snippet_04 - + def test_edi_snippet_variables_05(self): # BEGIN: Variables_Snippet_05 from pyomo.environ import units from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable(name='x', guess=1.0, units=units.m, description='The x variable') + x = f.Variable( name = 'x', + guess = 1.0, + units = units.m , + description = 'The x variable' ) # END: Variables_Snippet_05 - + def test_edi_snippet_variables_06(self): # BEGIN: Variables_Snippet_06 from pyomo.environ import units from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable( - name='x', guess=1.0, units='m', description='The x variable', size=5 - ) + x = f.Variable( name = 'x', + guess = 1.0, + units = 'm' , + description = 'The x variable', + size = 5 ) # END: Variables_Snippet_06 - + def test_edi_snippet_variables_07(self): # BEGIN: Variables_Snippet_07 from pyomo.environ import units from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable( - name='x', guess=1.0, units='m', description='The x variable', size=[10, 2] - ) + x = f.Variable( name = 'x', + guess = 1.0, + units = 'm' , + description = 'The x variable', + size = [10,2] ) # END: Variables_Snippet_07 - + def test_edi_snippet_variables_08(self): # BEGIN: Variables_Snippet_08 from pyomo.environ import units from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable( - name='x', guess=1.0, units='kg*m/s**2', description='The x variable' - ) + x = f.Variable( name = 'x', + guess = 1.0, + units = 'kg*m/s**2' , + description = 'The x variable' ) # END: Variables_Snippet_08 def test_edi_snippet_constants_01(self): # BEGIN: Constants_Snippet_01 from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Constant(name='c', value=1.0, units='m', description='A constant c') + x = f.Constant(name = 'c', value = 1.0, units = 'm' , description = 'A constant c') # END: Constants_Snippet_01 def test_edi_snippet_constants_02(self): # BEGIN: Constants_Snippet_02 from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Constant('c', 1.0, 'm') + x = f.Constant('c', 1.0, 'm' ) # END: Constants_Snippet_02 def test_edi_snippet_constants_03(self): # BEGIN: Constants_Snippet_03 from pyomo.environ import units from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Constant(name='c', value=1.0, units=units.m, description='A constant c') + x = f.Constant( name = 'c', + value = 1.0, + units = units.m , + description = 'A constant c' ) # END: Constants_Snippet_03 def test_edi_snippet_constants_04(self): # BEGIN: Constants_Snippet_04 from pyomo.environ import units from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Constant( - name='c', value=1.0, units='m', description='A constant c', size=5 - ) + x = f.Constant( name = 'c', + value = 1.0, + units = 'm' , + description = 'A constant c', + size = 5 ) # END: Constants_Snippet_04 def test_edi_snippet_constants_05(self): # BEGIN: Constants_Snippet_05 from pyomo.environ import units from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Constant( - name='c', value=1.0, units='m', description='A constant c', size=[10, 2] - ) + x = f.Constant( name = 'c', + value = 1.0, + units = 'm' , + description = 'A constant c', + size = [10,2] ) # END: Constants_Snippet_05 def test_edi_snippet_constants_06(self): # BEGIN: Constants_Snippet_06 from pyomo.environ import units from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Constant( - name='c', value=1.0, units='kg*m/s**2', description='A constant c' - ) + x = f.Constant( name = 'c', + value = 1.0, + units = 'kg*m/s**2' , + description = 'A constant c' ) # END: Constants_Snippet_06 def test_edi_snippet_objectives_01(self): # BEGIN: Objectives_Snippet_01 from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') - y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') - c = f.Constant(name='c', value=1.0, units='', description='A constant c') - f.Objective(c * x + y) # Default is minimize + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) + f.Objective( c*x + y ) # Default is minimize # END: Objectives_Snippet_01 def test_edi_snippet_objectives_02(self): # BEGIN: Objectives_Snippet_02 from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') - y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') - c = f.Constant(name='c', value=1.0, units='', description='A constant c') - f.Objective(c * x**4 + y**4) # Default is minimize + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) + f.Objective( c*x**4 + y**4 ) # Default is minimize # END: Objectives_Snippet_02 def test_edi_snippet_objectives_03(self): # BEGIN: Objectives_Snippet_03 from pyomo.contrib.edi import Formulation from pyomo.environ import minimize, maximize - f = Formulation() - x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') - y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') - c = f.Constant(name='c', value=1.0, units='', description='A constant c') - f.Objective(c * x**4 + y**4, sense=minimize) + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) + f.Objective( c*x**4 + y**4 , sense = minimize) # END: Objectives_Snippet_03 def test_edi_snippet_objectives_04(self): # BEGIN: Objectives_Snippet_04 from pyomo.contrib.edi import Formulation from pyomo.environ import minimize, maximize - f = Formulation() - x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') - y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') - c = f.Constant(name='c', value=1.0, units='', description='A constant c') - f.Objective(c * x**4 + y**4, sense=1) # 1 corresponds to minimize + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) + f.Objective( c*x**4 + y**4 , sense = 1 ) # 1 corresponds to minimize # END: Objectives_Snippet_04 def test_edi_snippet_objectives_05(self): # BEGIN: Objectives_Snippet_05 from pyomo.contrib.edi import Formulation from pyomo.environ import minimize, maximize - f = Formulation() - x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') - y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') - c = f.Constant(name='c', value=1.0, units='', description='A constant c') - f.Objective(-c * x**4 - y**4, sense=maximize) + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) + f.Objective( -c*x**4 - y**4 , sense = maximize) # END: Objectives_Snippet_05 def test_edi_snippet_objectives_06(self): # BEGIN: Objectives_Snippet_06 from pyomo.contrib.edi import Formulation from pyomo.environ import minimize, maximize - f = Formulation() - x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') - y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') - c = f.Constant(name='c', value=1.0, units='', description='A constant c') - f.Objective(-c * x**4 - y**4, sense=-1) # -1 corresponds to maximize + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) + f.Objective( -c*x**4 - y**4 , sense = -1 ) # -1 corresponds to maximize # END: Objectives_Snippet_06 def test_edi_snippet_objectives_07(self): # BEGIN: Objectives_Snippet_07 from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable( - name='x', - guess=1.0, - units='m', - description='The x variable', - bounds=[0, 100], - size=3, - ) - y = f.Variable( - name='y', guess=1.0, units='m', description='The y variable', size=[2, 2] - ) - c = f.Constant( - name='c', value=1.0, units='', description='A constant c', size=3 - ) - f.Objective( - c[0] * x[0] - + c[1] * x[1] - + c[2] * x[2] - + y[0, 0] ** 4 - + y[0, 1] ** 4 - + y[1, 0] ** 4 - + y[1, 1] ** 4 - ) # Default is minimize + x = f.Variable( name = 'x', + guess = 1.0, + units = 'm' , + description = 'The x variable', + bounds = [0,100] , + size = 3 ) + y = f.Variable( name = 'y', + guess = 1.0, + units = 'm' , + description = 'The y variable', + size = [2,2]) + c = f.Constant( name = 'c', + value = 1.0, + units = '' , + description = 'A constant c' , + size = 3 ) + f.Objective( c[0]*x[0] + c[1]*x[1] + c[2]*x[2] + + y[0,0]**4 + + y[0,1]**4 + + y[1,0]**4 + + y[1,1]**4 ) # Default is minimize # END: Objectives_Snippet_07 def test_edi_snippet_constraints_01(self): # BEGIN: Constraints_Snippet_01 from pyomo.environ import units from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') - y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') - c = f.Constant(name='c', value=1.0, units='', description='A constant c') - f.Objective(c * x + y) + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) + f.Objective( c*x + y ) f.ConstraintList( - [x**2 + y**2 <= 1.0 * units.m**2, x <= 0.75 * units.m, x >= y] + [ + x**2 + y**2 <= 1.0*units.m**2 , + x <= 0.75*units.m, + x >= y , + ] ) # END: Constraints_Snippet_01 @@ -519,58 +528,68 @@ def test_edi_snippet_constraints_02(self): # BEGIN: Constraints_Snippet_02 from pyomo.environ import units from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') - y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') - c = f.Constant(name='c', value=1.0, units='', description='A constant c') - f.Objective(c * x + y) - f.Constraint(x**2 + y**2 <= 1.0 * units.m**2) - f.Constraint(x <= 0.75 * units.m) - f.Constraint(x >= y) + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) + f.Objective( c*x + y ) + f.Constraint( x**2 + y**2 <= 1.0*units.m**2 ) + f.Constraint( x <= 0.75*units.m ) + f.Constraint( x >= y ) # END: Constraints_Snippet_02 def test_edi_snippet_constraints_03(self): # BEGIN: Constraints_Snippet_03 from pyomo.environ import units from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable( - name='x', - guess=1.0, - units='m', - description='The x variable', - bounds=[0, 100], - size=3, - ) - y = f.Variable( - name='y', guess=1.0, units='m', description='The y variable', size=[2, 2] - ) - c = f.Constant( - name='c', value=1.0, units='', description='A constant c', size=3 - ) - f.Objective( - c[0] * x[0] - + c[1] * x[1] - + c[2] * x[2] - + y[0, 0] ** 4 - + y[0, 1] ** 4 - + y[1, 0] ** 4 - + y[1, 1] ** 4 - ) # Default is minimize + x = f.Variable( name = 'x', + guess = 1.0, + units = 'm' , + description = 'The x variable', + bounds = [0,100] , + size = 3 ) + y = f.Variable( name = 'y', + guess = 1.0, + units = 'm' , + description = 'The y variable', + size = [2,2]) + c = f.Constant( name = 'c', + value = 1.0, + units = '' , + description = 'A constant c' , + size = 3 ) + f.Objective( c[0]*x[0] + c[1]*x[1] + c[2]*x[2] + + y[0,0]**4 + + y[0,1]**4 + + y[1,0]**4 + + y[1,1]**4 ) # Default is minimize f.ConstraintList( [ - x[0] ** 2 + x[1] ** 2 + x[2] ** 2 <= 1.0 * units.m, - y[0, 0] >= 1.0 * units.m, - y[0, 1] >= 1.0 * units.m, - y[1, 0] >= 1.0 * units.m, - y[1, 1] >= 1.0 * units.m, - x[0] >= y[0, 0], + x[0]**2 + x[1]**2 + x[2]**2 <= 1.0*units.m, + y[0,0] >= 1.0*units.m, + y[0,1] >= 1.0*units.m, + y[1,0] >= 1.0*units.m, + y[1,1] >= 1.0*units.m, + x[0] >= y[0,0] , ] ) # END: Constraints_Snippet_03 + + + + + + + + + + + + + + def test_edi_snippet_runtimeconstraints_01(self): # BEGIN: RuntimeConstraints_Snippet_01 import pyomo.environ as pyo @@ -578,7 +597,8 @@ def test_edi_snippet_runtimeconstraints_01(self): from pyomo.contrib.edi import BlackBoxFunctionModel class Parabola(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): + # Call parent init super().__init__() @@ -586,48 +606,53 @@ def __init__(self): self.description = 'This model evaluates the function: y = x**2' # Append the model inputs - self.inputs.append(name='x', units='ft', description='The x variable') + self.inputs.append( + name='x', units='ft' , description='The x variable' + ) # Append the model outputs self.outputs.append( - name='y', units='ft**2', description='The y variable' + name='y', units='ft**2' , description='The y variable' ) - + # Set the highest available derivative # Should be 1 for most cases but defaults to 0 self.availableDerivative = 1 - def BlackBox(self, x): # The actual function that does things + def BlackBox(self, x): # The actual function that does things + # Convert to correct units and cast to a float - x = pyo.value(units.convert(x, self.inputs['x'].units)) + x = pyo.value(units.convert(x,self.inputs['x'].units)) # Compute y y = x**2 # Compute dy/dx - dydx = 2 * x - + dydx = 2*x + # Add the units to the output y = y * self.outputs['y'].units - + # Add the units to the derivative for output dydx = dydx * self.outputs['y'].units / self.inputs['x'].units - + # Return using the output packing guidelines described in the documentation: # returnVal[0] = output_value # returnVal[1] = jacobian # returnVal[1][0] = derivative_scalarOutput_wrt_0th_input - return y, [dydx] - + return y, [dydx] # END: RuntimeConstraints_Snippet_01 + def test_edi_snippet_runtimeconstraints_02(self): + import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import BlackBoxFunctionModel class Parabola(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): + # BEGIN: RuntimeConstraints_Snippet_02 # Call parent init super().__init__() @@ -637,95 +662,104 @@ def __init__(self): self.description = 'This model evaluates the function: y = x**2' # Append the model inputs - self.inputs.append(name='x', units='ft', description='The x variable') + self.inputs.append( + name='x', units='ft' , description='The x variable' + ) # Append the model outputs self.outputs.append( - name='y', units='ft**2', description='The y variable' + name='y', units='ft**2' , description='The y variable' ) - + # BEGIN: RuntimeConstraints_Snippet_05 # Set the highest available derivative # Should be 1 for most cases but defaults to 0 self.availableDerivative = 1 # END: RuntimeConstraints_Snippet_05 - def BlackBox(self, x): # The actual function that does things + def BlackBox(self, x): # The actual function that does things + storeX = x # BEGIN: RuntimeConstraints_Snippet_06 - x = units.convert(x, self.inputs['x'].units) + x = units.convert(x,self.inputs['x'].units) # END: RuntimeConstraints_Snippet_06 x = storeX # BEGIN: RuntimeConstraints_Snippet_07 # Convert to correct units and cast to a float - x = pyo.value(units.convert(x, self.inputs['x'].units)) + x = pyo.value(units.convert(x,self.inputs['x'].units)) # END: RuntimeConstraints_Snippet_07 # Compute y y = x**2 # Compute dy/dx - dydx = 2 * x - + dydx = 2*x + # BEGIN: RuntimeConstraints_Snippet_08 # Add the units to the output y = y * self.outputs['y'].units - + # Add the units to the derivative for output dydx = dydx * self.outputs['y'].units / self.inputs['x'].units # END: RuntimeConstraints_Snippet_08 - + # BEGIN: RuntimeConstraints_Snippet_09 # Return using the output packing guidelines described in the documentation: # returnVal[0] = output_value # returnVal[1] = jacobian # returnVal[1][0] = derivative_scalarOutput_wrt_0th_input - return y, [dydx] + return y, [dydx] # END: RuntimeConstraints_Snippet_09 def test_edi_snippet_runtimeconstraints_03(self): import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel - f = Formulation() - x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') - y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') - z = f.Variable(name='z', guess=1.0, units='m^2', description='The z variable') + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The z variable') - f.Objective(x + y) + f.Objective( x + y ) class UnitCircle(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super().__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' # BEGIN: RuntimeConstraints_Snippet_03 - self.inputs.append(name='x', units='ft', description='The x variable') - self.inputs.append(name='y', units='ft', description='The y variable') + self.inputs.append( name = 'x', + units = 'ft' , + description = 'The x variable' ) + self.inputs.append( name = 'y', + units = 'ft' , + description = 'The y variable' ) # END: RuntimeConstraints_Snippet_03 - self.outputs.append( - name='z', - units='ft**2', - description='Resultant of the unit circle evaluation', - ) + self.outputs.append( name = 'z', + units = 'ft**2', + description = 'Resultant of the unit circle evaluation' ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, x, y): # The actual function that does things - x = pyo.value(units.convert(x, self.inputs['x'].units)) - y = pyo.value(units.convert(y, self.inputs['y'].units)) + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value(units.convert(x,self.inputs['x'].units)) + y = pyo.value(units.convert(y,self.inputs['y'].units)) z = x**2 + y**2 - dzdx = 2 * x - dzdy = 2 * y + dzdx = 2*x + dzdy = 2*y z = z * self.outputs['z'].units dzdx = dzdx * self.outputs['z'].units / self.inputs['x'].units dzdy = dzdy * self.outputs['z'].units / self.inputs['y'].units - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... - f.ConstraintList([(z, '==', [x, y], UnitCircle()), z <= 1 * units.m**2]) + f.ConstraintList( + [ + (z, '==', [x,y], UnitCircle() ) , + z <= 1*units.m**2 + ] + ) def test_edi_snippet_runtimeconstraints_04(self): import numpy as np @@ -739,113 +773,139 @@ def __init__(self): # Set up all the attributes by calling Model.__init__ super().__init__() - # Setup Inputs - self.inputs.append('x', '', 'X Variable') - self.inputs.append('y', '', 'Y Variable') + #Setup Inputs + self.inputs.append( 'x', '', 'X Variable') + self.inputs.append( 'y', '', 'Y Variable') # BEGIN: RuntimeConstraints_Snippet_04 - # Setup Outputs - self.outputs.append('u', '', 'U Variable') - self.outputs.append('v', '', 'V Variable') + #Setup Outputs + self.outputs.append( 'u', '', 'U Variable') + self.outputs.append( 'v', '', 'V Variable') # END: RuntimeConstraints_Snippet_04 - # Simple model description + #Simple model description self.description = 'This model is a pass through)' self.availableDerivative = 1 - # standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) + #standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) def BlackBox(self, *args, **kwargs): runCases, returnMode, extras = self.parseInputs(*args, **kwargs) - x = [ - self.pyomo_value(runCases[i]['x']) for i in range(0, len(runCases)) - ] - y = [ - self.pyomo_value(runCases[i]['x']) for i in range(0, len(runCases)) - ] - - u = [] + x = [ self.pyomo_value(runCases[i]['x']) for i in range(0,len(runCases)) ] + y = [ self.pyomo_value(runCases[i]['x']) for i in range(0,len(runCases)) ] + + u = [] dudx = [] dudy = [] - v = [] + v = [] dvdx = [] dvdy = [] for xval in x: - u.append(xval * units.dimensionless) - dudx.append(1.0 * units.dimensionless) - dudy.append(0.0 * units.dimensionless) - + u.append(xval*units.dimensionless) + dudx.append(1.0*units.dimensionless) + dudy.append(0.0*units.dimensionless) + for yval in y: - v.append(yval * units.dimensionless) - dvdx.append(0.0 * units.dimensionless) - dvdy.append(1.0 * units.dimensionless) - + v.append(yval*units.dimensionless) + dvdx.append(0.0*units.dimensionless) + dvdy.append(1.0*units.dimensionless) + if returnMode < 0: - returnMode = -1 * (returnMode + 1) + returnMode = -1*(returnMode + 1) if returnMode == 0: return [u[0], v[0]] if returnMode == 1: - return [u[0], v[0]], [[dudx[0], dudy[0]], [dvdx[0], dvdy[0]]] + return [u[0], v[0]] , [[dudx[0], dudy[0]], [dvdx[0], dvdy[0]]] else: if returnMode == 0: opt = [] - for i in range(0, len(y)): - opt.append([u[i], v[i]]) + for i in range(0,len(y)): + opt.append([ u[i], v[i] ]) return opt if returnMode == 1: opt = [] - for i in range(0, len(y)): - opt.append( - [[u[i], v[i]], [[dudx[i], dudy[i]], [dvdx[i], dvdy[i]]]] - ) + for i in range(0,len(y)): + opt.append([ [u[i], v[i]], [ [ dudx[i], dudy[i]], [dvdx[i], dvdy[i]] ] ]) return opt bb = PassThrough() - bbo = bb.BlackBox(1.0, 1.0) - bbo = bb.BlackBox({'x': np.linspace(0, 10, 11), 'y': np.linspace(0, 10, 11)}) + bbo = bb.BlackBox(1.0,1.0) + bbo = bb.BlackBox({'x':np.linspace(0,10,11),'y':np.linspace(0,10,11)}) def test_edi_snippet_runtimeconstraints_10(self): # BEGIN: RuntimeConstraints_Snippet_10 import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel - f = Formulation() - x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') - y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') - z = f.Variable(name='z', guess=1.0, units='m^2', description='The z variable') + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The z variable') - f.Objective(x + y) + f.Objective( x + y ) class UnitCircle(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super().__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append(name='x', units='ft', description='The x variable') - self.inputs.append(name='y', units='ft', description='The y variable') - self.outputs.append( - name='z', - units='ft**2', - description='Resultant of the unit circle evaluation', - ) + self.inputs.append( name = 'x', + units = 'ft' , + description = 'The x variable' ) + self.inputs.append( name = 'y', + units = 'ft' , + description = 'The y variable' ) + self.outputs.append( name = 'z', + units = 'ft**2', + description = 'Resultant of the unit circle evaluation' ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, x, y): # The actual function that does things - x = pyo.value(units.convert(x, self.inputs['x'].units)) - y = pyo.value(units.convert(y, self.inputs['y'].units)) + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value(units.convert(x,self.inputs['x'].units)) + y = pyo.value(units.convert(y,self.inputs['y'].units)) z = x**2 + y**2 - dzdx = 2 * x - dzdy = 2 * y + dzdx = 2*x + dzdy = 2*y z = z * self.outputs['z'].units dzdx = dzdx * self.outputs['z'].units / self.inputs['x'].units dzdy = dzdy * self.outputs['z'].units / self.inputs['y'].units - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... - f.ConstraintList([(z, '==', [x, y], UnitCircle()), z <= 1 * units.m**2]) + f.ConstraintList( + [ + (z, '==', [x,y], UnitCircle() ) , + z <= 1*units.m**2 + ] + ) # END: RuntimeConstraints_Snippet_10 + + + + + + + + + + + + + + + + + + + + + + + + + + def test_edi_snippet_advancedRTC_01(self): # BEGIN: AdvancedRTC_Snippet_01 import numpy as np @@ -857,39 +917,36 @@ class SignomialTest(BlackBoxFunctionModel): def __init__(self): # Set up all the attributes by calling Model.__init__ super().__init__() - - # Setup Inputs - self.inputs.append('x', '', 'Independent Variable') - - # Setup Outputs - self.outputs.append('y', '', 'Dependent Variable') - - # Simple model description - self.description = ( - 'This model evaluates the ' + 'function: max([-6*x-6, x**4-3*x**2])' - ) - + + #Setup Inputs + self.inputs.append( 'x', '', 'Independent Variable') + + #Setup Outputs + self.outputs.append( 'y', '', 'Dependent Variable') + + #Simple model description + self.description = ( 'This model evaluates the '+ + 'function: max([-6*x-6, x**4-3*x**2])' ) + self.availableDerivative = 1 - + def BlackBox(self, *args, **kwargs): runCases, returnMode, extras = self.parseInputs(*args, **kwargs) - x = np.array( - [pyo.value(runCases[i]['x']) for i in range(0, len(runCases))] - ) - - y = np.maximum(-6 * x - 6, x**4 - 3 * x**2) - dydx = 4 * x**3 - 6 * x - ddy_ddx = 12 * x**2 - 6 - gradientSwitch = -6 * x - 6 > x**4 - 3 * x**2 + x = np.array([ pyo.value(runCases[i]['x']) for i in range(0,len(runCases)) ]) + + y = np.maximum(-6*x-6, x**4-3*x**2) + dydx = 4*x**3 - 6*x + ddy_ddx = 12*x**2 - 6 + gradientSwitch = -6*x-6 > x**4-3*x**2 dydx[gradientSwitch] = -6 ddy_ddx[gradientSwitch] = 0 - - y = [yval * units.dimensionless for yval in y] - dydx = [dydx[i] * units.dimensionless for i in range(0, len(dydx))] - + + y = [ yval*units.dimensionless for yval in y ] + dydx = [dydx[i] * units.dimensionless for i in range(0,len(dydx))] + if returnMode < 0: - returnMode = -1 * (returnMode + 1) + returnMode = -1*(returnMode + 1) if returnMode == 0: return y[0] if returnMode == 1: @@ -897,33 +954,34 @@ def BlackBox(self, *args, **kwargs): else: if returnMode == 0: opt = [] - for i in range(0, len(y)): - opt.append([y[i]]) + for i in range(0,len(y)): + opt.append([ y[i] ]) return opt if returnMode == 1: opt = [] - for i in range(0, len(y)): - opt.append([[y[i]], [[[dydx[i]]]]]) + for i in range(0,len(y)): + opt.append([ [y[i]], [ [[dydx[i]]] ] ]) return opt s = SignomialTest() - ivals = [[x] for x in np.linspace(-2, 2, 11)] + ivals = [[x] for x in np.linspace(-2,2,11)] # How the black box may be called using EDI - bbo = s.BlackBox(**{'x': 0.5}) - bbo = s.BlackBox({'x': 0.5}) - bbo = s.BlackBox(**{'x': 0.5, 'optn': True}) + bbo = s.BlackBox(**{'x':0.5}) + bbo = s.BlackBox({'x':0.5}) + bbo = s.BlackBox(**{'x':0.5, 'optn':True}) # Additional options available with parseInputs bbo = s.BlackBox(*[0.5], **{'optn1': True, 'optn2': False}) - bbo = s.BlackBox(*[0.5, True], **{'optn': False}) - bbo = s.BlackBox({'x': [x for x in np.linspace(-2, 2, 11)]}) - bbo = s.BlackBox([{'x': x} for x in np.linspace(-2, 2, 11)]) - bbo = s.BlackBox([[x] for x in np.linspace(-2, 2, 11)]) - bbo = s.BlackBox([[x] for x in np.linspace(-2, 2, 11)], True, optn=False) - bbo = s.BlackBox([[x] for x in np.linspace(-2, 2, 11)], optn1=True, optn2=False) + bbo = s.BlackBox(*[0.5,True], **{'optn': False}) + bbo = s.BlackBox({'x':[x for x in np.linspace(-2,2,11)]}) + bbo = s.BlackBox([{'x':x} for x in np.linspace(-2,2,11)]) + bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)]) + bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)], True, optn=False) + bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)], optn1=True, optn2=False) # END: AdvancedRTC_Snippet_01 + def test_edi_snippet_advancedRTC_02(self): import numpy as np import pyomo.environ as pyo @@ -934,41 +992,38 @@ class SignomialTest(BlackBoxFunctionModel): def __init__(self): # Set up all the attributes by calling Model.__init__ super().__init__() - - # Setup Inputs - self.inputs.append('x', '', 'Independent Variable') - - # Setup Outputs - self.outputs.append('y', '', 'Dependent Variable') - - # Simple model description - self.description = ( - 'This model evaluates the ' + 'function: max([-6*x-6, x**4-3*x**2])' - ) - + + #Setup Inputs + self.inputs.append( 'x', '', 'Independent Variable') + + #Setup Outputs + self.outputs.append( 'y', '', 'Dependent Variable') + + #Simple model description + self.description = ( 'This model evaluates the '+ + 'function: max([-6*x-6, x**4-3*x**2])' ) + self.availableDerivative = 1 # BEGIN: AdvancedRTC_Snippet_02 def BlackBox(self, *args, **kwargs): runCases, returnMode, extras = self.parseInputs(*args, **kwargs) - # END: AdvancedRTC_Snippet_02 - - x = np.array( - [pyo.value(runCases[i]['x']) for i in range(0, len(runCases))] - ) - - y = np.maximum(-6 * x - 6, x**4 - 3 * x**2) - dydx = 4 * x**3 - 6 * x - ddy_ddx = 12 * x**2 - 6 - gradientSwitch = -6 * x - 6 > x**4 - 3 * x**2 + # END: AdvancedRTC_Snippet_02 + + x = np.array([ pyo.value(runCases[i]['x']) for i in range(0,len(runCases)) ]) + + y = np.maximum(-6*x-6, x**4-3*x**2) + dydx = 4*x**3 - 6*x + ddy_ddx = 12*x**2 - 6 + gradientSwitch = -6*x-6 > x**4-3*x**2 dydx[gradientSwitch] = -6 ddy_ddx[gradientSwitch] = 0 - - y = [yval * units.dimensionless for yval in y] - dydx = [dydx[i] * units.dimensionless for i in range(0, len(dydx))] - + + y = [ yval*units.dimensionless for yval in y ] + dydx = [dydx[i] * units.dimensionless for i in range(0,len(dydx))] + if returnMode < 0: - returnMode = -1 * (returnMode + 1) + returnMode = -1*(returnMode + 1) if returnMode == 0: return y[0] if returnMode == 1: @@ -976,32 +1031,36 @@ def BlackBox(self, *args, **kwargs): else: if returnMode == 0: opt = [] - for i in range(0, len(y)): - opt.append([y[i]]) + for i in range(0,len(y)): + opt.append([ y[i] ]) return opt if returnMode == 1: opt = [] - for i in range(0, len(y)): - opt.append([[y[i]], [[[dydx[i]]]]]) + for i in range(0,len(y)): + opt.append([ [y[i]], [ [[dydx[i]]] ] ]) return opt s = SignomialTest() - ivals = [[x] for x in np.linspace(-2, 2, 11)] + ivals = [[x] for x in np.linspace(-2,2,11)] # How the black box may be called using EDI - bbo = s.BlackBox(**{'x': 0.5}) - bbo = s.BlackBox({'x': 0.5}) - bbo = s.BlackBox(**{'x': 0.5, 'optn': True}) + bbo = s.BlackBox(**{'x':0.5}) + bbo = s.BlackBox({'x':0.5}) + bbo = s.BlackBox(**{'x':0.5, 'optn':True}) # Additional options available with parseInputs bbo = s.BlackBox(*[0.5], **{'optn1': True, 'optn2': False}) - bbo = s.BlackBox(*[0.5, True], **{'optn': False}) - bbo = s.BlackBox({'x': [x for x in np.linspace(-2, 2, 11)]}) - bbo = s.BlackBox([{'x': x} for x in np.linspace(-2, 2, 11)]) - bbo = s.BlackBox([[x] for x in np.linspace(-2, 2, 11)]) - bbo = s.BlackBox([[x] for x in np.linspace(-2, 2, 11)], True, optn=False) - bbo = s.BlackBox([[x] for x in np.linspace(-2, 2, 11)], optn1=True, optn2=False) + bbo = s.BlackBox(*[0.5,True], **{'optn': False}) + bbo = s.BlackBox({'x':[x for x in np.linspace(-2,2,11)]}) + bbo = s.BlackBox([{'x':x} for x in np.linspace(-2,2,11)]) + bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)]) + bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)], True, optn=False) + bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)], optn1=True, optn2=False) + + + + if __name__ == '__main__': - unittest.main() + unittest.main() \ No newline at end of file diff --git a/pyomo/contrib/edi/tests/test_examples.py b/pyomo/contrib/edi/tests/test_examples.py index 2bbad68bb08..8eb496f74f5 100644 --- a/pyomo/contrib/edi/tests/test_examples.py +++ b/pyomo/contrib/edi/tests/test_examples.py @@ -19,50 +19,45 @@ import pyomo.environ as pyo from pyomo.common.dependencies import attempt_import -from pyomo.core.base.units_container import pint_available - -import importlib - -<<<<<<< HEAD -testIndex = 0 - -from pyomo.core.base.units_container import pint_available +from pyomo.core.base.units_container import ( + pint_available, +) from pyomo.common.dependencies import numpy, numpy_available from pyomo.common.dependencies import scipy, scipy_available +egb, egb_available = attempt_import("pyomo.contrib.pynumero.interfaces.external_grey_box") +if numpy_available: + import numpy as np +@unittest.skipIf(not egb_available, 'Testing pyomo.contrib.edi requires pynumero external grey boxes') @unittest.skipIf(not numpy_available, 'Testing pyomo.contrib.edi requires numpy') @unittest.skipIf(not scipy_available, 'Testing pyomo.contrib.edi requires scipy') -@unittest.skipIf(not pint_available, 'Testing units requires pint') -======= - -testIndex = 0 - - ->>>>>>> parent of f76f4974b (addressing concerns in PR) +@unittest.skipIf(not pint_available, 'Testing units requires pint') class EDIExamples(unittest.TestCase): def test_edi_example_placeholder(self): "A placeholder" pass - def create_new(filename): def t_function(self): importName = filename[0:-3] # filename = ".."+filename try: - importlib.import_module("pyomo.contrib.edi.examples." + importName) + importlib.import_module("pyomo.contrib.edi.examples."+importName) except: - self.fail("This example is failing: %s" % (filename)) - + self.fail("This example is failing: %s"%(filename)) return t_function -pythonFileList = ["readme_example.py", "aircraft_gp.py"] + +pythonFileList = [ + "readme_example.py", + "aircraft_gp.py" +] for filename in pythonFileList: - testName = 'test_DocumentationExample_%d' % (testIndex) + testName = 'test_DocumentationExample_%d'%(testIndex) testIndex += 1 t_Function = create_new(filename) if pint_available: @@ -71,3 +66,4 @@ def t_function(self): if __name__ == '__main__': unittest.main() + diff --git a/pyomo/contrib/edi/tests/test_formulation.py b/pyomo/contrib/edi/tests/test_formulation.py index b9d5414e525..2ae33eac4f2 100644 --- a/pyomo/contrib/edi/tests/test_formulation.py +++ b/pyomo/contrib/edi/tests/test_formulation.py @@ -17,52 +17,34 @@ import pyomo.common.unittest as unittest import pyomo.environ as pyo - -# from pyomo.opt import check_optimal_termination from pyomo.common.dependencies import attempt_import -from pyomo.core.base.units_container import pint_available -<<<<<<< HEAD - -======= ->>>>>>> parent of f76f4974b (addressing concerns in PR) - -# np, numpy_available = attempt_import( -# 'numpy', 'edi requires numpy' -# ) -# scipy, scipy_available = attempt_import( -# 'scipy', 'inverse_reduced_hessian requires scipy' -# ) - -# if not (numpy_available and scipy_available): -# if not numpy_available: -# raise unittest.SkipTest( -# 'edi.formulation tests require numpy' -# ) +from pyomo.core.base.units_container import ( + pint_available, +) - -@unittest.skipIf(not pint_available, 'Testing units requires pint') +@unittest.skipIf(not pint_available, 'Testing pyomo.contrib.edi requires pint') class TestEDIFormulation(unittest.TestCase): def test_edi_formulation_init(self): "Tests that a formulation initializes to the correct type and has proper data" from pyomo.environ import ConcreteModel from pyomo.contrib.edi import Formulation - f = Formulation() self.assertIsInstance(f, Formulation) self.assertIsInstance(f, ConcreteModel) - self.assertEqual(f._objective_counter, 0) - self.assertEqual(f._constraint_counter, 0) - self.assertEqual(f._variable_keys, []) - self.assertEqual(f._constant_keys, []) - self.assertEqual(f._objective_keys, []) - self.assertEqual(f._runtimeObjective_keys, []) - self.assertEqual(f._objective_keys, []) - self.assertEqual(f._runtimeConstraint_keys, []) - self.assertEqual(f._constraint_keys, []) - self.assertEqual(f._allConstraint_keys, []) + self.assertEqual(f._objective_counter , 0 ) + self.assertEqual(f._constraint_counter , 0 ) + self.assertEqual(f._variable_keys , [] ) + self.assertEqual(f._constant_keys , [] ) + self.assertEqual(f._objective_keys , [] ) + self.assertEqual(f._runtimeObjective_keys , [] ) + self.assertEqual(f._objective_keys , [] ) + self.assertEqual(f._runtimeConstraint_keys , [] ) + self.assertEqual(f._constraint_keys , [] ) + self.assertEqual(f._allConstraint_keys , [] ) + def test_edi_formulation_variable(self): "Tests the variable constructor in edi.formulation" @@ -72,182 +54,26 @@ def test_edi_formulation_variable(self): f = Formulation() - x1 = f.Variable( - name='x1', - guess=1.0, - units='m', - description='The x variable', - size=None, - bounds=None, - domain=None, - ) - self.assertRaises(RuntimeError, f.Variable, *('x1', 1.0, 'm')) + x1 = f.Variable(name = 'x1', guess = 1.0, units = 'm' , description = 'The x variable', size = None, bounds=None, domain=None ) + self.assertRaises(RuntimeError,f.Variable,*('x1', 1.0, 'm')) x2 = f.Variable('x2', 1.0, 'm') - x3 = f.Variable('x3', 1.0, 'm', 'The x variable', None, None, None) - x4 = f.Variable( - name='x4', - guess=1.0, - units='m', - description='The x variable', - size=None, - bounds=None, - domain=PositiveReals, - ) - self.assertRaises( - RuntimeError, - f.Variable, - **{ - 'name': 'x5', - 'guess': 1.0, - 'units': 'm', - 'description': 'The x variable', - 'size': None, - 'bounds': None, - 'domain': "error", - } - ) - - x6 = f.Variable( - name='x6', - guess=1.0, - units='m', - description='The x variable', - size=0, - bounds=None, - domain=None, - ) - x7 = f.Variable( - name='x7', - guess=1.0, - units='m', - description='The x variable', - size=5, - bounds=None, - domain=None, - ) - self.assertRaises( - ValueError, - f.Variable, - **{ - 'name': 'x8', - 'guess': 1.0, - 'units': 'm', - 'description': 'The x variable', - 'size': 'error', - 'bounds': None, - 'domain': None, - } - ) - x9 = f.Variable( - name='x9', - guess=1.0, - units='m', - description='The x variable', - size=[2, 2], - bounds=None, - domain=None, - ) - self.assertRaises( - ValueError, - f.Variable, - **{ - 'name': 'x10', - 'guess': 1.0, - 'units': 'm', - 'description': 'The x variable', - 'size': ['2', '2'], - 'bounds': None, - 'domain': None, - } - ) - self.assertRaises( - ValueError, - f.Variable, - **{ - 'name': 'x11', - 'guess': 1.0, - 'units': 'm', - 'description': 'The x variable', - 'size': [2, 1], - 'bounds': None, - 'domain': None, - } - ) - - x12 = f.Variable( - name='x12', - guess=1.0, - units='m', - description='The x variable', - size=None, - bounds=[-10, 10], - domain=None, - ) - self.assertRaises( - ValueError, - f.Variable, - **{ - 'name': 'x13', - 'guess': 1.0, - 'units': 'm', - 'description': 'The x variable', - 'size': None, - 'bounds': [10, -10], - 'domain': None, - } - ) - self.assertRaises( - ValueError, - f.Variable, - **{ - 'name': 'x14', - 'guess': 1.0, - 'units': 'm', - 'description': 'The x variable', - 'size': None, - 'bounds': ["-10", "10"], - 'domain': None, - } - ) - self.assertRaises( - ValueError, - f.Variable, - **{ - 'name': 'x15', - 'guess': 1.0, - 'units': 'm', - 'description': 'The x variable', - 'size': None, - 'bounds': [1, 2, 3], - 'domain': None, - } - ) - self.assertRaises( - ValueError, - f.Variable, - **{ - 'name': 'x16', - 'guess': 1.0, - 'units': 'm', - 'description': 'The x variable', - 'size': None, - 'bounds': "error", - 'domain': None, - } - ) - self.assertRaises( - ValueError, - f.Variable, - **{ - 'name': 'x17', - 'guess': 1.0, - 'units': 'm', - 'description': 'The x variable', - 'size': None, - 'bounds': [0, "10"], - 'domain': None, - } - ) + x3 = f.Variable('x3', 1.0, 'm' , 'The x variable', None, None, None ) + x4 = f.Variable(name = 'x4', guess = 1.0, units = 'm' , description = 'The x variable', size = None, bounds=None, domain=PositiveReals ) + self.assertRaises(RuntimeError, f.Variable, **{'name':'x5', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': None, 'bounds':None, 'domain':"error" }) + + x6 = f.Variable(name = 'x6', guess = 1.0, units = 'm' , description = 'The x variable', size = 0, bounds=None, domain=None ) + x7 = f.Variable(name = 'x7', guess = 1.0, units = 'm' , description = 'The x variable', size = 5, bounds=None, domain=None ) + self.assertRaises(ValueError, f.Variable, **{'name':'x8', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': 'error', 'bounds':None, 'domain':None }) + x9 = f.Variable(name = 'x9', guess = 1.0, units = 'm' , description = 'The x variable', size = [2,2], bounds=None, domain=None ) + self.assertRaises(ValueError, f.Variable, **{'name':'x10', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': ['2','2'], 'bounds':None, 'domain':None }) + self.assertRaises(ValueError, f.Variable, **{'name':'x11', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': [2,1], 'bounds':None, 'domain':None }) + + x12 = f.Variable(name = 'x12', guess = 1.0, units = 'm' , description = 'The x variable', size = None, bounds=[-10,10], domain=None ) + self.assertRaises(ValueError, f.Variable, **{'name':'x13', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': None, 'bounds':[10,-10], 'domain':None }) + self.assertRaises(ValueError, f.Variable, **{'name':'x14', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': None, 'bounds':["-10","10"], 'domain':None }) + self.assertRaises(ValueError, f.Variable, **{'name':'x15', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': None, 'bounds':[1,2,3], 'domain':None }) + self.assertRaises(ValueError, f.Variable, **{'name':'x16', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': None, 'bounds':"error", 'domain':None }) + self.assertRaises(ValueError, f.Variable, **{'name':'x17', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': None, 'bounds':[0,"10"], 'domain':None }) # verifies alternate unit construction x18 = f.Variable('x18', 1.0, pyo.units.m) @@ -260,98 +86,20 @@ def test_edi_formulation_constant(self): f = Formulation() - c1 = f.Constant( - name='c1', - value=1.0, - units='m', - description='A constant c', - size=None, - within=None, - ) - self.assertRaises(RuntimeError, f.Constant, *('c1', 1.0, 'm')) + c1 = f.Constant(name = 'c1', value = 1.0, units = 'm', description = 'A constant c', size = None, within = None) + self.assertRaises(RuntimeError,f.Constant,*('c1', 1.0, 'm')) c2 = f.Constant('c2', 1.0, 'm') c3 = f.Constant('c3', 1.0, 'm', 'A constant c', None, None) - c4 = f.Constant( - name='c4', - value=1.0, - units='m', - description='A constant c', - size=None, - within=PositiveReals, - ) - self.assertRaises( - RuntimeError, - f.Constant, - **{ - 'name': 'c5', - 'value': 1.0, - 'units': 'm', - 'description': 'The x variable', - 'size': None, - 'within': "error", - } - ) + c4 = f.Constant(name = 'c4', value = 1.0, units = 'm' , description = 'A constant c', size = None, within=PositiveReals ) + self.assertRaises(RuntimeError, f.Constant, **{'name':'c5', 'value':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': None, 'within':"error" }) + + c6 = f.Constant(name = 'c6', value = 1.0, units = 'm' , description = 'A constant c', size = 0, within=None ) + c7 = f.Constant(name = 'c7', value = 1.0, units = 'm' , description = 'A constant c', size = 5, within=None ) + self.assertRaises(ValueError, f.Constant, **{'name':'c8', 'value':1.0 , 'units' :'m' , 'description': 'A constant c', 'size': 'error', 'within':None }) + c9 = f.Constant(name = 'c9', value = 1.0, units = 'm' , description = 'A constant c', size = [2,2], within=None ) + self.assertRaises(ValueError, f.Constant, **{'name':'c10', 'value':1.0 , 'units' :'m' , 'description': 'A constant c', 'size': ['2','2'], 'within':None }) + self.assertRaises(ValueError, f.Constant, **{'name':'c11', 'value':1.0 , 'units' :'m' , 'description': 'A constant c', 'size': [2,1], 'within':None }) - c6 = f.Constant( - name='c6', - value=1.0, - units='m', - description='A constant c', - size=0, - within=None, - ) - c7 = f.Constant( - name='c7', - value=1.0, - units='m', - description='A constant c', - size=5, - within=None, - ) - self.assertRaises( - ValueError, - f.Constant, - **{ - 'name': 'c8', - 'value': 1.0, - 'units': 'm', - 'description': 'A constant c', - 'size': 'error', - 'within': None, - } - ) - c9 = f.Constant( - name='c9', - value=1.0, - units='m', - description='A constant c', - size=[2, 2], - within=None, - ) - self.assertRaises( - ValueError, - f.Constant, - **{ - 'name': 'c10', - 'value': 1.0, - 'units': 'm', - 'description': 'A constant c', - 'size': ['2', '2'], - 'within': None, - } - ) - self.assertRaises( - ValueError, - f.Constant, - **{ - 'name': 'c11', - 'value': 1.0, - 'units': 'm', - 'description': 'A constant c', - 'size': [2, 1], - 'within': None, - } - ) def test_edi_formulation_objective(self): "Tests the objective constructor in edi.formulation" @@ -360,9 +108,9 @@ def test_edi_formulation_objective(self): from pyomo.contrib.edi import Formulation f = Formulation() - x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') - y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') - f.Objective(x + y) + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + f.Objective( x + y ) def test_edi_formulation_runtimeobjective(self): "Tests the runtime objective constructor in edi.formulation" @@ -374,436 +122,317 @@ def test_edi_formulation_constraint(self): import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') - y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') - f.Objective(x + y) - f.Constraint(x + y <= 1.0 * units.m) + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + f.Objective(x+y) + f.Constraint(x+y <= 1.0*units.m) + def test_edi_formulation_runtimeconstraint_tuple(self): "Tests the runtime constraint constructor in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel - f = Formulation() - x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') - y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') - z = f.Variable( - name='z', guess=1.0, units='m^2', description='The unit circle output' - ) - c = f.Constant( - name='c', value=1.0, units='', description='A constant c', size=2 - ) - f.Objective(x + y) - + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The unit circle output') + c = f.Constant(name = 'c', value = 1.0, units = '', description = 'A constant c', size = 2) + f.Objective( x + y ) class UnitCircle(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super(UnitCircle, self).__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append(name='x', units='ft', description='The x variable') - self.inputs.append(name='y', units='ft', description='The y variable') - self.outputs.append( - name='z', - units='ft**2', - description='Resultant of the unit circle evaluation', - ) + self.inputs.append(name = 'x', units = 'ft' , description = 'The x variable') + self.inputs.append(name = 'y', units = 'ft' , description = 'The y variable') + self.outputs.append(name = 'z', units = 'ft**2', description = 'Resultant of the unit circle evaluation') self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - - def BlackBox(self, x, y): # The actual function that does things - x = pyo.value( - units.convert(x, self.inputs[0].units) - ) # Converts to correct units then casts to float - y = pyo.value( - units.convert(y, self.inputs[1].units) - ) # Converts to correct units then casts to float - z = x**2 + y**2 # Compute z - dzdx = 2 * x # Compute dz/dx - dzdy = 2 * y # Compute dz/dy + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float + y = pyo.value(units.convert(y,self.inputs[1].units)) # Converts to correct units then casts to float + z = x**2 + y**2 # Compute z + dzdx = 2*x # Compute dz/dx + dzdy = 2*y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + f.Constraint( z <= 1*units.m**2 ) - f.Constraint(z <= 1 * units.m**2) - - f.RuntimeConstraint(*(z, '==', [x, y], UnitCircle())) + f.RuntimeConstraint( *( z, '==', [x,y], UnitCircle() ) ) def test_edi_formulation_runtimeconstraint_list(self): "Tests the runtime constraint constructor in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel - f = Formulation() - x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') - y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') - z = f.Variable( - name='z', guess=1.0, units='m^2', description='The unit circle output' - ) - c = f.Constant( - name='c', value=1.0, units='', description='A constant c', size=2 - ) - f.Objective(x + y) - + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The unit circle output') + c = f.Constant(name = 'c', value = 1.0, units = '', description = 'A constant c', size = 2) + f.Objective( x + y ) class UnitCircle(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super(UnitCircle, self).__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append(name='x', units='ft', description='The x variable') - self.inputs.append(name='y', units='ft', description='The y variable') - self.outputs.append( - name='z', - units='ft**2', - description='Resultant of the unit circle evaluation', - ) + self.inputs.append(name = 'x', units = 'ft' , description = 'The x variable') + self.inputs.append(name = 'y', units = 'ft' , description = 'The y variable') + self.outputs.append(name = 'z', units = 'ft**2', description = 'Resultant of the unit circle evaluation') self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - - def BlackBox(self, x, y): # The actual function that does things - x = pyo.value( - units.convert(x, self.inputs[0].units) - ) # Converts to correct units then casts to float - y = pyo.value( - units.convert(y, self.inputs[1].units) - ) # Converts to correct units then casts to float - z = x**2 + y**2 # Compute z - dzdx = 2 * x # Compute dz/dx - dzdy = 2 * y # Compute dz/dy + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float + y = pyo.value(units.convert(y,self.inputs[1].units)) # Converts to correct units then casts to float + z = x**2 + y**2 # Compute z + dzdx = 2*x # Compute dz/dx + dzdy = 2*y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... - - f.Constraint(z <= 1 * units.m**2) + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + f.Constraint( z <= 1*units.m**2 ) + + f.RuntimeConstraint( *[ [z], ['=='], [x,y], UnitCircle() ] ) - f.RuntimeConstraint(*[[z], ['=='], [x, y], UnitCircle()]) def test_edi_formulation_runtimeconstraint_dict(self): "Tests the runtime constraint constructor in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel - f = Formulation() - x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') - y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') - z = f.Variable( - name='z', guess=1.0, units='m^2', description='The unit circle output' - ) - c = f.Constant( - name='c', value=1.0, units='', description='A constant c', size=2 - ) - f.Objective(x + y) - + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The unit circle output') + c = f.Constant(name = 'c', value = 1.0, units = '', description = 'A constant c', size = 2) + f.Objective( x + y ) class UnitCircle(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super(UnitCircle, self).__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append(name='x', units='ft', description='The x variable') - self.inputs.append(name='y', units='ft', description='The y variable') - self.outputs.append( - name='z', - units='ft**2', - description='Resultant of the unit circle evaluation', - ) + self.inputs.append(name = 'x', units = 'ft' , description = 'The x variable') + self.inputs.append(name = 'y', units = 'ft' , description = 'The y variable') + self.outputs.append(name = 'z', units = 'ft**2', description = 'Resultant of the unit circle evaluation') self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - - def BlackBox(self, x, y): # The actual function that does things - x = pyo.value( - units.convert(x, self.inputs[0].units) - ) # Converts to correct units then casts to float - y = pyo.value( - units.convert(y, self.inputs[1].units) - ) # Converts to correct units then casts to float - z = x**2 + y**2 # Compute z - dzdx = 2 * x # Compute dz/dx - dzdy = 2 * y # Compute dz/dy + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float + y = pyo.value(units.convert(y,self.inputs[1].units)) # Converts to correct units then casts to float + z = x**2 + y**2 # Compute z + dzdx = 2*x # Compute dz/dx + dzdy = 2*y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... - - f.Constraint(z <= 1 * units.m**2) - - f.RuntimeConstraint( - **{ - 'outputs': z, - 'operators': '==', - 'inputs': [x, y], - 'black_box': UnitCircle(), - } - ) + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + f.Constraint( z <= 1*units.m**2 ) + + f.RuntimeConstraint( **{ 'outputs':z, 'operators':'==', 'inputs':[x,y], 'black_box':UnitCircle() } ) + def test_edi_formulation_constraintlist_1(self): "Tests the constraint list constructor in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel - f = Formulation() - x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') - y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') - z = f.Variable( - name='z', guess=1.0, units='m^2', description='The unit circle output' - ) - c = f.Constant( - name='c', value=1.0, units='', description='A constant c', size=2 - ) - f.Objective(x + y) + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The unit circle output') + c = f.Constant(name = 'c', value = 1.0, units = '', description = 'A constant c', size = 2) + f.Objective( x + y ) class UnitCircle(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super(UnitCircle, self).__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append(name='x', units='ft', description='The x variable') - self.inputs.append(name='y', units='ft', description='The y variable') - self.outputs.append( - name='z', - units='ft**2', - description='Resultant of the unit circle evaluation', - ) + self.inputs.append(name = 'x', units = 'ft' , description = 'The x variable') + self.inputs.append(name = 'y', units = 'ft' , description = 'The y variable') + self.outputs.append(name = 'z', units = 'ft**2', description = 'Resultant of the unit circle evaluation') self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, x, y): # The actual function that does things - x = pyo.value( - units.convert(x, self.inputs[0].units) - ) # Converts to correct units then casts to float - y = pyo.value( - units.convert(y, self.inputs[1].units) - ) # Converts to correct units then casts to float - z = x**2 + y**2 # Compute z - dzdx = 2 * x # Compute dz/dx - dzdy = 2 * y # Compute dz/dy + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float + y = pyo.value(units.convert(y,self.inputs[1].units)) # Converts to correct units then casts to float + z = x**2 + y**2 # Compute z + dzdx = 2*x # Compute dz/dx + dzdy = 2*y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + + f.ConstraintList( + [ + (z, '==', [x,y], UnitCircle() ) , + z <= 1*units.m**2 + ] + ) - f.ConstraintList([(z, '==', [x, y], UnitCircle()), z <= 1 * units.m**2]) + cl = f.get_constraints() - cl = f.get_constraints() + self.assertTrue(len(cl)==2) - self.assertTrue(len(cl) == 2) def test_edi_formulation_constraintlist_2(self): "Tests the constraint list constructor in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel - f = Formulation() - x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') - y = f.Variable(name='y', guess=1.0, units='m**2', description='The y variable') - f.Objective(y) + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm**2' , description = 'The y variable') + f.Objective( y ) class Parabola(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super(Parabola, self).__init__() self.description = 'This model evaluates the function: y = x**2' - self.inputs.append(name='x', units='ft', description='The x variable') - self.outputs.append( - name='y', units='ft**2', description='The y variable' - ) + self.inputs.append(name = 'x', units = 'ft' , description = 'The x variable') + self.outputs.append(name = 'y', units = 'ft**2' , description = 'The y variable') self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, x): # The actual function that does things - x = pyo.value( - units.convert(x, self.inputs[0].units) - ) # Converts to correct units then casts to float - y = x**2 # Compute y - dydx = 2 * x # Compute dy/dx + def BlackBox(self, x): # The actual function that does things + x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float + y = x**2 # Compute y + dydx = 2*x # Compute dy/dx y *= units.ft**2 - dydx *= units.ft # units.ft**2 / units.ft - return y, [dydx] # return z, grad(z), hess(z)... + dydx *= units.ft # units.ft**2 / units.ft + return y, [dydx] # return z, grad(z), hess(z)... f.ConstraintList( - [{'outputs': y, 'operators': '==', 'inputs': x, 'black_box': Parabola()}] + [ + {'outputs':y, 'operators':'==', 'inputs':x, 'black_box':Parabola() }, + ] ) - cl = f.get_constraints() + cl = f.get_constraints() + + self.assertTrue(len(cl)==1) - self.assertTrue(len(cl) == 1) def test_edi_formulation_constraintlist_3(self): "Tests the constraint list constructor in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel - f = Formulation() - x = f.Variable( - name='x', guess=1.0, units='m', description='The x variable', size=3 - ) - y = f.Variable( - name='y', guess=1.0, units='m**2', description='The y variable', size=3 - ) - f.Objective(y[0] + y[1] + y[2]) - + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable', size=3) + y = f.Variable(name = 'y', guess = 1.0, units = 'm**2' , description = 'The y variable', size=3) + f.Objective( y[0] + y[1] + y[2] ) class Parabola(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super(Parabola, self).__init__() self.description = 'This model evaluates the function: y = x**2' - self.inputs.append( - name='x', size=3, units='ft', description='The x variable' - ) - self.outputs.append( - name='y', size=3, units='ft**2', description='The y variable' - ) + self.inputs.append(name = 'x', size = 3, units = 'ft' , description = 'The x variable') + self.outputs.append(name = 'y', size = 3, units = 'ft**2' , description = 'The y variable') self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(*args, **kwargs): # The actual function that does things + def BlackBox(*args, **kwargs):# The actual function that does things args = list(args) self = args.pop(0) - runCases, returnMode, remainingKwargs = self.parseInputs( - *args, **kwargs - ) - + runCases, returnMode, remainingKwargs = self.parseInputs(*args, **kwargs) + x = self.sanitizeInputs(runCases[0]['x']) x = np.array([pyo.value(xval) for xval in x], dtype=np.float64) - y = x**2 # Compute y - dydx = 2 * x # Compute dy/dx - + y = x**2 # Compute y + dydx = 2*x # Compute dy/dx + y = y * units.ft**2 dydx = np.diag(dydx) - dydx = dydx * units.ft # units.ft**2 / units.ft + dydx = dydx * units.ft # units.ft**2 / units.ft + + return y, [dydx] # return z, grad(z), hess(z)... + f.ConstraintList([ {'outputs':y, 'operators':'==', 'inputs':x, 'black_box':Parabola() },]) + cl = f.get_constraints() + self.assertTrue(len(cl)==1) - return y, [dydx] # return z, grad(z), hess(z)... - - f.ConstraintList( - [{'outputs': y, 'operators': '==', 'inputs': x, 'black_box': Parabola()}] - ) - cl = f.get_constraints() - self.assertTrue(len(cl) == 1) def test_edi_formulation_runtimeconstraint_exceptions(self): "Tests the runtime constraint constructor in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel - f = Formulation() - x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') - y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') - z = f.Variable( - name='z', guess=1.0, units='m^2', description='The unit circle output' - ) - c = f.Constant( - name='c', value=1.0, units='', description='A constant c', size=2 - ) - f.Objective(x + y) - + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The unit circle output') + c = f.Constant(name = 'c', value = 1.0, units = '', description = 'A constant c', size = 2) + f.Objective( x + y ) class UnitCircle(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super(UnitCircle, self).__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append(name='x', units='ft', description='The x variable') - self.inputs.append(name='y', units='ft', description='The y variable') - self.outputs.append( - name='z', - units='ft**2', - description='Resultant of the unit circle evaluation', - ) + self.inputs.append(name = 'x', units = 'ft' , description = 'The x variable') + self.inputs.append(name = 'y', units = 'ft' , description = 'The y variable') + self.outputs.append(name = 'z', units = 'ft**2', description = 'Resultant of the unit circle evaluation') self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - - def BlackBox(self, x, y): # The actual function that does things - x = pyo.value( - units.convert(x, self.inputs[0].units) - ) # Converts to correct units then casts to float - y = pyo.value( - units.convert(y, self.inputs[1].units) - ) # Converts to correct units then casts to float - z = x**2 + y**2 # Compute z - dzdx = 2 * x # Compute dz/dx - dzdy = 2 * y # Compute dz/dy + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float + y = pyo.value(units.convert(y,self.inputs[1].units)) # Converts to correct units then casts to float + z = x**2 + y**2 # Compute z + dzdx = 2*x # Compute dz/dx + dzdy = 2*y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... - - f.Constraint(z <= 1 * units.m**2) + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + f.Constraint( z <= 1*units.m**2 ) # flaggs the input or as bad before assigning the incorrect black box - self.assertRaises( - ValueError, f.RuntimeConstraint, *(z, '==', 1.0, UnitCircle()) - ) - self.assertRaises( - ValueError, f.RuntimeConstraint, *(1.0, '==', [x, y], UnitCircle()) - ) + self.assertRaises(ValueError, f.RuntimeConstraint, *(z, '==', 1.0 , UnitCircle() )) + self.assertRaises(ValueError, f.RuntimeConstraint, *(1.0, '==', [x,y] , UnitCircle() )) - self.assertRaises( - ValueError, f.RuntimeConstraint, *(z, '==', [1.0, y], UnitCircle()) - ) - self.assertRaises( - ValueError, f.RuntimeConstraint, *(z, 1.0, [x, y], UnitCircle()) - ) - self.assertRaises( - ValueError, f.RuntimeConstraint, *(z, '=', [x, y], UnitCircle()) - ) + self.assertRaises(ValueError, f.RuntimeConstraint, *(z, '==', [1.0,y], UnitCircle() )) + self.assertRaises(ValueError, f.RuntimeConstraint, *(z, 1.0 , [x ,y], UnitCircle() )) + self.assertRaises(ValueError, f.RuntimeConstraint, *(z, '=', [x ,y], UnitCircle() )) def test_edi_formulation_getvariables(self): "Tests the get_variables function in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') - y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') vrs = f.get_variables() - self.assertListEqual(vrs, [x, y]) + self.assertListEqual(vrs, [x,y]) def test_edi_formulation_getconstants(self): "Tests the get_constants function in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') - y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') - - c1 = f.Constant( - name='c1', - value=1.0, - units='m', - description='A constant c1', - size=None, - within=None, - ) - c2 = f.Constant( - name='c2', - value=1.0, - units='m', - description='A constant c2', - size=None, - within=None, - ) + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + + c1 = f.Constant(name = 'c1', value = 1.0, units = 'm', description = 'A constant c1', size = None, within = None) + c2 = f.Constant(name = 'c2', value = 1.0, units = 'm', description = 'A constant c2', size = None, within = None) csts = f.get_constants() - self.assertListEqual(csts, [c1, c2]) + self.assertListEqual(csts, [c1,c2]) def test_edi_formulation_getobjectives(self): "Tests the get_objectives function in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') - y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') - f.Objective(x + y) + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + f.Objective(x+y) objList = f.get_objectives() - self.assertTrue(len(objList) == 1) + self.assertTrue(len(objList)==1) # not really sure how to check this, so I wont def test_edi_formulation_getconstraints(self): @@ -823,45 +452,40 @@ def test_edi_formulation_getconstraints(self): # ================= # Declare Variables # ================= - x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') - y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') - z = f.Variable( - name='z', guess=1.0, units='m^2', description='The unit circle output' - ) + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The unit circle output') # ================= # Declare Constants # ================= - c = f.Constant( - name='c', value=1.0, units='', description='A constant c', size=2 - ) + c = f.Constant(name = 'c', value = 1.0, units = '', description = 'A constant c', size = 2) # ===================== # Declare the Objective # ===================== - f.Objective(c[0] * x + c[1] * y) + f.Objective( + c[0]*x + c[1]*y + ) # =================== # Declare a Black Box # =================== class UnitCircle(BlackBoxFunctionModel): - def __init__(self): # The initialization function + def __init__(self): # The initialization function + # Initialize the black box model super(UnitCircle, self).__init__() # A brief description of the model self.description = 'This model evaluates the function: z = x**2 + y**2' - + # Declare the black box model inputs - self.inputs.append(name='x', units='ft', description='The x variable') - self.inputs.append(name='y', units='ft', description='The y variable') + self.inputs.append(name = 'x', units = 'ft' , description = 'The x variable') + self.inputs.append(name = 'y', units = 'ft' , description = 'The y variable') # Declare the black box model outputs - self.outputs.append( - name='z', - units='ft**2', - description='Resultant of the unit circle evaluation', - ) + self.outputs.append(name = 'z', units = 'ft**2', description = 'Resultant of the unit circle evaluation') # Declare the maximum available derivative self.availableDerivative = 1 @@ -869,36 +493,37 @@ def __init__(self): # The initialization function # Post-initialization setup self.post_init_setup(len(self.inputs)) - def BlackBox(self, x, y): # The actual function that does things - x = pyo.value( - units.convert(x, self.inputs[0].units) - ) # Converts to correct units then casts to float - y = pyo.value( - units.convert(y, self.inputs[1].units) - ) # Converts to correct units then casts to float + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float + y = pyo.value(units.convert(y,self.inputs[1].units)) # Converts to correct units then casts to float - z = x**2 + y**2 # Compute z - dzdx = 2 * x # Compute dz/dx - dzdy = 2 * y # Compute dz/dy + z = x**2 + y**2 # Compute z + dzdx = 2*x # Compute dz/dx + dzdy = 2*y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... # ======================= # Declare the Constraints # ======================= - f.ConstraintList([(z, '==', [x, y], UnitCircle()), z <= 1 * units.m**2]) + f.ConstraintList( + [ + (z, '==', [x,y], UnitCircle() ) , + z <= 1*units.m**2 + ] + ) - cl = f.get_constraints() + cl = f.get_constraints() ecl = f.get_explicitConstraints() rcl = f.get_runtimeConstraints() - self.assertTrue(len(cl) == 2) - self.assertTrue(len(ecl) == 1) - self.assertTrue(len(rcl) == 1) + self.assertTrue(len(cl)==2) + self.assertTrue(len(ecl)==1) + self.assertTrue(len(rcl)==1) def test_edi_formulation_checkunits(self): "Tests the check_units function in edi.formulation" @@ -906,28 +531,42 @@ def test_edi_formulation_checkunits(self): import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') - y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - f.Objective(x + y) - f.Constraint(x + y <= 1.0 * units.m) + f.Objective(x+y) + f.Constraint(x+y <= 1.0*units.m) f.check_units() - f.Constraint(2.0 * x + y <= 1.0) - self.assertRaises( - pyomo.core.base.units_container.UnitsError, f.check_units, *() - ) + f.Constraint(2.0*x+y <= 1.0) + self.assertRaises(pyomo.core.base.units_container.UnitsError, f.check_units, *()) + f2 = Formulation() - u = f2.Variable(name='u', guess=1.0, units='m', description='The u variable') - v = f2.Variable(name='v', guess=1.0, units='kg', description='The v variable') - f2.Objective(u + v) - self.assertRaises( - pyomo.core.base.units_container.UnitsError, f2.check_units, *() - ) + u = f2.Variable(name = 'u', guess = 1.0, units = 'm' , description = 'The u variable') + v = f2.Variable(name = 'v', guess = 1.0, units = 'kg' , description = 'The v variable') + f2.Objective(u+v) + self.assertRaises(pyomo.core.base.units_container.UnitsError, f2.check_units, *()) + + + if __name__ == '__main__': unittest.main() + + + + + + + + + + + + + + + From 6df90fd66ae35e1e517f7dc7fd565c55d48b20af Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Tue, 8 Aug 2023 10:43:37 -0600 Subject: [PATCH 33/61] applying black 3 --- pyomo/contrib/edi/blackBoxFunctionModel.py | 644 +++++++---- pyomo/contrib/edi/examples/aircraft_gp.py | 182 +-- pyomo/contrib/edi/examples/readme_example.py | 3 - pyomo/contrib/edi/formulation.py | 318 ++++-- pyomo/contrib/edi/tests/test_blackbox.py | 1039 ++++++++++-------- pyomo/contrib/edi/tests/test_docSnippets.py | 989 ++++++++--------- pyomo/contrib/edi/tests/test_examples.py | 34 +- pyomo/contrib/edi/tests/test_formulation.py | 896 ++++++++++----- 8 files changed, 2380 insertions(+), 1725 deletions(-) diff --git a/pyomo/contrib/edi/blackBoxFunctionModel.py b/pyomo/contrib/edi/blackBoxFunctionModel.py index 4ec0cbfc901..02fd09bb9fd 100644 --- a/pyomo/contrib/edi/blackBoxFunctionModel.py +++ b/pyomo/contrib/edi/blackBoxFunctionModel.py @@ -22,7 +22,9 @@ from pyomo.common.dependencies import attempt_import -egb, egb_available = attempt_import("pyomo.contrib.pynumero.interfaces.external_grey_box") +egb, egb_available = attempt_import( + "pyomo.contrib.pynumero.interfaces.external_grey_box" +) from pyomo.common.dependencies import numpy, numpy_available from pyomo.common.dependencies import scipy, scipy_available @@ -31,48 +33,63 @@ if numpy_available: import numpy as np else: - raise ImportError("pyomo.contrib.edi requires numpy to enable black box capability, fix with 'pip install numpy' ") + raise ImportError( + "pyomo.contrib.edi requires numpy to enable black box capability, fix with 'pip install numpy' " + ) -if scipy_available +if scipy_available: import scipy.sparse as sps else: - raise ImportError("pyomo.contrib.edi requires scipy to enable black box capability, fix with 'pip install scipy' ") + raise ImportError( + "pyomo.contrib.edi requires scipy to enable black box capability, fix with 'pip install scipy' " + ) if egb_available: - from pyomo.contrib.pynumero.interfaces.external_grey_box import ExternalGreyBoxModel, ExternalGreyBoxBlock + from pyomo.contrib.pynumero.interfaces.external_grey_box import ( + ExternalGreyBoxModel, + ExternalGreyBoxBlock, + ) else: - raise ImportError("pyomo.contrib.edi requires pyomo.contrib.pynumero to be installed to enable black box capability, this should have installed with base pyomo") + raise ImportError( + "pyomo.contrib.edi requires pyomo.contrib.pynumero to be installed to enable black box capability, this should have installed with base pyomo" + ) + class BlackBoxFunctionModel_Variable(object): - def __init__(self, name, units, description = '', size = 0): + def __init__(self, name, units, description='', size=0): # Order matters self.name = name self.units = units self.size = size self.description = description -# ===================================================================================================================== -# The printing function -# ===================================================================================================================== + + # ===================================================================================================================== + # The printing function + # ===================================================================================================================== def __repr__(self): - return self.name -# ===================================================================================================================== -# Define the name -# ===================================================================================================================== + return self.name + + # ===================================================================================================================== + # Define the name + # ===================================================================================================================== @property def name(self): return self._name + @name.setter - def name(self,val): + def name(self, val): if isinstance(val, str): self._name = val else: raise ValueError('Invalid name. Must be a string.') -# ===================================================================================================================== -# Define the units -# ===================================================================================================================== + + # ===================================================================================================================== + # Define the units + # ===================================================================================================================== @property def units(self): return self._units + @units.setter def units(self, val): # set dimensionless if a null string is passed in @@ -81,87 +98,101 @@ def units(self, val): val = 'dimensionless' if val is None: val = 'dimensionless' - + if isinstance(val, str): self._units = pyomo_units.__getattr__(val) elif isinstance(val, pyomo.core.base.units_container._PyomoUnit): self._units = val else: - raise ValueError('Invalid units. Must be a string compatible with pint or a unit instance.') -# ===================================================================================================================== -# Define the size -# ===================================================================================================================== + raise ValueError( + 'Invalid units. Must be a string compatible with pint or a unit instance.' + ) + + # ===================================================================================================================== + # Define the size + # ===================================================================================================================== @property def size(self): return self._size + @size.setter def size(self, val): invalid = False - if isinstance(val,(list, tuple)): + if isinstance(val, (list, tuple)): sizeTemp = [] for x in val: if isinstance(x, str): # is a vector of unknown length, should be 'inf', but any string accepted x = -1 # pass - elif not isinstance(x,int): - raise ValueError('Invalid size. Must be an integer or list/tuple of integers') + elif not isinstance(x, int): + raise ValueError( + 'Invalid size. Must be an integer or list/tuple of integers' + ) if x == 1: - raise ValueError('A value of 1 is not valid for defining size. Use fewer dimensions.') + raise ValueError( + 'A value of 1 is not valid for defining size. Use fewer dimensions.' + ) sizeTemp.append(x) self._size = val else: if val is None: - self._size = 0 # set to scalar + self._size = 0 # set to scalar elif isinstance(val, str): # is a 1D vector of unknown length, should be 'inf', but any string accepted self._size = -1 # pass elif isinstance(val, int): if val == 1: - raise ValueError('A value of 1 is not valid for defining size. Use 0 to indicate a scalar value.') + raise ValueError( + 'A value of 1 is not valid for defining size. Use 0 to indicate a scalar value.' + ) else: self._size = val else: - raise ValueError('Invalid size. Must be an integer or list/tuple of integers') -# ===================================================================================================================== -# Define the description -# ===================================================================================================================== + raise ValueError( + 'Invalid size. Must be an integer or list/tuple of integers' + ) + + # ===================================================================================================================== + # Define the description + # ===================================================================================================================== @property def description(self): return self._description + @description.setter def description(self, val): - if isinstance(val,str): + if isinstance(val, str): self._description = val else: raise ValueError('Invalid description. Must be a string.') class TypeCheckedList(list): - def __init__(self, checkItem, itemList = None): + def __init__(self, checkItem, itemList=None): super(TypeCheckedList, self).__init__() self.checkItem = checkItem - + if itemList is not None: if isinstance(itemList, list) or isinstance(itemList, tuple): for itm in itemList: self.append(itm) else: raise ValueError('Input to itemList is not iterable') - + def __setitem__(self, key, val): if isinstance(val, self.checkItem): super(TypeCheckedList, self).__setitem__(key, val) - elif isinstance(val, (tuple,list)): - cks = [isinstance(vl,self.checkItem) for vl in val] + elif isinstance(val, (tuple, list)): + cks = [isinstance(vl, self.checkItem) for vl in val] if sum(cks) == len(cks): super(TypeCheckedList, self).__setitem__(key, val) else: - raise ValueError('Input must be an instance of the defined type') + raise ValueError('Input must be an instance of the defined type') else: raise ValueError('Input must be an instance of the defined type') - + def append(self, val): if isinstance(val, self.checkItem): super(TypeCheckedList, self).append(val) @@ -171,7 +202,7 @@ def append(self, val): class BBList(TypeCheckedList): def __init__(self): - super(BBList, self).__init__(BlackBoxFunctionModel_Variable,[]) + super(BBList, self).__init__(BlackBoxFunctionModel_Variable, []) self._lookupDict = {} self._counter = 0 @@ -183,64 +214,75 @@ def __getitem__(self, val): else: raise ValueError('Input must be an integer or a valid variable name') - - def append(*args, **kwargs): args = list(args) self = args.pop(0) - + if len(args) + len(kwargs.values()) == 1: if len(args) == 1: inputData = args[0] if len(kwargs.values()) == 1: inputData = list(kwargs.values())[0] - + if isinstance(inputData, self.checkItem): if inputData.name in self._lookupDict.keys(): - raise ValueError("Key '%s' already exists in the input list"%(inputData.name)) + raise ValueError( + "Key '%s' already exists in the input list" % (inputData.name) + ) self._lookupDict[inputData.name] = self._counter self._counter += 1 super(BBList, self).append(inputData) else: if isinstance(inputData, str): - raise ValueError("Key '%s' not passed in to the black box variable constructor"%('units')) + raise ValueError( + "Key '%s' not passed in to the black box variable constructor" + % ('units') + ) else: raise ValueError('Invalid (single) input type') - + elif len(args) + len(kwargs.values()) <= 4: - argKeys = ['name','units','description','size'] - ipd = dict(zip(argKeys[0:len(args)],args)) + argKeys = ['name', 'units', 'description', 'size'] + ipd = dict(zip(argKeys[0 : len(args)], args)) for ky, vl in kwargs.items(): if ky in ipd: - raise ValueError("Key '%s' declared after non-keyword arguments and is out of order"%(ky)) + raise ValueError( + "Key '%s' declared after non-keyword arguments and is out of order" + % (ky) + ) else: - ipd[ky]=vl - + ipd[ky] = vl + for ak in argKeys: if ak not in ipd.keys(): if ak == 'description': - ipd['description']='' + ipd['description'] = '' elif ak == 'size': ipd['size'] = 0 else: - raise ValueError("Key '%s' not passed in to the black box variable constructor"%(ak)) - + raise ValueError( + "Key '%s' not passed in to the black box variable constructor" + % (ak) + ) if ipd['name'] in self._lookupDict.keys(): - raise ValueError("Key '%s' already exists in the input list"%(ipd['name'])) + raise ValueError( + "Key '%s' already exists in the input list" % (ipd['name']) + ) self._lookupDict[ipd['name']] = self._counter self._counter += 1 super(BBList, self).append(BlackBoxFunctionModel_Variable(**ipd)) - + else: raise ValueError('Too many inputs to a black box variable') errorString = 'This function is calling to the base class and has not been defined.' + class BlackBoxFunctionModel(ExternalGreyBoxModel): -# --------------------------------------------------------------------------------------------------------------------- -# --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- def __init__(self): super(BlackBoxFunctionModel, self).__init__() @@ -248,9 +290,9 @@ def __init__(self): self.inputs = BBList() self.outputs = BBList() - self.inputVariables_optimization = None + self.inputVariables_optimization = None self.outputVariables_optimization = None - + # A simple description of the model self.description = None @@ -259,15 +301,17 @@ def __init__(self): self._cache = None self._NunwrappedOutputs = None - self._NunwrappedInputs = None + self._NunwrappedInputs = None - def setOptimizationVariables(self, inputVariables_optimization, outputVariables_optimization): - self.inputVariables_optimization = inputVariables_optimization + def setOptimizationVariables( + self, inputVariables_optimization, outputVariables_optimization + ): + self.inputVariables_optimization = inputVariables_optimization self.outputVariables_optimization = outputVariables_optimization -# --------------------------------------------------------------------------------------------------------------------- -# pyomo things -# --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- + # pyomo things + # --------------------------------------------------------------------------------------------------------------------- def input_names(self): inputs_unwrapped = [] for ivar in self.inputVariables_optimization: @@ -278,10 +322,9 @@ def input_names(self): for vi in validIndices: inputs_unwrapped.append(ivar[vi]) else: - raise ValueError("Invalid type for input variable") - - return [ip.__str__() for ip in inputs_unwrapped] + raise ValueError("Invalid type for input variable") + return [ip.__str__() for ip in inputs_unwrapped] def output_names(self): outputs_unwrapped = [] @@ -293,7 +336,7 @@ def output_names(self): for vi in validIndices: outputs_unwrapped.append(ovar[vi]) else: - raise ValueError("Invalid type for output variable") + raise ValueError("Invalid type for output variable") return [op.__str__() for op in outputs_unwrapped] @@ -305,16 +348,15 @@ def evaluate_outputs(self): self.fillCache() opts = self._cache['pyomo_outputs'] return opts - + def evaluate_jacobian_outputs(self): self.fillCache() jac = self._cache['pyomo_jacobian'] return jac - def post_init_setup(self, defaultVal = 1.0): + def post_init_setup(self, defaultVal=1.0): self._input_values = np.ones(self._NunwrappedInputs) * defaultVal - def fillCache(self): if self._cache is None: self._cache = {} @@ -324,95 +366,113 @@ def fillCache(self): ptr = 0 - for i in range(0,len(self.inputVariables_optimization)): + for i in range(0, len(self.inputVariables_optimization)): optimizationInput = self.inputVariables_optimization[i] - if not isinstance(optimizationInput, (pyomo.core.base.var.IndexedVar, pyomo.core.base.var.ScalarVar)): + if not isinstance( + optimizationInput, + (pyomo.core.base.var.IndexedVar, pyomo.core.base.var.ScalarVar), + ): raise ValueError("Invalid input variable type") - ipt = self.inputs[i] + ipt = self.inputs[i] - shape = [len(idx) for idx in optimizationInput.index_set().subsets()] - localShape = ipt.size + shape = [len(idx) for idx in optimizationInput.index_set().subsets()] + localShape = ipt.size optimizationUnits = self.inputVariables_optimization[i].get_units() - localUnits = ipt.units + localUnits = ipt.units if isinstance(optimizationInput, pyomo.core.base.var.IndexedVar): - value = np.zeros(shape) + value = np.zeros(shape) for vix in list(optimizationInput.index_set().data()): - raw_val = float(raw_inputs[ptr]) * optimizationUnits - raw_val_correctedUnits = pyomo_units.convert(raw_val, localUnits) + raw_val = float(raw_inputs[ptr]) * optimizationUnits + raw_val_correctedUnits = pyomo_units.convert( + raw_val, localUnits + ) value[vix] = pyo.value(raw_val_correctedUnits) ptr += 1 - self.sizeCheck(localShape, value*localUnits) - bb_inputs.append(value*localUnits) + self.sizeCheck(localShape, value * localUnits) + bb_inputs.append(value * localUnits) - else: # isinstance(optimizationInput, pyomo.core.base.var.ScalarVar):, just checked this + else: # isinstance(optimizationInput, pyomo.core.base.var.ScalarVar):, just checked this value = raw_inputs[ptr] * optimizationUnits ptr += 1 self.sizeCheck(localShape, value) value_correctedUnits = pyomo_units.convert(value, localUnits) bb_inputs.append(value_correctedUnits) - bbo = self.BlackBox(*bb_inputs) - self._cache['raw'] = bbo - self._cache['raw_value'] = bbo[0] - self._cache['raw_jacobian'] = bbo[1] + self._cache['raw'] = bbo + self._cache['raw_value'] = bbo[0] + self._cache['raw_jacobian'] = bbo[1] outputVector = [] - if not isinstance(bbo[0], (list,tuple)): + if not isinstance(bbo[0], (list, tuple)): valueList = [bbo[0]] jacobianList = [bbo[1]] else: valueList = bbo[0] jacobianList = bbo[1] - for i in range(0,len(valueList)): + for i in range(0, len(valueList)): optimizationOutput = self.outputVariables_optimization[i] - if not isinstance(optimizationOutput, (pyomo.core.base.var.IndexedVar, pyomo.core.base.var.ScalarVar)): + if not isinstance( + optimizationOutput, + (pyomo.core.base.var.IndexedVar, pyomo.core.base.var.ScalarVar), + ): raise ValueError("Invalid output variable type") - opt = self.outputs[i] + opt = self.outputs[i] - modelOutputUnits = opt.units + modelOutputUnits = opt.units outputOptimizationUnits = optimizationOutput.get_units() vl = valueList[i] if isinstance(vl, pyomo.core.expr.numeric_expr.NumericNDArray): validIndexList = optimizationOutput.index_set().data() - for j in range(0,len(validIndexList)): + for j in range(0, len(validIndexList)): vi = validIndexList[j] - corrected_value = pyo.value(pyomo_units.convert(vl[vi], outputOptimizationUnits)) # now unitless in correct units - outputVector.append(corrected_value) - - elif isinstance(vl, (pyomo.core.expr.numeric_expr.NPV_ProductExpression, - pyomo.core.base.units_container._PyomoUnit)): - corrected_value = pyo.value(pyomo_units.convert(vl, outputOptimizationUnits)) # now unitless in correct units - outputVector.append(corrected_value) + corrected_value = pyo.value( + pyomo_units.convert(vl[vi], outputOptimizationUnits) + ) # now unitless in correct units + outputVector.append(corrected_value) + + elif isinstance( + vl, + ( + pyomo.core.expr.numeric_expr.NPV_ProductExpression, + pyomo.core.base.units_container._PyomoUnit, + ), + ): + corrected_value = pyo.value( + pyomo_units.convert(vl, outputOptimizationUnits) + ) # now unitless in correct units + outputVector.append(corrected_value) else: raise ValueError("Invalid output variable type") self._cache['pyomo_outputs'] = outputVector - outputJacobian = np.ones([self._NunwrappedOutputs, self._NunwrappedInputs]) * -1 + outputJacobian = ( + np.ones([self._NunwrappedOutputs, self._NunwrappedInputs]) * -1 + ) ptr_row = 0 - ptr_col = 0 + ptr_col = 0 - for i in range(0,len(jacobianList)): + for i in range(0, len(jacobianList)): oopt = self.outputVariables_optimization[i] # Checked about 20 lines above # if not isinstance(oopt, (pyomo.core.base.var.ScalarVar,pyomo.core.base.var.IndexedVar)): - # raise ValueError("Invalid type for output variable") + # raise ValueError("Invalid type for output variable") lopt = self.outputs[i] oounits = oopt.get_units() lounits = lopt.units # oshape = [len(idx) for idx in oopt.index_set().subsets()] ptr_col = 0 - for j in range(0,len(self.inputs)): + for j in range(0, len(self.inputs)): oipt = self.inputVariables_optimization[j] # This is checked about 80 lines up # if not isinstance(oipt, (pyomo.core.base.var.ScalarVar,pyomo.core.base.var.IndexedVar)): - # raise ValueError("Invalid type for output variable") + # raise ValueError("Invalid type for output variable") lipt = self.inputs[j] oiunits = oipt.get_units() liunits = lipt.units @@ -420,31 +480,44 @@ def fillCache(self): jacobianValue_raw = jacobianList[i][j] - if isinstance(jacobianValue_raw, (pyomo.core.expr.numeric_expr.NPV_ProductExpression, - pyomo.core.base.units_container._PyomoUnit)): - corrected_value = pyo.value(pyomo_units.convert(jacobianValue_raw, oounits/oiunits)) # now unitless in correct units - outputJacobian[ptr_row,ptr_col] = corrected_value + if isinstance( + jacobianValue_raw, + ( + pyomo.core.expr.numeric_expr.NPV_ProductExpression, + pyomo.core.base.units_container._PyomoUnit, + ), + ): + corrected_value = pyo.value( + pyomo_units.convert(jacobianValue_raw, oounits / oiunits) + ) # now unitless in correct units + outputJacobian[ptr_row, ptr_col] = corrected_value ptr_col += 1 ptr_row_step = 1 - elif isinstance(jacobianValue_raw,pyomo.core.expr.numeric_expr.NumericNDArray): + elif isinstance( + jacobianValue_raw, pyomo.core.expr.numeric_expr.NumericNDArray + ): jshape = jacobianValue_raw.shape if isinstance(oopt, pyomo.core.base.var.ScalarVar): oshape = 0 - else: # isinstance(oopt, pyomo.core.base.var.IndexedVar), checked above - oshape = [len(idx) for idx in oopt.index_set().subsets()] + else: # isinstance(oopt, pyomo.core.base.var.IndexedVar), checked above + oshape = [len(idx) for idx in oopt.index_set().subsets()] if isinstance(oipt, pyomo.core.base.var.ScalarVar): ishape = 0 - else: # isinstance(oipt, pyomo.core.base.var.IndexedVar), checked above - ishape = [len(idx) for idx in oipt.index_set().subsets()] + else: # isinstance(oipt, pyomo.core.base.var.IndexedVar), checked above + ishape = [len(idx) for idx in oipt.index_set().subsets()] if oshape == 0: validIndices = list(oipt.index_set().data()) for vi in validIndices: - corrected_value = pyo.value(pyomo_units.convert(jacobianValue_raw[vi], oounits/oiunits)) # now unitless in correct units - outputJacobian[ptr_row,ptr_col] = corrected_value + corrected_value = pyo.value( + pyomo_units.convert( + jacobianValue_raw[vi], oounits / oiunits + ) + ) # now unitless in correct units + outputJacobian[ptr_row, ptr_col] = corrected_value ptr_col += 1 ptr_row_step = 1 @@ -452,30 +525,39 @@ def fillCache(self): ptr_row_cache = ptr_row validIndices = list(oopt.index_set().data()) for vi in validIndices: - corrected_value = pyo.value(pyomo_units.convert(jacobianValue_raw[vi], oounits/oiunits)) # now unitless in correct units - outputJacobian[ptr_row,ptr_col] = corrected_value + corrected_value = pyo.value( + pyomo_units.convert( + jacobianValue_raw[vi], oounits / oiunits + ) + ) # now unitless in correct units + outputJacobian[ptr_row, ptr_col] = corrected_value ptr_row += 1 ptr_row = ptr_row_cache ptr_row_step = len(validIndices) # elif ishape == 0 and oshape == 0: # Handled by the scalar case above - else: + else: # both are dimensioned vectors - #oshape, ishape, jshape + # oshape, ishape, jshape ptr_row_cache = ptr_row ptr_col_cache = ptr_col validIndices_o = list(oopt.index_set().data()) validIndices_i = list(oipt.index_set().data()) for vio in validIndices_o: - if isinstance(vio, (float,int)): + if isinstance(vio, (float, int)): vio = (vio,) for vii in validIndices_i: - if isinstance(vii, (float,int)): + if isinstance(vii, (float, int)): vii = (vii,) - corrected_value = pyo.value(pyomo_units.convert(jacobianValue_raw[vio + vii], oounits/oiunits)) # now unitless in correct units - outputJacobian[ptr_row,ptr_col] = corrected_value + corrected_value = pyo.value( + pyomo_units.convert( + jacobianValue_raw[vio + vii], + oounits / oiunits, + ) + ) # now unitless in correct units + outputJacobian[ptr_row, ptr_col] = corrected_value ptr_col += 1 ptr_col = ptr_col_cache ptr_row += 1 @@ -485,37 +567,41 @@ def fillCache(self): else: raise ValueError("Invalid jacobian type") - ptr_row += ptr_row_step + ptr_row += ptr_row_step self._cache['pyomo_jacobian'] = sps.coo_matrix(outputJacobian) -# --------------------------------------------------------------------------------------------------------------------- -# --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- # These models must be defined in each individual model, just placeholders here def BlackBox(*args, **kwargs): raise AttributeError(errorString) - def convert(self, val, unts): try: val = val * pyomo_units.dimensionless except: - pass ## will handle later - - if isinstance(val, (pyomo.core.base.units_container._PyomoUnit,pyomo.core.expr.numeric_expr.NPV_ProductExpression)): + pass ## will handle later + + if isinstance( + val, + ( + pyomo.core.base.units_container._PyomoUnit, + pyomo.core.expr.numeric_expr.NPV_ProductExpression, + ), + ): return pyomo_units.convert(val, unts) elif isinstance(val, pyomo.core.expr.numeric_expr.NumericNDArray): shp = val.shape ix = np.ndindex(*shp) opt = np.zeros(shp) - for i in range(0,np.prod(shp)): + for i in range(0, np.prod(shp)): ixt = next(ix) - opt[ixt] = pyo.value(pyomo_units.convert(val[ixt],unts)) - return opt*unts + opt[ixt] = pyo.value(pyomo_units.convert(val[ixt], unts)) + return opt * unts else: raise ValueError('Invalid type passed to unit conversion function') - def pyomo_value(self, val): try: return pyo.value(val) @@ -524,19 +610,19 @@ def pyomo_value(self, val): shp = val.shape ix = np.ndindex(*shp) opt = np.zeros(shp) - for i in range(0,np.prod(shp)): + for i in range(0, np.prod(shp)): ixt = next(ix) opt[ixt] = pyo.value(val[ixt]) return opt else: raise ValueError('Invalid type passed to pyomo_value function') -# --------------------------------------------------------------------------------------------------------------------- -# --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- def parseInputs(self, *args, **kwargs): - args = list(args) # convert tuple to list + args = list(args) # convert tuple to list - inputNames = [self.inputs[i].name for i in range(0,len(self.inputs))] + inputNames = [self.inputs[i].name for i in range(0, len(self.inputs))] # ------------------------------ # ------------------------------ @@ -549,32 +635,44 @@ def parseInputs(self, *args, **kwargs): if len(inputNames) == 1: try: rs = self.sanitizeInputs(inputData) - return [dict(zip(inputNames,[rs]))], -self.availableDerivative-1, {} # one input being passed in + return ( + [dict(zip(inputNames, [rs]))], + -self.availableDerivative - 1, + {}, + ) # one input being passed in except: - pass #otherwise, proceed - - if isinstance(inputData, (list,tuple)): + pass # otherwise, proceed + + if isinstance(inputData, (list, tuple)): dataRuns = [] for idc in inputData: if isinstance(idc, dict): sips = self.sanitizeInputs(**idc) if len(inputNames) == 1: sips = [sips] - runDictS = dict(zip(inputNames,sips)) - dataRuns.append(runDictS) # the BlackBox([{'x1':x1, 'x2':x2},{'x1':x1, 'x2':x2},...]) case - elif isinstance(idc,(list,tuple)): + runDictS = dict(zip(inputNames, sips)) + dataRuns.append( + runDictS + ) # the BlackBox([{'x1':x1, 'x2':x2},{'x1':x1, 'x2':x2},...]) case + elif isinstance(idc, (list, tuple)): if len(idc) == len(inputNames): sips = self.sanitizeInputs(*idc) if len(inputNames) == 1: sips = [sips] - runDictS = dict(zip(inputNames,sips)) - dataRuns.append(runDictS) # the BlackBox([ [x1, x2], [x1, x2],...]) case + runDictS = dict(zip(inputNames, sips)) + dataRuns.append( + runDictS + ) # the BlackBox([ [x1, x2], [x1, x2],...]) case else: - raise ValueError('Entry in input data list has improper length') + raise ValueError( + 'Entry in input data list has improper length' + ) else: - raise ValueError("Invalid data type in the input list. Note that BlackBox([x1,x2,y]) must be passed in as BlackBox([[x1,x2,y]]) or "+ - "BlackBox(*[x1,x2,y]) or BlackBox({'x1':x1,'x2':x2,'y':y}) or simply BlackBox(x1, x2, y) to avoid processing singularities. "+ - "Best practice is BlackBox({'x1':x1,'x2':x2,'y':y})") + raise ValueError( + "Invalid data type in the input list. Note that BlackBox([x1,x2,y]) must be passed in as BlackBox([[x1,x2,y]]) or " + + "BlackBox(*[x1,x2,y]) or BlackBox({'x1':x1,'x2':x2,'y':y}) or simply BlackBox(x1, x2, y) to avoid processing singularities. " + + "Best practice is BlackBox({'x1':x1,'x2':x2,'y':y})" + ) return dataRuns, self.availableDerivative, {} elif isinstance(inputData, dict): @@ -585,45 +683,73 @@ def parseInputs(self, *args, **kwargs): sips = self.sanitizeInputs(**inputData) if len(inputNames) == 1: sips = [sips] - return [dict(zip(inputNames,sips))], -self.availableDerivative-1, {} # the BlackBox(*{'x1':x1, 'x2':x2}) case, somewhat likely - - if not all([inputLengths[i] == inputLengths[0] for i in range(0,len(inputLengths))]): + return ( + [dict(zip(inputNames, sips))], + -self.availableDerivative - 1, + {}, + ) # the BlackBox(*{'x1':x1, 'x2':x2}) case, somewhat likely + + if not all( + [ + inputLengths[i] == inputLengths[0] + for i in range(0, len(inputLengths)) + ] + ): sips = self.sanitizeInputs(**inputData) - return [dict(zip(inputNames,sips))], -self.availableDerivative-1, {} # the BlackBox(*{'x1':x1, 'x2':x2}) case where vectors for x1... are passed in (likely to fail on previous line) + return ( + [dict(zip(inputNames, sips))], + -self.availableDerivative - 1, + {}, + ) # the BlackBox(*{'x1':x1, 'x2':x2}) case where vectors for x1... are passed in (likely to fail on previous line) else: try: sips = self.sanitizeInputs(**inputData) - return [dict(zip(inputNames,sips))], -self.availableDerivative-1, {} # the BlackBox(*{'x1':x1, 'x2':x2}) case where vectors all inputs have same length intentionally (likely to fail on previous line) + return ( + [dict(zip(inputNames, sips))], + -self.availableDerivative - 1, + {}, + ) # the BlackBox(*{'x1':x1, 'x2':x2}) case where vectors all inputs have same length intentionally (likely to fail on previous line) except: dataRuns = [] - for i in range(0,inputLengths[0]): + for i in range(0, inputLengths[0]): runDict = {} - for ky,vl in inputData.items(): + for ky, vl in inputData.items(): runDict[ky] = vl[i] sips = self.sanitizeInputs(**runDict) if len(inputNames) == 1: sips = [sips] - runDictS = dict(zip(inputNames,sips)) + runDictS = dict(zip(inputNames, sips)) dataRuns.append(runDictS) - return dataRuns, self.availableDerivative, {} # the BlackBox({'x1':x1_vec, 'x2':x2_vec}) case, most likely + return ( + dataRuns, + self.availableDerivative, + {}, + ) # the BlackBox({'x1':x1_vec, 'x2':x2_vec}) case, most likely else: raise ValueError('Keywords did not match the expected list') else: - raise ValueError('Got unexpected data type %s'%(str(type(inputData)))) + raise ValueError('Got unexpected data type %s' % (str(type(inputData)))) # ------------------------------ # ------------------------------ else: - if any([ list(kwargs.keys())[i] in inputNames for i in range(0,len(list(kwargs.keys()))) ]): + if any( + [ + list(kwargs.keys())[i] in inputNames + for i in range(0, len(list(kwargs.keys()))) + ] + ): # some of the inputs are defined in the kwargs if len(args) >= len(inputNames): - raise ValueError('A keyword input is defining an input, but there are too many unkeyed arguments for this to occur. Check the inputs.') + raise ValueError( + 'A keyword input is defining an input, but there are too many unkeyed arguments for this to occur. Check the inputs.' + ) else: if len(args) != 0: - availableKeywords = inputNames[-len(args):] - else: + availableKeywords = inputNames[-len(args) :] + else: availableKeywords = inputNames - valList = args + [None]*(len(inputNames)-len(args)) + valList = args + [None] * (len(inputNames) - len(args)) for ky in availableKeywords: ix = inputNames.index(ky) valList[ix] = kwargs[ky] @@ -632,7 +758,7 @@ def parseInputs(self, *args, **kwargs): # if any([valList[i]==None for i in range(0,len(valList))]): # raise ValueError('Keywords did not properly fill in the remaining arguments. Check the inputs.') - sips = self.sanitizeInputs(*valList) + sips = self.sanitizeInputs(*valList) if len(inputNames) == 1: sips = [sips] @@ -645,18 +771,26 @@ def parseInputs(self, *args, **kwargs): # was in args pass - return [dict(zip(inputNames,sips))], -self.availableDerivative-1, remainingKwargs # Mix of args and kwargs define inputs + return ( + [dict(zip(inputNames, sips))], + -self.availableDerivative - 1, + remainingKwargs, + ) # Mix of args and kwargs define inputs else: # all of the inputs are in the args try: - sips = self.sanitizeInputs(*args[0:len(inputNames)]) + sips = self.sanitizeInputs(*args[0 : len(inputNames)]) if len(inputNames) == 1: sips = [sips] remainingKwargs = copy.deepcopy(kwargs) - remainingKwargs['remainingArgs'] = args[len(inputNames):] - return [dict(zip(inputNames,sips))], -self.availableDerivative-1, remainingKwargs # all inputs are in args + remainingKwargs['remainingArgs'] = args[len(inputNames) :] + return ( + [dict(zip(inputNames, sips))], + -self.availableDerivative - 1, + remainingKwargs, + ) # all inputs are in args # except: except Exception as e: # not possible due to other checks @@ -664,46 +798,62 @@ def parseInputs(self, *args, **kwargs): # raise ValueError(e) if str(e) == 'Not enough inputs': raise ValueError(e) - else:#otherwise, proceed - runCases, returnMode, extra_singleInput = self.parseInputs(args[0]) + else: # otherwise, proceed + runCases, returnMode, extra_singleInput = self.parseInputs( + args[0] + ) remainingKwargs = copy.deepcopy(kwargs) - remainingKwargs['remainingArgs'] = args[len(inputNames):] + remainingKwargs['remainingArgs'] = args[len(inputNames) :] return runCases, returnMode, remainingKwargs -# --------------------------------------------------------------------------------------------------------------------- -# --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- def sizeCheck(self, size, ipval_correctUnits): if size is not None: szVal = ipval_correctUnits - if isinstance(szVal, (pyomo.core.expr.numeric_expr.NPV_ProductExpression, - pyomo.core.base.units_container._PyomoUnit)): - if size != 0 and size != 1 : - raise ValueError('Size did not match the expected size %s (ie: Scalar)'%(str(size))) + if isinstance( + szVal, + ( + pyomo.core.expr.numeric_expr.NPV_ProductExpression, + pyomo.core.base.units_container._PyomoUnit, + ), + ): + if size != 0 and size != 1: + raise ValueError( + 'Size did not match the expected size %s (ie: Scalar)' + % (str(size)) + ) elif isinstance(szVal, pyomo.core.expr.numeric_expr.NumericNDArray): shp = szVal.shape - if isinstance(size,(int,float)): + if isinstance(size, (int, float)): size = [size] # else: if len(shp) != len(size): - raise ValueError('Shapes/Sizes of %s does not match the expected %s'%(str(shp),str(size))) - for j in range(0,len(shp)): + raise ValueError( + 'Shapes/Sizes of %s does not match the expected %s' + % (str(shp), str(size)) + ) + for j in range(0, len(shp)): if size[j] != -1: # was declared of flexible length if size[j] != shp[j]: - raise ValueError('Shapes/Sizes of %s does not match the expected %s'%(str(shp),str(size))) + raise ValueError( + 'Shapes/Sizes of %s does not match the expected %s' + % (str(shp), str(size)) + ) else: raise ValueError('Invalid type detected when checking size') -# --------------------------------------------------------------------------------------------------------------------- -# --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- def sanitizeInputs(self, *args, **kwargs): - nameList = [self.inputs[i].name for i in range(0,len(self.inputs))] + nameList = [self.inputs[i].name for i in range(0, len(self.inputs))] if len(args) + len(kwargs.values()) > len(nameList): raise ValueError('Too many inputs') if len(args) + len(kwargs.values()) < len(nameList): raise ValueError('Not enough inputs') inputDict = {} - for i in range(0,len(args)): + for i in range(0, len(args)): rg = args[i] inputDict[nameList[i]] = rg @@ -711,34 +861,42 @@ def sanitizeInputs(self, *args, **kwargs): if ky in nameList: inputDict[ky] = vl else: - raise ValueError('Unexpected input keyword argument %s in the inputs'%(ky)) + raise ValueError( + 'Unexpected input keyword argument %s in the inputs' % (ky) + ) opts = [] - for i in range(0,len(nameList)): + for i in range(0, len(nameList)): name = nameList[i] nameCheck = self.inputs[i].name unts = self.inputs[i].units size = self.inputs[i].size - + # should be impossible # if name != nameCheck: # raise RuntimeError('Something went wrong and values are not consistent. Check your defined inputs.') ipval = inputDict[name] - if isinstance(ipval, pyomo.core.expr.numeric_expr.NumericNDArray): - for ii in range(0,len(ipval)): + if isinstance(ipval, pyomo.core.expr.numeric_expr.NumericNDArray): + for ii in range(0, len(ipval)): try: - ipval[ii] = self.convert(ipval[ii], unts)#ipval.to(unts) + ipval[ii] = self.convert(ipval[ii], unts) # ipval.to(unts) except: - raise ValueError('Could not convert %s of %s to %s'%(name, str(ipval),str(unts))) + raise ValueError( + 'Could not convert %s of %s to %s' + % (name, str(ipval), str(unts)) + ) ipval_correctUnits = ipval else: try: - ipval_correctUnits = self.convert(ipval, unts)#ipval.to(unts) + ipval_correctUnits = self.convert(ipval, unts) # ipval.to(unts) except: - raise ValueError('Could not convert %s of %s to %s'%(name, str(ipval),str(unts))) + raise ValueError( + 'Could not convert %s of %s to %s' + % (name, str(ipval), str(unts)) + ) # superseded by the custom convert function # if not isinstance(ipval_correctUnits, (pyomo.core.expr.numeric_expr.NPV_ProductExpression, @@ -754,8 +912,8 @@ def sanitizeInputs(self, *args, **kwargs): return opts -# --------------------------------------------------------------------------------------------------------------------- -# --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- def checkOutputs(self, *args, **kwargs): raise NotImplementedError('Contact developers to use this function') # nameList = [self.outputs[i].name for i in range(0,len(self.outputs))] @@ -781,7 +939,7 @@ def checkOutputs(self, *args, **kwargs): # nameCheck = self.outputs[i].name # unts = self.outputs[i].units # size = self.outputs[i].size - + # if name != nameCheck: # raise RuntimeError('Something went wrong and values are not consistent. Check your defined inputs.') @@ -808,15 +966,15 @@ def checkOutputs(self, *args, **kwargs): # return opts -# --------------------------------------------------------------------------------------------------------------------- -# --------------------------------------------------------------------------------------------------------------------- - def getSummary(self, whitespace = 6): + # --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- + def getSummary(self, whitespace=6): pstr = '\n' pstr += 'Model Description\n' pstr += '=================\n' descr_str = self.description.__repr__() pstr += descr_str[1:-1] + '\n\n' - + longestName = 0 longestUnits = 0 longestSize = 0 @@ -824,7 +982,7 @@ def getSummary(self, whitespace = 6): nml = len(ipt.name) if nml > longestName: longestName = nml - unts = ipt.units.__str__()#_repr_html_() + unts = ipt.units.__str__() # _repr_html_() # unts = unts.replace('','^') # unts = unts.replace('','') # unts = unts.replace('\[', '[') @@ -841,37 +999,37 @@ def getSummary(self, whitespace = 6): namespace = max([4, longestName]) + whitespace unitspace = max([5, longestUnits]) + whitespace sizespace = max([4, longestSize]) + whitespace - fulllength = namespace+unitspace+sizespace+11 + fulllength = namespace + unitspace + sizespace + 11 pstr += 'Inputs\n' - pstr += '='*fulllength + pstr += '=' * fulllength pstr += '\n' pstr += 'Name'.ljust(namespace) pstr += 'Units'.ljust(unitspace) pstr += 'Size'.ljust(sizespace) pstr += 'Description' pstr += '\n' - pstr += '-'*(namespace - whitespace) - pstr += ' '*whitespace - pstr += '-'*(unitspace - whitespace) - pstr += ' '*whitespace - pstr += '-'*(sizespace - whitespace) - pstr += ' '*whitespace + pstr += '-' * (namespace - whitespace) + pstr += ' ' * whitespace + pstr += '-' * (unitspace - whitespace) + pstr += ' ' * whitespace + pstr += '-' * (sizespace - whitespace) + pstr += ' ' * whitespace pstr += '-----------' pstr += '\n' for ipt in self.inputs: pstr += ipt.name.ljust(namespace) - unts = ipt.units.__str__()#_repr_html_() + unts = ipt.units.__str__() # _repr_html_() # unts = unts.replace('','^') # unts = unts.replace('','') # unts = unts.replace('\[', '[') # unts = unts.replace('\]', ']') pstr += unts.ljust(unitspace) - lnstr = '%s'%(ipt.size.__repr__()) + lnstr = '%s' % (ipt.size.__repr__()) pstr += lnstr.ljust(sizespace) pstr += ipt.description pstr += '\n' pstr += '\n' - + longestName = 0 longestUnits = 0 longestSize = 0 @@ -879,7 +1037,7 @@ def getSummary(self, whitespace = 6): nml = len(opt.name) if nml > longestName: longestName = nml - unts = opt.units.__str__()#_repr_html_() + unts = opt.units.__str__() # _repr_html_() # unts = unts.replace('','^') # unts = unts.replace('','') # unts = unts.replace('\[', '[') @@ -896,51 +1054,51 @@ def getSummary(self, whitespace = 6): namespace = max([4, longestName]) + whitespace unitspace = max([5, longestUnits]) + whitespace sizespace = max([4, longestSize]) + whitespace - fulllength = namespace+unitspace+sizespace+11 + fulllength = namespace + unitspace + sizespace + 11 pstr += 'Outputs\n' - pstr += '='*fulllength + pstr += '=' * fulllength pstr += '\n' pstr += 'Name'.ljust(namespace) pstr += 'Units'.ljust(unitspace) pstr += 'Size'.ljust(sizespace) pstr += 'Description' pstr += '\n' - pstr += '-'*(namespace - whitespace) - pstr += ' '*whitespace - pstr += '-'*(unitspace - whitespace) - pstr += ' '*whitespace - pstr += '-'*(sizespace - whitespace) - pstr += ' '*whitespace + pstr += '-' * (namespace - whitespace) + pstr += ' ' * whitespace + pstr += '-' * (unitspace - whitespace) + pstr += ' ' * whitespace + pstr += '-' * (sizespace - whitespace) + pstr += ' ' * whitespace pstr += '-----------' pstr += '\n' for opt in self.outputs: pstr += opt.name.ljust(namespace) - unts = opt.units.__str__()#_repr_html_() + unts = opt.units.__str__() # _repr_html_() # unts = unts.replace('','^') # unts = unts.replace('','') # unts = unts.replace('\[', '[') # unts = unts.replace('\]', ']') pstr += unts.ljust(unitspace) - lnstr = '%s'%(opt.size.__repr__()) + lnstr = '%s' % (opt.size.__repr__()) pstr += lnstr.ljust(sizespace) pstr += opt.description pstr += '\n' pstr += '\n' - + return pstr -# --------------------------------------------------------------------------------------------------------------------- -# --------------------------------------------------------------------------------------------------------------------- - + # --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- + @property def summary(self): return self.getSummary() -# --------------------------------------------------------------------------------------------------------------------- -# --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- def __repr__(self): pstr = 'AnalysisModel( [' - for i in range(0,len(self.outputs)): + for i in range(0, len(self.outputs)): pstr += self.outputs[i].name pstr += ',' pstr = pstr[0:-1] @@ -955,4 +1113,4 @@ def __repr__(self): pstr += ' , ' pstr = pstr[0:-2] pstr += '])' - return pstr \ No newline at end of file + return pstr diff --git a/pyomo/contrib/edi/examples/aircraft_gp.py b/pyomo/contrib/edi/examples/aircraft_gp.py index de2af641afc..d4f2d026cb1 100644 --- a/pyomo/contrib/edi/examples/aircraft_gp.py +++ b/pyomo/contrib/edi/examples/aircraft_gp.py @@ -1,16 +1,6 @@ -# =========== -# Description -# =========== -# A simple aircraft sizing problem, formulated as a Geometric Program -# From: Hoburg and Abbeel -# Geometric Programming for Aircraft Design Optimization -# AIAA Journal -# 2014 - # ================= # Import Statements # ================= -import numpy as np import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation @@ -24,135 +14,67 @@ # ================= # Declare Variables # ================= -<<<<<<< HEAD - -A = f.Variable(name="A", guess=10.0, units="-", description="aspect ratio") -C_D = f.Variable( - name="C_D", guess=0.025, units="-", description="Drag coefficient of wing" -) -C_f = f.Variable( - name="C_f", guess=0.003, units="-", description="skin friction coefficient" -) -C_L = f.Variable( - name="C_L", guess=0.5, units="-", description="Lift coefficient of wing" -) -D = f.Variable(name="D", guess=300, units="N", description="total drag force") -Re = f.Variable(name="Re", guess=3e6, units="-", description="Reynold's number") -S = f.Variable(name="S", guess=10.0, units="m^2", description="total wing area") -V = f.Variable(name="V", guess=30.0, units="m/s", description="cruising speed") -W = f.Variable(name="W", guess=10000.0, units="N", description="total aircraft weight") -======= -D = f.Variable(name="D", guess=300, units="N", description="total drag force") -A = f.Variable(name="A", guess=10.0, units="-", description="aspect ratio") -S = f.Variable(name="S", guess=10.0, units="m^2", description="total wing area") -V = f.Variable(name="V", guess=30.0, units="m/s", description="cruising speed") -W = f.Variable(name="W", guess=10000.0, units="N", description="total aircraft weight") -Re = f.Variable(name="Re", guess=3e6, units="-", description="Reynold's number") -C_D = f.Variable( - name="C_D", guess=0.025, units="-", description="Drag coefficient of wing" -) -C_L = f.Variable( - name="C_L", guess=0.5, units="-", description="Lift coefficient of wing" -) -C_f = f.Variable( - name="C_f", guess=0.003, units="-", description="skin friction coefficient" -) ->>>>>>> parent of f76f4974b (addressing concerns in PR) -W_w = f.Variable(name="W_w", guess=2500, units="N", description="wing weight") +x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') +y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') +z = f.Variable(name='z', guess=1.0, units='m^2', description='Model output') # ================= # Declare Constants # ================= -<<<<<<< HEAD -C_Lmax = f.Constant( - name="C_Lmax", value=2.0, units="-", description="max CL with flaps down" -) -CDA0 = f.Constant( - name="CDA0", value=0.0306, units="m^2", description="fuselage drag area" -) -e = f.Constant(name="e", value=0.96, units="-", description="Oswald efficiency factor") -k = f.Constant(name="k", value=1.2, units="-", description="form factor") -mu = f.Constant( - name="mu", value=1.78e-5, units="kg/m/s", description="viscosity of air" -) -N_ult = f.Constant( - name="N_ult", value=2.5, units="-", description="ultimate load factor" -) -rho = f.Constant(name="rho", value=1.23, units="kg/m^3", description="density of air") -S_wetratio = f.Constant( - name="Srat", value=2.05, units="-", description="wetted area ratio" -) -tau = f.Constant( - name="tau", value=0.12, units="-", description="airfoil thickness to chord ratio" -) -V_min = f.Constant(name="V_min", value=22, units="m/s", description="takeoff speed") -W_0 = f.Constant( - name="W_0", value=4940.0, units="N", description="aircraft weight excluding wing" -) -======= -k = f.Constant(name="k", value=1.2, units="-", description="form factor") -e = f.Constant(name="e", value=0.96, units="-", description="Oswald efficiency factor") -mu = f.Constant( - name="mu", value=1.78e-5, units="kg/m/s", description="viscosity of air" -) -rho = f.Constant(name="rho", value=1.23, units="kg/m^3", description="density of air") -tau = f.Constant( - name="tau", value=0.12, units="-", description="airfoil thickness to chord ratio" -) -N_ult = f.Constant( - name="N_ult", value=2.5, units="-", description="ultimate load factor" -) -V_min = f.Constant(name="V_min", value=22, units="m/s", description="takeoff speed") -C_Lmax = f.Constant( - name="C_Lmax", value=2.0, units="-", description="max CL with flaps down" -) -S_wetratio = f.Constant( - name="Srat", value=2.05, units="-", description="wetted area ratio" -) ->>>>>>> parent of f76f4974b (addressing concerns in PR) -W_W_coeff1 = f.Constant( - name="W_c1", value=8.71e-5, units="1/m", description="Wing Weight Coefficient 1" -) -W_W_coeff2 = f.Constant( - name="W_c2", value=45.24, units="Pa", description="Wing Weight Coefficient 2" -) -<<<<<<< HEAD -======= -CDA0 = f.Constant( - name="CDA0", value=0.0306, units="m^2", description="fuselage drag area" -) -W_0 = f.Constant( - name="W_0", value=4940.0, units="N", description="aircraft weight excluding wing" -) ->>>>>>> parent of f76f4974b (addressing concerns in PR) +c = f.Constant(name='c', value=1.0, units='', description='A constant c', size=2) # ===================== # Declare the Objective # ===================== -f.Objective(D) - -# =================================== -# Declare some intermediate variables -# =================================== -pi = np.pi -C_D_fuse = CDA0 / S -C_D_wpar = k * C_f * S_wetratio -C_D_ind = C_L**2 / (pi * A * e) -W_w_strc = W_W_coeff1 * (N_ult * A**1.5 * (W_0 * W * S) ** 0.5) / tau -W_w_surf = W_W_coeff2 * S +f.Objective(c[0] * x + c[1] * y) + + +# =================== +# Declare a Black Box +# =================== +class UnitCircle(BlackBoxFunctionModel): + def __init__(self): # The initialization function + # Initialize the black box model + super().__init__() + + # A brief description of the model + self.description = 'This model evaluates the function: z = x**2 + y**2' + + # Declare the black box model inputs + self.inputs.append(name='x', units='ft', description='The x variable') + self.inputs.append(name='y', units='ft', description='The y variable') + + # Declare the black box model outputs + self.outputs.append( + name='z', units='ft**2', description='Resultant of the unit circle' + ) + + # Declare the maximum available derivative + self.availableDerivative = 1 + + def BlackBox(self, x, y): # The actual function that does things + # Converts to correct units then casts to float + x = pyo.value(units.convert(x, self.inputs['x'].units)) + y = pyo.value(units.convert(y, self.inputs['y'].units)) + + z = x**2 + y**2 # Compute z + dzdx = 2 * x # Compute dz/dx + dzdy = 2 * y # Compute dz/dy + + z *= units.ft**2 + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + # ======================= # Declare the Constraints # ======================= -f.ConstraintList( - [ - C_D >= C_D_fuse + C_D_wpar + C_D_ind, - W_w >= W_w_surf + W_w_strc, - D >= 0.5 * rho * S * C_D * V**2, - Re == (rho / mu) * V * (S / A) ** 0.5, - C_f == 0.074 / Re**0.2, - W == 0.5 * rho * S * C_L * V**2, - W == 0.5 * rho * S * C_Lmax * V_min**2, - W >= W_0 + W_w, - ] -) +f.ConstraintList([[z, '==', [x, y], UnitCircle()], z <= 1 * units.m**2]) + +# ============================================= +# Run the black box (improves coverage metrics) +# ============================================= +uc = UnitCircle() +bbo = uc.BlackBox(0.5 * units.m, 0.5 * units.m) diff --git a/pyomo/contrib/edi/examples/readme_example.py b/pyomo/contrib/edi/examples/readme_example.py index b9b6e12918c..d4f2d026cb1 100644 --- a/pyomo/contrib/edi/examples/readme_example.py +++ b/pyomo/contrib/edi/examples/readme_example.py @@ -72,12 +72,9 @@ def BlackBox(self, x, y): # The actual function that does things # Declare the Constraints # ======================= f.ConstraintList([[z, '==', [x, y], UnitCircle()], z <= 1 * units.m**2]) -<<<<<<< HEAD # ============================================= # Run the black box (improves coverage metrics) # ============================================= uc = UnitCircle() bbo = uc.BlackBox(0.5 * units.m, 0.5 * units.m) -======= ->>>>>>> parent of f76f4974b (addressing concerns in PR) diff --git a/pyomo/contrib/edi/formulation.py b/pyomo/contrib/edi/formulation.py index 65080707e6e..f0652771ec2 100644 --- a/pyomo/contrib/edi/formulation.py +++ b/pyomo/contrib/edi/formulation.py @@ -16,13 +16,16 @@ # ___________________________________________________________________________ import pyomo -import pyomo.environ as pyo +import pyomo.environ as pyo from pyomo.util.check_units import assert_units_consistent from pyomo.environ import ConcreteModel from pyomo.environ import Var, Param, Objective, Constraint, Set from pyomo.environ import maximize, minimize from pyomo.environ import units as pyomo_units -from pyomo.contrib.pynumero.interfaces.external_grey_box import ExternalGreyBoxModel, ExternalGreyBoxBlock +from pyomo.contrib.pynumero.interfaces.external_grey_box import ( + ExternalGreyBoxModel, + ExternalGreyBoxBlock, +) from pyomo.environ import ( Reals, PositiveReals, @@ -43,7 +46,7 @@ PercentFraction, RealInterval, IntegerInterval, -) +) domainList = [ Reals, @@ -67,15 +70,17 @@ IntegerInterval, ] + def decodeUnits(u_val): - if isinstance(u_val,str): - if u_val in ['','-','None',' ','dimensionless']: + if isinstance(u_val, str): + if u_val in ['', '-', 'None', ' ', 'dimensionless']: return pyomo_units.__getattr__('dimensionless') else: return pyomo_units.__getattr__(u_val) else: return u_val - + + class Formulation(ConcreteModel): def __init__(self): super(Formulation, self).__init__() @@ -84,17 +89,18 @@ def __init__(self): self._objective_counter = 0 self._constraint_counter = 0 - self._variable_keys = [] - self._constant_keys = [] + self._variable_keys = [] + self._constant_keys = [] self._objective_keys = [] self._runtimeObjective_keys = [] self._objective_keys = [] self._runtimeConstraint_keys = [] self._constraint_keys = [] self._allConstraint_keys = [] - - def Variable(self, name, guess, units, description='', size = None, bounds=None, domain=None): + def Variable( + self, name, guess, units, description='', size=None, bounds=None, domain=None + ): if domain is None: domain = Reals else: @@ -102,47 +108,102 @@ def Variable(self, name, guess, units, description='', size = None, bounds=None, raise RuntimeError("Invalid domain") if bounds is not None: - if not isinstance(bounds,(list,tuple)): - raise ValueError('The keyword bounds must be a 2 length list or tuple of floats') - if len(bounds)!=2: - raise ValueError('The keyword bounds must be a 2 length list or tuple of floats') + if not isinstance(bounds, (list, tuple)): + raise ValueError( + 'The keyword bounds must be a 2 length list or tuple of floats' + ) + if len(bounds) != 2: + raise ValueError( + 'The keyword bounds must be a 2 length list or tuple of floats' + ) if not isinstance(bounds[0], (float, int)): - raise ValueError('The keyword bounds must be a 2 length list or tuple of floats') + raise ValueError( + 'The keyword bounds must be a 2 length list or tuple of floats' + ) if not isinstance(bounds[1], (float, int)): - raise ValueError('The keyword bounds must be a 2 length list or tuple of floats') + raise ValueError( + 'The keyword bounds must be a 2 length list or tuple of floats' + ) if bounds[0] > bounds[1]: raise ValueError("Lower bound is higher than upper bound") - if size is not None: - if isinstance(size,(list, tuple)): - for i in range(0,len(size)): - if not isinstance(size[i],int): - raise ValueError('Invalid size. Must be an integer or list/tuple of integers') - if size[i] == 1 or size[i] == 0 : - raise ValueError('A value of 0 or 1 is not valid for defining size. Use fewer dimensions.') + if isinstance(size, (list, tuple)): + for i in range(0, len(size)): + if not isinstance(size[i], int): + raise ValueError( + 'Invalid size. Must be an integer or list/tuple of integers' + ) + if size[i] == 1 or size[i] == 0: + raise ValueError( + 'A value of 0 or 1 is not valid for defining size. Use fewer dimensions.' + ) if i == 0: - st = pyo.Set(initialize=list(range(0,size[i]))) + st = pyo.Set(initialize=list(range(0, size[i]))) else: - st *= pyo.Set(initialize=list(range(0,size[i]))) + st *= pyo.Set(initialize=list(range(0, size[i]))) st.construct() - self.add_component(name, pyo.Var(st, name=name, initialize=guess, domain=domain, bounds=bounds, doc=description, units=decodeUnits(units))) + self.add_component( + name, + pyo.Var( + st, + name=name, + initialize=guess, + domain=domain, + bounds=bounds, + doc=description, + units=decodeUnits(units), + ), + ) else: if isinstance(size, int): if size == 1 or size == 0: - self.add_component(name, pyo.Var(name=name, initialize=guess, domain=domain, bounds=bounds, doc=description, units=decodeUnits(units))) + self.add_component( + name, + pyo.Var( + name=name, + initialize=guess, + domain=domain, + bounds=bounds, + doc=description, + units=decodeUnits(units), + ), + ) else: - st = pyo.Set(initialize=list(range(0,size))) + st = pyo.Set(initialize=list(range(0, size))) st.construct() - self.add_component(name, pyo.Var(st, name=name, initialize=guess, domain=domain, bounds=bounds, doc=description, units=decodeUnits(units))) + self.add_component( + name, + pyo.Var( + st, + name=name, + initialize=guess, + domain=domain, + bounds=bounds, + doc=description, + units=decodeUnits(units), + ), + ) else: - raise ValueError('Invalid size. Must be an integer or list/tuple of integers') + raise ValueError( + 'Invalid size. Must be an integer or list/tuple of integers' + ) else: - self.add_component(name, pyo.Var(name=name, initialize=guess, domain=domain, bounds=bounds, doc=description, units=decodeUnits(units))) + self.add_component( + name, + pyo.Var( + name=name, + initialize=guess, + domain=domain, + bounds=bounds, + doc=description, + units=decodeUnits(units), + ), + ) self.__dict__[name].construct() self._variable_keys.append(name) return self.__dict__[name] - + def Constant(self, name, value, units, description='', size=None, within=None): if within is None: within = Reals @@ -151,76 +212,130 @@ def Constant(self, name, value, units, description='', size=None, within=None): raise RuntimeError("Invalid within") if size is not None: - if isinstance(size,(list, tuple)): - for i in range(0,len(size)): - if not isinstance(size[i],int): - raise ValueError('Invalid size. Must be an integer or list/tuple of integers') + if isinstance(size, (list, tuple)): + for i in range(0, len(size)): + if not isinstance(size[i], int): + raise ValueError( + 'Invalid size. Must be an integer or list/tuple of integers' + ) if size[i] == 1: - raise ValueError('A value of 1 is not valid for defining size. Use fewer dimensions.') + raise ValueError( + 'A value of 1 is not valid for defining size. Use fewer dimensions.' + ) if i == 0: - st = pyo.Set(initialize=list(range(0,size[i]))) + st = pyo.Set(initialize=list(range(0, size[i]))) else: - st *= pyo.Set(initialize=list(range(0,size[i]))) + st *= pyo.Set(initialize=list(range(0, size[i]))) st.construct() - self.add_component(name, pyo.Param(st, name=name, initialize=value, within=within, doc=description, units=decodeUnits(units), mutable=True)) + self.add_component( + name, + pyo.Param( + st, + name=name, + initialize=value, + within=within, + doc=description, + units=decodeUnits(units), + mutable=True, + ), + ) else: if isinstance(size, int): if size == 1 or size == 0: - self.add_component(name, pyo.Param(name=name, initialize=value, within=within, doc=description, units=decodeUnits(units), mutable=True)) + self.add_component( + name, + pyo.Param( + name=name, + initialize=value, + within=within, + doc=description, + units=decodeUnits(units), + mutable=True, + ), + ) else: - st = pyo.Set(initialize=list(range(0,size))) + st = pyo.Set(initialize=list(range(0, size))) st.construct() - self.add_component(name, pyo.Param(st, name=name, initialize=value, within=within, doc=description, units=decodeUnits(units), mutable=True)) + self.add_component( + name, + pyo.Param( + st, + name=name, + initialize=value, + within=within, + doc=description, + units=decodeUnits(units), + mutable=True, + ), + ) else: - raise ValueError('Invalid size. Must be an integer or list/tuple of integers') + raise ValueError( + 'Invalid size. Must be an integer or list/tuple of integers' + ) else: - self.add_component(name, pyo.Param(name=name, initialize=value, within=within, doc=description, units=decodeUnits(units), mutable=True)) - + self.add_component( + name, + pyo.Param( + name=name, + initialize=value, + within=within, + doc=description, + units=decodeUnits(units), + mutable=True, + ), + ) + self.__dict__[name].construct() self._constant_keys.append(name) return self.__dict__[name] - + def Objective(self, expr, sense=minimize): self._objective_counter += 1 - self.add_component('objective_'+str(self._objective_counter) , pyo.Objective(expr=expr,sense=sense)) - self._objective_keys.append('objective_'+str(self._objective_counter)) - self.__dict__['objective_'+str(self._objective_counter)].construct() - + self.add_component( + 'objective_' + str(self._objective_counter), + pyo.Objective(expr=expr, sense=sense), + ) + self._objective_keys.append('objective_' + str(self._objective_counter)) + self.__dict__['objective_' + str(self._objective_counter)].construct() + # def RuntimeObjective(self): # pass - + def Constraint(self, expr): self._constraint_counter += 1 - conName = 'constraint_'+str(self._constraint_counter) + conName = 'constraint_' + str(self._constraint_counter) self.add_component(conName, pyo.Constraint(expr=expr)) self._constraint_keys.append(conName) self._allConstraint_keys.append(conName) self.__dict__[conName].construct() - def RuntimeConstraint(self, outputs, operators, inputs, black_box): self._constraint_counter += 1 - conName = 'constraint_'+str(self._constraint_counter) + conName = 'constraint_' + str(self._constraint_counter) self._runtimeConstraint_keys.append(conName) self._allConstraint_keys.append(conName) - self.add_component(conName, ExternalGreyBoxBlock() ) + self.add_component(conName, ExternalGreyBoxBlock()) self.__dict__[conName].construct() # TODO: Need to include operators after Michael fixes things - inputs_raw = inputs - outputs_raw = outputs + inputs_raw = inputs + outputs_raw = outputs operators_raw = operators - if isinstance(inputs_raw, (pyomo.core.base.var.IndexedVar,pyomo.core.base.var.ScalarVar)): + if isinstance( + inputs_raw, (pyomo.core.base.var.IndexedVar, pyomo.core.base.var.ScalarVar) + ): inputs_raw = [inputs_raw] elif isinstance(inputs_raw, (list, tuple)): inputs_raw = list(inputs_raw) else: raise ValueError("Invalid type for input variables") - if isinstance(outputs_raw, (pyomo.core.base.var.IndexedVar,pyomo.core.base.var.ScalarVar)): + if isinstance( + outputs_raw, (pyomo.core.base.var.IndexedVar, pyomo.core.base.var.ScalarVar) + ): outputs_raw = [outputs_raw] elif isinstance(outputs_raw, (list, tuple)): outputs_raw = list(outputs_raw) @@ -228,17 +343,19 @@ def RuntimeConstraint(self, outputs, operators, inputs, black_box): raise ValueError("Invalid type for output variables") for lst in [outputs_raw, inputs_raw]: for vr in lst: - if not isinstance(vr, (pyomo.core.base.var.IndexedVar,pyomo.core.base.var.ScalarVar)): - raise ValueError("Invalid type when checking inputs and outputs") + if not isinstance( + vr, (pyomo.core.base.var.IndexedVar, pyomo.core.base.var.ScalarVar) + ): + raise ValueError("Invalid type when checking inputs and outputs") if isinstance(operators_raw, (list, tuple)): operators_raw = list(operators_raw) - elif isinstance(operators_raw,str): + elif isinstance(operators_raw, str): operators_raw = [operators_raw] else: raise ValueError("Invalid type for operators") for opr in operators_raw: - if opr not in ["==",">=","<="]: + if opr not in ["==", ">=", "<="]: raise ValueError("Invalid operator") black_box.setOptimizationVariables(inputs_raw, outputs_raw) @@ -250,7 +367,7 @@ def RuntimeConstraint(self, outputs, operators, inputs, black_box): for ovar in outputs_raw: if isinstance(ovar, pyomo.core.base.var.ScalarVar): outputs_unwrapped.append(ovar) - else: # isinstance(ovar, pyomo.core.base.var.IndexedVar), validated above + else: # isinstance(ovar, pyomo.core.base.var.IndexedVar), validated above validIndices = list(ovar.index_set().data()) for vi in validIndices: outputs_unwrapped.append(ovar[vi]) @@ -259,24 +376,25 @@ def RuntimeConstraint(self, outputs, operators, inputs, black_box): for ivar in inputs_raw: if isinstance(ivar, pyomo.core.base.var.ScalarVar): inputs_unwrapped.append(ivar) - else: # isinstance(ivar, pyomo.core.base.var.IndexedVar), validated above + else: # isinstance(ivar, pyomo.core.base.var.IndexedVar), validated above validIndices = list(ivar.index_set().data()) for vi in validIndices: - inputs_unwrapped.append(ivar[vi]) + inputs_unwrapped.append(ivar[vi]) black_box._NunwrappedOutputs = len(outputs_unwrapped) - black_box._NunwrappedInputs = len(inputs_unwrapped) + black_box._NunwrappedInputs = len(inputs_unwrapped) black_box.post_init_setup() - # TODO: Need to unwrap operators - + # TODO: Need to unwrap operators - self.__dict__[conName].set_external_model(black_box, inputs=inputs_unwrapped, outputs=outputs_unwrapped)#,operators=operators_unwrapped) + self.__dict__[conName].set_external_model( + black_box, inputs=inputs_unwrapped, outputs=outputs_unwrapped + ) # ,operators=operators_unwrapped) def ConstraintList(self, conList): - for i in range(0,len(conList)): + for i in range(0, len(conList)): con = conList[i] - if isinstance(con, (tuple,list)): + if isinstance(con, (tuple, list)): self.RuntimeConstraint(*con) elif isinstance(con, dict): self.RuntimeConstraint(**con) @@ -284,28 +402,54 @@ def ConstraintList(self, conList): self.Constraint(con) def get_variables(self): - return [self.__dict__[nm] for nm in self.__dict__.keys() if nm in self._variable_keys] + return [ + self.__dict__[nm] + for nm in self.__dict__.keys() + if nm in self._variable_keys + ] def get_constants(self): - return [self.__dict__[nm] for nm in self.__dict__.keys() if nm in self._constant_keys] + return [ + self.__dict__[nm] + for nm in self.__dict__.keys() + if nm in self._constant_keys + ] def get_objectives(self): - return [self.__dict__[nm] for nm in self.__dict__.keys() if nm in self._objective_keys] - + return [ + self.__dict__[nm] + for nm in self.__dict__.keys() + if nm in self._objective_keys + ] + def get_constraints(self): - return [self.__dict__[nm] for nm in self.__dict__.keys() if nm in self._allConstraint_keys] + return [ + self.__dict__[nm] + for nm in self.__dict__.keys() + if nm in self._allConstraint_keys + ] def get_explicitConstraints(self): - return [self.__dict__[nm] for nm in self.__dict__.keys() if nm in self._constraint_keys] + return [ + self.__dict__[nm] + for nm in self.__dict__.keys() + if nm in self._constraint_keys + ] def get_runtimeConstraints(self): - return [self.__dict__[nm] for nm in self.__dict__.keys() if nm in self._runtimeConstraint_keys] + return [ + self.__dict__[nm] + for nm in self.__dict__.keys() + if nm in self._runtimeConstraint_keys + ] def check_units(self): - for i in range(1,self._objective_counter+1): - assert_units_consistent(self.__dict__['objective_'+str(i)]) - - for i in range(1,self._constraint_counter+1): - if not isinstance(self.__dict__['constraint_'+str(i)],pyomo.contrib.pynumero.interfaces.external_grey_box.ExternalGreyBoxBlock): - assert_units_consistent(self.__dict__['constraint_'+str(i)]) - \ No newline at end of file + for i in range(1, self._objective_counter + 1): + assert_units_consistent(self.__dict__['objective_' + str(i)]) + + for i in range(1, self._constraint_counter + 1): + if not isinstance( + self.__dict__['constraint_' + str(i)], + pyomo.contrib.pynumero.interfaces.external_grey_box.ExternalGreyBoxBlock, + ): + assert_units_consistent(self.__dict__['constraint_' + str(i)]) diff --git a/pyomo/contrib/edi/tests/test_blackbox.py b/pyomo/contrib/edi/tests/test_blackbox.py index 383e2e54dec..e8eec4b36b8 100644 --- a/pyomo/contrib/edi/tests/test_blackbox.py +++ b/pyomo/contrib/edi/tests/test_blackbox.py @@ -19,125 +19,148 @@ import pyomo.environ as pyo from pyomo.common.dependencies import attempt_import -from pyomo.core.base.units_container import ( - pint_available, -) +from pyomo.core.base.units_container import pint_available from pyomo.common.dependencies import numpy, numpy_available from pyomo.common.dependencies import scipy, scipy_available -egb, egb_available = attempt_import("pyomo.contrib.pynumero.interfaces.external_grey_box") + +egb, egb_available = attempt_import( + "pyomo.contrib.pynumero.interfaces.external_grey_box" +) if numpy_available: import numpy as np -@unittest.skipIf(not egb_available, 'Testing pyomo.contrib.edi requires pynumero external grey boxes') + +@unittest.skipIf( + not egb_available, 'Testing pyomo.contrib.edi requires pynumero external grey boxes' +) @unittest.skipIf(not numpy_available, 'Testing pyomo.contrib.edi requires numpy') @unittest.skipIf(not scipy_available, 'Testing pyomo.contrib.edi requires scipy') -@unittest.skipIf(not pint_available, 'Testing units requires pint') +@unittest.skipIf(not pint_available, 'Testing units requires pint') class TestEDIBlackBox(unittest.TestCase): def test_edi_blackbox_variable(self): "Tests the black box variable class" from pyomo.environ import units from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + from pyomo.contrib.edi.blackBoxFunctionModel import ( + BlackBoxFunctionModel_Variable, + TypeCheckedList, + BBList, + BlackBoxFunctionModel, + ) - x = BlackBoxFunctionModel_Variable('x','') + x = BlackBoxFunctionModel_Variable('x', '') x_print = x.__repr__() x_name = x.name - x_units = x.units - x_size = x.size + x_units = x.units + x_size = x.size x_desc = x.description - self.assertRaises(ValueError,x.__init__,*(1.0, '')) + self.assertRaises(ValueError, x.__init__, *(1.0, '')) x.__init__('x', units.dimensionless) - self.assertRaises(ValueError,x.__init__,*('x', 1.0)) + self.assertRaises(ValueError, x.__init__, *('x', 1.0)) x.__init__('x', units.dimensionless, '', 'flex') - x.__init__('x', units.dimensionless, '', ['flex',2]) + x.__init__('x', units.dimensionless, '', ['flex', 2]) x.__init__('x', units.dimensionless, '', 2) - x.__init__('x', units.dimensionless, '', [2,2]) + x.__init__('x', units.dimensionless, '', [2, 2]) - self.assertRaises(ValueError,x.__init__,*('x', '','',[[],2])) - self.assertRaises(ValueError,x.__init__,*('x', '','',[2,1])) + self.assertRaises(ValueError, x.__init__, *('x', '', '', [[], 2])) + self.assertRaises(ValueError, x.__init__, *('x', '', '', [2, 1])) x.__init__('x', units.dimensionless, '', None) x.__init__('x', None, '', None) - self.assertRaises(ValueError,x.__init__,*('x', '','', 1 )) - self.assertRaises(ValueError,x.__init__,*('x', '','', {} )) - self.assertRaises(ValueError,x.__init__,*('x', '', 1.0 )) - - - + self.assertRaises(ValueError, x.__init__, *('x', '', '', 1)) + self.assertRaises(ValueError, x.__init__, *('x', '', '', {})) + self.assertRaises(ValueError, x.__init__, *('x', '', 1.0)) def test_edi_blackbox_tcl(self): "Tests the black box type checked list class" from pyomo.environ import units from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + from pyomo.contrib.edi.blackBoxFunctionModel import ( + BlackBoxFunctionModel_Variable, + TypeCheckedList, + BBList, + BlackBoxFunctionModel, + ) - tcl = TypeCheckedList(int, [1,2,3]) + tcl = TypeCheckedList(int, [1, 2, 3]) tcl[1] = 1 - tcl[0:2] = [1,2] - - self.assertRaises(ValueError,tcl.__init__,*( int, 1 )) - self.assertRaises(ValueError,tcl.__setitem__,*( 1, 3.333 )) - self.assertRaises(ValueError,tcl.__setitem__,*( 1, [1, 2.222] )) - self.assertRaises(ValueError,tcl.append,*( 2.222 ,)) - + tcl[0:2] = [1, 2] + self.assertRaises(ValueError, tcl.__init__, *(int, 1)) + self.assertRaises(ValueError, tcl.__setitem__, *(1, 3.333)) + self.assertRaises(ValueError, tcl.__setitem__, *(1, [1, 2.222])) + self.assertRaises(ValueError, tcl.append, *(2.222,)) def test_edi_blackbox_bbl(self): "Tests the black box BBList class" from pyomo.environ import units from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + from pyomo.contrib.edi.blackBoxFunctionModel import ( + BlackBoxFunctionModel_Variable, + TypeCheckedList, + BBList, + BlackBoxFunctionModel, + ) bbl = BBList() - bbl.append('x','') - bbl.append('y','') - bbl.append(BlackBoxFunctionModel_Variable('z','')) - bbl.append(var = BlackBoxFunctionModel_Variable('u','')) - self.assertRaises(ValueError,bbl.append,*( BlackBoxFunctionModel_Variable('u',''),)) - self.assertRaises(ValueError,bbl.append,*( 'badvar',)) - self.assertRaises(ValueError,bbl.append,*( 2.222,)) - - self.assertRaises(ValueError,bbl.append,*( 'bv','',''),**{'units':'m'}) - self.assertRaises(ValueError,bbl.append,**{'units':'m','description':'hi'}) - self.assertRaises(ValueError,bbl.append,**{'name':'x', 'units':''}) - self.assertRaises(ValueError,bbl.append,*( 'bv','','',0,'extra')) + bbl.append('x', '') + bbl.append('y', '') + bbl.append(BlackBoxFunctionModel_Variable('z', '')) + bbl.append(var=BlackBoxFunctionModel_Variable('u', '')) + self.assertRaises( + ValueError, bbl.append, *(BlackBoxFunctionModel_Variable('u', ''),) + ) + self.assertRaises(ValueError, bbl.append, *('badvar',)) + self.assertRaises(ValueError, bbl.append, *(2.222,)) + + self.assertRaises(ValueError, bbl.append, *('bv', '', ''), **{'units': 'm'}) + self.assertRaises(ValueError, bbl.append, **{'units': 'm', 'description': 'hi'}) + self.assertRaises(ValueError, bbl.append, **{'name': 'x', 'units': ''}) + self.assertRaises(ValueError, bbl.append, *('bv', '', '', 0, 'extra')) xv = bbl['x'] xv2 = bbl[0] - self.assertRaises(ValueError,bbl.__getitem__,*( 2.22, )) - + self.assertRaises(ValueError, bbl.__getitem__, *(2.22,)) def test_edi_blackbox_someexceptions(self): "Tests some of the exceptions in the black box model class" import numpy as np from pyomo.environ import units from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + from pyomo.contrib.edi.blackBoxFunctionModel import ( + BlackBoxFunctionModel_Variable, + TypeCheckedList, + BBList, + BlackBoxFunctionModel, + ) bb = BlackBoxFunctionModel() - bb.inputVariables_optimization = [1,2,3] + bb.inputVariables_optimization = [1, 2, 3] # bb.set_input_values(np.array([1,2,3])) - self.assertRaises(ValueError,bb.input_names,*( )) + self.assertRaises(ValueError, bb.input_names, *()) # self.assertRaises(ValueError,bb.fillCache,*( )) - bb = BlackBoxFunctionModel() - bb.outputVariables_optimization = [1,2,3] - self.assertRaises(ValueError,bb.output_names,*( )) - + bb.outputVariables_optimization = [1, 2, 3] + self.assertRaises(ValueError, bb.output_names, *()) def test_edi_blackbox_etc_1(self): "Tests a black box assertion issue" - from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel - bbfm = BlackBoxFunctionModel() - self.assertRaises(AttributeError,bbfm.BlackBox,()) + from pyomo.contrib.edi.blackBoxFunctionModel import ( + BlackBoxFunctionModel_Variable, + TypeCheckedList, + BBList, + BlackBoxFunctionModel, + ) + bbfm = BlackBoxFunctionModel() + self.assertRaises(AttributeError, bbfm.BlackBox, ()) def test_edi_blackbox_etc_2(self): "Tests a black box assertion issue" @@ -145,53 +168,56 @@ def test_edi_blackbox_etc_2(self): from pyomo.environ import units import pyomo.environ as pyo from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + from pyomo.contrib.edi.blackBoxFunctionModel import ( + BlackBoxFunctionModel_Variable, + TypeCheckedList, + BBList, + BlackBoxFunctionModel, + ) f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'y variable') - z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'Output var') - f.Objective( x + y ) + x = f.Variable(name='x', guess=1.0, units='m', description='x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='y variable') + z = f.Variable(name='z', guess=1.0, units='m^2', description='Output var') + f.Objective(x + y) + class UnitCircle(BlackBoxFunctionModel): def __init__(self): super().__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append( name = 'x', - units = 'ft' , - description = 'The x variable' ) - self.inputs.append( name = 'y', - units = 'ft' , - description = 'The y variable' ) - self.outputs.append(name = 'z', - units = 'ft**2', - description = 'Output variable' ) + self.inputs.append(name='x', units='ft', description='The x variable') + self.inputs.append(name='y', units='ft', description='The y variable') + self.outputs.append( + name='z', units='ft**2', description='Output variable' + ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, x, y): # The actual function that does things + + def BlackBox(self, x, y): # The actual function that does things # Converts to correct units then casts to float - x = pyo.value(units.convert(x,self.inputs[0].units)) - y = pyo.value(units.convert(y,self.inputs[1].units)) - z = x**2 + y**2 # Compute z - dzdx = 2*x # Compute dz/dx - dzdy = 2*y # Compute dz/dy + x = pyo.value(units.convert(x, self.inputs[0].units)) + y = pyo.value(units.convert(y, self.inputs[1].units)) + z = x**2 + y**2 # Compute z + dzdx = 2 * x # Compute dz/dx + dzdy = 2 * y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + f.ConstraintList([z <= 1 * units.m**2, [z, '==', [x, y], UnitCircle()]]) - f.ConstraintList( - [ - z <= 1*units.m**2 , - [ z, '==', [x,y], UnitCircle() ] , - ] + f.__dict__['constraint_2'].get_external_model().set_input_values( + np.array([2.0, 2.0]) ) - - f.__dict__['constraint_2'].get_external_model().set_input_values(np.array([2.0, 2.0])) - f.__dict__['constraint_2'].get_external_model().inputVariables_optimization = [1,2] + f.__dict__['constraint_2'].get_external_model().inputVariables_optimization = [ + 1, + 2, + ] # f.__dict__['constraint_2'].get_external_model().fillCache() - self.assertRaises(ValueError,f.__dict__['constraint_2'].get_external_model().fillCache,*()) - + self.assertRaises( + ValueError, f.__dict__['constraint_2'].get_external_model().fillCache, *() + ) def test_edi_blackbox_etc_3(self): "Tests a black box assertion issue" @@ -199,215 +225,239 @@ def test_edi_blackbox_etc_3(self): from pyomo.environ import units import pyomo.environ as pyo from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + from pyomo.contrib.edi.blackBoxFunctionModel import ( + BlackBoxFunctionModel_Variable, + TypeCheckedList, + BBList, + BlackBoxFunctionModel, + ) f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'y variable') - z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'Output var') - f.Objective( x + y ) + x = f.Variable(name='x', guess=1.0, units='m', description='x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='y variable') + z = f.Variable(name='z', guess=1.0, units='m^2', description='Output var') + f.Objective(x + y) + class UnitCircle(BlackBoxFunctionModel): def __init__(self): super().__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append( name = 'x', - units = 'ft' , - description = 'The x variable' ) - self.inputs.append( name = 'y', - units = 'ft' , - description = 'The y variable' ) - self.outputs.append(name = 'z', - units = 'ft**2', - description = 'Output variable' ) + self.inputs.append(name='x', units='ft', description='The x variable') + self.inputs.append(name='y', units='ft', description='The y variable') + self.outputs.append( + name='z', units='ft**2', description='Output variable' + ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, x, y): # The actual function that does things + + def BlackBox(self, x, y): # The actual function that does things # Converts to correct units then casts to float - x = pyo.value(units.convert(x,self.inputs[0].units)) - y = pyo.value(units.convert(y,self.inputs[1].units)) - z = x**2 + y**2 # Compute z - dzdx = 2*x # Compute dz/dx - dzdy = 2*y # Compute dz/dy + x = pyo.value(units.convert(x, self.inputs[0].units)) + y = pyo.value(units.convert(y, self.inputs[1].units)) + z = x**2 + y**2 # Compute z + dzdx = 2 * x # Compute dz/dx + dzdy = 2 * y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return ['err'], [[dzdx, dzdy]] # return z, grad(z), hess(z)... + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return ['err'], [[dzdx, dzdy]] # return z, grad(z), hess(z)... + f.ConstraintList([z <= 1 * units.m**2, [z, '==', [x, y], UnitCircle()]]) - f.ConstraintList( - [ - z <= 1*units.m**2 , - [ z, '==', [x,y], UnitCircle() ] , - ] + self.assertRaises( + ValueError, f.__dict__['constraint_2'].get_external_model().fillCache, *() ) - self.assertRaises(ValueError,f.__dict__['constraint_2'].get_external_model().fillCache,*()) - def test_edi_blackbox_etc_4(self): "Tests a black box assertion issue" import numpy as np from pyomo.environ import units import pyomo.environ as pyo from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + from pyomo.contrib.edi.blackBoxFunctionModel import ( + BlackBoxFunctionModel_Variable, + TypeCheckedList, + BBList, + BlackBoxFunctionModel, + ) f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'y variable') - z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'Output var') - f.Objective( x + y ) + x = f.Variable(name='x', guess=1.0, units='m', description='x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='y variable') + z = f.Variable(name='z', guess=1.0, units='m^2', description='Output var') + f.Objective(x + y) + class UnitCircle(BlackBoxFunctionModel): def __init__(self): super().__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append( name = 'x', - units = 'ft' , - description = 'The x variable' ) - self.inputs.append( name = 'y', - units = 'ft' , - description = 'The y variable' ) - self.outputs.append(name = 'z', - units = 'ft**2', - description = 'Output variable' ) + self.inputs.append(name='x', units='ft', description='The x variable') + self.inputs.append(name='y', units='ft', description='The y variable') + self.outputs.append( + name='z', units='ft**2', description='Output variable' + ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, x, y): # The actual function that does things + + def BlackBox(self, x, y): # The actual function that does things # Converts to correct units then casts to float - x = pyo.value(units.convert(x,self.inputs[0].units)) - y = pyo.value(units.convert(y,self.inputs[1].units)) - z = x**2 + y**2 # Compute z - dzdx = 2*x # Compute dz/dx - dzdy = 2*y # Compute dz/dy + x = pyo.value(units.convert(x, self.inputs[0].units)) + y = pyo.value(units.convert(y, self.inputs[1].units)) + z = x**2 + y**2 # Compute z + dzdx = 2 * x # Compute dz/dx + dzdy = 2 * y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + f.ConstraintList([z <= 1 * units.m**2, [z, '==', [x, y], UnitCircle()]]) - f.ConstraintList( - [ - z <= 1*units.m**2 , - [ z, '==', [x,y], UnitCircle() ] , - ] + f.__dict__['constraint_2'].get_external_model().set_input_values( + np.array([2.0, 2.0]) ) - - f.__dict__['constraint_2'].get_external_model().set_input_values(np.array([2.0, 2.0])) - f.__dict__['constraint_2'].get_external_model().outputVariables_optimization = [1] + f.__dict__['constraint_2'].get_external_model().outputVariables_optimization = [ + 1 + ] # f.__dict__['constraint_2'].get_external_model().fillCache() - self.assertRaises(ValueError,f.__dict__['constraint_2'].get_external_model().fillCache,*()) - + self.assertRaises( + ValueError, f.__dict__['constraint_2'].get_external_model().fillCache, *() + ) def test_edi_blackbox_example_1(self): "Tests a black box example construction" from pyomo.environ import units import pyomo.environ as pyo from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + from pyomo.contrib.edi.blackBoxFunctionModel import ( + BlackBoxFunctionModel_Variable, + TypeCheckedList, + BBList, + BlackBoxFunctionModel, + ) f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'y variable') - z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'Output var') - f.Objective( x + y ) + x = f.Variable(name='x', guess=1.0, units='m', description='x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='y variable') + z = f.Variable(name='z', guess=1.0, units='m^2', description='Output var') + f.Objective(x + y) + class UnitCircle(BlackBoxFunctionModel): def __init__(self): super().__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append( name = 'x', - units = 'ft' , - description = 'The x variable' ) - self.inputs.append( name = 'y', - units = 'ft' , - description = 'The y variable' ) - self.outputs.append(name = 'z', - units = 'ft**2', - description = 'Output variable' ) + self.inputs.append(name='x', units='ft', description='The x variable') + self.inputs.append(name='y', units='ft', description='The y variable') + self.outputs.append( + name='z', units='ft**2', description='Output variable' + ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, x, y): # The actual function that does things + + def BlackBox(self, x, y): # The actual function that does things # Converts to correct units then casts to float - x = pyo.value(units.convert(x,self.inputs[0].units)) - y = pyo.value(units.convert(y,self.inputs[1].units)) - z = x**2 + y**2 # Compute z - dzdx = 2*x # Compute dz/dx - dzdy = 2*y # Compute dz/dy + x = pyo.value(units.convert(x, self.inputs[0].units)) + y = pyo.value(units.convert(y, self.inputs[1].units)) + z = x**2 + y**2 # Compute z + dzdx = 2 * x # Compute dz/dx + dzdy = 2 * y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + f.ConstraintList([z <= 1 * units.m**2, [z, '==', [x, y], UnitCircle()]]) - f.ConstraintList( - [ - z <= 1*units.m**2 , - [ z, '==', [x,y], UnitCircle() ] , - ] + f.__dict__['constraint_2'].get_external_model().set_input_values( + np.array([2.0, 2.0]) + ) + opt = f.__dict__['constraint_2'].get_external_model().evaluate_outputs() + jac = ( + f.__dict__['constraint_2'] + .get_external_model() + .evaluate_jacobian_outputs() + .todense() ) - f.__dict__['constraint_2'].get_external_model().set_input_values(np.array([2.0, 2.0])) - opt = f.__dict__['constraint_2'].get_external_model().evaluate_outputs() - jac = f.__dict__['constraint_2'].get_external_model().evaluate_jacobian_outputs().todense() - - self.assertAlmostEqual(opt[0],8) - self.assertAlmostEqual(jac[0,0],4) - self.assertAlmostEqual(jac[0,1],4) + self.assertAlmostEqual(opt[0], 8) + self.assertAlmostEqual(jac[0, 0], 4) + self.assertAlmostEqual(jac[0, 1], 4) - sm = f.__dict__['constraint_2'].get_external_model().summary + sm = f.__dict__['constraint_2'].get_external_model().summary e_print = f.__dict__['constraint_2'].get_external_model().__repr__() - def test_edi_blackbox_example_2(self): "Tests a black box example construction" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable', size=3) - y = f.Variable(name = 'y', guess = 1.0, units = 'm**2' , description = 'The y variable', size=3) - f.Objective( y[0] + y[1] + y[2] ) + x = f.Variable( + name='x', guess=1.0, units='m', description='The x variable', size=3 + ) + y = f.Variable( + name='y', guess=1.0, units='m**2', description='The y variable', size=3 + ) + f.Objective(y[0] + y[1] + y[2]) + class Parabola(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super(Parabola, self).__init__() self.description = 'This model evaluates the function: y = x**2' - self.inputs.append(name = 'x', size = 3, units = 'ft' , description = 'The x variable') - self.outputs.append(name = 'y', size = 3, units = 'ft**2' , description = 'The y variable') + self.inputs.append( + name='x', size=3, units='ft', description='The x variable' + ) + self.outputs.append( + name='y', size=3, units='ft**2', description='The y variable' + ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, *args, **kwargs):# The actual function that does things - runCases, returnMode, remainingKwargs = self.parseInputs(*args, **kwargs) - + def BlackBox(self, *args, **kwargs): # The actual function that does things + runCases, returnMode, remainingKwargs = self.parseInputs( + *args, **kwargs + ) + x = self.sanitizeInputs(runCases[0]['x']) x = np.array([pyo.value(xval) for xval in x], dtype=np.float64) - y = x**2 # Compute y - dydx = 2*x # Compute dy/dx - + y = x**2 # Compute y + dydx = 2 * x # Compute dy/dx + y = y * units.ft**2 dydx = np.diag(dydx) - dydx = dydx * units.ft # units.ft**2 / units.ft - - return y, [dydx] # return z, grad(z), hess(z)... - f.ConstraintList([ {'outputs':y, 'operators':'==', 'inputs':x, 'black_box':Parabola() },]) + dydx = dydx * units.ft # units.ft**2 / units.ft + + return y, [dydx] # return z, grad(z), hess(z)... + + f.ConstraintList( + [{'outputs': y, 'operators': '==', 'inputs': x, 'black_box': Parabola()}] + ) - f.__dict__['constraint_1'].get_external_model().set_input_values(np.ones(3)*2) + f.__dict__['constraint_1'].get_external_model().set_input_values(np.ones(3) * 2) opt = f.__dict__['constraint_1'].get_external_model().evaluate_outputs() - jac = f.__dict__['constraint_1'].get_external_model().evaluate_jacobian_outputs().todense() - - self.assertAlmostEqual(opt[0],4) - self.assertAlmostEqual(opt[1],4) - self.assertAlmostEqual(opt[2],4) - self.assertAlmostEqual(jac[0,0],4) - self.assertAlmostEqual(jac[0,1],0) - self.assertAlmostEqual(jac[0,2],0) - self.assertAlmostEqual(jac[0,1],0) - self.assertAlmostEqual(jac[1,1],4) - self.assertAlmostEqual(jac[2,1],0) - self.assertAlmostEqual(jac[2,0],0) - self.assertAlmostEqual(jac[2,1],0) - self.assertAlmostEqual(jac[2,2],4) - - sm = f.__dict__['constraint_1'].get_external_model().summary + jac = ( + f.__dict__['constraint_1'] + .get_external_model() + .evaluate_jacobian_outputs() + .todense() + ) + + self.assertAlmostEqual(opt[0], 4) + self.assertAlmostEqual(opt[1], 4) + self.assertAlmostEqual(opt[2], 4) + self.assertAlmostEqual(jac[0, 0], 4) + self.assertAlmostEqual(jac[0, 1], 0) + self.assertAlmostEqual(jac[0, 2], 0) + self.assertAlmostEqual(jac[0, 1], 0) + self.assertAlmostEqual(jac[1, 1], 4) + self.assertAlmostEqual(jac[2, 1], 0) + self.assertAlmostEqual(jac[2, 0], 0) + self.assertAlmostEqual(jac[2, 1], 0) + self.assertAlmostEqual(jac[2, 2], 4) + + sm = f.__dict__['constraint_1'].get_external_model().summary e_print = f.__dict__['constraint_1'].get_external_model().__repr__() def test_edi_blackbox_example_3(self): @@ -416,109 +466,133 @@ def test_edi_blackbox_example_3(self): from pyomo.environ import units import pyomo.environ as pyo from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + from pyomo.contrib.edi.blackBoxFunctionModel import ( + BlackBoxFunctionModel_Variable, + TypeCheckedList, + BBList, + BlackBoxFunctionModel, + ) f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = '' , description = 'x variable', size = 3) - y = f.Variable(name = 'y', guess = 1.0, units = '' , description = 'y variable') - f.Objective( y ) + x = f.Variable(name='x', guess=1.0, units='', description='x variable', size=3) + y = f.Variable(name='y', guess=1.0, units='', description='y variable') + f.Objective(y) + class Norm_2(BlackBoxFunctionModel): def __init__(self): super().__init__() self.description = 'This model evaluates the two norm' - self.inputs.append( name = 'x', - units = '' , - description = 'The x variable' , - size = 3) - self.outputs.append(name = 'y', - units = '' , - description = 'The y variable' ) + self.inputs.append( + name='x', units='', description='The x variable', size=3 + ) + self.outputs.append(name='y', units='', description='The y variable') self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, *args, **kwargs):# The actual function that does things - runCases, returnMode, remainingKwargs = self.parseInputs(*args, **kwargs) + + def BlackBox(self, *args, **kwargs): # The actual function that does things + runCases, returnMode, remainingKwargs = self.parseInputs( + *args, **kwargs + ) x = self.sanitizeInputs(runCases[0]['x']) x = np.array([pyo.value(xval) for xval in x], dtype=np.float64) - y = x[0]**2 + x[1]**2 + x[2]**2 # Compute y - dydx0 = 2*x[0] # Compute dy/dx0 - dydx1 = 2*x[1] # Compute dy/dx1 - dydx2 = 2*x[2] # Compute dy/dx2 + y = x[0] ** 2 + x[1] ** 2 + x[2] ** 2 # Compute y + dydx0 = 2 * x[0] # Compute dy/dx0 + dydx1 = 2 * x[1] # Compute dy/dx1 + dydx2 = 2 * x[2] # Compute dy/dx2 y = y * units.dimensionless - dydx = np.array([dydx0,dydx1,dydx2]) * units.dimensionless + dydx = np.array([dydx0, dydx1, dydx2]) * units.dimensionless # dydx0 = dydx0 * units.dimensionless # dydx1 = dydx1 * units.dimensionless # dydx2 = dydx2 * units.dimensionless - return y, [dydx] # return z, grad(z), hess(z)... - - f.ConstraintList([ {'outputs':y, 'operators':'==', 'inputs':x, 'black_box':Norm_2() },]) + return y, [dydx] # return z, grad(z), hess(z)... + + f.ConstraintList( + [{'outputs': y, 'operators': '==', 'inputs': x, 'black_box': Norm_2()}] + ) - f.__dict__['constraint_1'].get_external_model().set_input_values(np.ones(3)*2) + f.__dict__['constraint_1'].get_external_model().set_input_values(np.ones(3) * 2) opt = f.__dict__['constraint_1'].get_external_model().evaluate_outputs() - jac = f.__dict__['constraint_1'].get_external_model().evaluate_jacobian_outputs().todense() + jac = ( + f.__dict__['constraint_1'] + .get_external_model() + .evaluate_jacobian_outputs() + .todense() + ) - self.assertAlmostEqual(opt[0],12) - self.assertAlmostEqual(jac[0,0],4) - self.assertAlmostEqual(jac[0,1],4) - self.assertAlmostEqual(jac[0,2],4) + self.assertAlmostEqual(opt[0], 12) + self.assertAlmostEqual(jac[0, 0], 4) + self.assertAlmostEqual(jac[0, 1], 4) + self.assertAlmostEqual(jac[0, 2], 4) - sm = f.__dict__['constraint_1'].get_external_model().summary + sm = f.__dict__['constraint_1'].get_external_model().summary e_print = f.__dict__['constraint_1'].get_external_model().__repr__() - def test_edi_blackbox_example_4(self): "Tests a black box example construction" import numpy as np from pyomo.environ import units import pyomo.environ as pyo from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + from pyomo.contrib.edi.blackBoxFunctionModel import ( + BlackBoxFunctionModel_Variable, + TypeCheckedList, + BBList, + BlackBoxFunctionModel, + ) f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = '' , description = 'x variable') - y = f.Variable(name = 'y', guess = 1.0, units = '' , description = 'y variable', size=3) - f.Objective( y[0]**2 + y[1]**2 + y[2]**2 ) + x = f.Variable(name='x', guess=1.0, units='', description='x variable') + y = f.Variable(name='y', guess=1.0, units='', description='y variable', size=3) + f.Objective(y[0] ** 2 + y[1] ** 2 + y[2] ** 2) + class VectorCast(BlackBoxFunctionModel): def __init__(self): super().__init__() self.description = 'This model evaluates the two norm' - self.inputs.append( name = 'x', - units = '' , - description = 'The x variable' ) - self.outputs.append(name = 'y', - units = '' , - description = 'The y variable' , - size = 3) + self.inputs.append(name='x', units='', description='The x variable') + self.outputs.append( + name='y', units='', description='The y variable', size=3 + ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, *args, **kwargs):# The actual function that does things - runCases, returnMode, remainingKwargs = self.parseInputs(*args, **kwargs) + def BlackBox(self, *args, **kwargs): # The actual function that does things + runCases, returnMode, remainingKwargs = self.parseInputs( + *args, **kwargs + ) x = pyo.value(self.sanitizeInputs(runCases[0]['x'])) - y = np.array([x,x,x]) * units.dimensionless - dydx = np.array([1.0,1.0,1.0]) * units.dimensionless + y = np.array([x, x, x]) * units.dimensionless + dydx = np.array([1.0, 1.0, 1.0]) * units.dimensionless - return y, [dydx] # return z, grad(z), hess(z)... + return y, [dydx] # return z, grad(z), hess(z)... - f.ConstraintList([ {'outputs':y, 'operators':'==', 'inputs':x, 'black_box':VectorCast() },]) + f.ConstraintList( + [{'outputs': y, 'operators': '==', 'inputs': x, 'black_box': VectorCast()}] + ) - f.__dict__['constraint_1'].get_external_model().set_input_values(np.ones(3)*2) + f.__dict__['constraint_1'].get_external_model().set_input_values(np.ones(3) * 2) opt = f.__dict__['constraint_1'].get_external_model().evaluate_outputs() - jac = f.__dict__['constraint_1'].get_external_model().evaluate_jacobian_outputs().todense() + jac = ( + f.__dict__['constraint_1'] + .get_external_model() + .evaluate_jacobian_outputs() + .todense() + ) - self.assertAlmostEqual(opt[0],2.0) - self.assertAlmostEqual(opt[1],2.0) - self.assertAlmostEqual(opt[2],2.0) - self.assertAlmostEqual(jac[0,0],1.0) - self.assertAlmostEqual(jac[1,0],1.0) - self.assertAlmostEqual(jac[2,0],1.0) + self.assertAlmostEqual(opt[0], 2.0) + self.assertAlmostEqual(opt[1], 2.0) + self.assertAlmostEqual(opt[2], 2.0) + self.assertAlmostEqual(jac[0, 0], 1.0) + self.assertAlmostEqual(jac[1, 0], 1.0) + self.assertAlmostEqual(jac[2, 0], 1.0) - sm = f.__dict__['constraint_1'].get_external_model().summary + sm = f.__dict__['constraint_1'].get_external_model().summary e_print = f.__dict__['constraint_1'].get_external_model().__repr__() def test_edi_blackbox_badexample_1(self): @@ -527,35 +601,41 @@ def test_edi_blackbox_badexample_1(self): from pyomo.environ import units import pyomo.environ as pyo from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + from pyomo.contrib.edi.blackBoxFunctionModel import ( + BlackBoxFunctionModel_Variable, + TypeCheckedList, + BBList, + BlackBoxFunctionModel, + ) f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = '' , description = 'x variable', size = 3) - y = f.Variable(name = 'y', guess = 1.0, units = '' , description = 'y variable') - f.Objective( y ) + x = f.Variable(name='x', guess=1.0, units='', description='x variable', size=3) + y = f.Variable(name='y', guess=1.0, units='', description='y variable') + f.Objective(y) + class Norm_2(BlackBoxFunctionModel): def __init__(self): super().__init__() self.description = 'This model evaluates the two norm' - self.inputs.append( name = 'x', - units = '' , - description = 'The x variable' , - size = 3) - self.outputs.append(name = 'y', - units = '' , - description = 'The y variable' ) + self.inputs.append( + name='x', units='', description='The x variable', size=3 + ) + self.outputs.append(name='y', units='', description='The y variable') self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, *args, **kwargs):# The actual function that does things - runCases, returnMode, remainingKwargs = self.parseInputs(*args, **kwargs) + + def BlackBox(self, *args, **kwargs): # The actual function that does things + runCases, returnMode, remainingKwargs = self.parseInputs( + *args, **kwargs + ) x = self.sanitizeInputs(runCases[0]['x']) x = np.array([pyo.value(xval) for xval in x], dtype=np.float64) - y = x[0]**2 + x[1]**2 + x[2]**2 # Compute y - dydx0 = 2*x[0] # Compute dy/dx0 - dydx1 = 2*x[1] # Compute dy/dx1 - dydx2 = 2*x[2] # Compute dy/dx2 + y = x[0] ** 2 + x[1] ** 2 + x[2] ** 2 # Compute y + dydx0 = 2 * x[0] # Compute dy/dx0 + dydx1 = 2 * x[1] # Compute dy/dx1 + dydx2 = 2 * x[2] # Compute dy/dx2 y = y * units.dimensionless # dydx = np.array([dydx0,dydx1,dydx2]) * units.dimensionless @@ -563,13 +643,18 @@ def BlackBox(self, *args, **kwargs):# The actual function that does things dydx1 = dydx1 * units.dimensionless dydx2 = dydx2 * units.dimensionless - return y, [[dydx0, dydx1, dydx2]] # return z, grad(z), hess(z)... - - f.ConstraintList([ {'outputs':y, 'operators':'==', 'inputs':x, 'black_box':Norm_2() },]) + return y, [[dydx0, dydx1, dydx2]] # return z, grad(z), hess(z)... - f.__dict__['constraint_1'].get_external_model().set_input_values(np.ones(3)*2) - self.assertRaises(ValueError,f.__dict__['constraint_1'].get_external_model().evaluate_outputs,*()) + f.ConstraintList( + [{'outputs': y, 'operators': '==', 'inputs': x, 'black_box': Norm_2()}] + ) + f.__dict__['constraint_1'].get_external_model().set_input_values(np.ones(3) * 2) + self.assertRaises( + ValueError, + f.__dict__['constraint_1'].get_external_model().evaluate_outputs, + *() + ) def test_edi_blackbox_smallfunctions(self): "Tests the more general value and convert functions" @@ -577,22 +662,31 @@ def test_edi_blackbox_smallfunctions(self): from pyomo.environ import units import pyomo.environ as pyo from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + from pyomo.contrib.edi.blackBoxFunctionModel import ( + BlackBoxFunctionModel_Variable, + TypeCheckedList, + BBList, + BlackBoxFunctionModel, + ) bb = BlackBoxFunctionModel() - t1 = bb.convert(2*units.m, units.ft) - t2 = bb.convert(np.ones([2,2])*units.m, units.ft) - self.assertRaises(ValueError,bb.convert,*('err',units.ft)) - - t3 = bb.pyomo_value(2*units.m) - t3 = bb.pyomo_value(np.ones([2,2])*units.m) - self.assertRaises(ValueError,bb.pyomo_value,*(np.ones([2,2]),)) - - bb.sizeCheck([2,2],np.ones([2,2])*units.m) - self.assertRaises(ValueError,bb.sizeCheck,*(2,3*units.m,)) - self.assertRaises(ValueError,bb.sizeCheck,*([10,10,10],np.ones([2,2])*units.m,)) - self.assertRaises(ValueError,bb.sizeCheck,*([10,10],np.ones([2,2])*units.m,)) - self.assertRaises(ValueError,bb.sizeCheck,*([10,10],[],)) + t1 = bb.convert(2 * units.m, units.ft) + t2 = bb.convert(np.ones([2, 2]) * units.m, units.ft) + self.assertRaises(ValueError, bb.convert, *('err', units.ft)) + + t3 = bb.pyomo_value(2 * units.m) + t3 = bb.pyomo_value(np.ones([2, 2]) * units.m) + self.assertRaises(ValueError, bb.pyomo_value, *(np.ones([2, 2]),)) + + bb.sizeCheck([2, 2], np.ones([2, 2]) * units.m) + self.assertRaises(ValueError, bb.sizeCheck, *(2, 3 * units.m)) + self.assertRaises( + ValueError, bb.sizeCheck, *([10, 10, 10], np.ones([2, 2]) * units.m) + ) + self.assertRaises( + ValueError, bb.sizeCheck, *([10, 10], np.ones([2, 2]) * units.m) + ) + self.assertRaises(ValueError, bb.sizeCheck, *([10, 10], [])) def test_edi_blackbox_bare_example_1(self): "Tests a black box example construction without an optimization problem" @@ -606,34 +700,38 @@ def __init__(self): # Set up all the attributes by calling Model.__init__ super().__init__() - #Setup Inputs - self.inputs.append( 'x', '', 'Independent Variable') + # Setup Inputs + self.inputs.append('x', '', 'Independent Variable') - #Setup Outputs - self.outputs.append( 'y', '', 'Dependent Variable') + # Setup Outputs + self.outputs.append('y', '', 'Dependent Variable') - #Simple model description - self.description = 'This model evaluates the function: max([-6*x-6, x**4-3*x**2])' + # Simple model description + self.description = ( + 'This model evaluates the function: max([-6*x-6, x**4-3*x**2])' + ) self.availableDerivative = 1 - #standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) + # standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) def BlackBox(self, *args, **kwargs): runCases, returnMode, extras = self.parseInputs(*args, **kwargs) - x = np.array([ pyo.value(runCases[i]['x']) for i in range(0,len(runCases)) ]) - - y = np.maximum(-6*x-6, x**4-3*x**2) - dydx = 4*x**3 - 6*x - ddy_ddx = 12*x**2 - 6 - gradientSwitch = -6*x-6 > x**4-3*x**2 + x = np.array( + [pyo.value(runCases[i]['x']) for i in range(0, len(runCases))] + ) + + y = np.maximum(-6 * x - 6, x**4 - 3 * x**2) + dydx = 4 * x**3 - 6 * x + ddy_ddx = 12 * x**2 - 6 + gradientSwitch = -6 * x - 6 > x**4 - 3 * x**2 dydx[gradientSwitch] = -6 ddy_ddx[gradientSwitch] = 0 - y = [ yval * units.dimensionless for yval in y] - dydx = [dydx[i] * units.dimensionless for i in range(0,len(dydx))] + y = [yval * units.dimensionless for yval in y] + dydx = [dydx[i] * units.dimensionless for i in range(0, len(dydx))] if returnMode < 0: - returnMode = -1*(returnMode + 1) + returnMode = -1 * (returnMode + 1) if returnMode == 0: return y[0] if returnMode == 1: @@ -641,43 +739,44 @@ def BlackBox(self, *args, **kwargs): else: if returnMode == 0: opt = [] - for i in range(0,len(y)): - opt.append([ y[i] ]) + for i in range(0, len(y)): + opt.append([y[i]]) return opt if returnMode == 1: opt = [] - for i in range(0,len(y)): - opt.append([ [y[i]], [ [[dydx[i]]] ] ]) + for i in range(0, len(y)): + opt.append([[y[i]], [[[dydx[i]]]]]) return opt s = SignomialTest() - ivals = [[x] for x in np.linspace(-2,2,11)] + ivals = [[x] for x in np.linspace(-2, 2, 11)] # How the black box may be called using EDI - bbo = s.BlackBox(**{'x':0.5}) - bbo = s.BlackBox({'x':0.5}) - bbo = s.BlackBox(**{'x':0.5, 'optn':True}) + bbo = s.BlackBox(**{'x': 0.5}) + bbo = s.BlackBox({'x': 0.5}) + bbo = s.BlackBox(**{'x': 0.5, 'optn': True}) # Additional options available with parseInputs bbo = s.BlackBox(*[0.5], **{'optn1': True, 'optn2': False}) - bbo = s.BlackBox(*[0.5,True], **{'optn': False}) - bbo = s.BlackBox({'x':[x for x in np.linspace(-2,2,11)]}) - bbo = s.BlackBox([{'x':x} for x in np.linspace(-2,2,11)]) - bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)]) - bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)], True, optn=False) - bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)], optn1=True, optn2=False) + bbo = s.BlackBox(*[0.5, True], **{'optn': False}) + bbo = s.BlackBox({'x': [x for x in np.linspace(-2, 2, 11)]}) + bbo = s.BlackBox([{'x': x} for x in np.linspace(-2, 2, 11)]) + bbo = s.BlackBox([[x] for x in np.linspace(-2, 2, 11)]) + bbo = s.BlackBox([[x] for x in np.linspace(-2, 2, 11)], True, optn=False) + bbo = s.BlackBox([[x] for x in np.linspace(-2, 2, 11)], optn1=True, optn2=False) sm = s.summary - self.assertRaises(ValueError,s.BlackBox,*( [ [[1,2],2,3] , [1,2,3]] )) - self.assertRaises(ValueError,s.BlackBox,*( [ [np.ones(2),2,3] , [1,2,3]] )) - - self.assertRaises(ValueError,s.sanitizeInputs,*( )) - self.assertRaises(ValueError,s.sanitizeInputs,*( 1 , 2, 3 )) - self.assertRaises(ValueError,s.sanitizeInputs,**{'z':2.0*units.dimensionless}) - self.assertRaises(ValueError,s.sanitizeInputs,*(2.0*units.ft,)) - self.assertRaises(NotImplementedError,s.checkOutputs,*()) + self.assertRaises(ValueError, s.BlackBox, *([[[1, 2], 2, 3], [1, 2, 3]])) + self.assertRaises(ValueError, s.BlackBox, *([[np.ones(2), 2, 3], [1, 2, 3]])) + self.assertRaises(ValueError, s.sanitizeInputs, *()) + self.assertRaises(ValueError, s.sanitizeInputs, *(1, 2, 3)) + self.assertRaises( + ValueError, s.sanitizeInputs, **{'z': 2.0 * units.dimensionless} + ) + self.assertRaises(ValueError, s.sanitizeInputs, *(2.0 * units.ft,)) + self.assertRaises(NotImplementedError, s.checkOutputs, *()) def test_edi_blackbox_bare_example_2(self): "Tests a black box example construction without an optimization problem" @@ -692,37 +791,39 @@ def __init__(self): # Set up all the attributes by calling Model.__init__ super().__init__() - #Setup Inputs - self.inputs.append( 'x', '', 'Independent Variable', size = [2,2]) + # Setup Inputs + self.inputs.append('x', '', 'Independent Variable', size=[2, 2]) - #Setup Outputs - self.outputs.append( 'y', '', 'Dependent Variable', size = [2,2]) + # Setup Outputs + self.outputs.append('y', '', 'Dependent Variable', size=[2, 2]) - #Simple model description + # Simple model description self.description = 'This model is a pass through)' self.availableDerivative = 1 - #standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) + # standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) def BlackBox(self, *args, **kwargs): runCases, returnMode, extras = self.parseInputs(*args, **kwargs) - x = [ self.pyomo_value(runCases[i]['x']) for i in range(0,len(runCases)) ] + x = [ + self.pyomo_value(runCases[i]['x']) for i in range(0, len(runCases)) + ] - y = [] + y = [] dydx = [] - + for xval in x: - y.append(xval*units.dimensionless) - dydx_temp = np.zeros([2,2,2,2]) - dydx_temp[0,0,0,0] = 1.0 - dydx_temp[0,1,0,1] = 1.0 - dydx_temp[1,0,1,0] = 1.0 - dydx_temp[1,1,1,1] = 1.0 - - dydx.append(dydx_temp*units.dimensionless) - + y.append(xval * units.dimensionless) + dydx_temp = np.zeros([2, 2, 2, 2]) + dydx_temp[0, 0, 0, 0] = 1.0 + dydx_temp[0, 1, 0, 1] = 1.0 + dydx_temp[1, 0, 1, 0] = 1.0 + dydx_temp[1, 1, 1, 1] = 1.0 + + dydx.append(dydx_temp * units.dimensionless) + if returnMode < 0: - returnMode = -1*(returnMode + 1) + returnMode = -1 * (returnMode + 1) if returnMode == 0: return y[0] if returnMode == 1: @@ -730,38 +831,41 @@ def BlackBox(self, *args, **kwargs): else: if returnMode == 0: opt = [] - for i in range(0,len(y)): - opt.append([ y[i] ]) + for i in range(0, len(y)): + opt.append([y[i]]) return opt if returnMode == 1: opt = [] - for i in range(0,len(y)): - opt.append([ [y[i]], [ [[dydx[i]]] ] ]) + for i in range(0, len(y)): + opt.append([[y[i]], [[[dydx[i]]]]]) return opt bb = PassThrough() - ivals = [[np.eye(2)*units.dimensionless], [np.ones([2,2])*units.dimensionless], [np.zeros([2,2])*units.dimensionless] ] + ivals = [ + [np.eye(2) * units.dimensionless], + [np.ones([2, 2]) * units.dimensionless], + [np.zeros([2, 2]) * units.dimensionless], + ] - xv = np.eye(2)*units.dimensionless + xv = np.eye(2) * units.dimensionless # How the black box may be called using EDI - bbo = bb.BlackBox(**{'x':xv}) - bbo = bb.BlackBox({'x':xv}) - bbo = bb.BlackBox(**{'x':xv, 'optn':True}) + bbo = bb.BlackBox(**{'x': xv}) + bbo = bb.BlackBox({'x': xv}) + bbo = bb.BlackBox(**{'x': xv, 'optn': True}) # # Additional options available with parseInputs bbo = bb.BlackBox(*[xv], **{'optn1': True, 'optn2': False}) - bbo = bb.BlackBox(*[xv,True], **{'optn': False}) - bbo = bb.BlackBox({'x':[x[0] for x in ivals]}) - bbo = bb.BlackBox([{'x':x[0]} for x in ivals]) - bbo = bb.BlackBox([ [x[0]] for x in ivals]) - bbo = bb.BlackBox([ [x[0]] for x in ivals], True, optn=False) - bbo = bb.BlackBox([ [x[0]] for x in ivals], optn1=True, optn2=False) + bbo = bb.BlackBox(*[xv, True], **{'optn': False}) + bbo = bb.BlackBox({'x': [x[0] for x in ivals]}) + bbo = bb.BlackBox([{'x': x[0]} for x in ivals]) + bbo = bb.BlackBox([[x[0]] for x in ivals]) + bbo = bb.BlackBox([[x[0]] for x in ivals], True, optn=False) + bbo = bb.BlackBox([[x[0]] for x in ivals], optn1=True, optn2=False) sm = bb.summary - self.assertRaises(ValueError,bb.sanitizeInputs,*(np.ones([2,2])*units.ft,)) - + self.assertRaises(ValueError, bb.sanitizeInputs, *(np.ones([2, 2]) * units.ft,)) def test_edi_blackbox_bare_example_3(self): "Tests a black box example construction" @@ -769,58 +873,91 @@ def test_edi_blackbox_bare_example_3(self): from pyomo.environ import units import pyomo.environ as pyo from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel_Variable, TypeCheckedList, BBList, BlackBoxFunctionModel + from pyomo.contrib.edi.blackBoxFunctionModel import ( + BlackBoxFunctionModel_Variable, + TypeCheckedList, + BBList, + BlackBoxFunctionModel, + ) class Norm_2(BlackBoxFunctionModel): def __init__(self): super().__init__() self.description = 'This model evaluates the two norm' - self.inputs.append( name = 'x', - units = '' , - description = 'The x variable' , - size = 3) - self.inputs.append( name = 'y', - units = '' , - description = 'The y variable' , - size = 2) - self.outputs.append(name = 'z', - units = '' , - description = 'The z variable') + self.inputs.append( + name='x', units='', description='The x variable', size=3 + ) + self.inputs.append( + name='y', units='', description='The y variable', size=2 + ) + self.outputs.append(name='z', units='', description='The z variable') self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, *args, **kwargs):# The actual function that does things - runCases, returnMode, remainingKwargs = self.parseInputs(*args, **kwargs) - x = [ rc['x'] for rc in runCases][0] + def BlackBox(self, *args, **kwargs): # The actual function that does things + runCases, returnMode, remainingKwargs = self.parseInputs( + *args, **kwargs + ) + + x = [rc['x'] for rc in runCases][0] x = np.array([self.pyomo_value(xval) for xval in x], dtype=np.float64) - y = [ rc['y'] for rc in runCases][0] + y = [rc['y'] for rc in runCases][0] y = np.array([self.pyomo_value(yval) for yval in y], dtype=np.float64) - z = x[0]**2 + x[1]**2 + x[2]**2 + y[0]**2 + y[1]**2 - dzdx0 = 2*x[0] # Compute dy/dx0 - dzdx1 = 2*x[1] # Compute dy/dx1 - dzdx2 = 2*x[2] # Compute dy/dx2 - dzdy0 = 2*y[0] - dzdy1 = 2*y[1] + z = x[0] ** 2 + x[1] ** 2 + x[2] ** 2 + y[0] ** 2 + y[1] ** 2 + dzdx0 = 2 * x[0] # Compute dy/dx0 + dzdx1 = 2 * x[1] # Compute dy/dx1 + dzdx2 = 2 * x[2] # Compute dy/dx2 + dzdy0 = 2 * y[0] + dzdy1 = 2 * y[1] z = z * units.dimensionless - dz = np.array([dzdx0,dzdx1,dzdx2,dzdy0,dzdy1]) * units.dimensionless + dz = np.array([dzdx0, dzdx1, dzdx2, dzdy0, dzdy1]) * units.dimensionless # dydx0 = dydx0 * units.dimensionless # dydx1 = dydx1 * units.dimensionless # dydx2 = dydx2 * units.dimensionless - return z, [dz] # return z, grad(z), hess(z)... + return z, [dz] # return z, grad(z), hess(z)... bb = Norm_2() - bbo = bb.BlackBox({'x':np.array([0,0,0])*units.dimensionless, 'y':np.array([0,0])*units.dimensionless}) - bbo = bb.BlackBox(np.array([0,0,0])*units.dimensionless, y=np.array([0,0])*units.dimensionless) - - self.assertRaises(ValueError,bb.BlackBox,*( {'er':np.array([0,0,0])*units.dimensionless, 'y':np.array([0,0])*units.dimensionless}, )) - self.assertRaises(ValueError,bb.BlackBox,*( 'err', )) - self.assertRaises(ValueError,bb.BlackBox,*( np.array([0,0,0])*units.dimensionless, np.array([0,0])*units.dimensionless ), **{'x':'err too many'}) - self.assertRaises(ValueError,bb.BlackBox,*( np.array([0,0,0])*units.dimensionless, ), **{'notY':np.array([0,0])*units.dimensionless}) + bbo = bb.BlackBox( + { + 'x': np.array([0, 0, 0]) * units.dimensionless, + 'y': np.array([0, 0]) * units.dimensionless, + } + ) + bbo = bb.BlackBox( + np.array([0, 0, 0]) * units.dimensionless, + y=np.array([0, 0]) * units.dimensionless, + ) + self.assertRaises( + ValueError, + bb.BlackBox, + *( + { + 'er': np.array([0, 0, 0]) * units.dimensionless, + 'y': np.array([0, 0]) * units.dimensionless, + }, + ) + ) + self.assertRaises(ValueError, bb.BlackBox, *('err',)) + self.assertRaises( + ValueError, + bb.BlackBox, + *( + np.array([0, 0, 0]) * units.dimensionless, + np.array([0, 0]) * units.dimensionless, + ), + **{'x': 'err too many'} + ) + self.assertRaises( + ValueError, + bb.BlackBox, + *(np.array([0, 0, 0]) * units.dimensionless,), + **{'notY': np.array([0, 0]) * units.dimensionless} + ) def test_edi_blackbox_bare_example_4(self): "Tests a black box example construction without an optimization problem" @@ -835,64 +972,70 @@ def __init__(self): # Set up all the attributes by calling Model.__init__ super().__init__() - #Setup Inputs - self.inputs.append( 'x', '', 'X Variable') - self.inputs.append( 'y', '', 'Y Variable') + # Setup Inputs + self.inputs.append('x', '', 'X Variable') + self.inputs.append('y', '', 'Y Variable') - #Setup Outputs - self.outputs.append( 'u', '', 'U Variable') - self.outputs.append( 'v', '', 'V Variable') + # Setup Outputs + self.outputs.append('u', '', 'U Variable') + self.outputs.append('v', '', 'V Variable') - #Simple model description + # Simple model description self.description = 'This model is a pass through)' self.availableDerivative = 1 - #standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) + # standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) def BlackBox(self, *args, **kwargs): runCases, returnMode, extras = self.parseInputs(*args, **kwargs) - x = [ self.pyomo_value(runCases[i]['x']) for i in range(0,len(runCases)) ] - y = [ self.pyomo_value(runCases[i]['x']) for i in range(0,len(runCases)) ] - - u = [] + x = [ + self.pyomo_value(runCases[i]['x']) for i in range(0, len(runCases)) + ] + y = [ + self.pyomo_value(runCases[i]['x']) for i in range(0, len(runCases)) + ] + + u = [] dudx = [] dudy = [] - v = [] + v = [] dvdx = [] dvdy = [] for xval in x: - u.append(xval*units.dimensionless) - dudx.append(1.0*units.dimensionless) - dudy.append(0.0*units.dimensionless) - + u.append(xval * units.dimensionless) + dudx.append(1.0 * units.dimensionless) + dudy.append(0.0 * units.dimensionless) + for yval in y: - v.append(yval*units.dimensionless) - dvdx.append(0.0*units.dimensionless) - dvdy.append(1.0*units.dimensionless) - + v.append(yval * units.dimensionless) + dvdx.append(0.0 * units.dimensionless) + dvdy.append(1.0 * units.dimensionless) + if returnMode < 0: - returnMode = -1*(returnMode + 1) + returnMode = -1 * (returnMode + 1) if returnMode == 0: return [u[0], v[0]] if returnMode == 1: - return [u[0], v[0]] , [[dudx[0], dudy[0]], [dvdx[0], dvdy[0]]] + return [u[0], v[0]], [[dudx[0], dudy[0]], [dvdx[0], dvdy[0]]] else: if returnMode == 0: opt = [] - for i in range(0,len(y)): - opt.append([ u[i], v[i] ]) + for i in range(0, len(y)): + opt.append([u[i], v[i]]) return opt if returnMode == 1: opt = [] - for i in range(0,len(y)): - opt.append([ [u[i], v[i]], [ [ dudx[i], dudy[i]], [dvdx[i], dvdy[i]] ] ]) + for i in range(0, len(y)): + opt.append( + [[u[i], v[i]], [[dudx[i], dudy[i]], [dvdx[i], dvdy[i]]]] + ) return opt bb = PassThrough() - bbo = bb.BlackBox(1.0,1.0) - bbo = bb.BlackBox({'x':np.linspace(0,10,11),'y':np.linspace(0,10,11)}) + bbo = bb.BlackBox(1.0, 1.0) + bbo = bb.BlackBox({'x': np.linspace(0, 10, 11), 'y': np.linspace(0, 10, 11)}) if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/pyomo/contrib/edi/tests/test_docSnippets.py b/pyomo/contrib/edi/tests/test_docSnippets.py index b125d524e14..1fff6e030ac 100644 --- a/pyomo/contrib/edi/tests/test_docSnippets.py +++ b/pyomo/contrib/edi/tests/test_docSnippets.py @@ -19,508 +19,503 @@ import pyomo.environ as pyo from pyomo.common.dependencies import attempt_import -from pyomo.core.base.units_container import ( - pint_available, -) +from pyomo.core.base.units_container import pint_available from pyomo.common.dependencies import numpy, numpy_available from pyomo.common.dependencies import scipy, scipy_available -egb, egb_available = attempt_import("pyomo.contrib.pynumero.interfaces.external_grey_box") + +egb, egb_available = attempt_import( + "pyomo.contrib.pynumero.interfaces.external_grey_box" +) if numpy_available: import numpy as np -@unittest.skipIf(not egb_available, 'Testing pyomo.contrib.edi requires pynumero external grey boxes') + +@unittest.skipIf( + not egb_available, 'Testing pyomo.contrib.edi requires pynumero external grey boxes' +) @unittest.skipIf(not numpy_available, 'Testing pyomo.contrib.edi requires numpy') @unittest.skipIf(not scipy_available, 'Testing pyomo.contrib.edi requires scipy') -@unittest.skipIf(not pint_available, 'Testing units requires pint') +@unittest.skipIf(not pint_available, 'Testing units requires pint') class TestEDISnippets(unittest.TestCase): def test_edi_snippet_formuation_01(self): # BEGIN: Formulation_Snippet_01 from pyomo.contrib.edi import Formulation + f = Formulation() # END: Formulation_Snippet_01 def test_edi_snippet_formuation_02(self): # BEGIN: Formulation_Snippet_02 from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = '') + x = f.Variable(name='x', guess=1.0, units='') # END: Formulation_Snippet_02 def test_edi_snippet_formuation_03(self): # BEGIN: Formulation_Snippet_03 from pyomo.contrib.edi import Formulation + f = Formulation() - c = f.Constant(name = 'c', value = 1.0, units = '') + c = f.Constant(name='c', value=1.0, units='') # END: Formulation_Snippet_03 def test_edi_snippet_formuation_04(self): # BEGIN: Formulation_Snippet_04 from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = '') - y = f.Variable(name = 'y', guess = 1.0, units = '') - c = f.Constant(name = 'c', value = 1.0, units = '') - f.Objective( c*x + y ) + x = f.Variable(name='x', guess=1.0, units='') + y = f.Variable(name='y', guess=1.0, units='') + c = f.Constant(name='c', value=1.0, units='') + f.Objective(c * x + y) # END: Formulation_Snippet_04 def test_edi_snippet_formuation_05(self): # BEGIN: Formulation_Snippet_05 from pyomo.contrib.edi import Formulation from pyomo.environ import maximize, minimize + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = '') - y = f.Variable(name = 'y', guess = 1.0, units = '') - c = f.Constant(name = 'c', value = 1.0, units = '') - f.Objective( c*x + y , sense = maximize) + x = f.Variable(name='x', guess=1.0, units='') + y = f.Variable(name='y', guess=1.0, units='') + c = f.Constant(name='c', value=1.0, units='') + f.Objective(c * x + y, sense=maximize) # END: Formulation_Snippet_05 - + def test_edi_snippet_formuation_06(self): # BEGIN: Formulation_Snippet_06 from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = '') - y = f.Variable(name = 'y', guess = 1.0, units = '') - c = f.Constant(name = 'c', value = 1.0, units = '') - f.Objective( c*x + y ) - f.Constraint( x**2 + y**2 <= 1.0 ) - f.Constraint( x >= 0) - f.Constraint( y <= 0) + x = f.Variable(name='x', guess=1.0, units='') + y = f.Variable(name='y', guess=1.0, units='') + c = f.Constant(name='c', value=1.0, units='') + f.Objective(c * x + y) + f.Constraint(x**2 + y**2 <= 1.0) + f.Constraint(x >= 0) + f.Constraint(y <= 0) # END: Formulation_Snippet_06 - + def test_edi_snippet_formuation_07(self): # BEGIN: Formulation_Snippet_07 from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = '') - y = f.Variable(name = 'y', guess = 1.0, units = '') - c = f.Constant(name = 'c', value = 1.0, units = '') - f.Objective( c*x + y ) - f.ConstraintList( - [ - x**2 + y**2 <= 1.0 , - x >= 0, - y <= 0, - ] - ) + x = f.Variable(name='x', guess=1.0, units='') + y = f.Variable(name='y', guess=1.0, units='') + c = f.Constant(name='c', value=1.0, units='') + f.Objective(c * x + y) + f.ConstraintList([x**2 + y**2 <= 1.0, x >= 0, y <= 0]) # END: Formulation_Snippet_07 - + def test_edi_snippet_formuation_08(self): # BEGIN: Formulation_Snippet_08 from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = '') - y = f.Variable(name = 'y', guess = 1.0, units = '') - c = f.Constant(name = 'c', value = 1.0, units = '') - f.Objective( c*x + y ) - - constraintList = [ - x**2 + y**2 <= 1.0 , - x >= 0, - y <= 0, - ] - - f.ConstraintList( constraintList ) + x = f.Variable(name='x', guess=1.0, units='') + y = f.Variable(name='y', guess=1.0, units='') + c = f.Constant(name='c', value=1.0, units='') + f.Objective(c * x + y) + + constraintList = [x**2 + y**2 <= 1.0, x >= 0, y <= 0] + + f.ConstraintList(constraintList) # END: Formulation_Snippet_08 - + def test_edi_snippet_formuation_09(self): # BEGIN: Formulation_Snippet_09 import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'y variable') - z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'Output var') - f.Objective( x + y ) + x = f.Variable(name='x', guess=1.0, units='m', description='x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='y variable') + z = f.Variable(name='z', guess=1.0, units='m^2', description='Output var') + f.Objective(x + y) + class UnitCircle(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super().__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append( name = 'x', - units = 'ft' , - description = 'The x variable' ) - self.inputs.append( name = 'y', - units = 'ft' , - description = 'The y variable' ) - self.outputs.append( name = 'z', - units = 'ft**2', - description = 'Output variable' ) + self.inputs.append(name='x', units='ft', description='The x variable') + self.inputs.append(name='y', units='ft', description='The y variable') + self.outputs.append( + name='z', units='ft**2', description='Output variable' + ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, x, y): # The actual function that does things + + def BlackBox(self, x, y): # The actual function that does things # Converts to correct units then casts to float - x = pyo.value(units.convert(x,self.inputs['x'].units)) - y = pyo.value(units.convert(y,self.inputs['y'].units)) - z = x**2 + y**2 # Compute z - dzdx = 2*x # Compute dz/dx - dzdy = 2*y # Compute dz/dy + x = pyo.value(units.convert(x, self.inputs['x'].units)) + y = pyo.value(units.convert(y, self.inputs['y'].units)) + z = x**2 + y**2 # Compute z + dzdx = 2 * x # Compute dz/dx + dzdy = 2 * y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... - f.Constraint( z <= 1*units.m**2 ) + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... - f.RuntimeConstraint( z, '==', [x,y], UnitCircle() ) + f.Constraint(z <= 1 * units.m**2) + + f.RuntimeConstraint(z, '==', [x, y], UnitCircle()) # END: Formulation_Snippet_09 - + ### This will fail validatation, but should construct appropriately # BEGIN: Formulation_Snippet_10 - f.RuntimeConstraint( *( z, '==', [x,y], UnitCircle() ) ) + f.RuntimeConstraint(*(z, '==', [x, y], UnitCircle())) # END: Formulation_Snippet_10 - + # BEGIN: Formulation_Snippet_11 - f.RuntimeConstraint( *[ z, '==', [x,y], UnitCircle() ] ) + f.RuntimeConstraint(*[z, '==', [x, y], UnitCircle()]) # END: Formulation_Snippet_11 - + # BEGIN: Formulation_Snippet_12 - f.RuntimeConstraint( **{ 'outputs' : z, - 'operators' : '==', - 'inputs' : [x,y], - 'black_box' : UnitCircle() } ) + f.RuntimeConstraint( + **{ + 'outputs': z, + 'operators': '==', + 'inputs': [x, y], + 'black_box': UnitCircle(), + } + ) # END: Formulation_Snippet_12 - + # BEGIN: Formulation_Snippet_13 - f.RuntimeConstraint( *( [z], ['=='], [x,y], UnitCircle() ) ) + f.RuntimeConstraint(*([z], ['=='], [x, y], UnitCircle())) # END: Formulation_Snippet_13 - + def test_edi_snippet_formuation_14(self): # BEGIN: Formulation_Snippet_14 import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'y variable') - z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'Output var') - f.Objective( x + y ) + x = f.Variable(name='x', guess=1.0, units='m', description='x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='y variable') + z = f.Variable(name='z', guess=1.0, units='m^2', description='Output var') + f.Objective(x + y) + class UnitCircle(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super().__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append( name = 'x', - units = 'ft' , - description = 'The x variable' ) - self.inputs.append( name = 'y', - units = 'ft' , - description = 'The y variable' ) - self.outputs.append( name = 'z', - units = 'ft**2', - description = 'Output variable' ) + self.inputs.append(name='x', units='ft', description='The x variable') + self.inputs.append(name='y', units='ft', description='The y variable') + self.outputs.append( + name='z', units='ft**2', description='Output variable' + ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, x, y): # The actual function that does things + + def BlackBox(self, x, y): # The actual function that does things # Converts to correct units then casts to float - x = pyo.value(units.convert(x,self.inputs[0].units)) - y = pyo.value(units.convert(y,self.inputs[1].units)) - z = x**2 + y**2 # Compute z - dzdx = 2*x # Compute dz/dx - dzdy = 2*y # Compute dz/dy + x = pyo.value(units.convert(x, self.inputs[0].units)) + y = pyo.value(units.convert(y, self.inputs[1].units)) + z = x**2 + y**2 # Compute z + dzdx = 2 * x # Compute dz/dx + dzdy = 2 * y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... - f.ConstraintList( - [ - z <= 1*units.m**2 , - [ z, '==', [x,y], UnitCircle() ] , - ] - ) + f.ConstraintList([z <= 1 * units.m**2, [z, '==', [x, y], UnitCircle()]]) # END: Formulation_Snippet_14 ### This will fail validatation, but should construct appropriately # BEGIN: Formulation_Snippet_15 - f.ConstraintList( - [ - z <= 1*units.m**2 , - ( z, '==', [x,y], UnitCircle() ) , - ] - ) + f.ConstraintList([z <= 1 * units.m**2, (z, '==', [x, y], UnitCircle())]) # END: Formulation_Snippet_15 - + # BEGIN: Formulation_Snippet_16 - f.ConstraintList( - [ - z <= 1*units.m**2 , - [ z, '==', [x,y], UnitCircle() ] , - ] - ) + f.ConstraintList([z <= 1 * units.m**2, [z, '==', [x, y], UnitCircle()]]) # END: Formulation_Snippet_16 - + # BEGIN: Formulation_Snippet_17 f.ConstraintList( [ - z <= 1*units.m**2 , - { 'outputs' : z, - 'operators' : '==', - 'inputs' : [x,y], - 'black_box' : UnitCircle() } , + z <= 1 * units.m**2, + { + 'outputs': z, + 'operators': '==', + 'inputs': [x, y], + 'black_box': UnitCircle(), + }, ] ) # END: Formulation_Snippet_17 - + # BEGIN: Formulation_Snippet_18 - f.ConstraintList( - [ - z <= 1*units.m**2 , - ( [z], ['=='], [x,y], UnitCircle() ) , - ] - ) + f.ConstraintList([z <= 1 * units.m**2, ([z], ['=='], [x, y], UnitCircle())]) # END: Formulation_Snippet_18 def test_edi_snippet_variables_01(self): # BEGIN: Variables_Snippet_01 from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') # END: Variables_Snippet_01 - + def test_edi_snippet_variables_02(self): # BEGIN: Variables_Snippet_02 from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Variable('x', 1.0, 'm' ) + x = f.Variable('x', 1.0, 'm') # END: Variables_Snippet_02 - + def test_edi_snippet_variables_03(self): # BEGIN: Variables_Snippet_03 from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Variable( name = 'x', - guess = 1.0, - units = 'm' , - description = 'The x variable', - bounds = [-10,10] ) + x = f.Variable( + name='x', + guess=1.0, + units='m', + description='The x variable', + bounds=[-10, 10], + ) # END: Variables_Snippet_03 - + def test_edi_snippet_variables_04(self): # BEGIN: Variables_Snippet_04 from pyomo.contrib.edi import Formulation from pyomo.environ import Integers + f = Formulation() - x = f.Variable( name = 'x', - guess = 1.0, - units = 'm' , - description = 'The x variable', - domain = Integers ) + x = f.Variable( + name='x', + guess=1.0, + units='m', + description='The x variable', + domain=Integers, + ) # END: Variables_Snippet_04 - + def test_edi_snippet_variables_05(self): # BEGIN: Variables_Snippet_05 from pyomo.environ import units from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Variable( name = 'x', - guess = 1.0, - units = units.m , - description = 'The x variable' ) + x = f.Variable(name='x', guess=1.0, units=units.m, description='The x variable') # END: Variables_Snippet_05 - + def test_edi_snippet_variables_06(self): # BEGIN: Variables_Snippet_06 from pyomo.environ import units from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Variable( name = 'x', - guess = 1.0, - units = 'm' , - description = 'The x variable', - size = 5 ) + x = f.Variable( + name='x', guess=1.0, units='m', description='The x variable', size=5 + ) # END: Variables_Snippet_06 - + def test_edi_snippet_variables_07(self): # BEGIN: Variables_Snippet_07 from pyomo.environ import units from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Variable( name = 'x', - guess = 1.0, - units = 'm' , - description = 'The x variable', - size = [10,2] ) + x = f.Variable( + name='x', guess=1.0, units='m', description='The x variable', size=[10, 2] + ) # END: Variables_Snippet_07 - + def test_edi_snippet_variables_08(self): # BEGIN: Variables_Snippet_08 from pyomo.environ import units from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Variable( name = 'x', - guess = 1.0, - units = 'kg*m/s**2' , - description = 'The x variable' ) + x = f.Variable( + name='x', guess=1.0, units='kg*m/s**2', description='The x variable' + ) # END: Variables_Snippet_08 def test_edi_snippet_constants_01(self): # BEGIN: Constants_Snippet_01 from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Constant(name = 'c', value = 1.0, units = 'm' , description = 'A constant c') + x = f.Constant(name='c', value=1.0, units='m', description='A constant c') # END: Constants_Snippet_01 def test_edi_snippet_constants_02(self): # BEGIN: Constants_Snippet_02 from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Constant('c', 1.0, 'm' ) + x = f.Constant('c', 1.0, 'm') # END: Constants_Snippet_02 def test_edi_snippet_constants_03(self): # BEGIN: Constants_Snippet_03 from pyomo.environ import units from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Constant( name = 'c', - value = 1.0, - units = units.m , - description = 'A constant c' ) + x = f.Constant(name='c', value=1.0, units=units.m, description='A constant c') # END: Constants_Snippet_03 def test_edi_snippet_constants_04(self): # BEGIN: Constants_Snippet_04 from pyomo.environ import units from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Constant( name = 'c', - value = 1.0, - units = 'm' , - description = 'A constant c', - size = 5 ) + x = f.Constant( + name='c', value=1.0, units='m', description='A constant c', size=5 + ) # END: Constants_Snippet_04 def test_edi_snippet_constants_05(self): # BEGIN: Constants_Snippet_05 from pyomo.environ import units from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Constant( name = 'c', - value = 1.0, - units = 'm' , - description = 'A constant c', - size = [10,2] ) + x = f.Constant( + name='c', value=1.0, units='m', description='A constant c', size=[10, 2] + ) # END: Constants_Snippet_05 def test_edi_snippet_constants_06(self): # BEGIN: Constants_Snippet_06 from pyomo.environ import units from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Constant( name = 'c', - value = 1.0, - units = 'kg*m/s**2' , - description = 'A constant c' ) + x = f.Constant( + name='c', value=1.0, units='kg*m/s**2', description='A constant c' + ) # END: Constants_Snippet_06 def test_edi_snippet_objectives_01(self): # BEGIN: Objectives_Snippet_01 from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) - f.Objective( c*x + y ) # Default is minimize + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + c = f.Constant(name='c', value=1.0, units='', description='A constant c') + f.Objective(c * x + y) # Default is minimize # END: Objectives_Snippet_01 def test_edi_snippet_objectives_02(self): # BEGIN: Objectives_Snippet_02 from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) - f.Objective( c*x**4 + y**4 ) # Default is minimize + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + c = f.Constant(name='c', value=1.0, units='', description='A constant c') + f.Objective(c * x**4 + y**4) # Default is minimize # END: Objectives_Snippet_02 def test_edi_snippet_objectives_03(self): # BEGIN: Objectives_Snippet_03 from pyomo.contrib.edi import Formulation from pyomo.environ import minimize, maximize + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) - f.Objective( c*x**4 + y**4 , sense = minimize) + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + c = f.Constant(name='c', value=1.0, units='', description='A constant c') + f.Objective(c * x**4 + y**4, sense=minimize) # END: Objectives_Snippet_03 def test_edi_snippet_objectives_04(self): # BEGIN: Objectives_Snippet_04 from pyomo.contrib.edi import Formulation from pyomo.environ import minimize, maximize + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) - f.Objective( c*x**4 + y**4 , sense = 1 ) # 1 corresponds to minimize + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + c = f.Constant(name='c', value=1.0, units='', description='A constant c') + f.Objective(c * x**4 + y**4, sense=1) # 1 corresponds to minimize # END: Objectives_Snippet_04 def test_edi_snippet_objectives_05(self): # BEGIN: Objectives_Snippet_05 from pyomo.contrib.edi import Formulation from pyomo.environ import minimize, maximize + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) - f.Objective( -c*x**4 - y**4 , sense = maximize) + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + c = f.Constant(name='c', value=1.0, units='', description='A constant c') + f.Objective(-c * x**4 - y**4, sense=maximize) # END: Objectives_Snippet_05 def test_edi_snippet_objectives_06(self): # BEGIN: Objectives_Snippet_06 from pyomo.contrib.edi import Formulation from pyomo.environ import minimize, maximize + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) - f.Objective( -c*x**4 - y**4 , sense = -1 ) # -1 corresponds to maximize + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + c = f.Constant(name='c', value=1.0, units='', description='A constant c') + f.Objective(-c * x**4 - y**4, sense=-1) # -1 corresponds to maximize # END: Objectives_Snippet_06 def test_edi_snippet_objectives_07(self): # BEGIN: Objectives_Snippet_07 from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Variable( name = 'x', - guess = 1.0, - units = 'm' , - description = 'The x variable', - bounds = [0,100] , - size = 3 ) - y = f.Variable( name = 'y', - guess = 1.0, - units = 'm' , - description = 'The y variable', - size = [2,2]) - c = f.Constant( name = 'c', - value = 1.0, - units = '' , - description = 'A constant c' , - size = 3 ) - f.Objective( c[0]*x[0] + c[1]*x[1] + c[2]*x[2] - + y[0,0]**4 - + y[0,1]**4 - + y[1,0]**4 - + y[1,1]**4 ) # Default is minimize + x = f.Variable( + name='x', + guess=1.0, + units='m', + description='The x variable', + bounds=[0, 100], + size=3, + ) + y = f.Variable( + name='y', guess=1.0, units='m', description='The y variable', size=[2, 2] + ) + c = f.Constant( + name='c', value=1.0, units='', description='A constant c', size=3 + ) + f.Objective( + c[0] * x[0] + + c[1] * x[1] + + c[2] * x[2] + + y[0, 0] ** 4 + + y[0, 1] ** 4 + + y[1, 0] ** 4 + + y[1, 1] ** 4 + ) # Default is minimize # END: Objectives_Snippet_07 def test_edi_snippet_constraints_01(self): # BEGIN: Constraints_Snippet_01 from pyomo.environ import units from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) - f.Objective( c*x + y ) + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + c = f.Constant(name='c', value=1.0, units='', description='A constant c') + f.Objective(c * x + y) f.ConstraintList( - [ - x**2 + y**2 <= 1.0*units.m**2 , - x <= 0.75*units.m, - x >= y , - ] + [x**2 + y**2 <= 1.0 * units.m**2, x <= 0.75 * units.m, x >= y] ) # END: Constraints_Snippet_01 @@ -528,68 +523,58 @@ def test_edi_snippet_constraints_02(self): # BEGIN: Constraints_Snippet_02 from pyomo.environ import units from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - c = f.Constant(name = 'c', value = 1.0, units = '' , description = 'A constant c' ) - f.Objective( c*x + y ) - f.Constraint( x**2 + y**2 <= 1.0*units.m**2 ) - f.Constraint( x <= 0.75*units.m ) - f.Constraint( x >= y ) + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + c = f.Constant(name='c', value=1.0, units='', description='A constant c') + f.Objective(c * x + y) + f.Constraint(x**2 + y**2 <= 1.0 * units.m**2) + f.Constraint(x <= 0.75 * units.m) + f.Constraint(x >= y) # END: Constraints_Snippet_02 def test_edi_snippet_constraints_03(self): # BEGIN: Constraints_Snippet_03 from pyomo.environ import units from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Variable( name = 'x', - guess = 1.0, - units = 'm' , - description = 'The x variable', - bounds = [0,100] , - size = 3 ) - y = f.Variable( name = 'y', - guess = 1.0, - units = 'm' , - description = 'The y variable', - size = [2,2]) - c = f.Constant( name = 'c', - value = 1.0, - units = '' , - description = 'A constant c' , - size = 3 ) - f.Objective( c[0]*x[0] + c[1]*x[1] + c[2]*x[2] - + y[0,0]**4 - + y[0,1]**4 - + y[1,0]**4 - + y[1,1]**4 ) # Default is minimize + x = f.Variable( + name='x', + guess=1.0, + units='m', + description='The x variable', + bounds=[0, 100], + size=3, + ) + y = f.Variable( + name='y', guess=1.0, units='m', description='The y variable', size=[2, 2] + ) + c = f.Constant( + name='c', value=1.0, units='', description='A constant c', size=3 + ) + f.Objective( + c[0] * x[0] + + c[1] * x[1] + + c[2] * x[2] + + y[0, 0] ** 4 + + y[0, 1] ** 4 + + y[1, 0] ** 4 + + y[1, 1] ** 4 + ) # Default is minimize f.ConstraintList( [ - x[0]**2 + x[1]**2 + x[2]**2 <= 1.0*units.m, - y[0,0] >= 1.0*units.m, - y[0,1] >= 1.0*units.m, - y[1,0] >= 1.0*units.m, - y[1,1] >= 1.0*units.m, - x[0] >= y[0,0] , + x[0] ** 2 + x[1] ** 2 + x[2] ** 2 <= 1.0 * units.m, + y[0, 0] >= 1.0 * units.m, + y[0, 1] >= 1.0 * units.m, + y[1, 0] >= 1.0 * units.m, + y[1, 1] >= 1.0 * units.m, + x[0] >= y[0, 0], ] ) # END: Constraints_Snippet_03 - - - - - - - - - - - - - - def test_edi_snippet_runtimeconstraints_01(self): # BEGIN: RuntimeConstraints_Snippet_01 import pyomo.environ as pyo @@ -597,8 +582,7 @@ def test_edi_snippet_runtimeconstraints_01(self): from pyomo.contrib.edi import BlackBoxFunctionModel class Parabola(BlackBoxFunctionModel): - def __init__(self): - + def __init__(self): # Call parent init super().__init__() @@ -606,53 +590,48 @@ def __init__(self): self.description = 'This model evaluates the function: y = x**2' # Append the model inputs - self.inputs.append( - name='x', units='ft' , description='The x variable' - ) + self.inputs.append(name='x', units='ft', description='The x variable') # Append the model outputs self.outputs.append( - name='y', units='ft**2' , description='The y variable' + name='y', units='ft**2', description='The y variable' ) - + # Set the highest available derivative # Should be 1 for most cases but defaults to 0 self.availableDerivative = 1 - def BlackBox(self, x): # The actual function that does things - + def BlackBox(self, x): # The actual function that does things # Convert to correct units and cast to a float - x = pyo.value(units.convert(x,self.inputs['x'].units)) + x = pyo.value(units.convert(x, self.inputs['x'].units)) # Compute y y = x**2 # Compute dy/dx - dydx = 2*x - + dydx = 2 * x + # Add the units to the output y = y * self.outputs['y'].units - + # Add the units to the derivative for output dydx = dydx * self.outputs['y'].units / self.inputs['x'].units - + # Return using the output packing guidelines described in the documentation: # returnVal[0] = output_value # returnVal[1] = jacobian # returnVal[1][0] = derivative_scalarOutput_wrt_0th_input - return y, [dydx] - # END: RuntimeConstraints_Snippet_01 + return y, [dydx] + # END: RuntimeConstraints_Snippet_01 def test_edi_snippet_runtimeconstraints_02(self): - import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import BlackBoxFunctionModel class Parabola(BlackBoxFunctionModel): - def __init__(self): - + def __init__(self): # BEGIN: RuntimeConstraints_Snippet_02 # Call parent init super().__init__() @@ -662,104 +641,95 @@ def __init__(self): self.description = 'This model evaluates the function: y = x**2' # Append the model inputs - self.inputs.append( - name='x', units='ft' , description='The x variable' - ) + self.inputs.append(name='x', units='ft', description='The x variable') # Append the model outputs self.outputs.append( - name='y', units='ft**2' , description='The y variable' + name='y', units='ft**2', description='The y variable' ) - + # BEGIN: RuntimeConstraints_Snippet_05 # Set the highest available derivative # Should be 1 for most cases but defaults to 0 self.availableDerivative = 1 # END: RuntimeConstraints_Snippet_05 - def BlackBox(self, x): # The actual function that does things - + def BlackBox(self, x): # The actual function that does things storeX = x # BEGIN: RuntimeConstraints_Snippet_06 - x = units.convert(x,self.inputs['x'].units) + x = units.convert(x, self.inputs['x'].units) # END: RuntimeConstraints_Snippet_06 x = storeX # BEGIN: RuntimeConstraints_Snippet_07 # Convert to correct units and cast to a float - x = pyo.value(units.convert(x,self.inputs['x'].units)) + x = pyo.value(units.convert(x, self.inputs['x'].units)) # END: RuntimeConstraints_Snippet_07 # Compute y y = x**2 # Compute dy/dx - dydx = 2*x - + dydx = 2 * x + # BEGIN: RuntimeConstraints_Snippet_08 # Add the units to the output y = y * self.outputs['y'].units - + # Add the units to the derivative for output dydx = dydx * self.outputs['y'].units / self.inputs['x'].units # END: RuntimeConstraints_Snippet_08 - + # BEGIN: RuntimeConstraints_Snippet_09 # Return using the output packing guidelines described in the documentation: # returnVal[0] = output_value # returnVal[1] = jacobian # returnVal[1][0] = derivative_scalarOutput_wrt_0th_input - return y, [dydx] + return y, [dydx] # END: RuntimeConstraints_Snippet_09 def test_edi_snippet_runtimeconstraints_03(self): import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The z variable') + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + z = f.Variable(name='z', guess=1.0, units='m^2', description='The z variable') - f.Objective( x + y ) + f.Objective(x + y) class UnitCircle(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super().__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' # BEGIN: RuntimeConstraints_Snippet_03 - self.inputs.append( name = 'x', - units = 'ft' , - description = 'The x variable' ) - self.inputs.append( name = 'y', - units = 'ft' , - description = 'The y variable' ) + self.inputs.append(name='x', units='ft', description='The x variable') + self.inputs.append(name='y', units='ft', description='The y variable') # END: RuntimeConstraints_Snippet_03 - self.outputs.append( name = 'z', - units = 'ft**2', - description = 'Resultant of the unit circle evaluation' ) + self.outputs.append( + name='z', + units='ft**2', + description='Resultant of the unit circle evaluation', + ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, x, y): # The actual function that does things - x = pyo.value(units.convert(x,self.inputs['x'].units)) - y = pyo.value(units.convert(y,self.inputs['y'].units)) + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value(units.convert(x, self.inputs['x'].units)) + y = pyo.value(units.convert(y, self.inputs['y'].units)) z = x**2 + y**2 - dzdx = 2*x - dzdy = 2*y + dzdx = 2 * x + dzdy = 2 * y z = z * self.outputs['z'].units dzdx = dzdx * self.outputs['z'].units / self.inputs['x'].units dzdy = dzdy * self.outputs['z'].units / self.inputs['y'].units - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... - f.ConstraintList( - [ - (z, '==', [x,y], UnitCircle() ) , - z <= 1*units.m**2 - ] - ) + f.ConstraintList([(z, '==', [x, y], UnitCircle()), z <= 1 * units.m**2]) def test_edi_snippet_runtimeconstraints_04(self): import numpy as np @@ -773,139 +743,113 @@ def __init__(self): # Set up all the attributes by calling Model.__init__ super().__init__() - #Setup Inputs - self.inputs.append( 'x', '', 'X Variable') - self.inputs.append( 'y', '', 'Y Variable') + # Setup Inputs + self.inputs.append('x', '', 'X Variable') + self.inputs.append('y', '', 'Y Variable') # BEGIN: RuntimeConstraints_Snippet_04 - #Setup Outputs - self.outputs.append( 'u', '', 'U Variable') - self.outputs.append( 'v', '', 'V Variable') + # Setup Outputs + self.outputs.append('u', '', 'U Variable') + self.outputs.append('v', '', 'V Variable') # END: RuntimeConstraints_Snippet_04 - #Simple model description + # Simple model description self.description = 'This model is a pass through)' self.availableDerivative = 1 - #standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) + # standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) def BlackBox(self, *args, **kwargs): runCases, returnMode, extras = self.parseInputs(*args, **kwargs) - x = [ self.pyomo_value(runCases[i]['x']) for i in range(0,len(runCases)) ] - y = [ self.pyomo_value(runCases[i]['x']) for i in range(0,len(runCases)) ] - - u = [] + x = [ + self.pyomo_value(runCases[i]['x']) for i in range(0, len(runCases)) + ] + y = [ + self.pyomo_value(runCases[i]['x']) for i in range(0, len(runCases)) + ] + + u = [] dudx = [] dudy = [] - v = [] + v = [] dvdx = [] dvdy = [] for xval in x: - u.append(xval*units.dimensionless) - dudx.append(1.0*units.dimensionless) - dudy.append(0.0*units.dimensionless) - + u.append(xval * units.dimensionless) + dudx.append(1.0 * units.dimensionless) + dudy.append(0.0 * units.dimensionless) + for yval in y: - v.append(yval*units.dimensionless) - dvdx.append(0.0*units.dimensionless) - dvdy.append(1.0*units.dimensionless) - + v.append(yval * units.dimensionless) + dvdx.append(0.0 * units.dimensionless) + dvdy.append(1.0 * units.dimensionless) + if returnMode < 0: - returnMode = -1*(returnMode + 1) + returnMode = -1 * (returnMode + 1) if returnMode == 0: return [u[0], v[0]] if returnMode == 1: - return [u[0], v[0]] , [[dudx[0], dudy[0]], [dvdx[0], dvdy[0]]] + return [u[0], v[0]], [[dudx[0], dudy[0]], [dvdx[0], dvdy[0]]] else: if returnMode == 0: opt = [] - for i in range(0,len(y)): - opt.append([ u[i], v[i] ]) + for i in range(0, len(y)): + opt.append([u[i], v[i]]) return opt if returnMode == 1: opt = [] - for i in range(0,len(y)): - opt.append([ [u[i], v[i]], [ [ dudx[i], dudy[i]], [dvdx[i], dvdy[i]] ] ]) + for i in range(0, len(y)): + opt.append( + [[u[i], v[i]], [[dudx[i], dudy[i]], [dvdx[i], dvdy[i]]]] + ) return opt bb = PassThrough() - bbo = bb.BlackBox(1.0,1.0) - bbo = bb.BlackBox({'x':np.linspace(0,10,11),'y':np.linspace(0,10,11)}) + bbo = bb.BlackBox(1.0, 1.0) + bbo = bb.BlackBox({'x': np.linspace(0, 10, 11), 'y': np.linspace(0, 10, 11)}) def test_edi_snippet_runtimeconstraints_10(self): # BEGIN: RuntimeConstraints_Snippet_10 import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The z variable') + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + z = f.Variable(name='z', guess=1.0, units='m^2', description='The z variable') - f.Objective( x + y ) + f.Objective(x + y) class UnitCircle(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super().__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append( name = 'x', - units = 'ft' , - description = 'The x variable' ) - self.inputs.append( name = 'y', - units = 'ft' , - description = 'The y variable' ) - self.outputs.append( name = 'z', - units = 'ft**2', - description = 'Resultant of the unit circle evaluation' ) + self.inputs.append(name='x', units='ft', description='The x variable') + self.inputs.append(name='y', units='ft', description='The y variable') + self.outputs.append( + name='z', + units='ft**2', + description='Resultant of the unit circle evaluation', + ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, x, y): # The actual function that does things - x = pyo.value(units.convert(x,self.inputs['x'].units)) - y = pyo.value(units.convert(y,self.inputs['y'].units)) + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value(units.convert(x, self.inputs['x'].units)) + y = pyo.value(units.convert(y, self.inputs['y'].units)) z = x**2 + y**2 - dzdx = 2*x - dzdy = 2*y + dzdx = 2 * x + dzdy = 2 * y z = z * self.outputs['z'].units dzdx = dzdx * self.outputs['z'].units / self.inputs['x'].units dzdy = dzdy * self.outputs['z'].units / self.inputs['y'].units - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... - f.ConstraintList( - [ - (z, '==', [x,y], UnitCircle() ) , - z <= 1*units.m**2 - ] - ) + f.ConstraintList([(z, '==', [x, y], UnitCircle()), z <= 1 * units.m**2]) # END: RuntimeConstraints_Snippet_10 - - - - - - - - - - - - - - - - - - - - - - - - - - def test_edi_snippet_advancedRTC_01(self): # BEGIN: AdvancedRTC_Snippet_01 import numpy as np @@ -917,36 +861,39 @@ class SignomialTest(BlackBoxFunctionModel): def __init__(self): # Set up all the attributes by calling Model.__init__ super().__init__() - - #Setup Inputs - self.inputs.append( 'x', '', 'Independent Variable') - - #Setup Outputs - self.outputs.append( 'y', '', 'Dependent Variable') - - #Simple model description - self.description = ( 'This model evaluates the '+ - 'function: max([-6*x-6, x**4-3*x**2])' ) - + + # Setup Inputs + self.inputs.append('x', '', 'Independent Variable') + + # Setup Outputs + self.outputs.append('y', '', 'Dependent Variable') + + # Simple model description + self.description = ( + 'This model evaluates the ' + 'function: max([-6*x-6, x**4-3*x**2])' + ) + self.availableDerivative = 1 - + def BlackBox(self, *args, **kwargs): runCases, returnMode, extras = self.parseInputs(*args, **kwargs) - x = np.array([ pyo.value(runCases[i]['x']) for i in range(0,len(runCases)) ]) - - y = np.maximum(-6*x-6, x**4-3*x**2) - dydx = 4*x**3 - 6*x - ddy_ddx = 12*x**2 - 6 - gradientSwitch = -6*x-6 > x**4-3*x**2 + x = np.array( + [pyo.value(runCases[i]['x']) for i in range(0, len(runCases))] + ) + + y = np.maximum(-6 * x - 6, x**4 - 3 * x**2) + dydx = 4 * x**3 - 6 * x + ddy_ddx = 12 * x**2 - 6 + gradientSwitch = -6 * x - 6 > x**4 - 3 * x**2 dydx[gradientSwitch] = -6 ddy_ddx[gradientSwitch] = 0 - - y = [ yval*units.dimensionless for yval in y ] - dydx = [dydx[i] * units.dimensionless for i in range(0,len(dydx))] - + + y = [yval * units.dimensionless for yval in y] + dydx = [dydx[i] * units.dimensionless for i in range(0, len(dydx))] + if returnMode < 0: - returnMode = -1*(returnMode + 1) + returnMode = -1 * (returnMode + 1) if returnMode == 0: return y[0] if returnMode == 1: @@ -954,34 +901,33 @@ def BlackBox(self, *args, **kwargs): else: if returnMode == 0: opt = [] - for i in range(0,len(y)): - opt.append([ y[i] ]) + for i in range(0, len(y)): + opt.append([y[i]]) return opt if returnMode == 1: opt = [] - for i in range(0,len(y)): - opt.append([ [y[i]], [ [[dydx[i]]] ] ]) + for i in range(0, len(y)): + opt.append([[y[i]], [[[dydx[i]]]]]) return opt s = SignomialTest() - ivals = [[x] for x in np.linspace(-2,2,11)] + ivals = [[x] for x in np.linspace(-2, 2, 11)] # How the black box may be called using EDI - bbo = s.BlackBox(**{'x':0.5}) - bbo = s.BlackBox({'x':0.5}) - bbo = s.BlackBox(**{'x':0.5, 'optn':True}) + bbo = s.BlackBox(**{'x': 0.5}) + bbo = s.BlackBox({'x': 0.5}) + bbo = s.BlackBox(**{'x': 0.5, 'optn': True}) # Additional options available with parseInputs bbo = s.BlackBox(*[0.5], **{'optn1': True, 'optn2': False}) - bbo = s.BlackBox(*[0.5,True], **{'optn': False}) - bbo = s.BlackBox({'x':[x for x in np.linspace(-2,2,11)]}) - bbo = s.BlackBox([{'x':x} for x in np.linspace(-2,2,11)]) - bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)]) - bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)], True, optn=False) - bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)], optn1=True, optn2=False) + bbo = s.BlackBox(*[0.5, True], **{'optn': False}) + bbo = s.BlackBox({'x': [x for x in np.linspace(-2, 2, 11)]}) + bbo = s.BlackBox([{'x': x} for x in np.linspace(-2, 2, 11)]) + bbo = s.BlackBox([[x] for x in np.linspace(-2, 2, 11)]) + bbo = s.BlackBox([[x] for x in np.linspace(-2, 2, 11)], True, optn=False) + bbo = s.BlackBox([[x] for x in np.linspace(-2, 2, 11)], optn1=True, optn2=False) # END: AdvancedRTC_Snippet_01 - def test_edi_snippet_advancedRTC_02(self): import numpy as np import pyomo.environ as pyo @@ -992,38 +938,41 @@ class SignomialTest(BlackBoxFunctionModel): def __init__(self): # Set up all the attributes by calling Model.__init__ super().__init__() - - #Setup Inputs - self.inputs.append( 'x', '', 'Independent Variable') - - #Setup Outputs - self.outputs.append( 'y', '', 'Dependent Variable') - - #Simple model description - self.description = ( 'This model evaluates the '+ - 'function: max([-6*x-6, x**4-3*x**2])' ) - + + # Setup Inputs + self.inputs.append('x', '', 'Independent Variable') + + # Setup Outputs + self.outputs.append('y', '', 'Dependent Variable') + + # Simple model description + self.description = ( + 'This model evaluates the ' + 'function: max([-6*x-6, x**4-3*x**2])' + ) + self.availableDerivative = 1 # BEGIN: AdvancedRTC_Snippet_02 def BlackBox(self, *args, **kwargs): runCases, returnMode, extras = self.parseInputs(*args, **kwargs) - # END: AdvancedRTC_Snippet_02 - - x = np.array([ pyo.value(runCases[i]['x']) for i in range(0,len(runCases)) ]) - - y = np.maximum(-6*x-6, x**4-3*x**2) - dydx = 4*x**3 - 6*x - ddy_ddx = 12*x**2 - 6 - gradientSwitch = -6*x-6 > x**4-3*x**2 + # END: AdvancedRTC_Snippet_02 + + x = np.array( + [pyo.value(runCases[i]['x']) for i in range(0, len(runCases))] + ) + + y = np.maximum(-6 * x - 6, x**4 - 3 * x**2) + dydx = 4 * x**3 - 6 * x + ddy_ddx = 12 * x**2 - 6 + gradientSwitch = -6 * x - 6 > x**4 - 3 * x**2 dydx[gradientSwitch] = -6 ddy_ddx[gradientSwitch] = 0 - - y = [ yval*units.dimensionless for yval in y ] - dydx = [dydx[i] * units.dimensionless for i in range(0,len(dydx))] - + + y = [yval * units.dimensionless for yval in y] + dydx = [dydx[i] * units.dimensionless for i in range(0, len(dydx))] + if returnMode < 0: - returnMode = -1*(returnMode + 1) + returnMode = -1 * (returnMode + 1) if returnMode == 0: return y[0] if returnMode == 1: @@ -1031,36 +980,32 @@ def BlackBox(self, *args, **kwargs): else: if returnMode == 0: opt = [] - for i in range(0,len(y)): - opt.append([ y[i] ]) + for i in range(0, len(y)): + opt.append([y[i]]) return opt if returnMode == 1: opt = [] - for i in range(0,len(y)): - opt.append([ [y[i]], [ [[dydx[i]]] ] ]) + for i in range(0, len(y)): + opt.append([[y[i]], [[[dydx[i]]]]]) return opt s = SignomialTest() - ivals = [[x] for x in np.linspace(-2,2,11)] + ivals = [[x] for x in np.linspace(-2, 2, 11)] # How the black box may be called using EDI - bbo = s.BlackBox(**{'x':0.5}) - bbo = s.BlackBox({'x':0.5}) - bbo = s.BlackBox(**{'x':0.5, 'optn':True}) + bbo = s.BlackBox(**{'x': 0.5}) + bbo = s.BlackBox({'x': 0.5}) + bbo = s.BlackBox(**{'x': 0.5, 'optn': True}) # Additional options available with parseInputs bbo = s.BlackBox(*[0.5], **{'optn1': True, 'optn2': False}) - bbo = s.BlackBox(*[0.5,True], **{'optn': False}) - bbo = s.BlackBox({'x':[x for x in np.linspace(-2,2,11)]}) - bbo = s.BlackBox([{'x':x} for x in np.linspace(-2,2,11)]) - bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)]) - bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)], True, optn=False) - bbo = s.BlackBox([ [x] for x in np.linspace(-2,2,11)], optn1=True, optn2=False) - - - - + bbo = s.BlackBox(*[0.5, True], **{'optn': False}) + bbo = s.BlackBox({'x': [x for x in np.linspace(-2, 2, 11)]}) + bbo = s.BlackBox([{'x': x} for x in np.linspace(-2, 2, 11)]) + bbo = s.BlackBox([[x] for x in np.linspace(-2, 2, 11)]) + bbo = s.BlackBox([[x] for x in np.linspace(-2, 2, 11)], True, optn=False) + bbo = s.BlackBox([[x] for x in np.linspace(-2, 2, 11)], optn1=True, optn2=False) if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/pyomo/contrib/edi/tests/test_examples.py b/pyomo/contrib/edi/tests/test_examples.py index 8eb496f74f5..f0f0a01bca8 100644 --- a/pyomo/contrib/edi/tests/test_examples.py +++ b/pyomo/contrib/edi/tests/test_examples.py @@ -19,45 +19,50 @@ import pyomo.environ as pyo from pyomo.common.dependencies import attempt_import -from pyomo.core.base.units_container import ( - pint_available, -) +testIndex = 0 +import importlib + +from pyomo.core.base.units_container import pint_available from pyomo.common.dependencies import numpy, numpy_available from pyomo.common.dependencies import scipy, scipy_available -egb, egb_available = attempt_import("pyomo.contrib.pynumero.interfaces.external_grey_box") + +egb, egb_available = attempt_import( + "pyomo.contrib.pynumero.interfaces.external_grey_box" +) if numpy_available: import numpy as np -@unittest.skipIf(not egb_available, 'Testing pyomo.contrib.edi requires pynumero external grey boxes') + +@unittest.skipIf( + not egb_available, 'Testing pyomo.contrib.edi requires pynumero external grey boxes' +) @unittest.skipIf(not numpy_available, 'Testing pyomo.contrib.edi requires numpy') @unittest.skipIf(not scipy_available, 'Testing pyomo.contrib.edi requires scipy') -@unittest.skipIf(not pint_available, 'Testing units requires pint') +@unittest.skipIf(not pint_available, 'Testing units requires pint') class EDIExamples(unittest.TestCase): def test_edi_example_placeholder(self): "A placeholder" pass + def create_new(filename): def t_function(self): importName = filename[0:-3] # filename = ".."+filename try: - importlib.import_module("pyomo.contrib.edi.examples."+importName) + importlib.import_module("pyomo.contrib.edi.examples." + importName) except: - self.fail("This example is failing: %s"%(filename)) - return t_function + self.fail("This example is failing: %s" % (filename)) + return t_function -pythonFileList = [ - "readme_example.py", - "aircraft_gp.py" -] +pythonFileList = ["readme_example.py", "aircraft_gp.py"] for filename in pythonFileList: - testName = 'test_DocumentationExample_%d'%(testIndex) + testName = 'test_DocumentationExample_%d' % (testIndex) testIndex += 1 t_Function = create_new(filename) if pint_available: @@ -66,4 +71,3 @@ def t_function(self): if __name__ == '__main__': unittest.main() - diff --git a/pyomo/contrib/edi/tests/test_formulation.py b/pyomo/contrib/edi/tests/test_formulation.py index 2ae33eac4f2..91346d722c3 100644 --- a/pyomo/contrib/edi/tests/test_formulation.py +++ b/pyomo/contrib/edi/tests/test_formulation.py @@ -19,9 +19,8 @@ import pyomo.environ as pyo from pyomo.common.dependencies import attempt_import -from pyomo.core.base.units_container import ( - pint_available, -) +from pyomo.core.base.units_container import pint_available + @unittest.skipIf(not pint_available, 'Testing pyomo.contrib.edi requires pint') class TestEDIFormulation(unittest.TestCase): @@ -29,22 +28,22 @@ def test_edi_formulation_init(self): "Tests that a formulation initializes to the correct type and has proper data" from pyomo.environ import ConcreteModel from pyomo.contrib.edi import Formulation + f = Formulation() self.assertIsInstance(f, Formulation) self.assertIsInstance(f, ConcreteModel) - self.assertEqual(f._objective_counter , 0 ) - self.assertEqual(f._constraint_counter , 0 ) - self.assertEqual(f._variable_keys , [] ) - self.assertEqual(f._constant_keys , [] ) - self.assertEqual(f._objective_keys , [] ) - self.assertEqual(f._runtimeObjective_keys , [] ) - self.assertEqual(f._objective_keys , [] ) - self.assertEqual(f._runtimeConstraint_keys , [] ) - self.assertEqual(f._constraint_keys , [] ) - self.assertEqual(f._allConstraint_keys , [] ) - + self.assertEqual(f._objective_counter, 0) + self.assertEqual(f._constraint_counter, 0) + self.assertEqual(f._variable_keys, []) + self.assertEqual(f._constant_keys, []) + self.assertEqual(f._objective_keys, []) + self.assertEqual(f._runtimeObjective_keys, []) + self.assertEqual(f._objective_keys, []) + self.assertEqual(f._runtimeConstraint_keys, []) + self.assertEqual(f._constraint_keys, []) + self.assertEqual(f._allConstraint_keys, []) def test_edi_formulation_variable(self): "Tests the variable constructor in edi.formulation" @@ -54,26 +53,182 @@ def test_edi_formulation_variable(self): f = Formulation() - x1 = f.Variable(name = 'x1', guess = 1.0, units = 'm' , description = 'The x variable', size = None, bounds=None, domain=None ) - self.assertRaises(RuntimeError,f.Variable,*('x1', 1.0, 'm')) + x1 = f.Variable( + name='x1', + guess=1.0, + units='m', + description='The x variable', + size=None, + bounds=None, + domain=None, + ) + self.assertRaises(RuntimeError, f.Variable, *('x1', 1.0, 'm')) x2 = f.Variable('x2', 1.0, 'm') - x3 = f.Variable('x3', 1.0, 'm' , 'The x variable', None, None, None ) - x4 = f.Variable(name = 'x4', guess = 1.0, units = 'm' , description = 'The x variable', size = None, bounds=None, domain=PositiveReals ) - self.assertRaises(RuntimeError, f.Variable, **{'name':'x5', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': None, 'bounds':None, 'domain':"error" }) - - x6 = f.Variable(name = 'x6', guess = 1.0, units = 'm' , description = 'The x variable', size = 0, bounds=None, domain=None ) - x7 = f.Variable(name = 'x7', guess = 1.0, units = 'm' , description = 'The x variable', size = 5, bounds=None, domain=None ) - self.assertRaises(ValueError, f.Variable, **{'name':'x8', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': 'error', 'bounds':None, 'domain':None }) - x9 = f.Variable(name = 'x9', guess = 1.0, units = 'm' , description = 'The x variable', size = [2,2], bounds=None, domain=None ) - self.assertRaises(ValueError, f.Variable, **{'name':'x10', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': ['2','2'], 'bounds':None, 'domain':None }) - self.assertRaises(ValueError, f.Variable, **{'name':'x11', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': [2,1], 'bounds':None, 'domain':None }) - - x12 = f.Variable(name = 'x12', guess = 1.0, units = 'm' , description = 'The x variable', size = None, bounds=[-10,10], domain=None ) - self.assertRaises(ValueError, f.Variable, **{'name':'x13', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': None, 'bounds':[10,-10], 'domain':None }) - self.assertRaises(ValueError, f.Variable, **{'name':'x14', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': None, 'bounds':["-10","10"], 'domain':None }) - self.assertRaises(ValueError, f.Variable, **{'name':'x15', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': None, 'bounds':[1,2,3], 'domain':None }) - self.assertRaises(ValueError, f.Variable, **{'name':'x16', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': None, 'bounds':"error", 'domain':None }) - self.assertRaises(ValueError, f.Variable, **{'name':'x17', 'guess':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': None, 'bounds':[0,"10"], 'domain':None }) + x3 = f.Variable('x3', 1.0, 'm', 'The x variable', None, None, None) + x4 = f.Variable( + name='x4', + guess=1.0, + units='m', + description='The x variable', + size=None, + bounds=None, + domain=PositiveReals, + ) + self.assertRaises( + RuntimeError, + f.Variable, + **{ + 'name': 'x5', + 'guess': 1.0, + 'units': 'm', + 'description': 'The x variable', + 'size': None, + 'bounds': None, + 'domain': "error", + } + ) + + x6 = f.Variable( + name='x6', + guess=1.0, + units='m', + description='The x variable', + size=0, + bounds=None, + domain=None, + ) + x7 = f.Variable( + name='x7', + guess=1.0, + units='m', + description='The x variable', + size=5, + bounds=None, + domain=None, + ) + self.assertRaises( + ValueError, + f.Variable, + **{ + 'name': 'x8', + 'guess': 1.0, + 'units': 'm', + 'description': 'The x variable', + 'size': 'error', + 'bounds': None, + 'domain': None, + } + ) + x9 = f.Variable( + name='x9', + guess=1.0, + units='m', + description='The x variable', + size=[2, 2], + bounds=None, + domain=None, + ) + self.assertRaises( + ValueError, + f.Variable, + **{ + 'name': 'x10', + 'guess': 1.0, + 'units': 'm', + 'description': 'The x variable', + 'size': ['2', '2'], + 'bounds': None, + 'domain': None, + } + ) + self.assertRaises( + ValueError, + f.Variable, + **{ + 'name': 'x11', + 'guess': 1.0, + 'units': 'm', + 'description': 'The x variable', + 'size': [2, 1], + 'bounds': None, + 'domain': None, + } + ) + + x12 = f.Variable( + name='x12', + guess=1.0, + units='m', + description='The x variable', + size=None, + bounds=[-10, 10], + domain=None, + ) + self.assertRaises( + ValueError, + f.Variable, + **{ + 'name': 'x13', + 'guess': 1.0, + 'units': 'm', + 'description': 'The x variable', + 'size': None, + 'bounds': [10, -10], + 'domain': None, + } + ) + self.assertRaises( + ValueError, + f.Variable, + **{ + 'name': 'x14', + 'guess': 1.0, + 'units': 'm', + 'description': 'The x variable', + 'size': None, + 'bounds': ["-10", "10"], + 'domain': None, + } + ) + self.assertRaises( + ValueError, + f.Variable, + **{ + 'name': 'x15', + 'guess': 1.0, + 'units': 'm', + 'description': 'The x variable', + 'size': None, + 'bounds': [1, 2, 3], + 'domain': None, + } + ) + self.assertRaises( + ValueError, + f.Variable, + **{ + 'name': 'x16', + 'guess': 1.0, + 'units': 'm', + 'description': 'The x variable', + 'size': None, + 'bounds': "error", + 'domain': None, + } + ) + self.assertRaises( + ValueError, + f.Variable, + **{ + 'name': 'x17', + 'guess': 1.0, + 'units': 'm', + 'description': 'The x variable', + 'size': None, + 'bounds': [0, "10"], + 'domain': None, + } + ) # verifies alternate unit construction x18 = f.Variable('x18', 1.0, pyo.units.m) @@ -86,20 +241,98 @@ def test_edi_formulation_constant(self): f = Formulation() - c1 = f.Constant(name = 'c1', value = 1.0, units = 'm', description = 'A constant c', size = None, within = None) - self.assertRaises(RuntimeError,f.Constant,*('c1', 1.0, 'm')) + c1 = f.Constant( + name='c1', + value=1.0, + units='m', + description='A constant c', + size=None, + within=None, + ) + self.assertRaises(RuntimeError, f.Constant, *('c1', 1.0, 'm')) c2 = f.Constant('c2', 1.0, 'm') c3 = f.Constant('c3', 1.0, 'm', 'A constant c', None, None) - c4 = f.Constant(name = 'c4', value = 1.0, units = 'm' , description = 'A constant c', size = None, within=PositiveReals ) - self.assertRaises(RuntimeError, f.Constant, **{'name':'c5', 'value':1.0 , 'units' :'m' , 'description': 'The x variable', 'size': None, 'within':"error" }) - - c6 = f.Constant(name = 'c6', value = 1.0, units = 'm' , description = 'A constant c', size = 0, within=None ) - c7 = f.Constant(name = 'c7', value = 1.0, units = 'm' , description = 'A constant c', size = 5, within=None ) - self.assertRaises(ValueError, f.Constant, **{'name':'c8', 'value':1.0 , 'units' :'m' , 'description': 'A constant c', 'size': 'error', 'within':None }) - c9 = f.Constant(name = 'c9', value = 1.0, units = 'm' , description = 'A constant c', size = [2,2], within=None ) - self.assertRaises(ValueError, f.Constant, **{'name':'c10', 'value':1.0 , 'units' :'m' , 'description': 'A constant c', 'size': ['2','2'], 'within':None }) - self.assertRaises(ValueError, f.Constant, **{'name':'c11', 'value':1.0 , 'units' :'m' , 'description': 'A constant c', 'size': [2,1], 'within':None }) + c4 = f.Constant( + name='c4', + value=1.0, + units='m', + description='A constant c', + size=None, + within=PositiveReals, + ) + self.assertRaises( + RuntimeError, + f.Constant, + **{ + 'name': 'c5', + 'value': 1.0, + 'units': 'm', + 'description': 'The x variable', + 'size': None, + 'within': "error", + } + ) + c6 = f.Constant( + name='c6', + value=1.0, + units='m', + description='A constant c', + size=0, + within=None, + ) + c7 = f.Constant( + name='c7', + value=1.0, + units='m', + description='A constant c', + size=5, + within=None, + ) + self.assertRaises( + ValueError, + f.Constant, + **{ + 'name': 'c8', + 'value': 1.0, + 'units': 'm', + 'description': 'A constant c', + 'size': 'error', + 'within': None, + } + ) + c9 = f.Constant( + name='c9', + value=1.0, + units='m', + description='A constant c', + size=[2, 2], + within=None, + ) + self.assertRaises( + ValueError, + f.Constant, + **{ + 'name': 'c10', + 'value': 1.0, + 'units': 'm', + 'description': 'A constant c', + 'size': ['2', '2'], + 'within': None, + } + ) + self.assertRaises( + ValueError, + f.Constant, + **{ + 'name': 'c11', + 'value': 1.0, + 'units': 'm', + 'description': 'A constant c', + 'size': [2, 1], + 'within': None, + } + ) def test_edi_formulation_objective(self): "Tests the objective constructor in edi.formulation" @@ -108,9 +341,9 @@ def test_edi_formulation_objective(self): from pyomo.contrib.edi import Formulation f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - f.Objective( x + y ) + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + f.Objective(x + y) def test_edi_formulation_runtimeobjective(self): "Tests the runtime objective constructor in edi.formulation" @@ -122,317 +355,436 @@ def test_edi_formulation_constraint(self): import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - f.Objective(x+y) - f.Constraint(x+y <= 1.0*units.m) + f = Formulation() + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + f.Objective(x + y) + f.Constraint(x + y <= 1.0 * units.m) def test_edi_formulation_runtimeconstraint_tuple(self): "Tests the runtime constraint constructor in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The unit circle output') - c = f.Constant(name = 'c', value = 1.0, units = '', description = 'A constant c', size = 2) - f.Objective( x + y ) + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + z = f.Variable( + name='z', guess=1.0, units='m^2', description='The unit circle output' + ) + c = f.Constant( + name='c', value=1.0, units='', description='A constant c', size=2 + ) + f.Objective(x + y) + class UnitCircle(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super(UnitCircle, self).__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append(name = 'x', units = 'ft' , description = 'The x variable') - self.inputs.append(name = 'y', units = 'ft' , description = 'The y variable') - self.outputs.append(name = 'z', units = 'ft**2', description = 'Resultant of the unit circle evaluation') + self.inputs.append(name='x', units='ft', description='The x variable') + self.inputs.append(name='y', units='ft', description='The y variable') + self.outputs.append( + name='z', + units='ft**2', + description='Resultant of the unit circle evaluation', + ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, x, y): # The actual function that does things - x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float - y = pyo.value(units.convert(y,self.inputs[1].units)) # Converts to correct units then casts to float - z = x**2 + y**2 # Compute z - dzdx = 2*x # Compute dz/dx - dzdy = 2*y # Compute dz/dy + + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value( + units.convert(x, self.inputs[0].units) + ) # Converts to correct units then casts to float + y = pyo.value( + units.convert(y, self.inputs[1].units) + ) # Converts to correct units then casts to float + z = x**2 + y**2 # Compute z + dzdx = 2 * x # Compute dz/dx + dzdy = 2 * y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... - f.Constraint( z <= 1*units.m**2 ) + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... - f.RuntimeConstraint( *( z, '==', [x,y], UnitCircle() ) ) + f.Constraint(z <= 1 * units.m**2) + + f.RuntimeConstraint(*(z, '==', [x, y], UnitCircle())) def test_edi_formulation_runtimeconstraint_list(self): "Tests the runtime constraint constructor in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The unit circle output') - c = f.Constant(name = 'c', value = 1.0, units = '', description = 'A constant c', size = 2) - f.Objective( x + y ) + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + z = f.Variable( + name='z', guess=1.0, units='m^2', description='The unit circle output' + ) + c = f.Constant( + name='c', value=1.0, units='', description='A constant c', size=2 + ) + f.Objective(x + y) + class UnitCircle(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super(UnitCircle, self).__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append(name = 'x', units = 'ft' , description = 'The x variable') - self.inputs.append(name = 'y', units = 'ft' , description = 'The y variable') - self.outputs.append(name = 'z', units = 'ft**2', description = 'Resultant of the unit circle evaluation') + self.inputs.append(name='x', units='ft', description='The x variable') + self.inputs.append(name='y', units='ft', description='The y variable') + self.outputs.append( + name='z', + units='ft**2', + description='Resultant of the unit circle evaluation', + ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, x, y): # The actual function that does things - x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float - y = pyo.value(units.convert(y,self.inputs[1].units)) # Converts to correct units then casts to float - z = x**2 + y**2 # Compute z - dzdx = 2*x # Compute dz/dx - dzdy = 2*y # Compute dz/dy + + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value( + units.convert(x, self.inputs[0].units) + ) # Converts to correct units then casts to float + y = pyo.value( + units.convert(y, self.inputs[1].units) + ) # Converts to correct units then casts to float + z = x**2 + y**2 # Compute z + dzdx = 2 * x # Compute dz/dx + dzdy = 2 * y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... - f.Constraint( z <= 1*units.m**2 ) - - f.RuntimeConstraint( *[ [z], ['=='], [x,y], UnitCircle() ] ) + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + + f.Constraint(z <= 1 * units.m**2) + f.RuntimeConstraint(*[[z], ['=='], [x, y], UnitCircle()]) def test_edi_formulation_runtimeconstraint_dict(self): "Tests the runtime constraint constructor in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The unit circle output') - c = f.Constant(name = 'c', value = 1.0, units = '', description = 'A constant c', size = 2) - f.Objective( x + y ) + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + z = f.Variable( + name='z', guess=1.0, units='m^2', description='The unit circle output' + ) + c = f.Constant( + name='c', value=1.0, units='', description='A constant c', size=2 + ) + f.Objective(x + y) + class UnitCircle(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super(UnitCircle, self).__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append(name = 'x', units = 'ft' , description = 'The x variable') - self.inputs.append(name = 'y', units = 'ft' , description = 'The y variable') - self.outputs.append(name = 'z', units = 'ft**2', description = 'Resultant of the unit circle evaluation') + self.inputs.append(name='x', units='ft', description='The x variable') + self.inputs.append(name='y', units='ft', description='The y variable') + self.outputs.append( + name='z', + units='ft**2', + description='Resultant of the unit circle evaluation', + ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, x, y): # The actual function that does things - x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float - y = pyo.value(units.convert(y,self.inputs[1].units)) # Converts to correct units then casts to float - z = x**2 + y**2 # Compute z - dzdx = 2*x # Compute dz/dx - dzdy = 2*y # Compute dz/dy + + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value( + units.convert(x, self.inputs[0].units) + ) # Converts to correct units then casts to float + y = pyo.value( + units.convert(y, self.inputs[1].units) + ) # Converts to correct units then casts to float + z = x**2 + y**2 # Compute z + dzdx = 2 * x # Compute dz/dx + dzdy = 2 * y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... - f.Constraint( z <= 1*units.m**2 ) - - f.RuntimeConstraint( **{ 'outputs':z, 'operators':'==', 'inputs':[x,y], 'black_box':UnitCircle() } ) - + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + + f.Constraint(z <= 1 * units.m**2) + + f.RuntimeConstraint( + **{ + 'outputs': z, + 'operators': '==', + 'inputs': [x, y], + 'black_box': UnitCircle(), + } + ) def test_edi_formulation_constraintlist_1(self): "Tests the constraint list constructor in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The unit circle output') - c = f.Constant(name = 'c', value = 1.0, units = '', description = 'A constant c', size = 2) - f.Objective( x + y ) + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + z = f.Variable( + name='z', guess=1.0, units='m^2', description='The unit circle output' + ) + c = f.Constant( + name='c', value=1.0, units='', description='A constant c', size=2 + ) + f.Objective(x + y) class UnitCircle(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super(UnitCircle, self).__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append(name = 'x', units = 'ft' , description = 'The x variable') - self.inputs.append(name = 'y', units = 'ft' , description = 'The y variable') - self.outputs.append(name = 'z', units = 'ft**2', description = 'Resultant of the unit circle evaluation') + self.inputs.append(name='x', units='ft', description='The x variable') + self.inputs.append(name='y', units='ft', description='The y variable') + self.outputs.append( + name='z', + units='ft**2', + description='Resultant of the unit circle evaluation', + ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, x, y): # The actual function that does things - x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float - y = pyo.value(units.convert(y,self.inputs[1].units)) # Converts to correct units then casts to float - z = x**2 + y**2 # Compute z - dzdx = 2*x # Compute dz/dx - dzdy = 2*y # Compute dz/dy + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value( + units.convert(x, self.inputs[0].units) + ) # Converts to correct units then casts to float + y = pyo.value( + units.convert(y, self.inputs[1].units) + ) # Converts to correct units then casts to float + z = x**2 + y**2 # Compute z + dzdx = 2 * x # Compute dz/dx + dzdy = 2 * y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... - - f.ConstraintList( - [ - (z, '==', [x,y], UnitCircle() ) , - z <= 1*units.m**2 - ] - ) + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... - cl = f.get_constraints() + f.ConstraintList([(z, '==', [x, y], UnitCircle()), z <= 1 * units.m**2]) - self.assertTrue(len(cl)==2) + cl = f.get_constraints() + self.assertTrue(len(cl) == 2) def test_edi_formulation_constraintlist_2(self): "Tests the constraint list constructor in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm**2' , description = 'The y variable') - f.Objective( y ) + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m**2', description='The y variable') + f.Objective(y) class Parabola(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super(Parabola, self).__init__() self.description = 'This model evaluates the function: y = x**2' - self.inputs.append(name = 'x', units = 'ft' , description = 'The x variable') - self.outputs.append(name = 'y', units = 'ft**2' , description = 'The y variable') + self.inputs.append(name='x', units='ft', description='The x variable') + self.outputs.append( + name='y', units='ft**2', description='The y variable' + ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, x): # The actual function that does things - x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float - y = x**2 # Compute y - dydx = 2*x # Compute dy/dx + def BlackBox(self, x): # The actual function that does things + x = pyo.value( + units.convert(x, self.inputs[0].units) + ) # Converts to correct units then casts to float + y = x**2 # Compute y + dydx = 2 * x # Compute dy/dx y *= units.ft**2 - dydx *= units.ft # units.ft**2 / units.ft - return y, [dydx] # return z, grad(z), hess(z)... + dydx *= units.ft # units.ft**2 / units.ft + return y, [dydx] # return z, grad(z), hess(z)... f.ConstraintList( - [ - {'outputs':y, 'operators':'==', 'inputs':x, 'black_box':Parabola() }, - ] + [{'outputs': y, 'operators': '==', 'inputs': x, 'black_box': Parabola()}] ) - cl = f.get_constraints() - - self.assertTrue(len(cl)==1) + cl = f.get_constraints() + self.assertTrue(len(cl) == 1) def test_edi_formulation_constraintlist_3(self): "Tests the constraint list constructor in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable', size=3) - y = f.Variable(name = 'y', guess = 1.0, units = 'm**2' , description = 'The y variable', size=3) - f.Objective( y[0] + y[1] + y[2] ) + x = f.Variable( + name='x', guess=1.0, units='m', description='The x variable', size=3 + ) + y = f.Variable( + name='y', guess=1.0, units='m**2', description='The y variable', size=3 + ) + f.Objective(y[0] + y[1] + y[2]) + class Parabola(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super(Parabola, self).__init__() self.description = 'This model evaluates the function: y = x**2' - self.inputs.append(name = 'x', size = 3, units = 'ft' , description = 'The x variable') - self.outputs.append(name = 'y', size = 3, units = 'ft**2' , description = 'The y variable') + self.inputs.append( + name='x', size=3, units='ft', description='The x variable' + ) + self.outputs.append( + name='y', size=3, units='ft**2', description='The y variable' + ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(*args, **kwargs):# The actual function that does things + def BlackBox(*args, **kwargs): # The actual function that does things args = list(args) self = args.pop(0) - runCases, returnMode, remainingKwargs = self.parseInputs(*args, **kwargs) - + runCases, returnMode, remainingKwargs = self.parseInputs( + *args, **kwargs + ) + x = self.sanitizeInputs(runCases[0]['x']) x = np.array([pyo.value(xval) for xval in x], dtype=np.float64) - y = x**2 # Compute y - dydx = 2*x # Compute dy/dx - + y = x**2 # Compute y + dydx = 2 * x # Compute dy/dx + y = y * units.ft**2 dydx = np.diag(dydx) - dydx = dydx * units.ft # units.ft**2 / units.ft - - return y, [dydx] # return z, grad(z), hess(z)... - f.ConstraintList([ {'outputs':y, 'operators':'==', 'inputs':x, 'black_box':Parabola() },]) - cl = f.get_constraints() - self.assertTrue(len(cl)==1) + dydx = dydx * units.ft # units.ft**2 / units.ft + + return y, [dydx] # return z, grad(z), hess(z)... + f.ConstraintList( + [{'outputs': y, 'operators': '==', 'inputs': x, 'black_box': Parabola()}] + ) + cl = f.get_constraints() + self.assertTrue(len(cl) == 1) def test_edi_formulation_runtimeconstraint_exceptions(self): "Tests the runtime constraint constructor in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The unit circle output') - c = f.Constant(name = 'c', value = 1.0, units = '', description = 'A constant c', size = 2) - f.Objective( x + y ) + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + z = f.Variable( + name='z', guess=1.0, units='m^2', description='The unit circle output' + ) + c = f.Constant( + name='c', value=1.0, units='', description='A constant c', size=2 + ) + f.Objective(x + y) + class UnitCircle(BlackBoxFunctionModel): - def __init__(self): + def __init__(self): super(UnitCircle, self).__init__() self.description = 'This model evaluates the function: z = x**2 + y**2' - self.inputs.append(name = 'x', units = 'ft' , description = 'The x variable') - self.inputs.append(name = 'y', units = 'ft' , description = 'The y variable') - self.outputs.append(name = 'z', units = 'ft**2', description = 'Resultant of the unit circle evaluation') + self.inputs.append(name='x', units='ft', description='The x variable') + self.inputs.append(name='y', units='ft', description='The y variable') + self.outputs.append( + name='z', + units='ft**2', + description='Resultant of the unit circle evaluation', + ) self.availableDerivative = 1 self.post_init_setup(len(self.inputs)) - def BlackBox(self, x, y): # The actual function that does things - x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float - y = pyo.value(units.convert(y,self.inputs[1].units)) # Converts to correct units then casts to float - z = x**2 + y**2 # Compute z - dzdx = 2*x # Compute dz/dx - dzdy = 2*y # Compute dz/dy + + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value( + units.convert(x, self.inputs[0].units) + ) # Converts to correct units then casts to float + y = pyo.value( + units.convert(y, self.inputs[1].units) + ) # Converts to correct units then casts to float + z = x**2 + y**2 # Compute z + dzdx = 2 * x # Compute dz/dx + dzdy = 2 * y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... - f.Constraint( z <= 1*units.m**2 ) + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + + f.Constraint(z <= 1 * units.m**2) # flaggs the input or as bad before assigning the incorrect black box - self.assertRaises(ValueError, f.RuntimeConstraint, *(z, '==', 1.0 , UnitCircle() )) - self.assertRaises(ValueError, f.RuntimeConstraint, *(1.0, '==', [x,y] , UnitCircle() )) + self.assertRaises( + ValueError, f.RuntimeConstraint, *(z, '==', 1.0, UnitCircle()) + ) + self.assertRaises( + ValueError, f.RuntimeConstraint, *(1.0, '==', [x, y], UnitCircle()) + ) - self.assertRaises(ValueError, f.RuntimeConstraint, *(z, '==', [1.0,y], UnitCircle() )) - self.assertRaises(ValueError, f.RuntimeConstraint, *(z, 1.0 , [x ,y], UnitCircle() )) - self.assertRaises(ValueError, f.RuntimeConstraint, *(z, '=', [x ,y], UnitCircle() )) + self.assertRaises( + ValueError, f.RuntimeConstraint, *(z, '==', [1.0, y], UnitCircle()) + ) + self.assertRaises( + ValueError, f.RuntimeConstraint, *(z, 1.0, [x, y], UnitCircle()) + ) + self.assertRaises( + ValueError, f.RuntimeConstraint, *(z, '=', [x, y], UnitCircle()) + ) def test_edi_formulation_getvariables(self): "Tests the get_variables function in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') vrs = f.get_variables() - self.assertListEqual(vrs, [x,y]) + self.assertListEqual(vrs, [x, y]) def test_edi_formulation_getconstants(self): "Tests the get_constants function in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation - f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - c1 = f.Constant(name = 'c1', value = 1.0, units = 'm', description = 'A constant c1', size = None, within = None) - c2 = f.Constant(name = 'c2', value = 1.0, units = 'm', description = 'A constant c2', size = None, within = None) + f = Formulation() + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + + c1 = f.Constant( + name='c1', + value=1.0, + units='m', + description='A constant c1', + size=None, + within=None, + ) + c2 = f.Constant( + name='c2', + value=1.0, + units='m', + description='A constant c2', + size=None, + within=None, + ) csts = f.get_constants() - self.assertListEqual(csts, [c1,c2]) + self.assertListEqual(csts, [c1, c2]) def test_edi_formulation_getobjectives(self): "Tests the get_objectives function in edi.formulation" import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - f.Objective(x+y) + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + f.Objective(x + y) objList = f.get_objectives() - self.assertTrue(len(objList)==1) + self.assertTrue(len(objList) == 1) # not really sure how to check this, so I wont def test_edi_formulation_getconstraints(self): @@ -452,40 +804,45 @@ def test_edi_formulation_getconstraints(self): # ================= # Declare Variables # ================= - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') - z = f.Variable(name = 'z', guess = 1.0, units = 'm^2', description = 'The unit circle output') + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + z = f.Variable( + name='z', guess=1.0, units='m^2', description='The unit circle output' + ) # ================= # Declare Constants # ================= - c = f.Constant(name = 'c', value = 1.0, units = '', description = 'A constant c', size = 2) + c = f.Constant( + name='c', value=1.0, units='', description='A constant c', size=2 + ) # ===================== # Declare the Objective # ===================== - f.Objective( - c[0]*x + c[1]*y - ) + f.Objective(c[0] * x + c[1] * y) # =================== # Declare a Black Box # =================== class UnitCircle(BlackBoxFunctionModel): - def __init__(self): # The initialization function - + def __init__(self): # The initialization function # Initialize the black box model super(UnitCircle, self).__init__() # A brief description of the model self.description = 'This model evaluates the function: z = x**2 + y**2' - + # Declare the black box model inputs - self.inputs.append(name = 'x', units = 'ft' , description = 'The x variable') - self.inputs.append(name = 'y', units = 'ft' , description = 'The y variable') + self.inputs.append(name='x', units='ft', description='The x variable') + self.inputs.append(name='y', units='ft', description='The y variable') # Declare the black box model outputs - self.outputs.append(name = 'z', units = 'ft**2', description = 'Resultant of the unit circle evaluation') + self.outputs.append( + name='z', + units='ft**2', + description='Resultant of the unit circle evaluation', + ) # Declare the maximum available derivative self.availableDerivative = 1 @@ -493,37 +850,36 @@ def __init__(self): # The initialization function # Post-initialization setup self.post_init_setup(len(self.inputs)) - def BlackBox(self, x, y): # The actual function that does things - x = pyo.value(units.convert(x,self.inputs[0].units)) # Converts to correct units then casts to float - y = pyo.value(units.convert(y,self.inputs[1].units)) # Converts to correct units then casts to float + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value( + units.convert(x, self.inputs[0].units) + ) # Converts to correct units then casts to float + y = pyo.value( + units.convert(y, self.inputs[1].units) + ) # Converts to correct units then casts to float - z = x**2 + y**2 # Compute z - dzdx = 2*x # Compute dz/dx - dzdy = 2*y # Compute dz/dy + z = x**2 + y**2 # Compute z + dzdx = 2 * x # Compute dz/dx + dzdy = 2 * y # Compute dz/dy z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + dzdx *= units.ft # units.ft**2 / units.ft + dzdy *= units.ft # units.ft**2 / units.ft + + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... # ======================= # Declare the Constraints # ======================= - f.ConstraintList( - [ - (z, '==', [x,y], UnitCircle() ) , - z <= 1*units.m**2 - ] - ) + f.ConstraintList([(z, '==', [x, y], UnitCircle()), z <= 1 * units.m**2]) - cl = f.get_constraints() + cl = f.get_constraints() ecl = f.get_explicitConstraints() rcl = f.get_runtimeConstraints() - self.assertTrue(len(cl)==2) - self.assertTrue(len(ecl)==1) - self.assertTrue(len(rcl)==1) + self.assertTrue(len(cl) == 2) + self.assertTrue(len(ecl) == 1) + self.assertTrue(len(rcl) == 1) def test_edi_formulation_checkunits(self): "Tests the check_units function in edi.formulation" @@ -531,42 +887,28 @@ def test_edi_formulation_checkunits(self): import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation + f = Formulation() - x = f.Variable(name = 'x', guess = 1.0, units = 'm' , description = 'The x variable') - y = f.Variable(name = 'y', guess = 1.0, units = 'm' , description = 'The y variable') + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') - f.Objective(x+y) - f.Constraint(x+y <= 1.0*units.m) + f.Objective(x + y) + f.Constraint(x + y <= 1.0 * units.m) f.check_units() - f.Constraint(2.0*x+y <= 1.0) - self.assertRaises(pyomo.core.base.units_container.UnitsError, f.check_units, *()) - + f.Constraint(2.0 * x + y <= 1.0) + self.assertRaises( + pyomo.core.base.units_container.UnitsError, f.check_units, *() + ) f2 = Formulation() - u = f2.Variable(name = 'u', guess = 1.0, units = 'm' , description = 'The u variable') - v = f2.Variable(name = 'v', guess = 1.0, units = 'kg' , description = 'The v variable') - f2.Objective(u+v) - self.assertRaises(pyomo.core.base.units_container.UnitsError, f2.check_units, *()) - - - + u = f2.Variable(name='u', guess=1.0, units='m', description='The u variable') + v = f2.Variable(name='v', guess=1.0, units='kg', description='The v variable') + f2.Objective(u + v) + self.assertRaises( + pyomo.core.base.units_container.UnitsError, f2.check_units, *() + ) if __name__ == '__main__': unittest.main() - - - - - - - - - - - - - - - From cc683cb1a806277697302ac3b13e2dc7eb2b9346 Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Tue, 8 Aug 2023 10:49:28 -0600 Subject: [PATCH 34/61] fixing the aircraft GP example --- pyomo/contrib/edi/examples/aircraft_gp.py | 133 +++++++++++++--------- 1 file changed, 81 insertions(+), 52 deletions(-) diff --git a/pyomo/contrib/edi/examples/aircraft_gp.py b/pyomo/contrib/edi/examples/aircraft_gp.py index d4f2d026cb1..94e1db70083 100644 --- a/pyomo/contrib/edi/examples/aircraft_gp.py +++ b/pyomo/contrib/edi/examples/aircraft_gp.py @@ -1,6 +1,16 @@ +# =========== +# Description +# =========== +# A simple aircraft sizing problem, formulated as a Geometric Program +# From: Hoburg and Abbeel +# Geometric Programming for Aircraft Design Optimization +# AIAA Journal +# 2014 + # ================= # Import Statements # ================= +import numpy as np import pyomo.environ as pyo from pyomo.environ import units from pyomo.contrib.edi import Formulation @@ -14,67 +24,86 @@ # ================= # Declare Variables # ================= -x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') -y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') -z = f.Variable(name='z', guess=1.0, units='m^2', description='Model output') + +A = f.Variable(name="A", guess=10.0, units="-", description="aspect ratio") +C_D = f.Variable( + name="C_D", guess=0.025, units="-", description="Drag coefficient of wing" +) +C_f = f.Variable( + name="C_f", guess=0.003, units="-", description="skin friction coefficient" +) +C_L = f.Variable( + name="C_L", guess=0.5, units="-", description="Lift coefficient of wing" +) +D = f.Variable(name="D", guess=300, units="N", description="total drag force") +Re = f.Variable(name="Re", guess=3e6, units="-", description="Reynold's number") +S = f.Variable(name="S", guess=10.0, units="m^2", description="total wing area") +V = f.Variable(name="V", guess=30.0, units="m/s", description="cruising speed") +W = f.Variable(name="W", guess=10000.0, units="N", description="total aircraft weight") +W_w = f.Variable(name="W_w", guess=2500, units="N", description="wing weight") # ================= # Declare Constants # ================= -c = f.Constant(name='c', value=1.0, units='', description='A constant c', size=2) +C_Lmax = f.Constant( + name="C_Lmax", value=2.0, units="-", description="max CL with flaps down" +) +CDA0 = f.Constant( + name="CDA0", value=0.0306, units="m^2", description="fuselage drag area" +) +e = f.Constant(name="e", value=0.96, units="-", description="Oswald efficiency factor") +k = f.Constant(name="k", value=1.2, units="-", description="form factor") +mu = f.Constant( + name="mu", value=1.78e-5, units="kg/m/s", description="viscosity of air" +) +N_ult = f.Constant( + name="N_ult", value=2.5, units="-", description="ultimate load factor" +) +rho = f.Constant(name="rho", value=1.23, units="kg/m^3", description="density of air") +S_wetratio = f.Constant( + name="Srat", value=2.05, units="-", description="wetted area ratio" +) +tau = f.Constant( + name="tau", value=0.12, units="-", description="airfoil thickness to chord ratio" +) +V_min = f.Constant(name="V_min", value=22, units="m/s", description="takeoff speed") +W_0 = f.Constant( + name="W_0", value=4940.0, units="N", description="aircraft weight excluding wing" +) +W_W_coeff1 = f.Constant( + name="W_c1", value=8.71e-5, units="1/m", description="Wing Weight Coefficient 1" +) +W_W_coeff2 = f.Constant( + name="W_c2", value=45.24, units="Pa", description="Wing Weight Coefficient 2" +) # ===================== # Declare the Objective # ===================== -f.Objective(c[0] * x + c[1] * y) - - -# =================== -# Declare a Black Box -# =================== -class UnitCircle(BlackBoxFunctionModel): - def __init__(self): # The initialization function - # Initialize the black box model - super().__init__() - - # A brief description of the model - self.description = 'This model evaluates the function: z = x**2 + y**2' - - # Declare the black box model inputs - self.inputs.append(name='x', units='ft', description='The x variable') - self.inputs.append(name='y', units='ft', description='The y variable') - - # Declare the black box model outputs - self.outputs.append( - name='z', units='ft**2', description='Resultant of the unit circle' - ) - - # Declare the maximum available derivative - self.availableDerivative = 1 - - def BlackBox(self, x, y): # The actual function that does things - # Converts to correct units then casts to float - x = pyo.value(units.convert(x, self.inputs['x'].units)) - y = pyo.value(units.convert(y, self.inputs['y'].units)) - - z = x**2 + y**2 # Compute z - dzdx = 2 * x # Compute dz/dx - dzdy = 2 * y # Compute dz/dy - - z *= units.ft**2 - dzdx *= units.ft # units.ft**2 / units.ft - dzdy *= units.ft # units.ft**2 / units.ft - - return z, [dzdx, dzdy] # return z, grad(z), hess(z)... - +f.Objective(D) + +# =================================== +# Declare some intermediate variables +# =================================== +pi = np.pi +C_D_fuse = CDA0 / S +C_D_wpar = k * C_f * S_wetratio +C_D_ind = C_L**2 / (pi * A * e) +W_w_strc = W_W_coeff1 * (N_ult * A**1.5 * (W_0 * W * S) ** 0.5) / tau +W_w_surf = W_W_coeff2 * S # ======================= # Declare the Constraints # ======================= -f.ConstraintList([[z, '==', [x, y], UnitCircle()], z <= 1 * units.m**2]) - -# ============================================= -# Run the black box (improves coverage metrics) -# ============================================= -uc = UnitCircle() -bbo = uc.BlackBox(0.5 * units.m, 0.5 * units.m) +f.ConstraintList( + [ + C_D >= C_D_fuse + C_D_wpar + C_D_ind, + W_w >= W_w_surf + W_w_strc, + D >= 0.5 * rho * S * C_D * V**2, + Re == (rho / mu) * V * (S / A) ** 0.5, + C_f == 0.074 / Re**0.2, + W == 0.5 * rho * S * C_L * V**2, + W == 0.5 * rho * S * C_Lmax * V_min**2, + W >= W_0 + W_w, + ] +) From 2acc63e8c63c402e2f41e9c38a7b324113d8bb84 Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Tue, 8 Aug 2023 15:55:53 -0600 Subject: [PATCH 35/61] trying to fix the import error --- pyomo/contrib/edi/tests/test_formulation.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/pyomo/contrib/edi/tests/test_formulation.py b/pyomo/contrib/edi/tests/test_formulation.py index 91346d722c3..2beb181b92d 100644 --- a/pyomo/contrib/edi/tests/test_formulation.py +++ b/pyomo/contrib/edi/tests/test_formulation.py @@ -21,8 +21,23 @@ from pyomo.core.base.units_container import pint_available +from pyomo.common.dependencies import numpy, numpy_available +from pyomo.common.dependencies import scipy, scipy_available -@unittest.skipIf(not pint_available, 'Testing pyomo.contrib.edi requires pint') +egb, egb_available = attempt_import( + "pyomo.contrib.pynumero.interfaces.external_grey_box" +) + +if numpy_available: + import numpy as np + + +@unittest.skipIf( + not egb_available, 'Testing pyomo.contrib.edi requires pynumero external grey boxes' +) +@unittest.skipIf(not numpy_available, 'Testing pyomo.contrib.edi requires numpy') +@unittest.skipIf(not scipy_available, 'Testing pyomo.contrib.edi requires scipy') +@unittest.skipIf(not pint_available, 'Testing units requires pint') class TestEDIFormulation(unittest.TestCase): def test_edi_formulation_init(self): "Tests that a formulation initializes to the correct type and has proper data" From fd0c45ba4e48fa53031c2efd1d3034f1b5dde298 Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Tue, 8 Aug 2023 16:14:48 -0600 Subject: [PATCH 36/61] adding a conditional wrapper to the pynumero import --- pyomo/contrib/edi/formulation.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/pyomo/contrib/edi/formulation.py b/pyomo/contrib/edi/formulation.py index f0652771ec2..caee5f9fb60 100644 --- a/pyomo/contrib/edi/formulation.py +++ b/pyomo/contrib/edi/formulation.py @@ -22,10 +22,20 @@ from pyomo.environ import Var, Param, Objective, Constraint, Set from pyomo.environ import maximize, minimize from pyomo.environ import units as pyomo_units -from pyomo.contrib.pynumero.interfaces.external_grey_box import ( - ExternalGreyBoxModel, - ExternalGreyBoxBlock, + +egb, egb_available = attempt_import( + "pyomo.contrib.pynumero.interfaces.external_grey_box" ) +if egb_available: + from pyomo.contrib.pynumero.interfaces.external_grey_box import ( + ExternalGreyBoxModel, + ExternalGreyBoxBlock, + ) +else: + raise ImportError( + "pyomo.contrib.edi requires pyomo.contrib.pynumero to be installed to enable black box capability, this should have installed with base pyomo" + ) + from pyomo.environ import ( Reals, PositiveReals, From 7a587234f3a7186e6aa3b78b70e5fc242ef9799a Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Tue, 8 Aug 2023 16:52:54 -0600 Subject: [PATCH 37/61] forgot an import statement, fixing --- pyomo/contrib/edi/formulation.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyomo/contrib/edi/formulation.py b/pyomo/contrib/edi/formulation.py index caee5f9fb60..897210b45cc 100644 --- a/pyomo/contrib/edi/formulation.py +++ b/pyomo/contrib/edi/formulation.py @@ -22,6 +22,7 @@ from pyomo.environ import Var, Param, Objective, Constraint, Set from pyomo.environ import maximize, minimize from pyomo.environ import units as pyomo_units +from pyomo.common.dependencies import attempt_import egb, egb_available = attempt_import( "pyomo.contrib.pynumero.interfaces.external_grey_box" @@ -35,7 +36,7 @@ raise ImportError( "pyomo.contrib.edi requires pyomo.contrib.pynumero to be installed to enable black box capability, this should have installed with base pyomo" ) - + from pyomo.environ import ( Reals, PositiveReals, From e76955380e89cbd4a7c718740c8938db821c11fc Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Wed, 9 Aug 2023 13:17:57 -0600 Subject: [PATCH 38/61] trying another import fix --- pyomo/contrib/edi/tests/test_blackbox.py | 19 +++++++++++++++++++ pyomo/contrib/edi/tests/test_docSnippets.py | 18 ++++++++++++++++++ pyomo/contrib/edi/tests/test_examples.py | 18 ++++++++++++++++++ pyomo/contrib/edi/tests/test_formulation.py | 18 ++++++++++++++++++ 4 files changed, 73 insertions(+) diff --git a/pyomo/contrib/edi/tests/test_blackbox.py b/pyomo/contrib/edi/tests/test_blackbox.py index e8eec4b36b8..15e4ff8debd 100644 --- a/pyomo/contrib/edi/tests/test_blackbox.py +++ b/pyomo/contrib/edi/tests/test_blackbox.py @@ -28,6 +28,23 @@ "pyomo.contrib.pynumero.interfaces.external_grey_box" ) +formulation_available = False +try: + from pyomo.contrib.edi import Formulation + formulation_available = True +except: + pass + # formulation_available = False + +blackbox_available = False +try: + from pyomo.contrib.edi import BlackBoxFunctionModel + blackbox_available = True +except: + pass + # blackbox_available = False + + if numpy_available: import numpy as np @@ -35,6 +52,8 @@ @unittest.skipIf( not egb_available, 'Testing pyomo.contrib.edi requires pynumero external grey boxes' ) +@unittest.skipIf(not formulation_available, 'Formulation import failed') +@unittest.skipIf(not blackbox_available, 'Blackbox import failed') @unittest.skipIf(not numpy_available, 'Testing pyomo.contrib.edi requires numpy') @unittest.skipIf(not scipy_available, 'Testing pyomo.contrib.edi requires scipy') @unittest.skipIf(not pint_available, 'Testing units requires pint') diff --git a/pyomo/contrib/edi/tests/test_docSnippets.py b/pyomo/contrib/edi/tests/test_docSnippets.py index 1fff6e030ac..687ad8d4149 100644 --- a/pyomo/contrib/edi/tests/test_docSnippets.py +++ b/pyomo/contrib/edi/tests/test_docSnippets.py @@ -28,6 +28,22 @@ "pyomo.contrib.pynumero.interfaces.external_grey_box" ) +formulation_available = False +try: + from pyomo.contrib.edi import Formulation + formulation_available = True +except: + pass + # formulation_available = False + +blackbox_available = False +try: + from pyomo.contrib.edi import BlackBoxFunctionModel + blackbox_available = True +except: + pass + # blackbox_available = False + if numpy_available: import numpy as np @@ -35,6 +51,8 @@ @unittest.skipIf( not egb_available, 'Testing pyomo.contrib.edi requires pynumero external grey boxes' ) +@unittest.skipIf(not formulation_available, 'Formulation import failed') +@unittest.skipIf(not blackbox_available, 'Blackbox import failed') @unittest.skipIf(not numpy_available, 'Testing pyomo.contrib.edi requires numpy') @unittest.skipIf(not scipy_available, 'Testing pyomo.contrib.edi requires scipy') @unittest.skipIf(not pint_available, 'Testing units requires pint') diff --git a/pyomo/contrib/edi/tests/test_examples.py b/pyomo/contrib/edi/tests/test_examples.py index f0f0a01bca8..afc59546cb3 100644 --- a/pyomo/contrib/edi/tests/test_examples.py +++ b/pyomo/contrib/edi/tests/test_examples.py @@ -31,6 +31,22 @@ "pyomo.contrib.pynumero.interfaces.external_grey_box" ) +formulation_available = False +try: + from pyomo.contrib.edi import Formulation + formulation_available = True +except: + pass + # formulation_available = False + +blackbox_available = False +try: + from pyomo.contrib.edi import BlackBoxFunctionModel + blackbox_available = True +except: + pass + # blackbox_available = False + if numpy_available: import numpy as np @@ -38,6 +54,8 @@ @unittest.skipIf( not egb_available, 'Testing pyomo.contrib.edi requires pynumero external grey boxes' ) +@unittest.skipIf(not formulation_available, 'Formulation import failed') +@unittest.skipIf(not blackbox_available, 'Blackbox import failed') @unittest.skipIf(not numpy_available, 'Testing pyomo.contrib.edi requires numpy') @unittest.skipIf(not scipy_available, 'Testing pyomo.contrib.edi requires scipy') @unittest.skipIf(not pint_available, 'Testing units requires pint') diff --git a/pyomo/contrib/edi/tests/test_formulation.py b/pyomo/contrib/edi/tests/test_formulation.py index 2beb181b92d..3ced7c68573 100644 --- a/pyomo/contrib/edi/tests/test_formulation.py +++ b/pyomo/contrib/edi/tests/test_formulation.py @@ -28,6 +28,22 @@ "pyomo.contrib.pynumero.interfaces.external_grey_box" ) +formulation_available = False +try: + from pyomo.contrib.edi import Formulation + formulation_available = True +except: + pass + # formulation_available = False + +blackbox_available = False +try: + from pyomo.contrib.edi import BlackBoxFunctionModel + blackbox_available = True +except: + pass + # blackbox_available = False + if numpy_available: import numpy as np @@ -35,6 +51,8 @@ @unittest.skipIf( not egb_available, 'Testing pyomo.contrib.edi requires pynumero external grey boxes' ) +@unittest.skipIf(not formulation_available, 'Formulation import failed') +@unittest.skipIf(not blackbox_available, 'Blackbox import failed') @unittest.skipIf(not numpy_available, 'Testing pyomo.contrib.edi requires numpy') @unittest.skipIf(not scipy_available, 'Testing pyomo.contrib.edi requires scipy') @unittest.skipIf(not pint_available, 'Testing units requires pint') From 4ebe52a7f2a72bb9ba6767ae7fa9f35d93dfc198 Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Wed, 9 Aug 2023 13:29:31 -0600 Subject: [PATCH 39/61] forgot to add black --- pyomo/contrib/edi/tests/test_blackbox.py | 6 ++++-- pyomo/contrib/edi/tests/test_docSnippets.py | 6 ++++-- pyomo/contrib/edi/tests/test_examples.py | 6 ++++-- pyomo/contrib/edi/tests/test_formulation.py | 6 ++++-- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/pyomo/contrib/edi/tests/test_blackbox.py b/pyomo/contrib/edi/tests/test_blackbox.py index 15e4ff8debd..9e051acea80 100644 --- a/pyomo/contrib/edi/tests/test_blackbox.py +++ b/pyomo/contrib/edi/tests/test_blackbox.py @@ -31,7 +31,8 @@ formulation_available = False try: from pyomo.contrib.edi import Formulation - formulation_available = True + + formulation_available = True except: pass # formulation_available = False @@ -39,7 +40,8 @@ blackbox_available = False try: from pyomo.contrib.edi import BlackBoxFunctionModel - blackbox_available = True + + blackbox_available = True except: pass # blackbox_available = False diff --git a/pyomo/contrib/edi/tests/test_docSnippets.py b/pyomo/contrib/edi/tests/test_docSnippets.py index 687ad8d4149..d6c7a9500fe 100644 --- a/pyomo/contrib/edi/tests/test_docSnippets.py +++ b/pyomo/contrib/edi/tests/test_docSnippets.py @@ -31,7 +31,8 @@ formulation_available = False try: from pyomo.contrib.edi import Formulation - formulation_available = True + + formulation_available = True except: pass # formulation_available = False @@ -39,7 +40,8 @@ blackbox_available = False try: from pyomo.contrib.edi import BlackBoxFunctionModel - blackbox_available = True + + blackbox_available = True except: pass # blackbox_available = False diff --git a/pyomo/contrib/edi/tests/test_examples.py b/pyomo/contrib/edi/tests/test_examples.py index afc59546cb3..8fee03eb2b0 100644 --- a/pyomo/contrib/edi/tests/test_examples.py +++ b/pyomo/contrib/edi/tests/test_examples.py @@ -34,7 +34,8 @@ formulation_available = False try: from pyomo.contrib.edi import Formulation - formulation_available = True + + formulation_available = True except: pass # formulation_available = False @@ -42,7 +43,8 @@ blackbox_available = False try: from pyomo.contrib.edi import BlackBoxFunctionModel - blackbox_available = True + + blackbox_available = True except: pass # blackbox_available = False diff --git a/pyomo/contrib/edi/tests/test_formulation.py b/pyomo/contrib/edi/tests/test_formulation.py index 3ced7c68573..1a86610d7cc 100644 --- a/pyomo/contrib/edi/tests/test_formulation.py +++ b/pyomo/contrib/edi/tests/test_formulation.py @@ -31,7 +31,8 @@ formulation_available = False try: from pyomo.contrib.edi import Formulation - formulation_available = True + + formulation_available = True except: pass # formulation_available = False @@ -39,7 +40,8 @@ blackbox_available = False try: from pyomo.contrib.edi import BlackBoxFunctionModel - blackbox_available = True + + blackbox_available = True except: pass # blackbox_available = False From 48abb030b95a81ecd24ae32c297ae9b709158e28 Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Wed, 9 Aug 2023 13:49:44 -0600 Subject: [PATCH 40/61] still updating imports --- pyomo/contrib/edi/__init__.py | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/pyomo/contrib/edi/__init__.py b/pyomo/contrib/edi/__init__.py index f54f05bef96..21213067168 100644 --- a/pyomo/contrib/edi/__init__.py +++ b/pyomo/contrib/edi/__init__.py @@ -19,16 +19,25 @@ import pyomo.environ # Import the relevant classes from Formulation -from pyomo.contrib.edi.formulation import Formulation +try: + from pyomo.contrib.edi.formulation import Formulation +except: + pass + # in this case, the dependencies are not installed, nothing will work + # Import the black box modeling tools -from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel -from pyomo.contrib.edi.blackBoxFunctionModel import ( - BlackBoxFunctionModel_Variable as BlackBoxVariable, -) -from pyomo.contrib.edi.blackBoxFunctionModel import ( - BlackBoxFunctionModel_Variable as BBVariable, -) -from pyomo.contrib.edi.blackBoxFunctionModel import ( - BlackBoxFunctionModel_Variable as BBV, -) +try: + from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel + from pyomo.contrib.edi.blackBoxFunctionModel import ( + BlackBoxFunctionModel_Variable as BlackBoxVariable, + ) + from pyomo.contrib.edi.blackBoxFunctionModel import ( + BlackBoxFunctionModel_Variable as BBVariable, + ) + from pyomo.contrib.edi.blackBoxFunctionModel import ( + BlackBoxFunctionModel_Variable as BBV, + ) +except: + pass + # in this case, the dependencies are not installed, nothing will work From 36b99f96af6de9e2db8e0f80a2b2f38f7d490812 Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Tue, 29 Aug 2023 08:28:41 -0600 Subject: [PATCH 41/61] changed the init file, not sure what I did --- pyomo/contrib/edi/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pyomo/contrib/edi/__init__.py b/pyomo/contrib/edi/__init__.py index 21213067168..2e8d537dd18 100644 --- a/pyomo/contrib/edi/__init__.py +++ b/pyomo/contrib/edi/__init__.py @@ -41,3 +41,4 @@ except: pass # in this case, the dependencies are not installed, nothing will work + From 7635017489c72ad7f11423c366dbc2810f6cef12 Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Tue, 5 Sep 2023 11:14:16 -0600 Subject: [PATCH 42/61] adding black to the init file --- pyomo/contrib/edi/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pyomo/contrib/edi/__init__.py b/pyomo/contrib/edi/__init__.py index 2e8d537dd18..21213067168 100644 --- a/pyomo/contrib/edi/__init__.py +++ b/pyomo/contrib/edi/__init__.py @@ -41,4 +41,3 @@ except: pass # in this case, the dependencies are not installed, nothing will work - From 29dd11b6bf119e2f276f29c979593cb1d865eeb5 Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Mon, 18 Sep 2023 15:14:02 -0600 Subject: [PATCH 43/61] fixing a unit test --- pyomo/contrib/edi/tests/test_blackbox.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyomo/contrib/edi/tests/test_blackbox.py b/pyomo/contrib/edi/tests/test_blackbox.py index 9e051acea80..35127a8ef49 100644 --- a/pyomo/contrib/edi/tests/test_blackbox.py +++ b/pyomo/contrib/edi/tests/test_blackbox.py @@ -697,7 +697,7 @@ def test_edi_blackbox_smallfunctions(self): t3 = bb.pyomo_value(2 * units.m) t3 = bb.pyomo_value(np.ones([2, 2]) * units.m) - self.assertRaises(ValueError, bb.pyomo_value, *(np.ones([2, 2]),)) + t4 = bb.pyomo_value(np.ones([2, 2])) bb.sizeCheck([2, 2], np.ones([2, 2]) * units.m) self.assertRaises(ValueError, bb.sizeCheck, *(2, 3 * units.m)) From f18548de9510f7650722774dbdd93efefeeb254c Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Mon, 11 Dec 2023 10:05:49 -0700 Subject: [PATCH 44/61] encountered sierra bug, caching work --- pyomo/contrib/edi/blackBoxFunctionModel.py | 232 ++++++- pyomo/contrib/edi/tests/test_blackbox.py | 702 ++++++++++----------- 2 files changed, 575 insertions(+), 359 deletions(-) diff --git a/pyomo/contrib/edi/blackBoxFunctionModel.py b/pyomo/contrib/edi/blackBoxFunctionModel.py index 02fd09bb9fd..b98ce3de0c4 100644 --- a/pyomo/contrib/edi/blackBoxFunctionModel.py +++ b/pyomo/contrib/edi/blackBoxFunctionModel.py @@ -16,6 +16,8 @@ # ___________________________________________________________________________ import copy +import itertools +from collections import namedtuple import pyomo import pyomo.environ as pyo from pyomo.environ import units as pyomo_units @@ -134,7 +136,7 @@ def size(self, val): 'A value of 1 is not valid for defining size. Use fewer dimensions.' ) sizeTemp.append(x) - self._size = val + self._size = sizeTemp else: if val is None: self._size = 0 # set to scalar @@ -280,6 +282,19 @@ def append(*args, **kwargs): errorString = 'This function is calling to the base class and has not been defined.' +def toList(itm, keylist): + if isinstance(itm, (namedtuple, tuple, list)): + return list(itm) + elif isinstance(itm, dict): + retList = [] + for ky in keylist: + if ky in itm.keys(): + retList.append(itm[ky]) + return retList + else: + return [itm] + + class BlackBoxFunctionModel(ExternalGreyBoxModel): # --------------------------------------------------------------------------------------------------------------------- # --------------------------------------------------------------------------------------------------------------------- @@ -569,7 +584,10 @@ def fillCache(self): ptr_row += ptr_row_step - self._cache['pyomo_jacobian'] = sps.coo_matrix(outputJacobian) + print(outputJacobian.dtype) + # print(sps.coo_matrix(outputJacobian)) + # outputJacobian = np.zeros([3,1]) + self._cache['pyomo_jacobian'] = sps.coo_matrix(outputJacobian, shape=outputJacobian.shape) # --------------------------------------------------------------------------------------------------------------------- # --------------------------------------------------------------------------------------------------------------------- @@ -577,6 +595,204 @@ def fillCache(self): def BlackBox(*args, **kwargs): raise AttributeError(errorString) + # --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- + # These functions wrap black box to provide more functionality + + def BlackBox_Standardized(self, *args, **kwargs): + # -------- + # Run + # -------- + opt_raw = self.BlackBox(*args, **kwargs) + + # -------- + # Setup + # -------- + outputNames = [o.name for o in self.outputs] + inputNames = [i.name for i in self.inputs] + + returnTuple = namedtuple('returnTuple', ['values', 'first', 'second']) + optTuple = namedtuple('optTuple', outputNames) + iptTuple = namedtuple('iptTuple', inputNames) + + structuredOutput = {} + structuredOutput['values'] = len(self.outputs) * [None] + structuredOutput['first'] = len(self.outputs) * [len(self.inputs) * [None]] + structuredOutput['second'] = len(self.outputs) * [ + len(self.inputs) * [len(self.inputs) * [None]] + ] + + opt = toList(opt_raw, ['values', 'first', 'second']) + + # -------- + # Values + # -------- + # values will always come back as an appropriate list + opt_values = toList(opt[0]) + for i in range(0, len(structuredOutput['values'])): + if self.outputs[i].size == 0: + structuredOutput['values'][i] = 0.0 * self.outputs[i].units + else: + currentSize = toList(self.outputs[i].size, []) + structuredOutput['values'][i] = ( + np.zeros(currentSize) * self.outputs[i].units + ) + + for i, vl in enumerate(opt_values): + if self.outputs[i].size == 0: + structuredOutput['values'][i] = opt_values[i].to(self.outputs[i].units) + else: + listOfIndices = list( + itertools.product(*[range(0, n) for n in self.outputs[i].size]) + ) + for ix in listOfIndices: + structuredOutput['values'][i][ix] = opt_values[i][ + ix + ] # unit conversion handled automatically by pint + + # structuredOutput['values'] = opt_values + + # -------- + # First + # -------- + # first orders will be [num] or [num, num, lst, num...] or [lst, lst, lst, lst...] + # should be [lst, lst, lst, lst...] + if len(opt) > 1: + opt_first = toList(opt[1], outputNames) + for i in range(0, len(structuredOutput['first'])): + currentSize_output = toList(self.outputs[i].size, []) + for j in range(0, len(structuredOutput['first'][i])): + if self.outputs[i].size == 0 and self.inputs[j].size == 0: + structuredOutput['values'][i][j] = ( + 0.0 * self.outputs[i].units / self.inputs[j].units + ) + elif self.outputs[i].size == 0: + currentSize_input = toList(self.inputs[j].size, []) + structuredOutput['values'][i][j] = ( + np.zeros(currentSize_input) + * self.outputs[i].units + / self.inputs[j].units + ) + elif self.inputs[j].size == 0: + structuredOutput['values'][i][j] = ( + np.zeros(currentSize_output) + * self.outputs[i].units + / self.inputs[j].units + ) + else: + currentSize_input = toList(self.inputs[j].size, []) + structuredOutput['values'][i][j] = ( + np.zeros(currentSize_output + currentSize_input) + * self.outputs[i].units + / self.inputs[j].units + ) + + for i, vl in enumerate(opt_first): + for j, vlj in enumerate(opt_first[i]): + if self.outputs[i].size == 0 and self.inputs[j].size == 0: + structuredOutput['first'][i][j] = opt_first[i][j].to( + self.outputs[i].units / self.inputs[j].units + ) + elif self.outputs[i].size == 0: + listOfIndices = list( + itertools.product( + *[range(0, n) for n in self.inputs[j].size] + ) + ) + for ix in listOfIndices: + structuredOutput['first'][i][j][ix] = opt_first[i][j][ + ix + ] # unit conversion handled automatically by pint + elif self.inputs[j].size == 0: + listOfIndices = list( + itertools.product( + *[range(0, n) for n in self.outputs[i].size] + ) + ) + for ix in listOfIndices: + structuredOutput['first'][i][j][ix] = opt_first[i][j][ + ix + ] # unit conversion handled automatically by pint + else: + listOfIndices = list( + itertools.product( + *[ + range(0, n) + for n in self.outputs[i].size + self.inputs[j].size + ] + ) + ) + for ix in listOfIndices: + structuredOutput['first'][i][j][ix] = opt_first[i][j][ + ix + ] # unit conversion handled automatically by pint + else: + structuredOutput['first'] = None + # for i,vl in enumerate(opt_first): + # opt_first[i] = toList(vl,inputNames) + # structuredOutput['first'] = opt_first + + # -------- + # Second + # -------- + # if len(opt)>2: + # opt_second = toList(opt[2],outputNames) + # # Not implemented in this version + # else: + structuredOutput['second'] = None + + # for i,vl in enumerate(opt_second): + # opt_second[i] = toList(vl,inputNames) + # for j, vlj in enumerate(opt_second[i]): + # opt_second[i][j] = toList(vlj,inputNames) + # structuredOutput['second'] = opt_second + + # -------- + # Pack + # -------- + for i in range(0, len(structuredOutput['second'])): + for j in range(0, len(structuredOutput['second'][i])): + structuredOutput['second'][i][j] = iptTuple( + *structuredOutput['second'][i][j] + ) + + for i in range(0, len(structuredOutput['first'])): + structuredOutput['first'][i] = iptTuple(*structuredOutput['first'][i]) + structuredOutput['second'][i] = iptTuple(*structuredOutput['second'][i]) + + structuredOutput['values'] = optTuple(*structuredOutput['values']) + structuredOutput['first'] = optTuple(*structuredOutput['first']) + structuredOutput['second'] = optTuple(*structuredOutput['second']) + + return returnTuple( + structuredOutput['values'], + structuredOutput['first'], + structuredOutput['second'], + ) + + def MultiCase(self, *args, **kwargs): + runCases, extras = self.parseInputs(*args, **kwargs) + + outputList = [] + # values = [] + # first = [] + # second = [] + for rc in runCases: + ipt = rc | extras + opt = self.BlackBox_Standardized(**ipt) + outputList.append(opt) + # values.append(opt[0]) + # first.append(opt[1]) + # second.append(opt[2]) + + # returnTuple = namedtuple('returnTuple', ['values','first','second']) + # return returnTuple(values, first, second) + return outputList + + # --------------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------------------------------- + # unit handling functions + def convert(self, val, unts): try: val = val * pyomo_units.dimensionless @@ -637,7 +853,7 @@ def parseInputs(self, *args, **kwargs): rs = self.sanitizeInputs(inputData) return ( [dict(zip(inputNames, [rs]))], - -self.availableDerivative - 1, + # -self.availableDerivative - 1, {}, ) # one input being passed in except: @@ -673,7 +889,7 @@ def parseInputs(self, *args, **kwargs): + "BlackBox(*[x1,x2,y]) or BlackBox({'x1':x1,'x2':x2,'y':y}) or simply BlackBox(x1, x2, y) to avoid processing singularities. " + "Best practice is BlackBox({'x1':x1,'x2':x2,'y':y})" ) - return dataRuns, self.availableDerivative, {} + return dataRuns, {} elif isinstance(inputData, dict): if set(list(inputData.keys())) == set(inputNames): @@ -685,7 +901,7 @@ def parseInputs(self, *args, **kwargs): sips = [sips] return ( [dict(zip(inputNames, sips))], - -self.availableDerivative - 1, + # -self.availableDerivative - 1, {}, ) # the BlackBox(*{'x1':x1, 'x2':x2}) case, somewhat likely @@ -698,7 +914,7 @@ def parseInputs(self, *args, **kwargs): sips = self.sanitizeInputs(**inputData) return ( [dict(zip(inputNames, sips))], - -self.availableDerivative - 1, + # -self.availableDerivative - 1, {}, ) # the BlackBox(*{'x1':x1, 'x2':x2}) case where vectors for x1... are passed in (likely to fail on previous line) else: @@ -706,7 +922,7 @@ def parseInputs(self, *args, **kwargs): sips = self.sanitizeInputs(**inputData) return ( [dict(zip(inputNames, sips))], - -self.availableDerivative - 1, + # -self.availableDerivative - 1, {}, ) # the BlackBox(*{'x1':x1, 'x2':x2}) case where vectors all inputs have same length intentionally (likely to fail on previous line) except: @@ -722,7 +938,7 @@ def parseInputs(self, *args, **kwargs): dataRuns.append(runDictS) return ( dataRuns, - self.availableDerivative, + # self.availableDerivative, {}, ) # the BlackBox({'x1':x1_vec, 'x2':x2_vec}) case, most likely else: diff --git a/pyomo/contrib/edi/tests/test_blackbox.py b/pyomo/contrib/edi/tests/test_blackbox.py index 35127a8ef49..5636d9f7b70 100644 --- a/pyomo/contrib/edi/tests/test_blackbox.py +++ b/pyomo/contrib/edi/tests/test_blackbox.py @@ -436,7 +436,7 @@ def __init__(self): self.post_init_setup(len(self.inputs)) def BlackBox(self, *args, **kwargs): # The actual function that does things - runCases, returnMode, remainingKwargs = self.parseInputs( + runCases, remainingKwargs = self.parseInputs( *args, **kwargs ) @@ -511,7 +511,7 @@ def __init__(self): self.post_init_setup(len(self.inputs)) def BlackBox(self, *args, **kwargs): # The actual function that does things - runCases, returnMode, remainingKwargs = self.parseInputs( + runCases, remainingKwargs = self.parseInputs( *args, **kwargs ) @@ -582,7 +582,7 @@ def __init__(self): self.post_init_setup(len(self.inputs)) def BlackBox(self, *args, **kwargs): # The actual function that does things - runCases, returnMode, remainingKwargs = self.parseInputs( + runCases, remainingKwargs = self.parseInputs( *args, **kwargs ) @@ -646,7 +646,7 @@ def __init__(self): self.post_init_setup(len(self.inputs)) def BlackBox(self, *args, **kwargs): # The actual function that does things - runCases, returnMode, remainingKwargs = self.parseInputs( + runCases, remainingKwargs = self.parseInputs( *args, **kwargs ) @@ -709,353 +709,353 @@ def test_edi_blackbox_smallfunctions(self): ) self.assertRaises(ValueError, bb.sizeCheck, *([10, 10], [])) - def test_edi_blackbox_bare_example_1(self): - "Tests a black box example construction without an optimization problem" - import numpy as np - import pyomo.environ as pyo - from pyomo.environ import units - from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel - - class SignomialTest(BlackBoxFunctionModel): - def __init__(self): - # Set up all the attributes by calling Model.__init__ - super().__init__() - - # Setup Inputs - self.inputs.append('x', '', 'Independent Variable') - - # Setup Outputs - self.outputs.append('y', '', 'Dependent Variable') - - # Simple model description - self.description = ( - 'This model evaluates the function: max([-6*x-6, x**4-3*x**2])' - ) - - self.availableDerivative = 1 - - # standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) - def BlackBox(self, *args, **kwargs): - runCases, returnMode, extras = self.parseInputs(*args, **kwargs) - x = np.array( - [pyo.value(runCases[i]['x']) for i in range(0, len(runCases))] - ) - - y = np.maximum(-6 * x - 6, x**4 - 3 * x**2) - dydx = 4 * x**3 - 6 * x - ddy_ddx = 12 * x**2 - 6 - gradientSwitch = -6 * x - 6 > x**4 - 3 * x**2 - dydx[gradientSwitch] = -6 - ddy_ddx[gradientSwitch] = 0 - - y = [yval * units.dimensionless for yval in y] - dydx = [dydx[i] * units.dimensionless for i in range(0, len(dydx))] - - if returnMode < 0: - returnMode = -1 * (returnMode + 1) - if returnMode == 0: - return y[0] - if returnMode == 1: - return y[0], dydx - else: - if returnMode == 0: - opt = [] - for i in range(0, len(y)): - opt.append([y[i]]) - return opt - if returnMode == 1: - opt = [] - for i in range(0, len(y)): - opt.append([[y[i]], [[[dydx[i]]]]]) - return opt - - s = SignomialTest() - ivals = [[x] for x in np.linspace(-2, 2, 11)] - - # How the black box may be called using EDI - bbo = s.BlackBox(**{'x': 0.5}) - bbo = s.BlackBox({'x': 0.5}) - bbo = s.BlackBox(**{'x': 0.5, 'optn': True}) - - # Additional options available with parseInputs - bbo = s.BlackBox(*[0.5], **{'optn1': True, 'optn2': False}) - bbo = s.BlackBox(*[0.5, True], **{'optn': False}) - bbo = s.BlackBox({'x': [x for x in np.linspace(-2, 2, 11)]}) - bbo = s.BlackBox([{'x': x} for x in np.linspace(-2, 2, 11)]) - bbo = s.BlackBox([[x] for x in np.linspace(-2, 2, 11)]) - bbo = s.BlackBox([[x] for x in np.linspace(-2, 2, 11)], True, optn=False) - bbo = s.BlackBox([[x] for x in np.linspace(-2, 2, 11)], optn1=True, optn2=False) - - sm = s.summary - - self.assertRaises(ValueError, s.BlackBox, *([[[1, 2], 2, 3], [1, 2, 3]])) - self.assertRaises(ValueError, s.BlackBox, *([[np.ones(2), 2, 3], [1, 2, 3]])) - - self.assertRaises(ValueError, s.sanitizeInputs, *()) - self.assertRaises(ValueError, s.sanitizeInputs, *(1, 2, 3)) - self.assertRaises( - ValueError, s.sanitizeInputs, **{'z': 2.0 * units.dimensionless} - ) - self.assertRaises(ValueError, s.sanitizeInputs, *(2.0 * units.ft,)) - self.assertRaises(NotImplementedError, s.checkOutputs, *()) - - def test_edi_blackbox_bare_example_2(self): - "Tests a black box example construction without an optimization problem" - import numpy as np - import pyomo.environ as pyo - from pyomo.environ import units - from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel - from pyomo.common.formatting import tostr - - class PassThrough(BlackBoxFunctionModel): - def __init__(self): - # Set up all the attributes by calling Model.__init__ - super().__init__() - - # Setup Inputs - self.inputs.append('x', '', 'Independent Variable', size=[2, 2]) - - # Setup Outputs - self.outputs.append('y', '', 'Dependent Variable', size=[2, 2]) - - # Simple model description - self.description = 'This model is a pass through)' - - self.availableDerivative = 1 - - # standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) - def BlackBox(self, *args, **kwargs): - runCases, returnMode, extras = self.parseInputs(*args, **kwargs) - x = [ - self.pyomo_value(runCases[i]['x']) for i in range(0, len(runCases)) - ] - - y = [] - dydx = [] - - for xval in x: - y.append(xval * units.dimensionless) - dydx_temp = np.zeros([2, 2, 2, 2]) - dydx_temp[0, 0, 0, 0] = 1.0 - dydx_temp[0, 1, 0, 1] = 1.0 - dydx_temp[1, 0, 1, 0] = 1.0 - dydx_temp[1, 1, 1, 1] = 1.0 - - dydx.append(dydx_temp * units.dimensionless) - - if returnMode < 0: - returnMode = -1 * (returnMode + 1) - if returnMode == 0: - return y[0] - if returnMode == 1: - return y[0], dydx - else: - if returnMode == 0: - opt = [] - for i in range(0, len(y)): - opt.append([y[i]]) - return opt - if returnMode == 1: - opt = [] - for i in range(0, len(y)): - opt.append([[y[i]], [[[dydx[i]]]]]) - return opt - - bb = PassThrough() - ivals = [ - [np.eye(2) * units.dimensionless], - [np.ones([2, 2]) * units.dimensionless], - [np.zeros([2, 2]) * units.dimensionless], - ] - - xv = np.eye(2) * units.dimensionless - - # How the black box may be called using EDI - bbo = bb.BlackBox(**{'x': xv}) - bbo = bb.BlackBox({'x': xv}) - bbo = bb.BlackBox(**{'x': xv, 'optn': True}) - - # # Additional options available with parseInputs - bbo = bb.BlackBox(*[xv], **{'optn1': True, 'optn2': False}) - bbo = bb.BlackBox(*[xv, True], **{'optn': False}) - bbo = bb.BlackBox({'x': [x[0] for x in ivals]}) - bbo = bb.BlackBox([{'x': x[0]} for x in ivals]) - bbo = bb.BlackBox([[x[0]] for x in ivals]) - bbo = bb.BlackBox([[x[0]] for x in ivals], True, optn=False) - bbo = bb.BlackBox([[x[0]] for x in ivals], optn1=True, optn2=False) - - sm = bb.summary - - self.assertRaises(ValueError, bb.sanitizeInputs, *(np.ones([2, 2]) * units.ft,)) - - def test_edi_blackbox_bare_example_3(self): - "Tests a black box example construction" - import numpy as np - from pyomo.environ import units - import pyomo.environ as pyo - from pyomo.contrib.edi import Formulation - from pyomo.contrib.edi.blackBoxFunctionModel import ( - BlackBoxFunctionModel_Variable, - TypeCheckedList, - BBList, - BlackBoxFunctionModel, - ) - - class Norm_2(BlackBoxFunctionModel): - def __init__(self): - super().__init__() - self.description = 'This model evaluates the two norm' - self.inputs.append( - name='x', units='', description='The x variable', size=3 - ) - self.inputs.append( - name='y', units='', description='The y variable', size=2 - ) - self.outputs.append(name='z', units='', description='The z variable') - self.availableDerivative = 1 - self.post_init_setup(len(self.inputs)) - - def BlackBox(self, *args, **kwargs): # The actual function that does things - runCases, returnMode, remainingKwargs = self.parseInputs( - *args, **kwargs - ) - - x = [rc['x'] for rc in runCases][0] - x = np.array([self.pyomo_value(xval) for xval in x], dtype=np.float64) - - y = [rc['y'] for rc in runCases][0] - y = np.array([self.pyomo_value(yval) for yval in y], dtype=np.float64) - - z = x[0] ** 2 + x[1] ** 2 + x[2] ** 2 + y[0] ** 2 + y[1] ** 2 - dzdx0 = 2 * x[0] # Compute dy/dx0 - dzdx1 = 2 * x[1] # Compute dy/dx1 - dzdx2 = 2 * x[2] # Compute dy/dx2 - dzdy0 = 2 * y[0] - dzdy1 = 2 * y[1] - - z = z * units.dimensionless - dz = np.array([dzdx0, dzdx1, dzdx2, dzdy0, dzdy1]) * units.dimensionless - # dydx0 = dydx0 * units.dimensionless - # dydx1 = dydx1 * units.dimensionless - # dydx2 = dydx2 * units.dimensionless - - return z, [dz] # return z, grad(z), hess(z)... - - bb = Norm_2() - bbo = bb.BlackBox( - { - 'x': np.array([0, 0, 0]) * units.dimensionless, - 'y': np.array([0, 0]) * units.dimensionless, - } - ) - bbo = bb.BlackBox( - np.array([0, 0, 0]) * units.dimensionless, - y=np.array([0, 0]) * units.dimensionless, - ) - - self.assertRaises( - ValueError, - bb.BlackBox, - *( - { - 'er': np.array([0, 0, 0]) * units.dimensionless, - 'y': np.array([0, 0]) * units.dimensionless, - }, - ) - ) - self.assertRaises(ValueError, bb.BlackBox, *('err',)) - self.assertRaises( - ValueError, - bb.BlackBox, - *( - np.array([0, 0, 0]) * units.dimensionless, - np.array([0, 0]) * units.dimensionless, - ), - **{'x': 'err too many'} - ) - self.assertRaises( - ValueError, - bb.BlackBox, - *(np.array([0, 0, 0]) * units.dimensionless,), - **{'notY': np.array([0, 0]) * units.dimensionless} - ) - - def test_edi_blackbox_bare_example_4(self): - "Tests a black box example construction without an optimization problem" - import numpy as np - import pyomo.environ as pyo - from pyomo.environ import units - from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel - from pyomo.common.formatting import tostr - - class PassThrough(BlackBoxFunctionModel): - def __init__(self): - # Set up all the attributes by calling Model.__init__ - super().__init__() - - # Setup Inputs - self.inputs.append('x', '', 'X Variable') - self.inputs.append('y', '', 'Y Variable') - - # Setup Outputs - self.outputs.append('u', '', 'U Variable') - self.outputs.append('v', '', 'V Variable') - - # Simple model description - self.description = 'This model is a pass through)' - - self.availableDerivative = 1 - - # standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) - def BlackBox(self, *args, **kwargs): - runCases, returnMode, extras = self.parseInputs(*args, **kwargs) - x = [ - self.pyomo_value(runCases[i]['x']) for i in range(0, len(runCases)) - ] - y = [ - self.pyomo_value(runCases[i]['x']) for i in range(0, len(runCases)) - ] - - u = [] - dudx = [] - dudy = [] - v = [] - dvdx = [] - dvdy = [] - - for xval in x: - u.append(xval * units.dimensionless) - dudx.append(1.0 * units.dimensionless) - dudy.append(0.0 * units.dimensionless) - - for yval in y: - v.append(yval * units.dimensionless) - dvdx.append(0.0 * units.dimensionless) - dvdy.append(1.0 * units.dimensionless) - - if returnMode < 0: - returnMode = -1 * (returnMode + 1) - if returnMode == 0: - return [u[0], v[0]] - if returnMode == 1: - return [u[0], v[0]], [[dudx[0], dudy[0]], [dvdx[0], dvdy[0]]] - else: - if returnMode == 0: - opt = [] - for i in range(0, len(y)): - opt.append([u[i], v[i]]) - return opt - if returnMode == 1: - opt = [] - for i in range(0, len(y)): - opt.append( - [[u[i], v[i]], [[dudx[i], dudy[i]], [dvdx[i], dvdy[i]]]] - ) - return opt - - bb = PassThrough() - bbo = bb.BlackBox(1.0, 1.0) - bbo = bb.BlackBox({'x': np.linspace(0, 10, 11), 'y': np.linspace(0, 10, 11)}) + # def test_edi_blackbox_bare_example_1(self): + # "Tests a black box example construction without an optimization problem" + # import numpy as np + # import pyomo.environ as pyo + # from pyomo.environ import units + # from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + + # class SignomialTest(BlackBoxFunctionModel): + # def __init__(self): + # # Set up all the attributes by calling Model.__init__ + # super().__init__() + + # # Setup Inputs + # self.inputs.append('x', '', 'Independent Variable') + + # # Setup Outputs + # self.outputs.append('y', '', 'Dependent Variable') + + # # Simple model description + # self.description = ( + # 'This model evaluates the function: max([-6*x-6, x**4-3*x**2])' + # ) + + # self.availableDerivative = 1 + + # # standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) + # def BlackBox(self, *args, **kwargs): + # runCases, returnMode, extras = self.parseInputs(*args, **kwargs) + # x = np.array( + # [pyo.value(runCases[i]['x']) for i in range(0, len(runCases))] + # ) + + # y = np.maximum(-6 * x - 6, x**4 - 3 * x**2) + # dydx = 4 * x**3 - 6 * x + # ddy_ddx = 12 * x**2 - 6 + # gradientSwitch = -6 * x - 6 > x**4 - 3 * x**2 + # dydx[gradientSwitch] = -6 + # ddy_ddx[gradientSwitch] = 0 + + # y = [yval * units.dimensionless for yval in y] + # dydx = [dydx[i] * units.dimensionless for i in range(0, len(dydx))] + + # if returnMode < 0: + # returnMode = -1 * (returnMode + 1) + # if returnMode == 0: + # return y[0] + # if returnMode == 1: + # return y[0], dydx + # else: + # if returnMode == 0: + # opt = [] + # for i in range(0, len(y)): + # opt.append([y[i]]) + # return opt + # if returnMode == 1: + # opt = [] + # for i in range(0, len(y)): + # opt.append([[y[i]], [[[dydx[i]]]]]) + # return opt + + # s = SignomialTest() + # ivals = [[x] for x in np.linspace(-2, 2, 11)] + + # # How the black box may be called using EDI + # bbo = s.BlackBox(**{'x': 0.5}) + # bbo = s.BlackBox({'x': 0.5}) + # bbo = s.BlackBox(**{'x': 0.5, 'optn': True}) + + # # Additional options available with parseInputs + # bbo = s.BlackBox(*[0.5], **{'optn1': True, 'optn2': False}) + # bbo = s.BlackBox(*[0.5, True], **{'optn': False}) + # bbo = s.BlackBox({'x': [x for x in np.linspace(-2, 2, 11)]}) + # bbo = s.BlackBox([{'x': x} for x in np.linspace(-2, 2, 11)]) + # bbo = s.BlackBox([[x] for x in np.linspace(-2, 2, 11)]) + # bbo = s.BlackBox([[x] for x in np.linspace(-2, 2, 11)], True, optn=False) + # bbo = s.BlackBox([[x] for x in np.linspace(-2, 2, 11)], optn1=True, optn2=False) + + # sm = s.summary + + # self.assertRaises(ValueError, s.BlackBox, *([[[1, 2], 2, 3], [1, 2, 3]])) + # self.assertRaises(ValueError, s.BlackBox, *([[np.ones(2), 2, 3], [1, 2, 3]])) + + # self.assertRaises(ValueError, s.sanitizeInputs, *()) + # self.assertRaises(ValueError, s.sanitizeInputs, *(1, 2, 3)) + # self.assertRaises( + # ValueError, s.sanitizeInputs, **{'z': 2.0 * units.dimensionless} + # ) + # self.assertRaises(ValueError, s.sanitizeInputs, *(2.0 * units.ft,)) + # self.assertRaises(NotImplementedError, s.checkOutputs, *()) + + # def test_edi_blackbox_bare_example_2(self): + # "Tests a black box example construction without an optimization problem" + # import numpy as np + # import pyomo.environ as pyo + # from pyomo.environ import units + # from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + # from pyomo.common.formatting import tostr + + # class PassThrough(BlackBoxFunctionModel): + # def __init__(self): + # # Set up all the attributes by calling Model.__init__ + # super().__init__() + + # # Setup Inputs + # self.inputs.append('x', '', 'Independent Variable', size=[2, 2]) + + # # Setup Outputs + # self.outputs.append('y', '', 'Dependent Variable', size=[2, 2]) + + # # Simple model description + # self.description = 'This model is a pass through)' + + # self.availableDerivative = 1 + + # # standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) + # def BlackBox(self, *args, **kwargs): + # runCases, returnMode, extras = self.parseInputs(*args, **kwargs) + # x = [ + # self.pyomo_value(runCases[i]['x']) for i in range(0, len(runCases)) + # ] + + # y = [] + # dydx = [] + + # for xval in x: + # y.append(xval * units.dimensionless) + # dydx_temp = np.zeros([2, 2, 2, 2]) + # dydx_temp[0, 0, 0, 0] = 1.0 + # dydx_temp[0, 1, 0, 1] = 1.0 + # dydx_temp[1, 0, 1, 0] = 1.0 + # dydx_temp[1, 1, 1, 1] = 1.0 + + # dydx.append(dydx_temp * units.dimensionless) + + # if returnMode < 0: + # returnMode = -1 * (returnMode + 1) + # if returnMode == 0: + # return y[0] + # if returnMode == 1: + # return y[0], dydx + # else: + # if returnMode == 0: + # opt = [] + # for i in range(0, len(y)): + # opt.append([y[i]]) + # return opt + # if returnMode == 1: + # opt = [] + # for i in range(0, len(y)): + # opt.append([[y[i]], [[[dydx[i]]]]]) + # return opt + + # bb = PassThrough() + # ivals = [ + # [np.eye(2) * units.dimensionless], + # [np.ones([2, 2]) * units.dimensionless], + # [np.zeros([2, 2]) * units.dimensionless], + # ] + + # xv = np.eye(2) * units.dimensionless + + # # How the black box may be called using EDI + # bbo = bb.BlackBox(**{'x': xv}) + # bbo = bb.BlackBox({'x': xv}) + # bbo = bb.BlackBox(**{'x': xv, 'optn': True}) + + # # # Additional options available with parseInputs + # bbo = bb.BlackBox(*[xv], **{'optn1': True, 'optn2': False}) + # bbo = bb.BlackBox(*[xv, True], **{'optn': False}) + # bbo = bb.BlackBox({'x': [x[0] for x in ivals]}) + # bbo = bb.BlackBox([{'x': x[0]} for x in ivals]) + # bbo = bb.BlackBox([[x[0]] for x in ivals]) + # bbo = bb.BlackBox([[x[0]] for x in ivals], True, optn=False) + # bbo = bb.BlackBox([[x[0]] for x in ivals], optn1=True, optn2=False) + + # sm = bb.summary + + # self.assertRaises(ValueError, bb.sanitizeInputs, *(np.ones([2, 2]) * units.ft,)) + + # def test_edi_blackbox_bare_example_3(self): + # "Tests a black box example construction" + # import numpy as np + # from pyomo.environ import units + # import pyomo.environ as pyo + # from pyomo.contrib.edi import Formulation + # from pyomo.contrib.edi.blackBoxFunctionModel import ( + # BlackBoxFunctionModel_Variable, + # TypeCheckedList, + # BBList, + # BlackBoxFunctionModel, + # ) + + # class Norm_2(BlackBoxFunctionModel): + # def __init__(self): + # super().__init__() + # self.description = 'This model evaluates the two norm' + # self.inputs.append( + # name='x', units='', description='The x variable', size=3 + # ) + # self.inputs.append( + # name='y', units='', description='The y variable', size=2 + # ) + # self.outputs.append(name='z', units='', description='The z variable') + # self.availableDerivative = 1 + # self.post_init_setup(len(self.inputs)) + + # def BlackBox(self, *args, **kwargs): # The actual function that does things + # runCases, returnMode, remainingKwargs = self.parseInputs( + # *args, **kwargs + # ) + + # x = [rc['x'] for rc in runCases][0] + # x = np.array([self.pyomo_value(xval) for xval in x], dtype=np.float64) + + # y = [rc['y'] for rc in runCases][0] + # y = np.array([self.pyomo_value(yval) for yval in y], dtype=np.float64) + + # z = x[0] ** 2 + x[1] ** 2 + x[2] ** 2 + y[0] ** 2 + y[1] ** 2 + # dzdx0 = 2 * x[0] # Compute dy/dx0 + # dzdx1 = 2 * x[1] # Compute dy/dx1 + # dzdx2 = 2 * x[2] # Compute dy/dx2 + # dzdy0 = 2 * y[0] + # dzdy1 = 2 * y[1] + + # z = z * units.dimensionless + # dz = np.array([dzdx0, dzdx1, dzdx2, dzdy0, dzdy1]) * units.dimensionless + # # dydx0 = dydx0 * units.dimensionless + # # dydx1 = dydx1 * units.dimensionless + # # dydx2 = dydx2 * units.dimensionless + + # return z, [dz] # return z, grad(z), hess(z)... + + # bb = Norm_2() + # bbo = bb.BlackBox( + # { + # 'x': np.array([0, 0, 0]) * units.dimensionless, + # 'y': np.array([0, 0]) * units.dimensionless, + # } + # ) + # bbo = bb.BlackBox( + # np.array([0, 0, 0]) * units.dimensionless, + # y=np.array([0, 0]) * units.dimensionless, + # ) + + # self.assertRaises( + # ValueError, + # bb.BlackBox, + # *( + # { + # 'er': np.array([0, 0, 0]) * units.dimensionless, + # 'y': np.array([0, 0]) * units.dimensionless, + # }, + # ) + # ) + # self.assertRaises(ValueError, bb.BlackBox, *('err',)) + # self.assertRaises( + # ValueError, + # bb.BlackBox, + # *( + # np.array([0, 0, 0]) * units.dimensionless, + # np.array([0, 0]) * units.dimensionless, + # ), + # **{'x': 'err too many'} + # ) + # self.assertRaises( + # ValueError, + # bb.BlackBox, + # *(np.array([0, 0, 0]) * units.dimensionless,), + # **{'notY': np.array([0, 0]) * units.dimensionless} + # ) + + # def test_edi_blackbox_bare_example_4(self): + # "Tests a black box example construction without an optimization problem" + # import numpy as np + # import pyomo.environ as pyo + # from pyomo.environ import units + # from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + # from pyomo.common.formatting import tostr + + # class PassThrough(BlackBoxFunctionModel): + # def __init__(self): + # # Set up all the attributes by calling Model.__init__ + # super().__init__() + + # # Setup Inputs + # self.inputs.append('x', '', 'X Variable') + # self.inputs.append('y', '', 'Y Variable') + + # # Setup Outputs + # self.outputs.append('u', '', 'U Variable') + # self.outputs.append('v', '', 'V Variable') + + # # Simple model description + # self.description = 'This model is a pass through)' + + # self.availableDerivative = 1 + + # # standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) + # def BlackBox(self, *args, **kwargs): + # runCases, returnMode, extras = self.parseInputs(*args, **kwargs) + # x = [ + # self.pyomo_value(runCases[i]['x']) for i in range(0, len(runCases)) + # ] + # y = [ + # self.pyomo_value(runCases[i]['x']) for i in range(0, len(runCases)) + # ] + + # u = [] + # dudx = [] + # dudy = [] + # v = [] + # dvdx = [] + # dvdy = [] + + # for xval in x: + # u.append(xval * units.dimensionless) + # dudx.append(1.0 * units.dimensionless) + # dudy.append(0.0 * units.dimensionless) + + # for yval in y: + # v.append(yval * units.dimensionless) + # dvdx.append(0.0 * units.dimensionless) + # dvdy.append(1.0 * units.dimensionless) + + # if returnMode < 0: + # returnMode = -1 * (returnMode + 1) + # if returnMode == 0: + # return [u[0], v[0]] + # if returnMode == 1: + # return [u[0], v[0]], [[dudx[0], dudy[0]], [dvdx[0], dvdy[0]]] + # else: + # if returnMode == 0: + # opt = [] + # for i in range(0, len(y)): + # opt.append([u[i], v[i]]) + # return opt + # if returnMode == 1: + # opt = [] + # for i in range(0, len(y)): + # opt.append( + # [[u[i], v[i]], [[dudx[i], dudy[i]], [dvdx[i], dvdy[i]]]] + # ) + # return opt + + # bb = PassThrough() + # bbo = bb.BlackBox(1.0, 1.0) + # bbo = bb.BlackBox({'x': np.linspace(0, 10, 11), 'y': np.linspace(0, 10, 11)}) if __name__ == '__main__': From 12371160cd51863da2825fc18407ecf0c8ad104b Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Mon, 11 Dec 2023 14:49:05 -0700 Subject: [PATCH 45/61] updating the black box tests to pass --- .../contrib/edi/.blackBoxFunctionModel.py.swp | Bin 0 -> 16384 bytes pyomo/contrib/edi/blackBoxFunctionModel.py | 8 +- pyomo/contrib/edi/tests/test_blackbox.py | 700 +++++++++--------- pyomo/contrib/edi/tests/test_docSnippets.py | 12 +- 4 files changed, 366 insertions(+), 354 deletions(-) create mode 100644 pyomo/contrib/edi/.blackBoxFunctionModel.py.swp diff --git a/pyomo/contrib/edi/.blackBoxFunctionModel.py.swp b/pyomo/contrib/edi/.blackBoxFunctionModel.py.swp new file mode 100644 index 0000000000000000000000000000000000000000..095a5a5cc3b8b0e78aa1ed1ea0e57f019c145f3a GIT binary patch literal 16384 zcmeHNO^h5z8Lb48kdS~wK!}J##S5BQJUbq*G1x|{*t@f~7J0o#$|66N zakLl*BCIC8S8BoWP64OD#tPiVc2DmzOMPPdUHp!B+_!O2$2bL?0!{&^fK$LJ;1qBQ zI0c*nZ#V_gWE=YodUd_+nXuRE#;*Ud>613UHOBwj@@3;9MuniRLylnXu z%YSZ+f64NPEdR_H|Dxp=E&o2-p~C)U%P(2}u>8j?|DHGH{}lG0 zxBTJ4{uuvH%inAHC&&1ISiabi-SXy9`@HWQ22x#CWPzIj4jj=BQbzl~_9{Ay{jQs%kCh%oI0RmV6?gakwR>qzM{tWyG zI0t+dco?VyHvu;SHvrGw!r1SCUjgTV72qha2Scv%X(p0I! zO9Wn89oRw%sb!>7FY-kh7Ast*YI0Cuf@_iTkv2Fgc|6L(kjl!aO)e&$)*|3CG7bcy z#j{AJx~xUms!%_y%>yR83Ztn|tnaN7X{I8y6}GAkQ)C@Gg^5JPiBM_J6q(>NTaj$r zb>Bu-%;GtJ*VsRh;4DN}SB3 z5(xNo$gfW&y(sGn74tNvm}2fIVTc<7ev61&;pX%oZ^oy&?X!I5$Ygyr6{iAG)mXG(? zSQo2g)2XzbVg6J`ClO)q>uJAbpGk%tu`{2Do3p%FF?@@7Ed0n&MWkep6C3@hj3oY9bwSO>JqvvQ;Kw^`V~fm?+1~(Y|IcxubP@agUjn}X&I3;Y z2Y?IM^FIwd0elU32zU@+z&Y&wL*S#pdx1;X>;E433GiLu5#V9K1M0vI;CA2&+P(<< z9C#8SU%n2w

lUI0c*nP64NYQ@|`j@;z#sa-ci`jB>g3>y zj>()c-0|A}sp-|Li*tP6xjOfnY*5qvW{1rT6LN>8IZ+dN!E@$1Yr+Vf5 o%@`@BZ4M2SmHI&c+uoM;fyVUpq8}}xfvEij^@(WxIbR~;zkO*{2><{9 literal 0 HcmV?d00001 diff --git a/pyomo/contrib/edi/blackBoxFunctionModel.py b/pyomo/contrib/edi/blackBoxFunctionModel.py index b98ce3de0c4..76751772378 100644 --- a/pyomo/contrib/edi/blackBoxFunctionModel.py +++ b/pyomo/contrib/edi/blackBoxFunctionModel.py @@ -989,7 +989,7 @@ def parseInputs(self, *args, **kwargs): return ( [dict(zip(inputNames, sips))], - -self.availableDerivative - 1, + # -self.availableDerivative - 1, remainingKwargs, ) # Mix of args and kwargs define inputs else: @@ -1004,7 +1004,7 @@ def parseInputs(self, *args, **kwargs): remainingKwargs['remainingArgs'] = args[len(inputNames) :] return ( [dict(zip(inputNames, sips))], - -self.availableDerivative - 1, + # -self.availableDerivative - 1, remainingKwargs, ) # all inputs are in args # except: @@ -1015,12 +1015,12 @@ def parseInputs(self, *args, **kwargs): if str(e) == 'Not enough inputs': raise ValueError(e) else: # otherwise, proceed - runCases, returnMode, extra_singleInput = self.parseInputs( + runCases, extra_singleInput = self.parseInputs( args[0] ) remainingKwargs = copy.deepcopy(kwargs) remainingKwargs['remainingArgs'] = args[len(inputNames) :] - return runCases, returnMode, remainingKwargs + return runCases, remainingKwargs # --------------------------------------------------------------------------------------------------------------------- # --------------------------------------------------------------------------------------------------------------------- diff --git a/pyomo/contrib/edi/tests/test_blackbox.py b/pyomo/contrib/edi/tests/test_blackbox.py index 5636d9f7b70..64332389494 100644 --- a/pyomo/contrib/edi/tests/test_blackbox.py +++ b/pyomo/contrib/edi/tests/test_blackbox.py @@ -709,353 +709,359 @@ def test_edi_blackbox_smallfunctions(self): ) self.assertRaises(ValueError, bb.sizeCheck, *([10, 10], [])) - # def test_edi_blackbox_bare_example_1(self): - # "Tests a black box example construction without an optimization problem" - # import numpy as np - # import pyomo.environ as pyo - # from pyomo.environ import units - # from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel - - # class SignomialTest(BlackBoxFunctionModel): - # def __init__(self): - # # Set up all the attributes by calling Model.__init__ - # super().__init__() - - # # Setup Inputs - # self.inputs.append('x', '', 'Independent Variable') - - # # Setup Outputs - # self.outputs.append('y', '', 'Dependent Variable') - - # # Simple model description - # self.description = ( - # 'This model evaluates the function: max([-6*x-6, x**4-3*x**2])' - # ) - - # self.availableDerivative = 1 - - # # standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) - # def BlackBox(self, *args, **kwargs): - # runCases, returnMode, extras = self.parseInputs(*args, **kwargs) - # x = np.array( - # [pyo.value(runCases[i]['x']) for i in range(0, len(runCases))] - # ) - - # y = np.maximum(-6 * x - 6, x**4 - 3 * x**2) - # dydx = 4 * x**3 - 6 * x - # ddy_ddx = 12 * x**2 - 6 - # gradientSwitch = -6 * x - 6 > x**4 - 3 * x**2 - # dydx[gradientSwitch] = -6 - # ddy_ddx[gradientSwitch] = 0 - - # y = [yval * units.dimensionless for yval in y] - # dydx = [dydx[i] * units.dimensionless for i in range(0, len(dydx))] - - # if returnMode < 0: - # returnMode = -1 * (returnMode + 1) - # if returnMode == 0: - # return y[0] - # if returnMode == 1: - # return y[0], dydx - # else: - # if returnMode == 0: - # opt = [] - # for i in range(0, len(y)): - # opt.append([y[i]]) - # return opt - # if returnMode == 1: - # opt = [] - # for i in range(0, len(y)): - # opt.append([[y[i]], [[[dydx[i]]]]]) - # return opt - - # s = SignomialTest() - # ivals = [[x] for x in np.linspace(-2, 2, 11)] - - # # How the black box may be called using EDI - # bbo = s.BlackBox(**{'x': 0.5}) - # bbo = s.BlackBox({'x': 0.5}) - # bbo = s.BlackBox(**{'x': 0.5, 'optn': True}) - - # # Additional options available with parseInputs - # bbo = s.BlackBox(*[0.5], **{'optn1': True, 'optn2': False}) - # bbo = s.BlackBox(*[0.5, True], **{'optn': False}) - # bbo = s.BlackBox({'x': [x for x in np.linspace(-2, 2, 11)]}) - # bbo = s.BlackBox([{'x': x} for x in np.linspace(-2, 2, 11)]) - # bbo = s.BlackBox([[x] for x in np.linspace(-2, 2, 11)]) - # bbo = s.BlackBox([[x] for x in np.linspace(-2, 2, 11)], True, optn=False) - # bbo = s.BlackBox([[x] for x in np.linspace(-2, 2, 11)], optn1=True, optn2=False) - - # sm = s.summary - - # self.assertRaises(ValueError, s.BlackBox, *([[[1, 2], 2, 3], [1, 2, 3]])) - # self.assertRaises(ValueError, s.BlackBox, *([[np.ones(2), 2, 3], [1, 2, 3]])) - - # self.assertRaises(ValueError, s.sanitizeInputs, *()) - # self.assertRaises(ValueError, s.sanitizeInputs, *(1, 2, 3)) - # self.assertRaises( - # ValueError, s.sanitizeInputs, **{'z': 2.0 * units.dimensionless} - # ) - # self.assertRaises(ValueError, s.sanitizeInputs, *(2.0 * units.ft,)) - # self.assertRaises(NotImplementedError, s.checkOutputs, *()) - - # def test_edi_blackbox_bare_example_2(self): - # "Tests a black box example construction without an optimization problem" - # import numpy as np - # import pyomo.environ as pyo - # from pyomo.environ import units - # from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel - # from pyomo.common.formatting import tostr - - # class PassThrough(BlackBoxFunctionModel): - # def __init__(self): - # # Set up all the attributes by calling Model.__init__ - # super().__init__() - - # # Setup Inputs - # self.inputs.append('x', '', 'Independent Variable', size=[2, 2]) - - # # Setup Outputs - # self.outputs.append('y', '', 'Dependent Variable', size=[2, 2]) - - # # Simple model description - # self.description = 'This model is a pass through)' - - # self.availableDerivative = 1 - - # # standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) - # def BlackBox(self, *args, **kwargs): - # runCases, returnMode, extras = self.parseInputs(*args, **kwargs) - # x = [ - # self.pyomo_value(runCases[i]['x']) for i in range(0, len(runCases)) - # ] - - # y = [] - # dydx = [] - - # for xval in x: - # y.append(xval * units.dimensionless) - # dydx_temp = np.zeros([2, 2, 2, 2]) - # dydx_temp[0, 0, 0, 0] = 1.0 - # dydx_temp[0, 1, 0, 1] = 1.0 - # dydx_temp[1, 0, 1, 0] = 1.0 - # dydx_temp[1, 1, 1, 1] = 1.0 - - # dydx.append(dydx_temp * units.dimensionless) - - # if returnMode < 0: - # returnMode = -1 * (returnMode + 1) - # if returnMode == 0: - # return y[0] - # if returnMode == 1: - # return y[0], dydx - # else: - # if returnMode == 0: - # opt = [] - # for i in range(0, len(y)): - # opt.append([y[i]]) - # return opt - # if returnMode == 1: - # opt = [] - # for i in range(0, len(y)): - # opt.append([[y[i]], [[[dydx[i]]]]]) - # return opt - - # bb = PassThrough() - # ivals = [ - # [np.eye(2) * units.dimensionless], - # [np.ones([2, 2]) * units.dimensionless], - # [np.zeros([2, 2]) * units.dimensionless], - # ] - - # xv = np.eye(2) * units.dimensionless - - # # How the black box may be called using EDI - # bbo = bb.BlackBox(**{'x': xv}) - # bbo = bb.BlackBox({'x': xv}) - # bbo = bb.BlackBox(**{'x': xv, 'optn': True}) - - # # # Additional options available with parseInputs - # bbo = bb.BlackBox(*[xv], **{'optn1': True, 'optn2': False}) - # bbo = bb.BlackBox(*[xv, True], **{'optn': False}) - # bbo = bb.BlackBox({'x': [x[0] for x in ivals]}) - # bbo = bb.BlackBox([{'x': x[0]} for x in ivals]) - # bbo = bb.BlackBox([[x[0]] for x in ivals]) - # bbo = bb.BlackBox([[x[0]] for x in ivals], True, optn=False) - # bbo = bb.BlackBox([[x[0]] for x in ivals], optn1=True, optn2=False) - - # sm = bb.summary - - # self.assertRaises(ValueError, bb.sanitizeInputs, *(np.ones([2, 2]) * units.ft,)) - - # def test_edi_blackbox_bare_example_3(self): - # "Tests a black box example construction" - # import numpy as np - # from pyomo.environ import units - # import pyomo.environ as pyo - # from pyomo.contrib.edi import Formulation - # from pyomo.contrib.edi.blackBoxFunctionModel import ( - # BlackBoxFunctionModel_Variable, - # TypeCheckedList, - # BBList, - # BlackBoxFunctionModel, - # ) - - # class Norm_2(BlackBoxFunctionModel): - # def __init__(self): - # super().__init__() - # self.description = 'This model evaluates the two norm' - # self.inputs.append( - # name='x', units='', description='The x variable', size=3 - # ) - # self.inputs.append( - # name='y', units='', description='The y variable', size=2 - # ) - # self.outputs.append(name='z', units='', description='The z variable') - # self.availableDerivative = 1 - # self.post_init_setup(len(self.inputs)) - - # def BlackBox(self, *args, **kwargs): # The actual function that does things - # runCases, returnMode, remainingKwargs = self.parseInputs( - # *args, **kwargs - # ) - - # x = [rc['x'] for rc in runCases][0] - # x = np.array([self.pyomo_value(xval) for xval in x], dtype=np.float64) - - # y = [rc['y'] for rc in runCases][0] - # y = np.array([self.pyomo_value(yval) for yval in y], dtype=np.float64) - - # z = x[0] ** 2 + x[1] ** 2 + x[2] ** 2 + y[0] ** 2 + y[1] ** 2 - # dzdx0 = 2 * x[0] # Compute dy/dx0 - # dzdx1 = 2 * x[1] # Compute dy/dx1 - # dzdx2 = 2 * x[2] # Compute dy/dx2 - # dzdy0 = 2 * y[0] - # dzdy1 = 2 * y[1] - - # z = z * units.dimensionless - # dz = np.array([dzdx0, dzdx1, dzdx2, dzdy0, dzdy1]) * units.dimensionless - # # dydx0 = dydx0 * units.dimensionless - # # dydx1 = dydx1 * units.dimensionless - # # dydx2 = dydx2 * units.dimensionless - - # return z, [dz] # return z, grad(z), hess(z)... - - # bb = Norm_2() - # bbo = bb.BlackBox( - # { - # 'x': np.array([0, 0, 0]) * units.dimensionless, - # 'y': np.array([0, 0]) * units.dimensionless, - # } - # ) - # bbo = bb.BlackBox( - # np.array([0, 0, 0]) * units.dimensionless, - # y=np.array([0, 0]) * units.dimensionless, - # ) - - # self.assertRaises( - # ValueError, - # bb.BlackBox, - # *( - # { - # 'er': np.array([0, 0, 0]) * units.dimensionless, - # 'y': np.array([0, 0]) * units.dimensionless, - # }, - # ) - # ) - # self.assertRaises(ValueError, bb.BlackBox, *('err',)) - # self.assertRaises( - # ValueError, - # bb.BlackBox, - # *( - # np.array([0, 0, 0]) * units.dimensionless, - # np.array([0, 0]) * units.dimensionless, - # ), - # **{'x': 'err too many'} - # ) - # self.assertRaises( - # ValueError, - # bb.BlackBox, - # *(np.array([0, 0, 0]) * units.dimensionless,), - # **{'notY': np.array([0, 0]) * units.dimensionless} - # ) - - # def test_edi_blackbox_bare_example_4(self): - # "Tests a black box example construction without an optimization problem" - # import numpy as np - # import pyomo.environ as pyo - # from pyomo.environ import units - # from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel - # from pyomo.common.formatting import tostr - - # class PassThrough(BlackBoxFunctionModel): - # def __init__(self): - # # Set up all the attributes by calling Model.__init__ - # super().__init__() - - # # Setup Inputs - # self.inputs.append('x', '', 'X Variable') - # self.inputs.append('y', '', 'Y Variable') - - # # Setup Outputs - # self.outputs.append('u', '', 'U Variable') - # self.outputs.append('v', '', 'V Variable') - - # # Simple model description - # self.description = 'This model is a pass through)' - - # self.availableDerivative = 1 - - # # standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) - # def BlackBox(self, *args, **kwargs): - # runCases, returnMode, extras = self.parseInputs(*args, **kwargs) - # x = [ - # self.pyomo_value(runCases[i]['x']) for i in range(0, len(runCases)) - # ] - # y = [ - # self.pyomo_value(runCases[i]['x']) for i in range(0, len(runCases)) - # ] - - # u = [] - # dudx = [] - # dudy = [] - # v = [] - # dvdx = [] - # dvdy = [] - - # for xval in x: - # u.append(xval * units.dimensionless) - # dudx.append(1.0 * units.dimensionless) - # dudy.append(0.0 * units.dimensionless) - - # for yval in y: - # v.append(yval * units.dimensionless) - # dvdx.append(0.0 * units.dimensionless) - # dvdy.append(1.0 * units.dimensionless) - - # if returnMode < 0: - # returnMode = -1 * (returnMode + 1) - # if returnMode == 0: - # return [u[0], v[0]] - # if returnMode == 1: - # return [u[0], v[0]], [[dudx[0], dudy[0]], [dvdx[0], dvdy[0]]] - # else: - # if returnMode == 0: - # opt = [] - # for i in range(0, len(y)): - # opt.append([u[i], v[i]]) - # return opt - # if returnMode == 1: - # opt = [] - # for i in range(0, len(y)): - # opt.append( - # [[u[i], v[i]], [[dudx[i], dudy[i]], [dvdx[i], dvdy[i]]]] - # ) - # return opt - - # bb = PassThrough() - # bbo = bb.BlackBox(1.0, 1.0) - # bbo = bb.BlackBox({'x': np.linspace(0, 10, 11), 'y': np.linspace(0, 10, 11)}) + def test_edi_blackbox_bare_example_1(self): + "Tests a black box example construction without an optimization problem" + import numpy as np + import pyomo.environ as pyo + from pyomo.environ import units + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + + class SignomialTest(BlackBoxFunctionModel): + def __init__(self): + # Set up all the attributes by calling Model.__init__ + super().__init__() + + # Setup Inputs + self.inputs.append('x', '', 'Independent Variable') + + # Setup Outputs + self.outputs.append('y', '', 'Dependent Variable') + + # Simple model description + self.description = ( + 'This model evaluates the function: max([-6*x-6, x**4-3*x**2])' + ) + + self.availableDerivative = 1 + + # standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) + def BlackBox(self, *args, **kwargs): + runCases, extras = self.parseInputs(*args, **kwargs) + x = np.array( + [pyo.value(runCases[i]['x']) for i in range(0, len(runCases))] + ) + + y = np.maximum(-6 * x - 6, x**4 - 3 * x**2) + dydx = 4 * x**3 - 6 * x + ddy_ddx = 12 * x**2 - 6 + gradientSwitch = -6 * x - 6 > x**4 - 3 * x**2 + dydx[gradientSwitch] = -6 + ddy_ddx[gradientSwitch] = 0 + + y = [yval * units.dimensionless for yval in y] + dydx = [dydx[i] * units.dimensionless for i in range(0, len(dydx))] + + returnMode = 1 + + if returnMode < 0: + returnMode = -1 * (returnMode + 1) + if returnMode == 0: + return y[0] + if returnMode == 1: + return y[0], dydx + else: + if returnMode == 0: + opt = [] + for i in range(0, len(y)): + opt.append([y[i]]) + return opt + if returnMode == 1: + opt = [] + for i in range(0, len(y)): + opt.append([[y[i]], [[[dydx[i]]]]]) + return opt + + s = SignomialTest() + ivals = [[x] for x in np.linspace(-2, 2, 11)] + + # How the black box may be called using EDI + bbo = s.BlackBox(**{'x': 0.5}) + bbo = s.BlackBox({'x': 0.5}) + bbo = s.BlackBox(**{'x': 0.5, 'optn': True}) + + # Additional options available with parseInputs + bbo = s.BlackBox(*[0.5], **{'optn1': True, 'optn2': False}) + bbo = s.BlackBox(*[0.5, True], **{'optn': False}) + bbo = s.BlackBox({'x': [x for x in np.linspace(-2, 2, 11)]}) + bbo = s.BlackBox([{'x': x} for x in np.linspace(-2, 2, 11)]) + bbo = s.BlackBox([[x] for x in np.linspace(-2, 2, 11)]) + bbo = s.BlackBox([[x] for x in np.linspace(-2, 2, 11)], True, optn=False) + bbo = s.BlackBox([[x] for x in np.linspace(-2, 2, 11)], optn1=True, optn2=False) + + sm = s.summary + + self.assertRaises(ValueError, s.BlackBox, *([[[1, 2], 2, 3], [1, 2, 3]])) + self.assertRaises(ValueError, s.BlackBox, *([[np.ones(2), 2, 3], [1, 2, 3]])) + + self.assertRaises(ValueError, s.sanitizeInputs, *()) + self.assertRaises(ValueError, s.sanitizeInputs, *(1, 2, 3)) + self.assertRaises( + ValueError, s.sanitizeInputs, **{'z': 2.0 * units.dimensionless} + ) + self.assertRaises(ValueError, s.sanitizeInputs, *(2.0 * units.ft,)) + self.assertRaises(NotImplementedError, s.checkOutputs, *()) + + def test_edi_blackbox_bare_example_2(self): + "Tests a black box example construction without an optimization problem" + import numpy as np + import pyomo.environ as pyo + from pyomo.environ import units + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + from pyomo.common.formatting import tostr + + class PassThrough(BlackBoxFunctionModel): + def __init__(self): + # Set up all the attributes by calling Model.__init__ + super().__init__() + + # Setup Inputs + self.inputs.append('x', '', 'Independent Variable', size=[2, 2]) + + # Setup Outputs + self.outputs.append('y', '', 'Dependent Variable', size=[2, 2]) + + # Simple model description + self.description = 'This model is a pass through)' + + self.availableDerivative = 1 + + # standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) + def BlackBox(self, *args, **kwargs): + runCases, extras = self.parseInputs(*args, **kwargs) + x = [ + self.pyomo_value(runCases[i]['x']) for i in range(0, len(runCases)) + ] + + y = [] + dydx = [] + + for xval in x: + y.append(xval * units.dimensionless) + dydx_temp = np.zeros([2, 2, 2, 2]) + dydx_temp[0, 0, 0, 0] = 1.0 + dydx_temp[0, 1, 0, 1] = 1.0 + dydx_temp[1, 0, 1, 0] = 1.0 + dydx_temp[1, 1, 1, 1] = 1.0 + + dydx.append(dydx_temp * units.dimensionless) + + returnMode = 1 + + if returnMode < 0: + returnMode = -1 * (returnMode + 1) + if returnMode == 0: + return y[0] + if returnMode == 1: + return y[0], dydx + else: + if returnMode == 0: + opt = [] + for i in range(0, len(y)): + opt.append([y[i]]) + return opt + if returnMode == 1: + opt = [] + for i in range(0, len(y)): + opt.append([[y[i]], [[[dydx[i]]]]]) + return opt + + bb = PassThrough() + ivals = [ + [np.eye(2) * units.dimensionless], + [np.ones([2, 2]) * units.dimensionless], + [np.zeros([2, 2]) * units.dimensionless], + ] + + xv = np.eye(2) * units.dimensionless + + # How the black box may be called using EDI + bbo = bb.BlackBox(**{'x': xv}) + bbo = bb.BlackBox({'x': xv}) + bbo = bb.BlackBox(**{'x': xv, 'optn': True}) + + # # Additional options available with parseInputs + bbo = bb.BlackBox(*[xv], **{'optn1': True, 'optn2': False}) + bbo = bb.BlackBox(*[xv, True], **{'optn': False}) + bbo = bb.BlackBox({'x': [x[0] for x in ivals]}) + bbo = bb.BlackBox([{'x': x[0]} for x in ivals]) + bbo = bb.BlackBox([[x[0]] for x in ivals]) + bbo = bb.BlackBox([[x[0]] for x in ivals], True, optn=False) + bbo = bb.BlackBox([[x[0]] for x in ivals], optn1=True, optn2=False) + + sm = bb.summary + + self.assertRaises(ValueError, bb.sanitizeInputs, *(np.ones([2, 2]) * units.ft,)) + + def test_edi_blackbox_bare_example_3(self): + "Tests a black box example construction" + import numpy as np + from pyomo.environ import units + import pyomo.environ as pyo + from pyomo.contrib.edi import Formulation + from pyomo.contrib.edi.blackBoxFunctionModel import ( + BlackBoxFunctionModel_Variable, + TypeCheckedList, + BBList, + BlackBoxFunctionModel, + ) + + class Norm_2(BlackBoxFunctionModel): + def __init__(self): + super().__init__() + self.description = 'This model evaluates the two norm' + self.inputs.append( + name='x', units='', description='The x variable', size=3 + ) + self.inputs.append( + name='y', units='', description='The y variable', size=2 + ) + self.outputs.append(name='z', units='', description='The z variable') + self.availableDerivative = 1 + self.post_init_setup(len(self.inputs)) + + def BlackBox(self, *args, **kwargs): # The actual function that does things + runCases, remainingKwargs = self.parseInputs( + *args, **kwargs + ) + + x = [rc['x'] for rc in runCases][0] + x = np.array([self.pyomo_value(xval) for xval in x], dtype=np.float64) + + y = [rc['y'] for rc in runCases][0] + y = np.array([self.pyomo_value(yval) for yval in y], dtype=np.float64) + + z = x[0] ** 2 + x[1] ** 2 + x[2] ** 2 + y[0] ** 2 + y[1] ** 2 + dzdx0 = 2 * x[0] # Compute dy/dx0 + dzdx1 = 2 * x[1] # Compute dy/dx1 + dzdx2 = 2 * x[2] # Compute dy/dx2 + dzdy0 = 2 * y[0] + dzdy1 = 2 * y[1] + + z = z * units.dimensionless + dz = np.array([dzdx0, dzdx1, dzdx2, dzdy0, dzdy1]) * units.dimensionless + # dydx0 = dydx0 * units.dimensionless + # dydx1 = dydx1 * units.dimensionless + # dydx2 = dydx2 * units.dimensionless + + return z, [dz] # return z, grad(z), hess(z)... + + bb = Norm_2() + bbo = bb.BlackBox( + { + 'x': np.array([0, 0, 0]) * units.dimensionless, + 'y': np.array([0, 0]) * units.dimensionless, + } + ) + bbo = bb.BlackBox( + np.array([0, 0, 0]) * units.dimensionless, + y=np.array([0, 0]) * units.dimensionless, + ) + + self.assertRaises( + ValueError, + bb.BlackBox, + *( + { + 'er': np.array([0, 0, 0]) * units.dimensionless, + 'y': np.array([0, 0]) * units.dimensionless, + }, + ) + ) + self.assertRaises(ValueError, bb.BlackBox, *('err',)) + self.assertRaises( + ValueError, + bb.BlackBox, + *( + np.array([0, 0, 0]) * units.dimensionless, + np.array([0, 0]) * units.dimensionless, + ), + **{'x': 'err too many'} + ) + self.assertRaises( + ValueError, + bb.BlackBox, + *(np.array([0, 0, 0]) * units.dimensionless,), + **{'notY': np.array([0, 0]) * units.dimensionless} + ) + + def test_edi_blackbox_bare_example_4(self): + "Tests a black box example construction without an optimization problem" + import numpy as np + import pyomo.environ as pyo + from pyomo.environ import units + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + from pyomo.common.formatting import tostr + + class PassThrough(BlackBoxFunctionModel): + def __init__(self): + # Set up all the attributes by calling Model.__init__ + super().__init__() + + # Setup Inputs + self.inputs.append('x', '', 'X Variable') + self.inputs.append('y', '', 'Y Variable') + + # Setup Outputs + self.outputs.append('u', '', 'U Variable') + self.outputs.append('v', '', 'V Variable') + + # Simple model description + self.description = 'This model is a pass through)' + + self.availableDerivative = 1 + + # standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) + def BlackBox(self, *args, **kwargs): + runCases, extras = self.parseInputs(*args, **kwargs) + x = [ + self.pyomo_value(runCases[i]['x']) for i in range(0, len(runCases)) + ] + y = [ + self.pyomo_value(runCases[i]['x']) for i in range(0, len(runCases)) + ] + + u = [] + dudx = [] + dudy = [] + v = [] + dvdx = [] + dvdy = [] + + for xval in x: + u.append(xval * units.dimensionless) + dudx.append(1.0 * units.dimensionless) + dudy.append(0.0 * units.dimensionless) + + for yval in y: + v.append(yval * units.dimensionless) + dvdx.append(0.0 * units.dimensionless) + dvdy.append(1.0 * units.dimensionless) + + returnMode = 1 + + if returnMode < 0: + returnMode = -1 * (returnMode + 1) + if returnMode == 0: + return [u[0], v[0]] + if returnMode == 1: + return [u[0], v[0]], [[dudx[0], dudy[0]], [dvdx[0], dvdy[0]]] + else: + if returnMode == 0: + opt = [] + for i in range(0, len(y)): + opt.append([u[i], v[i]]) + return opt + if returnMode == 1: + opt = [] + for i in range(0, len(y)): + opt.append( + [[u[i], v[i]], [[dudx[i], dudy[i]], [dvdx[i], dvdy[i]]]] + ) + return opt + + bb = PassThrough() + bbo = bb.BlackBox(1.0, 1.0) + bbo = bb.BlackBox({'x': np.linspace(0, 10, 11), 'y': np.linspace(0, 10, 11)}) if __name__ == '__main__': diff --git a/pyomo/contrib/edi/tests/test_docSnippets.py b/pyomo/contrib/edi/tests/test_docSnippets.py index d6c7a9500fe..1ca53d3200f 100644 --- a/pyomo/contrib/edi/tests/test_docSnippets.py +++ b/pyomo/contrib/edi/tests/test_docSnippets.py @@ -780,7 +780,7 @@ def __init__(self): # standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) def BlackBox(self, *args, **kwargs): - runCases, returnMode, extras = self.parseInputs(*args, **kwargs) + runCases, extras = self.parseInputs(*args, **kwargs) x = [ self.pyomo_value(runCases[i]['x']) for i in range(0, len(runCases)) ] @@ -805,6 +805,8 @@ def BlackBox(self, *args, **kwargs): dvdx.append(0.0 * units.dimensionless) dvdy.append(1.0 * units.dimensionless) + returnMode = 1 + if returnMode < 0: returnMode = -1 * (returnMode + 1) if returnMode == 0: @@ -896,7 +898,7 @@ def __init__(self): self.availableDerivative = 1 def BlackBox(self, *args, **kwargs): - runCases, returnMode, extras = self.parseInputs(*args, **kwargs) + runCases, extras = self.parseInputs(*args, **kwargs) x = np.array( [pyo.value(runCases[i]['x']) for i in range(0, len(runCases))] @@ -912,6 +914,8 @@ def BlackBox(self, *args, **kwargs): y = [yval * units.dimensionless for yval in y] dydx = [dydx[i] * units.dimensionless for i in range(0, len(dydx))] + returnMode = 1 + if returnMode < 0: returnMode = -1 * (returnMode + 1) if returnMode == 0: @@ -974,7 +978,7 @@ def __init__(self): # BEGIN: AdvancedRTC_Snippet_02 def BlackBox(self, *args, **kwargs): - runCases, returnMode, extras = self.parseInputs(*args, **kwargs) + runCases, extras = self.parseInputs(*args, **kwargs) # END: AdvancedRTC_Snippet_02 x = np.array( @@ -991,6 +995,8 @@ def BlackBox(self, *args, **kwargs): y = [yval * units.dimensionless for yval in y] dydx = [dydx[i] * units.dimensionless for i in range(0, len(dydx))] + returnMode = 1 + if returnMode < 0: returnMode = -1 * (returnMode + 1) if returnMode == 0: From 782696fa9b6372fcf32dca7a278b5c7c19887aa0 Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Wed, 6 Mar 2024 22:43:35 -0700 Subject: [PATCH 46/61] figuring out what Im doing --- .../edi/blackboxconstraints.rst | 157 +++++++++++++++--- .../contributed_packages/edi/formulation.rst | 29 +--- .../contributed_packages/edi/index.rst | 1 - .../contrib/edi/.blackBoxFunctionModel.py.swp | Bin 16384 -> 0 bytes pyomo/contrib/edi/__init__.py | 9 +- pyomo/contrib/edi/blackBoxFunctionModel.py | 70 +++++--- pyomo/contrib/edi/tests/test_blackbox.py | 22 +-- pyomo/contrib/edi/tests/test_docSnippets.py | 128 +++++++++++--- 8 files changed, 300 insertions(+), 116 deletions(-) delete mode 100644 pyomo/contrib/edi/.blackBoxFunctionModel.py.swp diff --git a/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst b/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst index b9d8f821711..9c14ef21573 100644 --- a/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst +++ b/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst @@ -7,7 +7,7 @@ Overview While some constraints are explicitly known and can be written directly into the optimization problem, it is common (particularly in engineering design) for some relationships to be too complex to be directly coded as a constraint. -EDI refers to these types of constraints as ``RuntimeConstraints`` because they are not constructed until they are needed by the solver. A particular subset of Runtime Constraints of interest are Black-Box constraints, that is, constraints which call to an external routine. To the average pyomo and EDI user, ``RuntimeConstraints`` are for all intents and purposes Black-Box constraint, and the distinction is semantic. +EDI refers to these types of constraints as ``RuntimeConstraints`` because they are not constructed until they are needed by the solver. A particular subset of Runtime Constraints of interest are Black-Box constraints, that is, constraints which call to an external routine. To the average pyomo and EDI user, ``RuntimeConstraints`` are (for all intents and purposes) Black-Box constraints, and the distinction is semantic. In other words, if you wish to code a black-box constraint using EDI, you will be using the Runtime Constraint constructor. @@ -17,7 +17,7 @@ In this context, a *Black-Box* is defined as a routine that performs hidden comp Construction ------------ -Runtime constraints consist of two separate elements that need to be constructed. +Runtime constraints consist of two separate elements that need to be constructed: an ``__init__`` function and a ``BlackBox`` function. Additionally, there are two pre-implemented functions that intermediate to advanced users will wish to interface with: ``BlackBox_Standardized`` and ``MultiCase``. Constructing a Black Box @@ -75,7 +75,6 @@ Models with multiple inputs simply call the ``self.input.append()`` command mult Input names must be unique, and an error is raised if a repeated name is attempted to be set. - Next, outputs must be added to the model. This is done identically to inputs, however the function is now ``self.outputs.append()`` .. py:function:: self.outputs.append(name, units, description='', size=0) @@ -113,10 +112,12 @@ Finally, the highest available derivative must be set. For models being used in The BlackBox method ******************* -The ``BlackBox`` is extremely flexible, but here we present standard usage for a beginner user. Advanced users should also check the :doc:`advanced <./advancedruntimeconstraints>` documentation for more flexible ways of building black-box models +The ``BlackBox`` is extremely flexible, but here we present standard usage for a typical user. The ``BlackBox`` method assumes to take in the inputs as arguments in the order defined during the ``__init__()`` method. Note that the method assumes inputs **with units** and expects outputs **with units**. In general, the units on inputs and outputs need not be in any specific system, but should be convertible (ex, meters and feet) to whatever has been specified as the input units when defining in the ``__init__()`` function. +The unit handling system in Pyomo can be rough at times, and so the BlackBox function is expected to return values that are ``pint.Quantity`` types. These are obtained using the ``pyo.as_quantity()`` function. + Since the units cannot be assumed on input, the first step in any black box is to convert to the model units: .. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py @@ -134,11 +135,11 @@ And frequently, it is a good idea to cast these to a float value using ``pyomo.e :start-after: # BEGIN: RuntimeConstraints_Snippet_07 :end-before: # END: RuntimeConstraints_Snippet_07 -The assumed units can now be added if desired, but this may cause a slowdown in performance. +The assumed units can now be added if desired, but this may cause a slowdown in performance. Typical usage is to strip units then append at the end, unless many unit systems are being used in the actual computations. Operations can now be performed to compute the output and derivatives as desired. -When preparing the outputs, note that all outputs must have units: +When preparing the outputs, note that all outputs must have units and be of type ``pint.Quantity``: .. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py :language: python @@ -147,37 +148,147 @@ When preparing the outputs, note that all outputs must have units: :end-before: # END: RuntimeConstraints_Snippet_08 -The ``BlackBox`` method then outputs a tuple of length ``self.availableDerivative+1``. Entry [0] is the values specified during the ``__init__()``, entry [1] is first derivative information, and similar for higher order if available. +There are multiple options for packing the output. In general, the ``BlackBox`` method should output in a way that is convenient for the modeler. For simple black boxes, with less than 10 scalar inputs and scalar outputs, it is probably easiest to output as a tuple, as was done in the example here. Consider as a second example a function of form ``[u,v,w]=f(x,y,z)``. The simple packing would be: + +.. code-block:: python + + return [u,v,w], [[du_dx, du_dy, du_dz], [dv_dx, dv_dy, dv_dz], [dw_dx, dw_dy, dw_dz]] + +If only one output, the following is also allowed: + +.. code-block:: python + + return u, [du_dx, du_dy, du_dz] + +For more complex models, it is encouraged to switch to the ``NamedTuple`` output also used by the ``BlackBox_Standardized`` method: + +.. code-block:: python + + returnTuple = namedtuple('returnTuple', ['values', 'first', 'second']) + optTuple = namedtuple('optTuple', ['u','v','w']) + iptTuple = namedtuple('iptTuple', ['x','y','z']) + + values = optTuple(u,v,w) + first = optTuple( iptTuple(du_dx, du_dy, du_dz), + iptTuple(dv_dx, dv_dy, dv_dz), + iptTuple(dw_dx, dw_dy, dw_dz) ) + second = None # Second derivatives not currently supported + + return returnTuple(values,first,second) + +Dictionaries with the same keywords are also supported: + +.. code-block:: python + + values = {'u':u , 'v':v , 'w':w} + first = { 'u': {'x':du_dx ,'y':du_dy 'z':du_dz}, + 'v': {'x':dv_dx ,'y':dv_dy 'z':dv_dz}, + 'w': {'x':dw_dx ,'y':dw_dy 'z':dw_dz} } + second = None # Second derivatives not currently supported + + return { 'values': values, + 'first': first, + 'second': second } + +As are combinations of any of these options. + +In the event that the inputs and/or outputs are non-scalar, then outputs should be passed out as structures of their appropriate shape. Derivatives are a little more complicated. If the input **or** output is a scalar, then the derivative ``du_dx`` should have the shape of the non-scalar input/output. However, if **both** are non-scalar, then the output should be a numpy array that takes in indices of the inputs as indices. For example, an input (x) of dimension 2x2 and an output (u) of dimension 4x4x4, then the derivative information would be packed as: + +.. code-block:: python + + du_dx[0,0,0,0,0] # derivative of u[0,0,0] with respect to x[0,0] + du_dx[0,0,1,0,0] # derivative of u[0,0,1] with respect to x[0,0] + du_dx[0,0,0,1,1] # derivative of u[0,0,0] with respect to x[1,1] + +Note that this may change in the future, as developers are unsatisfied with extensions of this method to second order and higher derivatives. + +The BlackBox_Standardized Method +******************************** +The ``BlackBox`` method is designed to be highly flexible for use by practicing engineers. However, integrating these black box analysis tools into optimization often requires a common, structured framework for the output. The ``BlackBox_Standardized`` method provides this common interface. + +The ``BlackBox_Standardized`` method will **always** provide an output that is a nested series of ``NamedTuples``: + +- [0]--'values' + - [0]--'name_of_first_output': First output of the black box + - [1]--'name_of_first_output': Second output of the black box + - ... + - [n]--'name_of_last_output': Last output of the black box +- [1]--'first' + - [0]--'name_of_first_output' + - [0]--'name_of_first_input': Derivative of the first output of the black box wrt. the first input of the black box + - ... + - [n]--'name_of_last_input': Derivative of the first output of the black box wrt. the last input of the black box + - ... + - [n]--'name_of_last_output' + - [0]--'name_of_first_input': Derivative of the last output of the black box wrt. the first input of the black box + - ... + - [n]--'name_of_last_input': Derivative of the last output of the black box wrt. the last input of the black box +- [2]--'second' + - At present, this will always be ``None``, though second order support is planned in future versions + +For example, for a black box function ``[u,v,w] = f(x,y,z)``, the ``BlackBox_Standardized`` method would look as follows: + +.. code-block:: python + + opt = m.BlackBox_Standardized(x,y,z) + opt.values.u # The output u + opt[0][2] # The output w + opt.first.u.x # The derivative du/dx + opt.first.w.y # The derivative dw/dy + opt[1][1][2] # The derivative dv/dz + opt.second # Will be None in the current version + opt[2] # Will be None in the current version + +Note that while the current implementation does have a default ``BlackBox_Standardized`` method, this method is written for robustness and not performance. For users who want maximum performance, we recommend writing your own ``BlackBox_Standardized`` method and overriding the base class. Performance improvements will be most significant when a model has a large number of non-scalar inputs and/or outputs. + +Note also that the names in the named tuple are the names used in the black box model and **not** the names in the optimization problem. These two namespaces are intentionally separated. If you wish to use the namespace access, you must either get the names from ``[x.name for x in m.inputs]`` and ``[x.name for x in m.outputs]`` and use python's ``getattr`` function, or else have some other prior knowledge of the local black box namespace. + +Below is an example using the default ``BlackBox_Standardized`` method: .. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py :language: python - :dedent: 16 - :start-after: # BEGIN: RuntimeConstraints_Snippet_09 - :end-before: # END: RuntimeConstraints_Snippet_09 + :dedent: 8 + :start-after: # BEGIN: RuntimeConstraints_Snippet_11 + :end-before: # END: RuntimeConstraints_Snippet_11 + +And now if we wish to implement our own custom ``BlackBox_Standardized`` method: + +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: RuntimeConstraints_Snippet_12 + :end-before: # END: RuntimeConstraints_Snippet_12 + +Note that if you are writing solvers for EDI, you should **exclusively** be calling the ``BlackBox_Standardized`` method, and never the basic ``BlackBox`` method, as this will ensure you are always working with a predictable data structure. + +As a modeler, you should **always** verify the output of ``BlackBox_Standardized`` as a final check in your effort to ensure it is packing the data as intended. Please notify the developers of any issues. + +The MultiCase Method +******************** +The ``MultiCase`` method provides a native capability to call the ``BlackBox`` method across multiple inputs simultaneously. This function is **not** vectorized in the base class and is **not** optimized for performance. If you wish to have a high performance vectorized function, you will need to implement your own method. + +The native ``MultiCase`` function can take in a variety of different input formats: + + + + + + + +The output is a list of ``NamedTuple`` objects that are output from the ``BlackBox_Standardized`` method. +Below is an example of overriding the default ``MultiCase`` method: -The full unpacking is as follows: -:: - output[0] = - output[0][] = - output[1] = - output[1][][] = -if there is one single scalar output, the unpacking may be simplified to the following (as shown in the above example) -:: - output[0] = - output[1] = - output[1][] = -See the :doc:`advanced <./advancedruntimeconstraints>` documentation for cases where the inputs and outputs are not scalar. Including a Black-Box in an EDI Formulation @@ -275,7 +386,7 @@ Any of the alternative declarations above are valid to pass into the ``f.Constra Examples -------- -More examples are in the :doc:`advanced <./advancedruntimeconstraints>` documentation, and will be added over time. Feel free to reach out to the developers if you have questions regarding model development. +More examples will be added over time. Feel free to reach out to the developers if you have questions regarding model development. A standard construction +++++++++++++++++++++++ diff --git a/doc/OnlineDocs/contributed_packages/edi/formulation.rst b/doc/OnlineDocs/contributed_packages/edi/formulation.rst index 6519722b9a6..51f6b81f8f5 100644 --- a/doc/OnlineDocs/contributed_packages/edi/formulation.rst +++ b/doc/OnlineDocs/contributed_packages/edi/formulation.rst @@ -147,33 +147,6 @@ The ``f.RuntimeConstraint()`` constructor takes in the following inputs: :param black_box: The object that stores the black-box function. See the :doc:`black box constraint documentation <./blackboxconstraints>` for details on constructing this object :type black_box: pyomo.contrib.edi.BlackBoxFunctionModel - -The following are alternative construction methods that may be of use: - -.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py - :language: python - :dedent: 8 - :start-after: # BEGIN: Formulation_Snippet_10 - :end-before: # END: Formulation_Snippet_10 - -.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py - :language: python - :dedent: 8 - :start-after: # BEGIN: Formulation_Snippet_11 - :end-before: # END: Formulation_Snippet_11 - -.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py - :language: python - :dedent: 8 - :start-after: # BEGIN: Formulation_Snippet_12 - :end-before: # END: Formulation_Snippet_12 - -.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py - :language: python - :dedent: 8 - :start-after: # BEGIN: Formulation_Snippet_13 - :end-before: # END: Formulation_Snippet_13 - However, more commonly we expect users to construct Runtime Constraints as a part of a ``f.ConstraintList()`` declaration. Simply include a list, tuple, or dict as a part of the ConstraintList as follows: .. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py @@ -182,7 +155,7 @@ However, more commonly we expect users to construct Runtime Constraints as a par :start-after: # BEGIN: Formulation_Snippet_14 :end-before: # END: Formulation_Snippet_14 -Any of the alternative declarations above are valid to pass into the ``f.ConstraintList()`` constructor, for example: +The ``f.ConstraintList()`` constructor tests for types ``tuple``, ``list``, or ``dict``, and attempts to construct a ``RuntimeConstraint`` from this data. Types of ``tuple`` and ``list`` should include 4 elements that are the ordered entries to the ``RuntimeConstraint`` constructor (ie, ``*args``), and type ``dict`` should have keyword arguments to the ``RuntimeConstraint`` constructor (ie, ``**kwrgs``). Examples are as follows: .. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py :language: python diff --git a/doc/OnlineDocs/contributed_packages/edi/index.rst b/doc/OnlineDocs/contributed_packages/edi/index.rst index 00a139ee751..9ffd8bc0ae6 100644 --- a/doc/OnlineDocs/contributed_packages/edi/index.rst +++ b/doc/OnlineDocs/contributed_packages/edi/index.rst @@ -37,7 +37,6 @@ User's Guide blackboxobjectives.rst constraints.rst blackboxconstraints.rst - advancedruntimeconstraints.rst examples.rst additionaltips.rst diff --git a/pyomo/contrib/edi/.blackBoxFunctionModel.py.swp b/pyomo/contrib/edi/.blackBoxFunctionModel.py.swp deleted file mode 100644 index 095a5a5cc3b8b0e78aa1ed1ea0e57f019c145f3a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16384 zcmeHNO^h5z8Lb48kdS~wK!}J##S5BQJUbq*G1x|{*t@f~7J0o#$|66N zakLl*BCIC8S8BoWP64OD#tPiVc2DmzOMPPdUHp!B+_!O2$2bL?0!{&^fK$LJ;1qBQ zI0c*nZ#V_gWE=YodUd_+nXuRE#;*Ud>613UHOBwj@@3;9MuniRLylnXu z%YSZ+f64NPEdR_H|Dxp=E&o2-p~C)U%P(2}u>8j?|DHGH{}lG0 zxBTJ4{uuvH%inAHC&&1ISiabi-SXy9`@HWQ22x#CWPzIj4jj=BQbzl~_9{Ay{jQs%kCh%oI0RmV6?gakwR>qzM{tWyG zI0t+dco?VyHvu;SHvrGw!r1SCUjgTV72qha2Scv%X(p0I! zO9Wn89oRw%sb!>7FY-kh7Ast*YI0Cuf@_iTkv2Fgc|6L(kjl!aO)e&$)*|3CG7bcy z#j{AJx~xUms!%_y%>yR83Ztn|tnaN7X{I8y6}GAkQ)C@Gg^5JPiBM_J6q(>NTaj$r zb>Bu-%;GtJ*VsRh;4DN}SB3 z5(xNo$gfW&y(sGn74tNvm}2fIVTc<7ev61&;pX%oZ^oy&?X!I5$Ygyr6{iAG)mXG(? zSQo2g)2XzbVg6J`ClO)q>uJAbpGk%tu`{2Do3p%FF?@@7Ed0n&MWkep6C3@hj3oY9bwSO>JqvvQ;Kw^`V~fm?+1~(Y|IcxubP@agUjn}X&I3;Y z2Y?IM^FIwd0elU32zU@+z&Y&wL*S#pdx1;X>;E433GiLu5#V9K1M0vI;CA2&+P(<< z9C#8SU%n2w

lUI0c*nP64NYQ@|`j@;z#sa-ci`jB>g3>y zj>()c-0|A}sp-|Li*tP6xjOfnY*5qvW{1rT6LN>8IZ+dN!E@$1Yr+Vf5 o%@`@BZ4M2SmHI&c+uoM;fyVUpq8}}xfvEij^@(WxIbR~;zkO*{2><{9 diff --git a/pyomo/contrib/edi/__init__.py b/pyomo/contrib/edi/__init__.py index 21213067168..86fe5c034e5 100644 --- a/pyomo/contrib/edi/__init__.py +++ b/pyomo/contrib/edi/__init__.py @@ -15,6 +15,14 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ +# # Register the pint quantity type to prevent warnings +# from pyomo.common.numeric_types import RegisterNumericType +# # try: +# import pint +# RegisterNumericType(pint.Quantity) +# # except: +# # pass + # Recommended just to build all of the appropriate things import pyomo.environ @@ -25,7 +33,6 @@ pass # in this case, the dependencies are not installed, nothing will work - # Import the black box modeling tools try: from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel diff --git a/pyomo/contrib/edi/blackBoxFunctionModel.py b/pyomo/contrib/edi/blackBoxFunctionModel.py index 76751772378..f4ba6019dd9 100644 --- a/pyomo/contrib/edi/blackBoxFunctionModel.py +++ b/pyomo/contrib/edi/blackBoxFunctionModel.py @@ -21,8 +21,17 @@ import pyomo import pyomo.environ as pyo from pyomo.environ import units as pyomo_units +from pyomo.core.base.units_container import as_quantity from pyomo.common.dependencies import attempt_import +# from pyomo.common.numeric_types import RegisterNumericType +try: + import pint +except: + raise ImportError( + "pyomo.contrib.edi requires pint to enable black box capability, fix with 'pip install pint' " + ) + egb, egb_available = attempt_import( "pyomo.contrib.pynumero.interfaces.external_grey_box" @@ -283,7 +292,7 @@ def append(*args, **kwargs): def toList(itm, keylist): - if isinstance(itm, (namedtuple, tuple, list)): + if isinstance(itm, (tuple, list)): return list(itm) elif isinstance(itm, dict): retList = [] @@ -299,6 +308,7 @@ class BlackBoxFunctionModel(ExternalGreyBoxModel): # --------------------------------------------------------------------------------------------------------------------- # --------------------------------------------------------------------------------------------------------------------- def __init__(self): + # pyomo.common.numeric_types.RegisterNumericType(pint.Quantity) super(BlackBoxFunctionModel, self).__init__() # List of the inputs and outputs @@ -587,7 +597,9 @@ def fillCache(self): print(outputJacobian.dtype) # print(sps.coo_matrix(outputJacobian)) # outputJacobian = np.zeros([3,1]) - self._cache['pyomo_jacobian'] = sps.coo_matrix(outputJacobian, shape=outputJacobian.shape) + self._cache['pyomo_jacobian'] = sps.coo_matrix( + outputJacobian, shape=outputJacobian.shape + ) # --------------------------------------------------------------------------------------------------------------------- # --------------------------------------------------------------------------------------------------------------------- @@ -628,7 +640,7 @@ def BlackBox_Standardized(self, *args, **kwargs): # Values # -------- # values will always come back as an appropriate list - opt_values = toList(opt[0]) + opt_values = toList(opt[0], []) for i in range(0, len(structuredOutput['values'])): if self.outputs[i].size == 0: structuredOutput['values'][i] = 0.0 * self.outputs[i].units @@ -640,7 +652,9 @@ def BlackBox_Standardized(self, *args, **kwargs): for i, vl in enumerate(opt_values): if self.outputs[i].size == 0: - structuredOutput['values'][i] = opt_values[i].to(self.outputs[i].units) + structuredOutput['values'][i] = self.convert( + opt_values[i], self.outputs[i].units + ) else: listOfIndices = list( itertools.product(*[range(0, n) for n in self.outputs[i].size]) @@ -659,29 +673,34 @@ def BlackBox_Standardized(self, *args, **kwargs): # should be [lst, lst, lst, lst...] if len(opt) > 1: opt_first = toList(opt[1], outputNames) + if len(self.outputs) == 1 and len(opt_first) == len(self.inputs): + opt_first = [opt_first] + for i, vl in enumerate(opt_first): + opt_first[i] = toList(vl, inputNames) + for i in range(0, len(structuredOutput['first'])): currentSize_output = toList(self.outputs[i].size, []) for j in range(0, len(structuredOutput['first'][i])): if self.outputs[i].size == 0 and self.inputs[j].size == 0: - structuredOutput['values'][i][j] = ( + structuredOutput['first'][i][j] = ( 0.0 * self.outputs[i].units / self.inputs[j].units ) elif self.outputs[i].size == 0: currentSize_input = toList(self.inputs[j].size, []) - structuredOutput['values'][i][j] = ( + structuredOutput['first'][i][j] = ( np.zeros(currentSize_input) * self.outputs[i].units / self.inputs[j].units ) elif self.inputs[j].size == 0: - structuredOutput['values'][i][j] = ( + structuredOutput['first'][i][j] = ( np.zeros(currentSize_output) * self.outputs[i].units / self.inputs[j].units ) else: currentSize_input = toList(self.inputs[j].size, []) - structuredOutput['values'][i][j] = ( + structuredOutput['first'][i][j] = ( np.zeros(currentSize_output + currentSize_input) * self.outputs[i].units / self.inputs[j].units @@ -689,9 +708,16 @@ def BlackBox_Standardized(self, *args, **kwargs): for i, vl in enumerate(opt_first): for j, vlj in enumerate(opt_first[i]): + # print(opt_first) + # print(opt_first[i]) + # print(self.outputs) + # print(self.inputs) + # print(i) + # print(j) if self.outputs[i].size == 0 and self.inputs[j].size == 0: - structuredOutput['first'][i][j] = opt_first[i][j].to( - self.outputs[i].units / self.inputs[j].units + structuredOutput['first'][i][j] = self.convert( + opt_first[i][j], + self.outputs[i].units / self.inputs[j].units, ) elif self.outputs[i].size == 0: listOfIndices = list( @@ -750,19 +776,19 @@ def BlackBox_Standardized(self, *args, **kwargs): # -------- # Pack # -------- - for i in range(0, len(structuredOutput['second'])): - for j in range(0, len(structuredOutput['second'][i])): - structuredOutput['second'][i][j] = iptTuple( - *structuredOutput['second'][i][j] - ) + # for i in range(0, len(structuredOutput['second'])): + # for j in range(0, len(structuredOutput['second'][i])): + # structuredOutput['second'][i][j] = iptTuple( + # *structuredOutput['second'][i][j] + # ) for i in range(0, len(structuredOutput['first'])): structuredOutput['first'][i] = iptTuple(*structuredOutput['first'][i]) - structuredOutput['second'][i] = iptTuple(*structuredOutput['second'][i]) + # structuredOutput['second'][i] = iptTuple(*structuredOutput['second'][i]) structuredOutput['values'] = optTuple(*structuredOutput['values']) structuredOutput['first'] = optTuple(*structuredOutput['first']) - structuredOutput['second'] = optTuple(*structuredOutput['second']) + # structuredOutput['second'] = optTuple(*structuredOutput['second']) return returnTuple( structuredOutput['values'], @@ -794,6 +820,10 @@ def MultiCase(self, *args, **kwargs): # unit handling functions def convert(self, val, unts): + if isinstance(val, pint.Quantity): + val.to(pyomo_units._get_pint_units(unts)) + return val + try: val = val * pyomo_units.dimensionless except: @@ -1015,12 +1045,10 @@ def parseInputs(self, *args, **kwargs): if str(e) == 'Not enough inputs': raise ValueError(e) else: # otherwise, proceed - runCases, extra_singleInput = self.parseInputs( - args[0] - ) + runCases, extra_singleInput = self.parseInputs(args[0]) remainingKwargs = copy.deepcopy(kwargs) remainingKwargs['remainingArgs'] = args[len(inputNames) :] - return runCases, remainingKwargs + return runCases, remainingKwargs # --------------------------------------------------------------------------------------------------------------------- # --------------------------------------------------------------------------------------------------------------------- diff --git a/pyomo/contrib/edi/tests/test_blackbox.py b/pyomo/contrib/edi/tests/test_blackbox.py index 64332389494..f4a36b9f77a 100644 --- a/pyomo/contrib/edi/tests/test_blackbox.py +++ b/pyomo/contrib/edi/tests/test_blackbox.py @@ -436,9 +436,7 @@ def __init__(self): self.post_init_setup(len(self.inputs)) def BlackBox(self, *args, **kwargs): # The actual function that does things - runCases, remainingKwargs = self.parseInputs( - *args, **kwargs - ) + runCases, remainingKwargs = self.parseInputs(*args, **kwargs) x = self.sanitizeInputs(runCases[0]['x']) x = np.array([pyo.value(xval) for xval in x], dtype=np.float64) @@ -511,9 +509,7 @@ def __init__(self): self.post_init_setup(len(self.inputs)) def BlackBox(self, *args, **kwargs): # The actual function that does things - runCases, remainingKwargs = self.parseInputs( - *args, **kwargs - ) + runCases, remainingKwargs = self.parseInputs(*args, **kwargs) x = self.sanitizeInputs(runCases[0]['x']) x = np.array([pyo.value(xval) for xval in x], dtype=np.float64) @@ -582,9 +578,7 @@ def __init__(self): self.post_init_setup(len(self.inputs)) def BlackBox(self, *args, **kwargs): # The actual function that does things - runCases, remainingKwargs = self.parseInputs( - *args, **kwargs - ) + runCases, remainingKwargs = self.parseInputs(*args, **kwargs) x = pyo.value(self.sanitizeInputs(runCases[0]['x'])) @@ -646,9 +640,7 @@ def __init__(self): self.post_init_setup(len(self.inputs)) def BlackBox(self, *args, **kwargs): # The actual function that does things - runCases, remainingKwargs = self.parseInputs( - *args, **kwargs - ) + runCases, remainingKwargs = self.parseInputs(*args, **kwargs) x = self.sanitizeInputs(runCases[0]['x']) x = np.array([pyo.value(xval) for xval in x], dtype=np.float64) @@ -752,7 +744,7 @@ def BlackBox(self, *args, **kwargs): dydx = [dydx[i] * units.dimensionless for i in range(0, len(dydx))] returnMode = 1 - + if returnMode < 0: returnMode = -1 * (returnMode + 1) if returnMode == 0: @@ -920,9 +912,7 @@ def __init__(self): self.post_init_setup(len(self.inputs)) def BlackBox(self, *args, **kwargs): # The actual function that does things - runCases, remainingKwargs = self.parseInputs( - *args, **kwargs - ) + runCases, remainingKwargs = self.parseInputs(*args, **kwargs) x = [rc['x'] for rc in runCases][0] x = np.array([self.pyomo_value(xval) for xval in x], dtype=np.float64) diff --git a/pyomo/contrib/edi/tests/test_docSnippets.py b/pyomo/contrib/edi/tests/test_docSnippets.py index 1ca53d3200f..169b224860c 100644 --- a/pyomo/contrib/edi/tests/test_docSnippets.py +++ b/pyomo/contrib/edi/tests/test_docSnippets.py @@ -187,31 +187,6 @@ def BlackBox(self, x, y): # The actual function that does things f.RuntimeConstraint(z, '==', [x, y], UnitCircle()) # END: Formulation_Snippet_09 - ### This will fail validatation, but should construct appropriately - - # BEGIN: Formulation_Snippet_10 - f.RuntimeConstraint(*(z, '==', [x, y], UnitCircle())) - # END: Formulation_Snippet_10 - - # BEGIN: Formulation_Snippet_11 - f.RuntimeConstraint(*[z, '==', [x, y], UnitCircle()]) - # END: Formulation_Snippet_11 - - # BEGIN: Formulation_Snippet_12 - f.RuntimeConstraint( - **{ - 'outputs': z, - 'operators': '==', - 'inputs': [x, y], - 'black_box': UnitCircle(), - } - ) - # END: Formulation_Snippet_12 - - # BEGIN: Formulation_Snippet_13 - f.RuntimeConstraint(*([z], ['=='], [x, y], UnitCircle())) - # END: Formulation_Snippet_13 - def test_edi_snippet_formuation_14(self): # BEGIN: Formulation_Snippet_14 import pyomo.environ as pyo @@ -872,6 +847,106 @@ def BlackBox(self, x, y): # The actual function that does things f.ConstraintList([(z, '==', [x, y], UnitCircle()), z <= 1 * units.m**2]) # END: RuntimeConstraints_Snippet_10 + def test_edi_snippet_runtimeconstraints_11(self): + # BEGIN: RuntimeConstraints_Snippet_11 + import pyomo.environ as pyo + from pyomo.environ import units + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + + f = Formulation() + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + z = f.Variable(name='z', guess=1.0, units='m^2', description='The z variable') + + f.Objective(x + y) + + class UnitCircle(BlackBoxFunctionModel): + def __init__(self): + super().__init__() + self.description = 'This model evaluates the function: z = x**2 + y**2' + self.inputs.append(name='x', units='ft', description='The x variable') + self.inputs.append(name='y', units='ft', description='The y variable') + self.outputs.append( + name='z', + units='ft**2', + description='Resultant of the unit circle evaluation', + ) + self.availableDerivative = 1 + self.post_init_setup(len(self.inputs)) + + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value(units.convert(x, self.inputs['x'].units)) + y = pyo.value(units.convert(y, self.inputs['y'].units)) + z = x**2 + y**2 + dzdx = 2 * x + dzdy = 2 * y + z = z * self.outputs['z'].units + dzdx = dzdx * self.outputs['z'].units / self.inputs['x'].units + dzdy = dzdy * self.outputs['z'].units / self.inputs['y'].units + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + + m = UnitCircle() + bb = m.BlackBox(0.5 * units.m, 0.5 * units.m) + bbs = m.BlackBox_Standardized(0.5 * units.m, 0.5 * units.m) + # END: RuntimeConstraints_Snippet_11 + + def test_edi_snippet_runtimeconstraints_12(self): + # BEGIN: RuntimeConstraints_Snippet_12 + from collections import namedtuple + import pyomo.environ as pyo + from pyomo.environ import units + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + + f = Formulation() + x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') + y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') + z = f.Variable(name='z', guess=1.0, units='m^2', description='The z variable') + + f.Objective(x + y) + + class UnitCircle(BlackBoxFunctionModel): + def __init__(self): + super().__init__() + self.description = 'This model evaluates the function: z = x**2 + y**2' + self.inputs.append(name='x', units='ft', description='The x variable') + self.inputs.append(name='y', units='ft', description='The y variable') + self.outputs.append( + name='z', + units='ft**2', + description='Resultant of the unit circle evaluation', + ) + self.availableDerivative = 1 + self.post_init_setup(len(self.inputs)) + + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value(units.convert(x, self.inputs['x'].units)) + y = pyo.value(units.convert(y, self.inputs['y'].units)) + z = x**2 + y**2 + dzdx = 2 * x + dzdy = 2 * y + z = z * self.outputs['z'].units + dzdx = dzdx * self.outputs['z'].units / self.inputs['x'].units + dzdy = dzdy * self.outputs['z'].units / self.inputs['y'].units + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + + def BlackBox_Standardized(self, x, y): # The standardized function + res = self.BlackBox(x, y) + + returnTuple = namedtuple('returnTuple', ['values', 'first', 'second']) + optTuple = namedtuple('optTuple', [o.name for o in self.outputs]) + iptTuple = namedtuple('iptTuple', [i.name for i in self.inputs]) + + values = optTuple(res[0]) + first = optTuple(iptTuple(res[1][0], res[1][1])) + second = None # Second derivatives not currently supported + + return returnTuple(values, first, second) + + m = UnitCircle() + bb = m.BlackBox(0.5 * units.m, 0.5 * units.m) + bbs = m.BlackBox_Standardized(0.5 * units.m, 0.5 * units.m) + # END: RuntimeConstraints_Snippet_12 + def test_edi_snippet_advancedRTC_01(self): # BEGIN: AdvancedRTC_Snippet_01 import numpy as np @@ -915,7 +990,7 @@ def BlackBox(self, *args, **kwargs): dydx = [dydx[i] * units.dimensionless for i in range(0, len(dydx))] returnMode = 1 - + if returnMode < 0: returnMode = -1 * (returnMode + 1) if returnMode == 0: @@ -945,6 +1020,7 @@ def BlackBox(self, *args, **kwargs): # Additional options available with parseInputs bbo = s.BlackBox(*[0.5], **{'optn1': True, 'optn2': False}) bbo = s.BlackBox(*[0.5, True], **{'optn': False}) + bbo = s.BlackBox({'x': np.linspace(-2, 2, 11)}) bbo = s.BlackBox({'x': [x for x in np.linspace(-2, 2, 11)]}) bbo = s.BlackBox([{'x': x} for x in np.linspace(-2, 2, 11)]) bbo = s.BlackBox([[x] for x in np.linspace(-2, 2, 11)]) From e07dce36c7db74664f352eb41de0870f3d67cb6f Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Wed, 6 Mar 2024 22:52:21 -0700 Subject: [PATCH 47/61] needed to pip upgrade black --- pyomo/contrib/edi/tests/test_docSnippets.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pyomo/contrib/edi/tests/test_docSnippets.py b/pyomo/contrib/edi/tests/test_docSnippets.py index 169b224860c..e0f67bcff33 100644 --- a/pyomo/contrib/edi/tests/test_docSnippets.py +++ b/pyomo/contrib/edi/tests/test_docSnippets.py @@ -509,9 +509,7 @@ def test_edi_snippet_constraints_01(self): y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') c = f.Constant(name='c', value=1.0, units='', description='A constant c') f.Objective(c * x + y) - f.ConstraintList( - [x**2 + y**2 <= 1.0 * units.m**2, x <= 0.75 * units.m, x >= y] - ) + f.ConstraintList([x**2 + y**2 <= 1.0 * units.m**2, x <= 0.75 * units.m, x >= y]) # END: Constraints_Snippet_01 def test_edi_snippet_constraints_02(self): From 18502673143af6ae22169cfe7baaeb908c3f56f5 Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Wed, 6 Mar 2024 23:13:52 -0700 Subject: [PATCH 48/61] adding a todo list --- pyomo/contrib/edi/README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pyomo/contrib/edi/README.md b/pyomo/contrib/edi/README.md index 8a6b592baa0..eb763e77011 100644 --- a/pyomo/contrib/edi/README.md +++ b/pyomo/contrib/edi/README.md @@ -2,6 +2,13 @@ The Pyomo Engineering Design Interface (EDI) is a lightweight wrapper on the Pyomo language that is targeted at composing engineering design optimization problems. The language and interface have been designed to mimic many of the features found in [GPkit](https://github.com/convexengineering/gpkit) and [CVXPY](https://github.com/cvxpy/cvxpy) while also providing a simple, clean interface for black-box analysis codes that are common in engineering design applications. +## TODO +- Fix broken unit tests +- Add unit tests for the BlackBox_Standardized method +- Add unit tests for the MultiCase method +- Add documentation for the MultiCase method +- Address Bethany's comments + ## Installation EDI is a part of the standard installation process for Pyomo: From 7def60bbf00fecdfbf77ad7a56b2fd47a3e8bc6b Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Tue, 20 Aug 2024 14:23:01 -0700 Subject: [PATCH 49/61] finally updating EDI --- .../edi/blackboxconstraints.rst | 32 +- pyomo/contrib/edi/README.md | 1 - pyomo/contrib/edi/__init__.py | 34 +- pyomo/contrib/edi/blackBoxFunctionModel.py | 99 ++- pyomo/contrib/edi/tests/test_blackbox.py | 669 +++++++++++++++++- pyomo/contrib/edi/tests/test_docSnippets.py | 65 +- 6 files changed, 841 insertions(+), 59 deletions(-) diff --git a/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst b/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst index 9c14ef21573..1e6ceaa28c4 100644 --- a/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst +++ b/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst @@ -116,7 +116,9 @@ The ``BlackBox`` is extremely flexible, but here we present standard usage for a The ``BlackBox`` method assumes to take in the inputs as arguments in the order defined during the ``__init__()`` method. Note that the method assumes inputs **with units** and expects outputs **with units**. In general, the units on inputs and outputs need not be in any specific system, but should be convertible (ex, meters and feet) to whatever has been specified as the input units when defining in the ``__init__()`` function. -The unit handling system in Pyomo can be rough at times, and so the BlackBox function is expected to return values that are ``pint.Quantity`` types. These are obtained using the ``pyo.as_quantity()`` function. +Various unpacking schemes are enabled by default via the ``parse_inputs`` function. Use of this function is not necessary, but provides for the parsing of index argumented lists (ex: ``function(x1, x2, x3)``) and keyword argumented dictionaries (ex: ``function({'x2':x2, 'x1':x1, 'x3',x3})``), along with a few other possibilities. + +The unit handling system in Pyomo can be rough at times, and so best practice is generally for the BlackBox function is expected to return values that are ``pint.Quantity`` types. These are obtained using the ``pyo.as_quantity()`` function. Since the units cannot be assumed on input, the first step in any black box is to convert to the model units: @@ -200,7 +202,7 @@ In the event that the inputs and/or outputs are non-scalar, then outputs should du_dx[0,0,1,0,0] # derivative of u[0,0,1] with respect to x[0,0] du_dx[0,0,0,1,1] # derivative of u[0,0,0] with respect to x[1,1] -Note that this may change in the future, as developers are unsatisfied with extensions of this method to second order and higher derivatives. +Note that this may change in the future, as developers are currently unsatisfied with extensions of this method to second order and higher derivatives. The BlackBox_Standardized Method ******************************** @@ -267,28 +269,18 @@ The MultiCase Method ******************** The ``MultiCase`` method provides a native capability to call the ``BlackBox`` method across multiple inputs simultaneously. This function is **not** vectorized in the base class and is **not** optimized for performance. If you wish to have a high performance vectorized function, you will need to implement your own method. -The native ``MultiCase`` function can take in a variety of different input formats: - - - - +Inputs to the ``MultiCase`` funciton should be a list of cases, which can be packed in any form accepted by the ``BlackBox_Standardized`` method. Overloading these functions may allow different forms of unpacking scheme. - - -The output is a list of ``NamedTuple`` objects that are output from the ``BlackBox_Standardized`` method. +The output is a list of ``NamedTuple`` objects that are output from the ``BlackBox_Standardized`` method. If overloading, you may choose to output via a differnt packing scheme. Below is an example of overriding the default ``MultiCase`` method: - - - - - - - - - +.. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py + :language: python + :dedent: 8 + :start-after: # BEGIN: RuntimeConstraints_Snippet_13 + :end-before: # END: RuntimeConstraints_Snippet_13 Including a Black-Box in an EDI Formulation @@ -408,7 +400,7 @@ Tips * Embrace units. They will save you so many times, it is well worth the minor additional overhead * Pyomo units work slightly diffenrently than pint (for those with pint experience), but those differences should be hidden from the model creator for the most part * It is common to use this framework to call to a piece of software external to python -* See the :doc:`advanced <./advancedruntimeconstraints>` documentation for extra tips and tricks +* A model summary can be printed by calling ``print(model_instance.summary)`` Known Issues diff --git a/pyomo/contrib/edi/README.md b/pyomo/contrib/edi/README.md index eb763e77011..b829120e9f6 100644 --- a/pyomo/contrib/edi/README.md +++ b/pyomo/contrib/edi/README.md @@ -3,7 +3,6 @@ The Pyomo Engineering Design Interface (EDI) is a lightweight wrapper on the Pyomo language that is targeted at composing engineering design optimization problems. The language and interface have been designed to mimic many of the features found in [GPkit](https://github.com/convexengineering/gpkit) and [CVXPY](https://github.com/cvxpy/cvxpy) while also providing a simple, clean interface for black-box analysis codes that are common in engineering design applications. ## TODO -- Fix broken unit tests - Add unit tests for the BlackBox_Standardized method - Add unit tests for the MultiCase method - Add documentation for the MultiCase method diff --git a/pyomo/contrib/edi/__init__.py b/pyomo/contrib/edi/__init__.py index 86fe5c034e5..99ab37bc474 100644 --- a/pyomo/contrib/edi/__init__.py +++ b/pyomo/contrib/edi/__init__.py @@ -24,7 +24,7 @@ # # pass # Recommended just to build all of the appropriate things -import pyomo.environ +import pyomo.environ as pyo # Import the relevant classes from Formulation try: @@ -48,3 +48,35 @@ except: pass # in this case, the dependencies are not installed, nothing will work + + +# the printer that does units ok +import copy +from pyomo.core.base.units_container import _PyomoUnit +from pyomo.core.expr.numeric_expr import NPV_ProductExpression, NPV_DivisionExpression +from collections import namedtuple +import numpy as np + + +def recursive_sub(x_in): + x = list(copy.deepcopy(x_in)) + for i in range(0, len(x)): + if isinstance(x[i], _PyomoUnit): + x[i] = '1.0*' + str(x[i]) + elif ( + isinstance( + x[i], (NPV_ProductExpression, NPV_DivisionExpression, np.float64) + ) + or x[i] is None + ): + if pyo.value(x[i]) == 1: + x[i] = '1.0*' + str(x[i]) + else: + x[i] = str(x[i]) + else: + x[i] = recursive_sub(list(x[i])) + return x + + +def ediprint(x): + print(recursive_sub(x)) diff --git a/pyomo/contrib/edi/blackBoxFunctionModel.py b/pyomo/contrib/edi/blackBoxFunctionModel.py index f4ba6019dd9..61408ba87e8 100644 --- a/pyomo/contrib/edi/blackBoxFunctionModel.py +++ b/pyomo/contrib/edi/blackBoxFunctionModel.py @@ -24,6 +24,8 @@ from pyomo.core.base.units_container import as_quantity from pyomo.common.dependencies import attempt_import +from pyomo.core.expr.ndarray import NumericNDArray + # from pyomo.common.numeric_types import RegisterNumericType try: import pint @@ -451,7 +453,7 @@ def fillCache(self): modelOutputUnits = opt.units outputOptimizationUnits = optimizationOutput.get_units() vl = valueList[i] - if isinstance(vl, pyomo.core.expr.numeric_expr.NumericNDArray): + if isinstance(vl, NumericNDArray): validIndexList = optimizationOutput.index_set().data() for j in range(0, len(validIndexList)): vi = validIndexList[j] @@ -519,9 +521,7 @@ def fillCache(self): ptr_col += 1 ptr_row_step = 1 - elif isinstance( - jacobianValue_raw, pyomo.core.expr.numeric_expr.NumericNDArray - ): + elif isinstance(jacobianValue_raw, NumericNDArray): jshape = jacobianValue_raw.shape if isinstance(oopt, pyomo.core.base.var.ScalarVar): @@ -594,7 +594,7 @@ def fillCache(self): ptr_row += ptr_row_step - print(outputJacobian.dtype) + # print(outputJacobian.dtype) # print(sps.coo_matrix(outputJacobian)) # outputJacobian = np.zeros([3,1]) self._cache['pyomo_jacobian'] = sps.coo_matrix( @@ -612,6 +612,11 @@ def BlackBox(*args, **kwargs): # These functions wrap black box to provide more functionality def BlackBox_Standardized(self, *args, **kwargs): + runCases, extras = self.parseInputs(*args, **kwargs) + if len(runCases) != 1: + # This is actually a MultiCase, run the correct function instead + return self.MultiCase(*args, **kwargs) + # -------- # Run # -------- @@ -628,13 +633,16 @@ def BlackBox_Standardized(self, *args, **kwargs): iptTuple = namedtuple('iptTuple', inputNames) structuredOutput = {} - structuredOutput['values'] = len(self.outputs) * [None] - structuredOutput['first'] = len(self.outputs) * [len(self.inputs) * [None]] - structuredOutput['second'] = len(self.outputs) * [ - len(self.inputs) * [len(self.inputs) * [None]] - ] + structuredOutput['values'] = np.zeros(len(self.outputs)).tolist() + structuredOutput['first'] = np.zeros( + [len(self.outputs), len(self.inputs)] + ).tolist() + structuredOutput['second'] = np.zeros( + [len(self.outputs), len(self.inputs), len(self.inputs)] + ).tolist() opt = toList(opt_raw, ['values', 'first', 'second']) + # print(opt) # -------- # Values @@ -655,6 +663,9 @@ def BlackBox_Standardized(self, *args, **kwargs): structuredOutput['values'][i] = self.convert( opt_values[i], self.outputs[i].units ) + elif isinstance(self.outputs[i].size, int): + for ix in range(0, self.outputs[i].size): + structuredOutput['values'][i][ix] = opt_values[i][ix] else: listOfIndices = list( itertools.product(*[range(0, n) for n in self.outputs[i].size]) @@ -708,8 +719,11 @@ def BlackBox_Standardized(self, *args, **kwargs): for i, vl in enumerate(opt_first): for j, vlj in enumerate(opt_first[i]): - # print(opt_first) - # print(opt_first[i]) + # from pyomo.contrib.edi import ediprint + # print() + # ediprint(opt_first) + # ediprint(opt_first[i]) + # ediprint(opt_first[i][j]) # print(self.outputs) # print(self.inputs) # print(i) @@ -720,35 +734,61 @@ def BlackBox_Standardized(self, *args, **kwargs): self.outputs[i].units / self.inputs[j].units, ) elif self.outputs[i].size == 0: + # print(self.inputs[j].size) + if isinstance(self.inputs[j].size, int): + sizelist = [self.inputs[j].size] + else: + assert isinstance(self.inputs[j].size, list) + sizelist = self.inputs[j].size listOfIndices = list( - itertools.product( - *[range(0, n) for n in self.inputs[j].size] - ) + itertools.product(*[range(0, n) for n in sizelist]) ) + # print(listOfIndices) for ix in listOfIndices: + if len(ix) == 1: + ix = ix[0] structuredOutput['first'][i][j][ix] = opt_first[i][j][ ix ] # unit conversion handled automatically by pint elif self.inputs[j].size == 0: + if isinstance(self.outputs[i].size, int): + sizelist = [self.outputs[i].size] + else: + assert isinstance(self.outputs[i].size, list) + sizelist = self.outputs[i].size listOfIndices = list( - itertools.product( - *[range(0, n) for n in self.outputs[i].size] - ) + itertools.product(*[range(0, n) for n in sizelist]) ) for ix in listOfIndices: + if len(ix) == 1: + ix = ix[0] structuredOutput['first'][i][j][ix] = opt_first[i][j][ ix ] # unit conversion handled automatically by pint else: + if isinstance(self.inputs[j].size, int): + sizelist_inputs = [self.inputs[j].size] + else: + assert isinstance(self.inputs[j].size, list) + sizelist_inputs = self.inputs[j].size + + if isinstance(self.outputs[i].size, int): + sizelist_outputs = [self.outputs[i].size] + else: + assert isinstance(self.inputs[j].size, list) + sizelist_outputs = self.outputs[i].size + listOfIndices = list( itertools.product( *[ range(0, n) - for n in self.outputs[i].size + self.inputs[j].size + for n in sizelist_outputs + sizelist_inputs ] ) ) for ix in listOfIndices: + if len(ix) == 1: + ix = ix[0] structuredOutput['first'][i][j][ix] = opt_first[i][j][ ix ] # unit conversion handled automatically by pint @@ -826,6 +866,7 @@ def convert(self, val, unts): try: val = val * pyomo_units.dimensionless + val = pyo.value(val) * pyomo_units.get_units(val) except: pass ## will handle later @@ -836,8 +877,9 @@ def convert(self, val, unts): pyomo.core.expr.numeric_expr.NPV_ProductExpression, ), ): - return pyomo_units.convert(val, unts) - elif isinstance(val, pyomo.core.expr.numeric_expr.NumericNDArray): + rval = pyomo_units.convert(val, unts) + return pyo.value(rval) * pyomo_units.get_units(rval) + elif isinstance(val, NumericNDArray): shp = val.shape ix = np.ndindex(*shp) opt = np.zeros(shp) @@ -845,6 +887,7 @@ def convert(self, val, unts): ixt = next(ix) opt[ixt] = pyo.value(pyomo_units.convert(val[ixt], unts)) return opt * unts + # elif isinstance(val, (list, tuple)): else: raise ValueError('Invalid type passed to unit conversion function') @@ -852,7 +895,7 @@ def pyomo_value(self, val): try: return pyo.value(val) except: - if isinstance(val, pyomo.core.expr.numeric_expr.NumericNDArray): + if isinstance(val, NumericNDArray): shp = val.shape ix = np.ndindex(*shp) opt = np.zeros(shp) @@ -867,7 +910,6 @@ def pyomo_value(self, val): # --------------------------------------------------------------------------------------------------------------------- def parseInputs(self, *args, **kwargs): args = list(args) # convert tuple to list - inputNames = [self.inputs[i].name for i in range(0, len(self.inputs))] # ------------------------------ @@ -917,7 +959,8 @@ def parseInputs(self, *args, **kwargs): raise ValueError( "Invalid data type in the input list. Note that BlackBox([x1,x2,y]) must be passed in as BlackBox([[x1,x2,y]]) or " + "BlackBox(*[x1,x2,y]) or BlackBox({'x1':x1,'x2':x2,'y':y}) or simply BlackBox(x1, x2, y) to avoid processing singularities. " - + "Best practice is BlackBox({'x1':x1,'x2':x2,'y':y})" + + "Best practice is BlackBox({'x1':x1,'x2':x2,'y':y}). For multi-case input, be sure to wrap even single inputs in lists," + + ' ex: [[x],[x],[x]] and not [x,x,x].' ) return dataRuns, {} @@ -1067,7 +1110,7 @@ def sizeCheck(self, size, ipval_correctUnits): 'Size did not match the expected size %s (ie: Scalar)' % (str(size)) ) - elif isinstance(szVal, pyomo.core.expr.numeric_expr.NumericNDArray): + elif isinstance(szVal, NumericNDArray): shp = szVal.shape if isinstance(size, (int, float)): size = [size] @@ -1123,7 +1166,7 @@ def sanitizeInputs(self, *args, **kwargs): ipval = inputDict[name] - if isinstance(ipval, pyomo.core.expr.numeric_expr.NumericNDArray): + if isinstance(ipval, NumericNDArray): for ii in range(0, len(ipval)): try: ipval[ii] = self.convert(ipval[ii], unts) # ipval.to(unts) @@ -1144,7 +1187,7 @@ def sanitizeInputs(self, *args, **kwargs): # superseded by the custom convert function # if not isinstance(ipval_correctUnits, (pyomo.core.expr.numeric_expr.NPV_ProductExpression, - # pyomo.core.expr.numeric_expr.NumericNDArray, + # NumericNDArray, # pyomo.core.base.units_container._PyomoUnit)): # ipval_correctUnits = ipval_correctUnits * pyomo_units.dimensionless @@ -1198,7 +1241,7 @@ def checkOutputs(self, *args, **kwargs): # ipval_correctUnits = ipval # if not isinstance(ipval_correctUnits, (pyomo.core.expr.numeric_expr.NPV_ProductExpression, - # pyomo.core.expr.numeric_expr.NumericNDArray, + # NumericNDArray, # pyomo.core.base.units_container._PyomoUnit)): # ipval_correctUnits = ipval_correctUnits * pyomo_units.dimensionless diff --git a/pyomo/contrib/edi/tests/test_blackbox.py b/pyomo/contrib/edi/tests/test_blackbox.py index f4a36b9f77a..de98f4d7edd 100644 --- a/pyomo/contrib/edi/tests/test_blackbox.py +++ b/pyomo/contrib/edi/tests/test_blackbox.py @@ -1007,7 +1007,7 @@ def BlackBox(self, *args, **kwargs): self.pyomo_value(runCases[i]['x']) for i in range(0, len(runCases)) ] y = [ - self.pyomo_value(runCases[i]['x']) for i in range(0, len(runCases)) + self.pyomo_value(runCases[i]['y']) for i in range(0, len(runCases)) ] u = [] @@ -1053,6 +1053,673 @@ def BlackBox(self, *args, **kwargs): bbo = bb.BlackBox(1.0, 1.0) bbo = bb.BlackBox({'x': np.linspace(0, 10, 11), 'y': np.linspace(0, 10, 11)}) + def test_edi_blackbox_standardized_1(self): + "Tests the blackbox_standardized function" + import pyomo + import numpy as np + import pyomo.environ as pyo + from pyomo.environ import units + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel, ediprint + from pyomo.common.formatting import tostr + from pyomo.environ import units as pyomo_units + + class PassThrough(BlackBoxFunctionModel): + def __init__(self): + # Set up all the attributes by calling Model.__init__ + super().__init__() + + # Setup Inputs + self.inputs.append('x', '', 'X Variable') + self.inputs.append('y', '', 'Y Variable') + + # Setup Outputs + self.outputs.append('u', '', 'U Variable') + self.outputs.append('v', '', 'V Variable') + + # Simple model description + self.description = 'This model is a pass through)' + + self.availableDerivative = 1 + + # standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) + def BlackBox(self, *args, **kwargs): + # Note: This function as written only operates on the first input on a multi-input list + runCases, extras = self.parseInputs(*args, **kwargs) + x = [ + self.pyomo_value(runCases[i]['x']) for i in range(0, len(runCases)) + ] + y = [ + self.pyomo_value(runCases[i]['y']) for i in range(0, len(runCases)) + ] + + u = [] + dudx = [] + dudy = [] + v = [] + dvdx = [] + dvdy = [] + + for xval in x: + u.append(xval * units.dimensionless) + dudx.append(1.0 * units.dimensionless) + dudy.append(0.0 * units.dimensionless) + + for yval in y: + v.append(yval * units.dimensionless) + dvdx.append(0.0 * units.dimensionless) + dvdy.append(1.0 * units.dimensionless) + + if len(runCases) == 1: + returnMode = -2 + else: + returnMode = 1 + + if returnMode < 0: + returnMode = -1 * (returnMode + 1) + if returnMode == 0: + return [u[0], v[0]] + if returnMode == 1: + return [u[0], v[0]], [[dudx[0], dudy[0]], [dvdx[0], dvdy[0]]] + else: + if returnMode == 0: + opt = [] + for i in range(0, len(y)): + opt.append([u[i], v[i]]) + return opt + if returnMode == 1: + opt = [] + for i in range(0, len(y)): + opt.append( + [[u[i], v[i]], [[dudx[i], dudy[i]], [dvdx[i], dvdy[i]]]] + ) + return opt + + bb = PassThrough() + bbo = bb.BlackBox(1.0, 1.5) + bbs = bb.BlackBox_Standardized(1.0, 1.5) + + self.assertAlmostEqual(pyo.value(bbo[0][0]), pyo.value(bbs[0][0])) + self.assertAlmostEqual( + pyomo_units.get_units(bbo[0][0]), pyomo_units.get_units(bbs[0][0]) + ) + self.assertAlmostEqual(pyo.value(bbo[0][1]), pyo.value(bbs[0][1])) + self.assertAlmostEqual( + pyomo_units.get_units(bbo[0][1]), pyomo_units.get_units(bbs[0][1]) + ) + + self.assertAlmostEqual(pyo.value(bbo[1][0][0]), pyo.value(bbs[1][0][0])) + self.assertAlmostEqual( + pyomo_units.get_units(bbo[1][0][0]), pyomo_units.get_units(bbs[1][0][0]) + ) + self.assertAlmostEqual(pyo.value(bbo[1][0][1]), pyo.value(bbs[1][0][1])) + self.assertAlmostEqual( + pyomo_units.get_units(bbo[1][0][1]), pyomo_units.get_units(bbs[1][0][1]) + ) + self.assertAlmostEqual(pyo.value(bbo[1][1][0]), pyo.value(bbs[1][1][0])) + self.assertAlmostEqual( + pyomo_units.get_units(bbo[1][1][0]), pyomo_units.get_units(bbs[1][1][0]) + ) + self.assertAlmostEqual(pyo.value(bbo[1][1][1]), pyo.value(bbs[1][1][1])) + self.assertAlmostEqual( + pyomo_units.get_units(bbo[1][1][1]), pyomo_units.get_units(bbs[1][1][1]) + ) + + def test_edi_blackbox_standardized_2(self): + "Tests the blackbox_standardized function" + import pyomo + import numpy as np + import pyomo.environ as pyo + from pyomo.environ import units + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel, ediprint + from pyomo.common.formatting import tostr + from pyomo.environ import units as pyomo_units + + class Norm_2(BlackBoxFunctionModel): + def __init__(self): + super().__init__() + self.description = 'This model evaluates the two norm' + self.inputs.append( + name='x', units='', description='The x variable', size=3 + ) + self.outputs.append(name='y', units='', description='The y variable') + self.availableDerivative = 1 + self.post_init_setup(len(self.inputs)) + + def BlackBox(self, *args, **kwargs): # The actual function that does things + runCases, remainingKwargs = self.parseInputs(*args, **kwargs) + + x = self.sanitizeInputs(runCases[0]['x']) + x = np.array([pyo.value(xval) for xval in x], dtype=np.float64) + + y = x[0] ** 2 + x[1] ** 2 + x[2] ** 2 # Compute y + dydx0 = 2 * x[0] # Compute dy/dx0 + dydx1 = 2 * x[1] # Compute dy/dx1 + dydx2 = 2 * x[2] # Compute dy/dx2 + + y = y * units.dimensionless + # dydx = np.array([dydx0,dydx1,dydx2]) * units.dimensionless + dydx0 = dydx0 * units.dimensionless + dydx1 = dydx1 * units.dimensionless + dydx2 = dydx2 * units.dimensionless + + return y, [[dydx0, dydx1, dydx2]] # return z, grad(z), hess(z)... + + bb = Norm_2() + bbo = bb.BlackBox(np.array([1, 1, 1])) + bbs = bb.BlackBox_Standardized(np.array([1, 1, 1])) + + self.assertAlmostEqual(pyo.value(bbs.values.y), pyo.value(bbo[0])) + self.assertAlmostEqual( + pyomo_units.get_units(bbs.values.y), pyomo_units.get_units(bbo[0]) + ) + + self.assertAlmostEqual(pyo.value(bbs.first.y.x[0]), pyo.value(bbo[1][0][0])) + self.assertAlmostEqual( + pyomo_units.get_units(bbs.first.y.x[0]), pyomo_units.get_units(bbo[1][0][0]) + ) + + self.assertAlmostEqual(pyo.value(bbo[0]), pyo.value(bbs[0][0])) + self.assertAlmostEqual( + pyomo_units.get_units(bbo[0]), pyomo_units.get_units(bbs[0][0]) + ) + + self.assertAlmostEqual(pyo.value(bbo[1][0][0]), pyo.value(bbs[1][0][0][0])) + self.assertAlmostEqual( + pyomo_units.get_units(bbo[1][0][0]), pyomo_units.get_units(bbs[1][0][0][0]) + ) + + self.assertAlmostEqual(pyo.value(bbo[1][0][1]), pyo.value(bbs[1][0][0][1])) + self.assertAlmostEqual( + pyomo_units.get_units(bbo[1][0][1]), pyomo_units.get_units(bbs[1][0][0][1]) + ) + + self.assertAlmostEqual(pyo.value(bbo[1][0][2]), pyo.value(bbs[1][0][0][2])) + self.assertAlmostEqual( + pyomo_units.get_units(bbo[1][0][2]), pyomo_units.get_units(bbs[1][0][0][2]) + ) + + def test_edi_blackbox_standardized_3(self): + "Tests the blackbox_standardized function" + import pyomo + import numpy as np + import pyomo.environ as pyo + from pyomo.environ import units + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel, ediprint + from pyomo.common.formatting import tostr + from pyomo.environ import units as pyomo_units + + class VectorCast(BlackBoxFunctionModel): + def __init__(self): + super().__init__() + self.description = 'This model evaluates the two norm' + self.inputs.append(name='x', units='', description='The x variable') + self.outputs.append( + name='y', units='', description='The y variable', size=3 + ) + self.availableDerivative = 1 + self.post_init_setup(len(self.inputs)) + + def BlackBox(self, *args, **kwargs): # The actual function that does things + runCases, remainingKwargs = self.parseInputs(*args, **kwargs) + + x = pyo.value(self.sanitizeInputs(runCases[0]['x'])) + + y = np.array([x, x, x]) * units.dimensionless + dydx = np.array([1.0, 1.0, 1.0]) * units.dimensionless + + return y, [dydx] # return z, grad(z), hess(z)... + + bb = VectorCast() + bbo = bb.BlackBox(2) + bbs = bb.BlackBox_Standardized(2) + + self.assertAlmostEqual(pyo.value(bbo[0][0]), pyo.value(bbs[0][0][0])) + self.assertAlmostEqual( + pyomo_units.get_units(bbo[0][0]), pyomo_units.get_units(bbs[0][0][0]) + ) + + self.assertAlmostEqual(pyo.value(bbo[0][1]), pyo.value(bbs[0][0][1])) + self.assertAlmostEqual( + pyomo_units.get_units(bbo[0][1]), pyomo_units.get_units(bbs[0][0][1]) + ) + + self.assertAlmostEqual(pyo.value(bbo[0][2]), pyo.value(bbs[0][0][2])) + self.assertAlmostEqual( + pyomo_units.get_units(bbo[0][2]), pyomo_units.get_units(bbs[0][0][2]) + ) + + self.assertAlmostEqual(pyo.value(bbo[1][0][0]), pyo.value(bbs[1][0][0][0])) + self.assertAlmostEqual( + pyomo_units.get_units(bbo[1][0][0]), pyomo_units.get_units(bbs[1][0][0][0]) + ) + + self.assertAlmostEqual(pyo.value(bbo[1][0][1]), pyo.value(bbs[1][0][0][1])) + self.assertAlmostEqual( + pyomo_units.get_units(bbo[1][0][1]), pyomo_units.get_units(bbs[1][0][0][1]) + ) + + self.assertAlmostEqual(pyo.value(bbo[1][0][2]), pyo.value(bbs[1][0][0][2])) + self.assertAlmostEqual( + pyomo_units.get_units(bbo[1][0][2]), pyomo_units.get_units(bbs[1][0][0][2]) + ) + + def test_edi_blackbox_standardized_4(self): + "Tests the blackbox_standardized function" + import pyomo + import numpy as np + import pyomo.environ as pyo + from pyomo.environ import units + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel, ediprint + from pyomo.common.formatting import tostr + from pyomo.environ import units as pyomo_units + + class Parabola(BlackBoxFunctionModel): + def __init__(self): + super(Parabola, self).__init__() + self.description = 'This model evaluates the function: y = x**2' + self.inputs.append( + name='x', size=3, units='', description='The x variable' + ) + self.outputs.append( + name='y', size=3, units='', description='The y variable' + ) + self.availableDerivative = 1 + self.post_init_setup(len(self.inputs)) + + def BlackBox(self, *args, **kwargs): # The actual function that does things + runCases, remainingKwargs = self.parseInputs(*args, **kwargs) + + x = self.sanitizeInputs(runCases[0]['x']) + x = np.array([pyo.value(xval) for xval in x], dtype=np.float64) + + y = x**2 # Compute y + dydx = 2 * x # Compute dy/dx + + y = y + dydx = np.diag(dydx) + dydx = dydx + + return y, [dydx] # return z, grad(z), hess(z)... + + bb = Parabola() + bbo = bb.BlackBox(np.array([1, 1, 1])) + bbs = bb.BlackBox_Standardized(np.array([1, 1, 1])) + + self.assertAlmostEqual(pyo.value(bbo[0][0]), pyo.value(bbs[0][0][0])) + self.assertAlmostEqual( + pyomo_units.get_units(bbo[0][0]), pyomo_units.get_units(bbs[0][0][0]) + ) + + self.assertAlmostEqual(pyo.value(bbo[0][1]), pyo.value(bbs[0][0][1])) + self.assertAlmostEqual( + pyomo_units.get_units(bbo[0][1]), pyomo_units.get_units(bbs[0][0][1]) + ) + + self.assertAlmostEqual(pyo.value(bbo[0][2]), pyo.value(bbs[0][0][2])) + self.assertAlmostEqual( + pyomo_units.get_units(bbo[0][2]), pyomo_units.get_units(bbs[0][0][2]) + ) + + self.assertAlmostEqual( + pyo.value(bbo[1][0][0][0]), pyo.value(bbs[1][0][0][0][0]) + ) + self.assertAlmostEqual( + pyomo_units.get_units(bbo[1][0][0][0]), + pyomo_units.get_units(bbs[1][0][0][0][0]), + ) + + self.assertAlmostEqual( + pyo.value(bbo[1][0][0][1]), pyo.value(bbs[1][0][0][0][1]) + ) + self.assertAlmostEqual( + pyomo_units.get_units(bbo[1][0][0][1]), + pyomo_units.get_units(bbs[1][0][0][0][1]), + ) + + self.assertAlmostEqual( + pyo.value(bbo[1][0][0][2]), pyo.value(bbs[1][0][0][0][2]) + ) + self.assertAlmostEqual( + pyomo_units.get_units(bbo[1][0][0][2]), + pyomo_units.get_units(bbs[1][0][0][0][2]), + ) + + self.assertAlmostEqual( + pyo.value(bbo[1][0][1][0]), pyo.value(bbs[1][0][0][1][0]) + ) + self.assertAlmostEqual( + pyomo_units.get_units(bbo[1][0][1][0]), + pyomo_units.get_units(bbs[1][0][0][1][0]), + ) + + self.assertAlmostEqual( + pyo.value(bbo[1][0][1][1]), pyo.value(bbs[1][0][0][1][1]) + ) + self.assertAlmostEqual( + pyomo_units.get_units(bbo[1][0][1][1]), + pyomo_units.get_units(bbs[1][0][0][1][1]), + ) + + self.assertAlmostEqual( + pyo.value(bbo[1][0][1][2]), pyo.value(bbs[1][0][0][1][2]) + ) + self.assertAlmostEqual( + pyomo_units.get_units(bbo[1][0][1][2]), + pyomo_units.get_units(bbs[1][0][0][1][2]), + ) + + self.assertAlmostEqual( + pyo.value(bbo[1][0][2][0]), pyo.value(bbs[1][0][0][2][0]) + ) + self.assertAlmostEqual( + pyomo_units.get_units(bbo[1][0][2][0]), + pyomo_units.get_units(bbs[1][0][0][2][0]), + ) + + self.assertAlmostEqual( + pyo.value(bbo[1][0][2][1]), pyo.value(bbs[1][0][0][2][1]) + ) + self.assertAlmostEqual( + pyomo_units.get_units(bbo[1][0][2][1]), + pyomo_units.get_units(bbs[1][0][0][2][1]), + ) + + self.assertAlmostEqual( + pyo.value(bbo[1][0][2][2]), pyo.value(bbs[1][0][0][2][2]) + ) + self.assertAlmostEqual( + pyomo_units.get_units(bbo[1][0][2][2]), + pyomo_units.get_units(bbs[1][0][0][2][2]), + ) + + def test_edi_blackbox_standardized_5(self): + "Tests the blackbox_standardized function" + import pyomo + import numpy as np + import pyomo.environ as pyo + from pyomo.environ import units + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel, ediprint + from pyomo.common.formatting import tostr + from pyomo.environ import units as pyomo_units + + class MatrixSum(BlackBoxFunctionModel): + def __init__(self): + # Set up all the attributes by calling Model.__init__ + super().__init__() + + # Setup Inputs + self.inputs.append('x', '', 'Independent Variable', size=[2, 2]) + + # Setup Outputs + self.outputs.append('y', '', 'Dependent Variable') + + # Simple model description + self.description = 'This model sums all elements of a 2x2 matrix' + + self.availableDerivative = 1 + + # standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) + def BlackBox(self, *args, **kwargs): + runCases, extras = self.parseInputs(*args, **kwargs) + x = [ + self.pyomo_value(runCases[i]['x']) for i in range(0, len(runCases)) + ] + + y = np.sum(x) * units.dimensionless + dydx = np.ones([2, 2]) * units.dimensionless + + return y, dydx + + bb = MatrixSum() + bbo = bb.BlackBox(np.array([[1, 1], [1, 1]])) + bbs = bb.BlackBox_Standardized(np.array([[1, 1], [1, 1]])) + + self.assertAlmostEqual(pyo.value(bbo[0]), pyo.value(bbs[0][0])) + self.assertAlmostEqual( + pyomo_units.get_units(bbo[0]), pyomo_units.get_units(bbs[0][0]) + ) + + self.assertAlmostEqual(pyo.value(bbo[1][0][0]), pyo.value(bbs[1][0][0][0][0])) + self.assertAlmostEqual( + pyomo_units.get_units(bbo[1][0][0]), + pyomo_units.get_units(bbs[1][0][0][0][0]), + ) + + self.assertAlmostEqual(pyo.value(bbo[1][0][1]), pyo.value(bbs[1][0][0][0][1])) + self.assertAlmostEqual( + pyomo_units.get_units(bbo[1][0][1]), + pyomo_units.get_units(bbs[1][0][0][0][1]), + ) + + self.assertAlmostEqual(pyo.value(bbo[1][1][0]), pyo.value(bbs[1][0][0][1][0])) + self.assertAlmostEqual( + pyomo_units.get_units(bbo[1][1][0]), + pyomo_units.get_units(bbs[1][0][0][1][0]), + ) + + self.assertAlmostEqual(pyo.value(bbo[1][1][1]), pyo.value(bbs[1][0][0][1][1])) + self.assertAlmostEqual( + pyomo_units.get_units(bbo[1][1][1]), + pyomo_units.get_units(bbs[1][0][0][1][1]), + ) + + def test_edi_blackbox_standardized_6(self): + "Tests the blackbox_standardized function" + import pyomo + import numpy as np + import pyomo.environ as pyo + from pyomo.environ import units + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel, ediprint + from pyomo.common.formatting import tostr + from pyomo.environ import units as pyomo_units + + class MatrixCast(BlackBoxFunctionModel): + def __init__(self): + # Set up all the attributes by calling Model.__init__ + super().__init__() + + # Setup Inputs + self.inputs.append('x', '', 'Independent Variable') + + # Setup Outputs + self.outputs.append('y', '', 'Dependent Variable', size=[2, 2]) + + # Simple model description + self.description = 'This model casts a number to a 2x2 matrix' + + self.availableDerivative = 1 + + # standard function call is y(, dydx, ...) = self.BlackBox(**{'x1':x1, 'x2':x2, ...}) + def BlackBox(self, *args, **kwargs): + runCases, extras = self.parseInputs(*args, **kwargs) + x = [ + self.pyomo_value(runCases[i]['x']) for i in range(0, len(runCases)) + ] + + y = x[0] * np.ones([2, 2]) * units.dimensionless + dydx = np.ones([2, 2]) * units.dimensionless + + return y, dydx + + bb = MatrixCast() + bbo = bb.BlackBox(2) + bbs = bb.BlackBox_Standardized(2) + + self.assertAlmostEqual(pyo.value(bbo[0][0][0]), pyo.value(bbs[0][0][0][0])) + self.assertAlmostEqual( + pyomo_units.get_units(bbo[0][0][0]), pyomo_units.get_units(bbs[0][0][0][0]) + ) + + self.assertAlmostEqual(pyo.value(bbo[0][0][1]), pyo.value(bbs[0][0][0][1])) + self.assertAlmostEqual( + pyomo_units.get_units(bbo[0][0][1]), pyomo_units.get_units(bbs[0][0][0][1]) + ) + + self.assertAlmostEqual(pyo.value(bbo[0][1][0]), pyo.value(bbs[0][0][1][0])) + self.assertAlmostEqual( + pyomo_units.get_units(bbo[0][1][0]), pyomo_units.get_units(bbs[0][0][1][0]) + ) + + self.assertAlmostEqual(pyo.value(bbo[0][1][1]), pyo.value(bbs[0][0][1][1])) + self.assertAlmostEqual( + pyomo_units.get_units(bbo[0][1][1]), pyomo_units.get_units(bbs[0][0][1][1]) + ) + + self.assertAlmostEqual(pyo.value(bbo[1][0][0]), pyo.value(bbs[1][0][0][0][0])) + self.assertAlmostEqual( + pyomo_units.get_units(bbo[1][0][0]), + pyomo_units.get_units(bbs[1][0][0][0][0]), + ) + + self.assertAlmostEqual(pyo.value(bbo[1][0][1]), pyo.value(bbs[1][0][0][0][1])) + self.assertAlmostEqual( + pyomo_units.get_units(bbo[1][0][1]), + pyomo_units.get_units(bbs[1][0][0][0][1]), + ) + + self.assertAlmostEqual(pyo.value(bbo[1][1][0]), pyo.value(bbs[1][0][0][1][0])) + self.assertAlmostEqual( + pyomo_units.get_units(bbo[1][1][0]), + pyomo_units.get_units(bbs[1][0][0][1][0]), + ) + + self.assertAlmostEqual(pyo.value(bbo[1][1][1]), pyo.value(bbs[1][0][0][1][1])) + self.assertAlmostEqual( + pyomo_units.get_units(bbo[1][1][1]), + pyomo_units.get_units(bbs[1][0][0][1][1]), + ) + + def test_edi_blackbox_multicase(self): + "Tests the multicase function" + import pyomo + import numpy as np + import pyomo.environ as pyo + from pyomo.environ import units + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel, ediprint + from pyomo.common.formatting import tostr + from pyomo.environ import units as pyomo_units + + class VectorCast(BlackBoxFunctionModel): + def __init__(self): + super().__init__() + self.description = 'This model evaluates the two norm' + self.inputs.append(name='x', units='', description='The x variable') + self.outputs.append( + name='y', units='', description='The y variable', size=3 + ) + self.availableDerivative = 1 + self.post_init_setup(len(self.inputs)) + + def BlackBox(self, *args, **kwargs): # The actual function that does things + runCases, remainingKwargs = self.parseInputs(*args, **kwargs) + + x = pyo.value(self.sanitizeInputs(runCases[0]['x'])) + + y = np.array([x, x, x]) * units.dimensionless + dydx = np.array([1.0, 1.0, 1.0]) * units.dimensionless + + return y, [dydx] # return z, grad(z), hess(z)... + + bb = VectorCast() + bbs = bb.BlackBox_Standardized([[1], [2]]) + bbm = bb.MultiCase([[1], [2]]) + + self.assertAlmostEqual( + pyo.value(bbs[0].values.y[0]), pyo.value(bbm[0].values.y[0]) + ) + self.assertAlmostEqual( + pyomo_units.get_units(bbs[0].values.y[0]), + pyomo_units.get_units(bbm[0].values.y[0]), + ) + + self.assertAlmostEqual( + pyo.value(bbs[0].values.y[1]), pyo.value(bbm[0].values.y[1]) + ) + self.assertAlmostEqual( + pyomo_units.get_units(bbs[0].values.y[1]), + pyomo_units.get_units(bbm[0].values.y[1]), + ) + + self.assertAlmostEqual( + pyo.value(bbs[0].values.y[2]), pyo.value(bbm[0].values.y[2]) + ) + self.assertAlmostEqual( + pyomo_units.get_units(bbs[0].values.y[2]), + pyomo_units.get_units(bbm[0].values.y[2]), + ) + + self.assertAlmostEqual( + pyo.value(bbs[0].first.y.x[0]), pyo.value(bbm[0].first.y.x[0]) + ) + self.assertAlmostEqual( + pyomo_units.get_units(bbs[0].first.y.x[0]), + pyomo_units.get_units(bbm[0].first.y.x[0]), + ) + + self.assertAlmostEqual( + pyo.value(bbs[0].first.y.x[1]), pyo.value(bbm[0].first.y.x[1]) + ) + self.assertAlmostEqual( + pyomo_units.get_units(bbs[0].first.y.x[1]), + pyomo_units.get_units(bbm[0].first.y.x[1]), + ) + + self.assertAlmostEqual( + pyo.value(bbs[0].first.y.x[2]), pyo.value(bbm[0].first.y.x[2]) + ) + self.assertAlmostEqual( + pyomo_units.get_units(bbs[0].first.y.x[2]), + pyomo_units.get_units(bbm[0].first.y.x[2]), + ) + + self.assertAlmostEqual( + pyo.value(bbs[1].values.y[0]), pyo.value(bbm[1].values.y[0]) + ) + self.assertAlmostEqual( + pyomo_units.get_units(bbs[1].values.y[0]), + pyomo_units.get_units(bbm[1].values.y[0]), + ) + + self.assertAlmostEqual( + pyo.value(bbs[1].values.y[1]), pyo.value(bbm[1].values.y[1]) + ) + self.assertAlmostEqual( + pyomo_units.get_units(bbs[1].values.y[1]), + pyomo_units.get_units(bbm[1].values.y[1]), + ) + + self.assertAlmostEqual( + pyo.value(bbs[1].values.y[2]), pyo.value(bbm[1].values.y[2]) + ) + self.assertAlmostEqual( + pyomo_units.get_units(bbs[1].values.y[2]), + pyomo_units.get_units(bbm[1].values.y[2]), + ) + + self.assertAlmostEqual( + pyo.value(bbs[1].first.y.x[0]), pyo.value(bbm[1].first.y.x[0]) + ) + self.assertAlmostEqual( + pyomo_units.get_units(bbs[1].first.y.x[0]), + pyomo_units.get_units(bbm[1].first.y.x[0]), + ) + + self.assertAlmostEqual( + pyo.value(bbs[1].first.y.x[1]), pyo.value(bbm[1].first.y.x[1]) + ) + self.assertAlmostEqual( + pyomo_units.get_units(bbs[1].first.y.x[1]), + pyomo_units.get_units(bbm[1].first.y.x[1]), + ) + + self.assertAlmostEqual( + pyo.value(bbs[1].first.y.x[2]), pyo.value(bbm[1].first.y.x[2]) + ) + self.assertAlmostEqual( + pyomo_units.get_units(bbs[1].first.y.x[2]), + pyomo_units.get_units(bbm[1].first.y.x[2]), + ) + if __name__ == '__main__': unittest.main() diff --git a/pyomo/contrib/edi/tests/test_docSnippets.py b/pyomo/contrib/edi/tests/test_docSnippets.py index e0f67bcff33..7147343b5f5 100644 --- a/pyomo/contrib/edi/tests/test_docSnippets.py +++ b/pyomo/contrib/edi/tests/test_docSnippets.py @@ -509,7 +509,9 @@ def test_edi_snippet_constraints_01(self): y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') c = f.Constant(name='c', value=1.0, units='', description='A constant c') f.Objective(c * x + y) - f.ConstraintList([x**2 + y**2 <= 1.0 * units.m**2, x <= 0.75 * units.m, x >= y]) + f.ConstraintList( + [x**2 + y**2 <= 1.0 * units.m**2, x <= 0.75 * units.m, x >= y] + ) # END: Constraints_Snippet_01 def test_edi_snippet_constraints_02(self): @@ -895,13 +897,6 @@ def test_edi_snippet_runtimeconstraints_12(self): from pyomo.environ import units from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel - f = Formulation() - x = f.Variable(name='x', guess=1.0, units='m', description='The x variable') - y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') - z = f.Variable(name='z', guess=1.0, units='m^2', description='The z variable') - - f.Objective(x + y) - class UnitCircle(BlackBoxFunctionModel): def __init__(self): super().__init__() @@ -945,6 +940,60 @@ def BlackBox_Standardized(self, x, y): # The standardized function bbs = m.BlackBox_Standardized(0.5 * units.m, 0.5 * units.m) # END: RuntimeConstraints_Snippet_12 + + def test_edi_snippet_runtimeconstraints_13(self): + # BEGIN: RuntimeConstraints_Snippet_13 + from collections import namedtuple + import pyomo.environ as pyo + from pyomo.environ import units + from pyomo.contrib.edi import Formulation, BlackBoxFunctionModel + + class UnitCircle(BlackBoxFunctionModel): + def __init__(self): + super().__init__() + self.description = 'This model evaluates the function: z = x**2 + y**2' + self.inputs.append(name='x', units='ft', description='The x variable') + self.inputs.append(name='y', units='ft', description='The y variable') + self.outputs.append( + name='z', + units='ft**2', + description='Resultant of the unit circle evaluation', + ) + self.availableDerivative = 1 + self.post_init_setup(len(self.inputs)) + + def BlackBox(self, x, y): # The actual function that does things + x = pyo.value(units.convert(x, self.inputs['x'].units)) + y = pyo.value(units.convert(y, self.inputs['y'].units)) + z = x**2 + y**2 + dzdx = 2 * x + dzdy = 2 * y + z = z * self.outputs['z'].units + dzdx = dzdx * self.outputs['z'].units / self.inputs['x'].units + dzdy = dzdy * self.outputs['z'].units / self.inputs['y'].units + return z, [dzdx, dzdy] # return z, grad(z), hess(z)... + + def MultiCase(self, list_of_cases): # The multi case function + returnTuple = namedtuple('returnTuple', ['values', 'first', 'second']) + optTuple = namedtuple('optTuple', [o.name for o in self.outputs]) + iptTuple = namedtuple('iptTuple', [i.name for i in self.inputs]) + + outputList = [] + + for i in range(0,len(list_of_cases)): # Could parallelize this loop + res = self.BlackBox_Standardized(list_of_cases[i][0], list_of_cases[i][1]) + values = optTuple(res.values) + first = optTuple(iptTuple(res.first.z.x, res.first.z.y)) + second = None # Second derivatives not currently supported + outputList.append(returnTuple(values, first, second)) + + return outputList + + m = UnitCircle() + bb = m.BlackBox(0.5 * units.m, 0.5 * units.m) + bbm = m.MultiCase([[0.5 * units.m, 0.5 * units.m],[1.0 * units.m, 1.0 * units.m]]) + # END: RuntimeConstraints_Snippet_13 + def test_edi_snippet_advancedRTC_01(self): # BEGIN: AdvancedRTC_Snippet_01 import numpy as np From 28df35301c16effea8a1b86778bc15b8fbfb76e4 Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Tue, 20 Aug 2024 14:52:17 -0700 Subject: [PATCH 50/61] fixing documentation and typos --- .../edi/blackboxconstraints.rst | 14 ++++++------- .../edi/blackboxobjectives.rst | 2 +- .../contributed_packages/edi/constants.rst | 8 ++++---- .../contributed_packages/edi/constraints.rst | 8 ++++---- .../contributed_packages/edi/formulation.rst | 20 +++++++++---------- .../contributed_packages/edi/index.rst | 6 +++--- .../contributed_packages/edi/objectives.rst | 4 ++-- .../contributed_packages/edi/variables.rst | 10 +++++----- pyomo/contrib/edi/examples/readme_example.py | 8 ++++---- pyomo/contrib/edi/tests/test_docSnippets.py | 11 ++++++---- 10 files changed, 47 insertions(+), 44 deletions(-) diff --git a/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst b/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst index 1e6ceaa28c4..60c8ab66465 100644 --- a/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst +++ b/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst @@ -7,11 +7,11 @@ Overview While some constraints are explicitly known and can be written directly into the optimization problem, it is common (particularly in engineering design) for some relationships to be too complex to be directly coded as a constraint. -EDI refers to these types of constraints as ``RuntimeConstraints`` because they are not constructed until they are needed by the solver. A particular subset of Runtime Constraints of interest are Black-Box constraints, that is, constraints which call to an external routine. To the average pyomo and EDI user, ``RuntimeConstraints`` are (for all intents and purposes) Black-Box constraints, and the distinction is semantic. +EDI refers to these types of constraints as ``RuntimeConstraints`` because they are not constructed until they are needed by the solver. A particular subset of Runtime Constraints of interest are Black-Box constraints, that is, constraints which call to an external routine. To the average Pyomo and EDI user, ``RuntimeConstraints`` are (for all intents and purposes) Black-Box constraints, and the distinction is semantic. In other words, if you wish to code a black-box constraint using EDI, you will be using the Runtime Constraint constructor. -In this context, a *Black-Box* is defined as a routine that performs hidden computation not visible EDI, pyomo, or more generally the optimization algorithm. However, it is **not** assumed that black-boxes are unable to return gradient information. A black-box in this context may be capable of returning arbitrary derivative information. +In this context, a *Black-Box* is defined as a routine that performs hidden computation not visible EDI, Pyomo, or more generally the optimization algorithm. However, it is **not** assumed that black-boxes are unable to return gradient information. A black-box in this context may be capable of returning arbitrary derivative information. Construction @@ -23,7 +23,7 @@ Runtime constraints consist of two separate elements that need to be constructed Constructing a Black Box ++++++++++++++++++++++++ -First, we need to create an object which is visible to pyomo/EDI that calls the black-box function. EDI calls this a ``BlackBoxFunctionModel``, and it is a base class that gets inherited into the objects you will create as a user. +First, we need to create an object which is visible to Pyomo/EDI that calls the black-box function. EDI calls this a ``BlackBoxFunctionModel``, and it is a base class that gets inherited into the objects you will create as a user. A simple example is shown below: @@ -269,9 +269,9 @@ The MultiCase Method ******************** The ``MultiCase`` method provides a native capability to call the ``BlackBox`` method across multiple inputs simultaneously. This function is **not** vectorized in the base class and is **not** optimized for performance. If you wish to have a high performance vectorized function, you will need to implement your own method. -Inputs to the ``MultiCase`` funciton should be a list of cases, which can be packed in any form accepted by the ``BlackBox_Standardized`` method. Overloading these functions may allow different forms of unpacking scheme. +Inputs to the ``MultiCase`` function should be a list of cases, which can be packed in any form accepted by the ``BlackBox_Standardized`` method. Overloading these functions may allow different forms of unpacking scheme. -The output is a list of ``NamedTuple`` objects that are output from the ``BlackBox_Standardized`` method. If overloading, you may choose to output via a differnt packing scheme. +The output is a list of ``NamedTuple`` objects that are output from the ``BlackBox_Standardized`` method. If overloading, you may choose to output via a different packing scheme. Below is an example of overriding the default ``MultiCase`` method: @@ -393,7 +393,7 @@ A standard construction Tips ---- -* Use the pyomo ``tostr()`` function (``from pyomo.common.formatting import tostr``) to print the results of black-boxes for more meaningful printouts +* Use the Pyomo ``tostr()`` function (``from pyomo.common.formatting import tostr``) to print the results of black-boxes for more meaningful printouts * Align input and output declarations just as is recommended for optimization variable and constant declarations * Declare an input/output all on one line, no matter what the style guides say * This interface is really designed for subject matter experts who are not python users to have a simple, easy path to include their tools into a python based optimization architecture. Try to let them build their own models as a means of fostering trust in the optimization tools @@ -406,6 +406,6 @@ Tips Known Issues ------------ -* Currently only equality constraints are supported, pending an update to pyomo (see `this issue `__) +* Currently only equality constraints are supported, pending an update to Pyomo (see `this issue `__) * Runtime constraints must output to a variable, numbers and constants are not permitted (see `this issue `__) * This functionality is not well tested when returning derivatives higher than first order. Though it should work, exercise caution and reach out to the dev team if questions arise. diff --git a/doc/OnlineDocs/contributed_packages/edi/blackboxobjectives.rst b/doc/OnlineDocs/contributed_packages/edi/blackboxobjectives.rst index b6e04cdd001..cc2d869f72a 100644 --- a/doc/OnlineDocs/contributed_packages/edi/blackboxobjectives.rst +++ b/doc/OnlineDocs/contributed_packages/edi/blackboxobjectives.rst @@ -1,6 +1,6 @@ Runtime (Black-Box) Objectives ============================== -Black-box objectives are not currently supported and are awaiting an update to pyomo (see `this issue `_). +Black-box objectives are not currently supported and are awaiting an update to Pyomo (see `this issue `_). Please contact the developers if you are looking for this functionality diff --git a/doc/OnlineDocs/contributed_packages/edi/constants.rst b/doc/OnlineDocs/contributed_packages/edi/constants.rst index 3f443886934..7eee386babb 100644 --- a/doc/OnlineDocs/contributed_packages/edi/constants.rst +++ b/doc/OnlineDocs/contributed_packages/edi/constants.rst @@ -5,7 +5,7 @@ Overview -------- Constants are a key mechanism used to capture the relationship between variables. In engineering design, constants are often defined by physics or operational limits. -The Constant constructor is a very thin wrapper on pyomo ``Param``, and so experienced pyomo users will not see any significant differences from base pyomo. +The Constant constructor is a very thin wrapper on Pyomo ``Param``, and so experienced Pyomo users will not see any significant differences from base Pyomo. Construction @@ -19,7 +19,7 @@ Constants are constructed by 1) creating an instance of a new parameter in a EDI :param name: The name of the constant for the purposes of tracking in the formulation. Commonly, this will be the same as the constant name in local namespace. :type name: str - :param value: The value of the constant. For scalar constants, this should be a valid float or int for the specified domain. For vector constants, this will most often also be a single float or int, but a dictionary of index-value pairs is also accepted as in accordance with base pyomo. Numpy arrays will be supported in a future release (see `this issue `__) + :param value: The value of the constant. For scalar constants, this should be a valid float or int for the specified domain. For vector constants, this will most often also be a single float or int, but a dictionary of index-value pairs is also accepted as in accordance with base Pyomo. Numpy arrays will be supported in a future release (see `this issue `__) :type value: float or int or dict :param units: The units of the constant. Every entry in a vector constant must have the same units. Entries of '', ' ', '-', 'None', and 'dimensionless' all become units.dimensionless :type units: str or pyomo.core.base.units_container._PyomoUnit @@ -37,9 +37,9 @@ Constants are constructed by 1) creating an instance of a new parameter in a EDI Relation to Pyomo Param ----------------------- -The fields: name and within, and bounds are directly passed to the pyomo ``Param`` constructor, with some minor checking. The value field is passed to the ``Param`` initialize field. The description field is passed to the doc field in the pyomo ``Param``. Units are passed directly with an additional check. All Constants set the pyomo ``Param`` mutable field to True. +The fields: name and within, and bounds are directly passed to the Pyomo ``Param`` constructor, with some minor checking. The value field is passed to the ``Param`` initialize field. The description field is passed to the doc field in the Pyomo ``Param``. Units are passed directly with an additional check. All Constants set the Pyomo ``Param`` mutable field to True. -Non-scalar constants are constructed using pyomo ``Sets``. Sets are constructed to be integer sets that fill the entire interval from lower bound to upper bound, ie a vector constant of length 5 would create a pyomo ``Set`` with valid indices [0,1,2,3,4] with no skips. In this way, non-scalar constatants are slightly less flexible than general non-scalar pyomo ``Param``. +Non-scalar constants are constructed using Pyomo ``Sets``. Sets are constructed to be integer sets that fill the entire interval from lower bound to upper bound, ie a vector constant of length 5 would create a Pyomo ``Set`` with valid indices [0,1,2,3,4] with no skips. In this way, non-scalar constatants are slightly less flexible than general non-scalar Pyomo ``Param``. Examples diff --git a/doc/OnlineDocs/contributed_packages/edi/constraints.rst b/doc/OnlineDocs/contributed_packages/edi/constraints.rst index 7153c5331ff..6217c7e8ea1 100644 --- a/doc/OnlineDocs/contributed_packages/edi/constraints.rst +++ b/doc/OnlineDocs/contributed_packages/edi/constraints.rst @@ -7,7 +7,7 @@ Overview Constraints are the mathematical representation of rules that are imposed on your decisions/variables when minimizing or maximizing. In engineering design, constraints are often imposed by physics or operational limits. -The Constraint constructor is a very thin wrapper on pyomo ``Constraint``, and so experienced pyomo users will not see any significant differences from base pyomo. +The Constraint constructor is a very thin wrapper on Pyomo ``Constraint``, and so experienced Pyomo users will not see any significant differences from base Pyomo. Construction @@ -32,7 +32,7 @@ However, the expected use case is the ``f.ConstraintList()`` function: Declares new constraints in a pyomo.edi.formulation from a list of inputs - :param conList: The list of constraints to be generated. Entries will be pyomo expressions, or lists/tuples/dicts that are used to create RuntimeConstraints (see :doc:`here <./blackboxconstraints>`) + :param conList: The list of constraints to be generated. Entries will be Pyomo expressions, or lists/tuples/dicts that are used to create RuntimeConstraints (see :doc:`here <./blackboxconstraints>`) :type conList: list :return: None @@ -42,7 +42,7 @@ However, the expected use case is the ``f.ConstraintList()`` function: Relation to Pyomo Constraint ---------------------------- -The EDI constraint constructor is essentially a direct pass through to base pyomo. Constraints will be added to the ``pyomo.ConcreteModel`` in increasing order with key ``constraint_###`` where the the index of the objective appears after the underscore. First constraint is labeled as ``constraint_1``, and constraint names are never padded with zeros. RuntimeConstraints also contribute to this counter. +The EDI constraint constructor is essentially a direct pass through to base Pyomo. Constraints will be added to the ``pyomo.ConcreteModel`` in increasing order with key ``constraint_###`` where the the index of the objective appears after the underscore. First constraint is labeled as ``constraint_1``, and constraint names are never padded with zeros. RuntimeConstraints also contribute to this counter. Examples @@ -85,5 +85,5 @@ Tips Known Issues ------------ -* Indexed variables must be broken up using either indices or a pyomo rule (see `this issue `__) +* Indexed variables must be broken up using either indices or a Pyomo rule (see `this issue `__) * Units that are inconsistent, but not the same (ie, meters and feet) will flag as invalid when checking units (see `this issue `__) diff --git a/doc/OnlineDocs/contributed_packages/edi/formulation.rst b/doc/OnlineDocs/contributed_packages/edi/formulation.rst index 51f6b81f8f5..64968eff425 100644 --- a/doc/OnlineDocs/contributed_packages/edi/formulation.rst +++ b/doc/OnlineDocs/contributed_packages/edi/formulation.rst @@ -6,7 +6,7 @@ Formulation
-The core object in EDI is called a *Formulation*. For experienced pyomo users, a Formulation inherits from a pyomo *ConcreteModel*, and can therefore be treated exactly as a typical *ConcreteModel* with a few additional features. +The core object in EDI is called a *Formulation*. The core object in EDI is called a *Formulation*. For experienced Pyomo users, a Formulation inherits from a Pyomo :class:`~pyomo.environ.ConcreteModel`, and can therefore be treated exactly as a typical :class:`~pyomo.environ.ConcreteModel` with a few additional features. Each modeling element (ex: Variable, Constant, Objective, and Constraint) has a constructor that is used to create the corresponding element in the *Formulation* instance. In addition, there are a number of helper functions that collect and return model elements or perform supporting actions. @@ -21,7 +21,7 @@ A *Formulation* is constructed as follows: :end-before: # END: Formulation_Snippet_01 -Standard practice is to construct a formulation to namespace variable ``f``, but any valid python name can be used. Standard pyomo practice would be to construct this to ``model`` or ``m``. +Standard practice is to construct a formulation to namespace variable ``f``, but any valid Python name can be used. Standard Pyomo practice would be to construct this to ``model`` or ``m``. Declaring Variables @@ -29,7 +29,7 @@ Declaring Variables See the :doc:`Variables <./variables>` Documentation -Variables are declared using the ``f.Variable()`` function. This function creates an instance of ``pyomo.environ.Var`` and adds it to the ``edi.contrib.Formulation``. The function returns an instance of a ``pyomo.environ.Var`` that can be used in later construction. +Variables are declared using the ``f.Variable()`` function. This function creates an instance of :class:`~pyomo.environ.Var` and adds it to the ``pyomo.contrib.edi.Formulation``. The function returns an instance of a :class:`~pyomo.environ.Var` that can be used in later construction. .. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py :language: python @@ -43,7 +43,7 @@ Declaring Constants See the :doc:`Constants <./constants>` Documentation -Constants (referred to in base pyomo as parameters or ``Params``) are declared using the ``f.Constant()`` function. This function creates an instance of a ``pyomo.environ.Param`` and adds it to the ``edi.contrib.Formulation``. This function also returns an instance of ``pyomo.environ.Param`` that can be used in later construction. +Constants (referred to in base Pyomo as parameters or ``Params``) are declared using the ``f.Constant()`` function. This function creates an instance of a :class:`~pyomo.environ.Param` and adds it to the ``pyomo.contrib.ediFormulation``. This function also returns an instance of :class:`~pyomo.environ.Param` that can be used in later construction. .. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py :language: python @@ -58,7 +58,7 @@ Declaring Objectives See the :doc:`Objectives <./objectives>` Documentation -Objectives are declared using the ``f.Objective()`` function. This function creates an instance of a ``pyomo.environ.Objective`` and adds it to the ``edi.contrib.Formulation``. Multiple objectives can be declared, but interpretation of multiple objectives will depend on the solver. The returned values of the ``f.Variable()`` and ``f.Constant()`` declarations can be used to construct the objective. Black-box (ie, Runtime) objectives are not supported at this time, but are planned in a future update. +Objectives are declared using the ``f.Objective()`` function. This function creates an instance of a :class:`~pyomo.environ.Objective` and adds it to the ``pyomo.contrib.edi.Formulation``. Multiple objectives can be declared, but interpretation of multiple objectives will depend on the solver. The returned values of the ``f.Variable()`` and ``f.Constant()`` declarations can be used to construct the objective. Black-box (i.e. Runtime) objectives are not supported at this time, but are planned in a future update. .. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py :language: python @@ -67,7 +67,7 @@ Objectives are declared using the ``f.Objective()`` function. This function cre :end-before: # END: Formulation_Snippet_04 -By default, objectives are minimized, but can be switched to a maximize using the ``sense`` keyword from pyomo: +By default, objectives are minimized, but can be switched to a maximize using the ``sense`` keyword from Pyomo: .. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py :language: python @@ -84,7 +84,7 @@ Declaring Constraints See the :doc:`Constraints <./constraints>` Documentation -Constraints can be declared in two ways. First is using the standard ``f.Constraint()`` constructor. This function creates an instance of ``pyomo.environ.Constraint`` and adds it to the ``edi.contrib.Formulation``. The operators ``<=``, ``>=``, and ``==`` are used as constraint constructors. +Constraints can be declared in two ways. First is using the standard ``f.Constraint()`` constructor. This function creates an instance of :class:`~pyomo.environ.Constraint` and adds it to the ``pyomo.contrib.edi.Formulation``. The operators ``<=``, ``>=``, and ``==`` are used as constraint constructors. .. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py :language: python @@ -116,11 +116,11 @@ Declaring Runtime (Black-Box) Constraints See the :doc:`Runtime (Black-Box) Constraints <./blackboxconstraints>` Documentation -One of the main features of EDI is the streamlined implementation of Black-Box Constraints. A *Black-Box* is defined as a routine that performs hidden computation not visible EDI, pyomo, or more generally the optimization algorithm. However, it is **not** assumed that black-boxes are unable to return gradient information. A black-box in this context may be capable of returning arbitrary derivative information. +One of the main features of EDI is the streamlined implementation of Black-Box Constraints. A *Black-Box* is defined as a routine that performs hidden computation not visible to EDI, Pyomo, or more generally the optimization algorithm. However, it is **not** assumed that black-boxes are unable to return gradient information. A black-box in this context may be capable of returning arbitrary derivative information. -Black-box constraints are considered to be a sub-class of a more general class of constraints called *Runtime Constraints*, that is constraints that are not actually constructed until the optimization routine is actually running. In most cases, Runtime Constraints are approximated as linear by the solver, and therefore a Runtime Constraint is expected to provide function evaluations and gradient information. +Black-box constraints are considered to be a sub-class of a more general class of constraints called *Runtime Constraints*, that is constraints that are not actually constructed until the optimization routine is running. In most cases, Runtime Constraints are approximated as linear by the solver, and therefore a Runtime Constraint is expected to provide function evaluations and gradient information. -The use of Runtime constraints requires a black box model that is discussed in detail in the dedicated documentation (see :doc:`here <./blackboxconstraints>`), but for the purposes of demonstrating the constructors, a simple black box example will appear in all of the code snippets below. +The use of Runtime Constraints requires a black box model that is discussed in detail in the dedicated documentation (see :doc:`here <./blackboxconstraints>`), but for the purposes of demonstrating the constructors, a simple black box example will appear in all of the code snippets below. Runtime Constraints are declared one of two ways, just as regular constraints. The ``f.RuntimeConstraint()`` constructor is available: diff --git a/doc/OnlineDocs/contributed_packages/edi/index.rst b/doc/OnlineDocs/contributed_packages/edi/index.rst index 9ffd8bc0ae6..244262a64c4 100644 --- a/doc/OnlineDocs/contributed_packages/edi/index.rst +++ b/doc/OnlineDocs/contributed_packages/edi/index.rst @@ -7,14 +7,14 @@ The Pyomo Engineering Design Interface (EDI) is a lightweight wrapper on the Pyo Installation ------------ -EDI installs as a part of the standard pyomo install: +EDI installs as a part of the standard Pyomo install: :: pip install pyomo -EDI also requires packages that are optional in base pyomo: +EDI also requires packages that are optional in base Pyomo: :: @@ -44,4 +44,4 @@ User's Guide Developers ---------- -The pyomo EDI interface is developed and maintained by `Cody Karcher `_ +The Pyomo EDI interface is developed and maintained by `Cody Karcher `_ diff --git a/doc/OnlineDocs/contributed_packages/edi/objectives.rst b/doc/OnlineDocs/contributed_packages/edi/objectives.rst index 936e46b1637..ff4db17f40a 100644 --- a/doc/OnlineDocs/contributed_packages/edi/objectives.rst +++ b/doc/OnlineDocs/contributed_packages/edi/objectives.rst @@ -6,7 +6,7 @@ Overview Objectives are the mathematical representation of what you wish to minimize or maximize. In engineering design, objectives are often minimizing cost, material, or time, or alternatively maximizing profit or utility. -The Objective constructor is a very thin wrapper on pyomo ``Objective``, and so experienced pyomo users will not see any significant differences from base pyomo. +The Objective constructor is a very thin wrapper on Pyomo ``Objective``, and so experienced Pyomo users will not see any significant differences from base Pyomo. Construction @@ -30,7 +30,7 @@ Objectives are constructed by creating an instance of a new objective in a EDI F Relation to Pyomo Objective --------------------------- -The EDI objective constructor is essentially a direct pass through to base pyomo. Objectives will be added to the ``pyomo.ConcreteModel`` in increasing order with key ``objective_###`` where the the index of the objective appears after the underscore. First objective is labeled as ``objective_1``, and objective names are never padded with zeros. +The EDI objective constructor is essentially a direct pass through to base Pyomo. Objectives will be added to the ``pyomo.ConcreteModel`` in increasing order with key ``objective_###`` where the the index of the objective appears after the underscore. First objective is labeled as ``objective_1``, and objective names are never padded with zeros. Examples diff --git a/doc/OnlineDocs/contributed_packages/edi/variables.rst b/doc/OnlineDocs/contributed_packages/edi/variables.rst index 06f0c6581cf..9d4c14a8a9d 100644 --- a/doc/OnlineDocs/contributed_packages/edi/variables.rst +++ b/doc/OnlineDocs/contributed_packages/edi/variables.rst @@ -7,7 +7,7 @@ Variables are the mathematical representation of individual decisions being cons Using the EDI package, variables can be defined as both scalar (``pyomo.core.base.var.ScalarVar``) and vector/matrix/tensor (``pyomo.core.base.var.IndexedVar``), and can exist in many mathematical spaces (All Real, Integers, etc). -The Variable constructor is a very thin wrapper, and so experienced pyomo users will not see any significant differences from base pyomo. +The Variable constructor is a very thin wrapper, and so experienced Pyomo users will not see any significant differences from base Pyomo. Construction @@ -17,11 +17,11 @@ Variables are constructed by 1) creating an instance of a new variable in a EDI .. py:function:: f.Variable(name, guess, units, description='', size=None, bounds=None, domain=None) - Declares a variable in a pyomo.edi.formulation + Declares a variable in a pyomo.contrib.edi.formulation :param name: The name of the variable for the purposes of tracking in the formulation. Commonly, this will be the same as the variable name in local namespace. :type name: str - :param guess: The initial guess of the variable. For scalar variables, this should be a valid float or int for the specified domain. For vector variables, this will most often also be a single float or int, but a dictionary of index-value pairs is also accepted as in accordance with base pyomo. Numpy arrays will be supported in a future release (see `this issue `_) + :param guess: The initial guess of the variable. For scalar variables, this should be a valid float or int for the specified domain. For vector variables, this will most often also be a single float or int, but a dictionary of index-value pairs is also accepted as in accordance with base Pyomo. NumPy arrays will be supported in a future release (see `this issue `_) :type guess: float or int or dict :param units: The units of the variable. Every entry in a vector variable must have the same units. Entries of '', ' ', '-', 'None', and 'dimensionless' all become units.dimensionless :type units: str or pyomo.core.base.units_container._PyomoUnit @@ -41,9 +41,9 @@ Variables are constructed by 1) creating an instance of a new variable in a EDI Relation to Pyomo Var --------------------- -The fields: name, domain, and bounds are directly passed to the pyomo ``Var`` constructor, with some minor checking. The guess field is passed to initialize. The description field is passed to the doc field in the pyomo ``Var``. Units are passed directly with an additional check. +The fields: name, domain, and bounds are directly passed to the Pyomo ``Var`` constructor, with some minor checking. The guess field is passed to initialize. The description field is passed to the doc field in the Pyomo ``Var``. Units are passed directly with an additional check. -Non-scalar variables are constructed using pyomo ``Sets``. Sets are constructed to be integer sets that fill the entire interval from lower bound to upper bound, ie a vector variable of length 5 would create a pyomo ``Set`` with valid indices [0,1,2,3,4] with no skips. In this way, non-scalar constatants are slightly less flexible than general non-scalar pyomo ``Params``. +Non-scalar variables are constructed using Pyomo ``Sets``. Sets are constructed to be integer sets that fill the entire interval from lower bound to upper bound, ie a vector variable of length 5 would create a Pyomo ``Set`` with valid indices [0,1,2,3,4] with no skips. In this way, non-scalar constatants are slightly less flexible than general non-scalar Pyomo ``Params``. Examples diff --git a/pyomo/contrib/edi/examples/readme_example.py b/pyomo/contrib/edi/examples/readme_example.py index d4f2d026cb1..95e4fe42e9c 100644 --- a/pyomo/contrib/edi/examples/readme_example.py +++ b/pyomo/contrib/edi/examples/readme_example.py @@ -61,7 +61,7 @@ def BlackBox(self, x, y): # The actual function that does things dzdx = 2 * x # Compute dz/dx dzdy = 2 * y # Compute dz/dy - z *= units.ft**2 + z *= self.outputs['z'].units dzdx *= units.ft # units.ft**2 / units.ft dzdy *= units.ft # units.ft**2 / units.ft @@ -73,8 +73,8 @@ def BlackBox(self, x, y): # The actual function that does things # ======================= f.ConstraintList([[z, '==', [x, y], UnitCircle()], z <= 1 * units.m**2]) -# ============================================= -# Run the black box (improves coverage metrics) -# ============================================= +# ================= +# Run the black box +# ================= uc = UnitCircle() bbo = uc.BlackBox(0.5 * units.m, 0.5 * units.m) diff --git a/pyomo/contrib/edi/tests/test_docSnippets.py b/pyomo/contrib/edi/tests/test_docSnippets.py index 7147343b5f5..133842f9493 100644 --- a/pyomo/contrib/edi/tests/test_docSnippets.py +++ b/pyomo/contrib/edi/tests/test_docSnippets.py @@ -940,7 +940,6 @@ def BlackBox_Standardized(self, x, y): # The standardized function bbs = m.BlackBox_Standardized(0.5 * units.m, 0.5 * units.m) # END: RuntimeConstraints_Snippet_12 - def test_edi_snippet_runtimeconstraints_13(self): # BEGIN: RuntimeConstraints_Snippet_13 from collections import namedtuple @@ -980,8 +979,10 @@ def MultiCase(self, list_of_cases): # The multi case function outputList = [] - for i in range(0,len(list_of_cases)): # Could parallelize this loop - res = self.BlackBox_Standardized(list_of_cases[i][0], list_of_cases[i][1]) + for i in range(0, len(list_of_cases)): # Could parallelize this loop + res = self.BlackBox_Standardized( + list_of_cases[i][0], list_of_cases[i][1] + ) values = optTuple(res.values) first = optTuple(iptTuple(res.first.z.x, res.first.z.y)) second = None # Second derivatives not currently supported @@ -991,7 +992,9 @@ def MultiCase(self, list_of_cases): # The multi case function m = UnitCircle() bb = m.BlackBox(0.5 * units.m, 0.5 * units.m) - bbm = m.MultiCase([[0.5 * units.m, 0.5 * units.m],[1.0 * units.m, 1.0 * units.m]]) + bbm = m.MultiCase( + [[0.5 * units.m, 0.5 * units.m], [1.0 * units.m, 1.0 * units.m]] + ) # END: RuntimeConstraints_Snippet_13 def test_edi_snippet_advancedRTC_01(self): From e255302ed99ae6f8081b7cd0b66ae27e93f5425e Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Tue, 20 Aug 2024 15:17:20 -0700 Subject: [PATCH 51/61] had an outdated version of black --- pyomo/contrib/edi/tests/test_docSnippets.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pyomo/contrib/edi/tests/test_docSnippets.py b/pyomo/contrib/edi/tests/test_docSnippets.py index 133842f9493..7ae8d18cc16 100644 --- a/pyomo/contrib/edi/tests/test_docSnippets.py +++ b/pyomo/contrib/edi/tests/test_docSnippets.py @@ -509,9 +509,7 @@ def test_edi_snippet_constraints_01(self): y = f.Variable(name='y', guess=1.0, units='m', description='The y variable') c = f.Constant(name='c', value=1.0, units='', description='A constant c') f.Objective(c * x + y) - f.ConstraintList( - [x**2 + y**2 <= 1.0 * units.m**2, x <= 0.75 * units.m, x >= y] - ) + f.ConstraintList([x**2 + y**2 <= 1.0 * units.m**2, x <= 0.75 * units.m, x >= y]) # END: Constraints_Snippet_01 def test_edi_snippet_constraints_02(self): From 65f23dda5e1cbdb7aaf34339ce794b9f85634674 Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Tue, 20 Aug 2024 15:33:02 -0700 Subject: [PATCH 52/61] removing broken links from the readme --- .../contributed_packages/edi/constants.rst | 12 ++++++------ .../contributed_packages/edi/variables.rst | 2 +- pyomo/contrib/edi/README.md | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/OnlineDocs/contributed_packages/edi/constants.rst b/doc/OnlineDocs/contributed_packages/edi/constants.rst index 7eee386babb..2a83d4de8a4 100644 --- a/doc/OnlineDocs/contributed_packages/edi/constants.rst +++ b/doc/OnlineDocs/contributed_packages/edi/constants.rst @@ -5,27 +5,27 @@ Overview -------- Constants are a key mechanism used to capture the relationship between variables. In engineering design, constants are often defined by physics or operational limits. -The Constant constructor is a very thin wrapper on Pyomo ``Param``, and so experienced Pyomo users will not see any significant differences from base Pyomo. +The Constant constructor is a very thin wrapper on Pyomo :py:class:`Param `, and so experienced Pyomo users will not see any significant differences from base Pyomo. Construction ------------ -Constants are constructed by 1) creating an instance of a new parameter in a EDI Formulation and 2) passing out this newly constructed parameter to be used in objective and constraint construction. +Constants are constructed by 1) creating an instance of a new constant in a EDI Formulation and 2) passing out this newly constructed constant to be used in objective and constraint construction. .. py:function:: f.Constant(name, value, units, description='', size=None, within=None) - Declares a constant in a pyomo.edi.formulation + Declares a constant in a pyomo.contrib.edi.formulation :param name: The name of the constant for the purposes of tracking in the formulation. Commonly, this will be the same as the constant name in local namespace. :type name: str - :param value: The value of the constant. For scalar constants, this should be a valid float or int for the specified domain. For vector constants, this will most often also be a single float or int, but a dictionary of index-value pairs is also accepted as in accordance with base Pyomo. Numpy arrays will be supported in a future release (see `this issue `__) + :param value: The value of the constant. For scalar constants, this should be a valid float or int for the specified domain. For vector constants, this will most often also be a single float or int, but a dictionary of index-value pairs is also accepted as in accordance with base Pyomo. NumPy arrays will be supported in a future release (see `this issue `__) :type value: float or int or dict :param units: The units of the constant. Every entry in a vector constant must have the same units. Entries of '', ' ', '-', 'None', and 'dimensionless' all become units.dimensionless :type units: str or pyomo.core.base.units_container._PyomoUnit :param description: A description of the constant :type description: str - :param size: The size (or shape) of the constant. Entries of 0, 1, and None all correspond to scalar constants. Other integers correspond to vector constants. Matrix and tensor constants are declared using lists of ints, ex: [10,10]. Matrix and tensor constants with a dimension of 1 (ie, [10,10,1]) will be rejected as the extra dimension holds no meaningful value. + :param size: The size (or shape) of the constant. Entries of 0, 1, and None all correspond to scalar constants. Other integers correspond to vector constants. Matrix and tensor constants are declared using lists of ints, ex: [10,10]. Matrix and tensor constants with a dimension of 1 (i.e., [10,10,1]) will be rejected as the extra dimension holds no meaningful value. :type size: int or list :param within: The domain of the constant (ex: Reals, Integers, etc). Default of None constructs a constant in Reals. This option should rarely be used. :type within: pyomo set @@ -37,7 +37,7 @@ Constants are constructed by 1) creating an instance of a new parameter in a EDI Relation to Pyomo Param ----------------------- -The fields: name and within, and bounds are directly passed to the Pyomo ``Param`` constructor, with some minor checking. The value field is passed to the ``Param`` initialize field. The description field is passed to the doc field in the Pyomo ``Param``. Units are passed directly with an additional check. All Constants set the Pyomo ``Param`` mutable field to True. +The arguments ``name``, ``within``, and ``bounds`` are directly passed to the Pyomo ``Param`` constructor, with some minor checking. The ``value`` field is passed to the ``Param`` ``initialize`` field. The ``description`` field is passed to the ``doc`` field in the Pyomo ``Param``. Units are passed directly with an additional check. All Constants set the Pyomo ``Param`` ``mutable`` field to True. Non-scalar constants are constructed using Pyomo ``Sets``. Sets are constructed to be integer sets that fill the entire interval from lower bound to upper bound, ie a vector constant of length 5 would create a Pyomo ``Set`` with valid indices [0,1,2,3,4] with no skips. In this way, non-scalar constatants are slightly less flexible than general non-scalar Pyomo ``Param``. diff --git a/doc/OnlineDocs/contributed_packages/edi/variables.rst b/doc/OnlineDocs/contributed_packages/edi/variables.rst index 9d4c14a8a9d..87c584852eb 100644 --- a/doc/OnlineDocs/contributed_packages/edi/variables.rst +++ b/doc/OnlineDocs/contributed_packages/edi/variables.rst @@ -43,7 +43,7 @@ Relation to Pyomo Var The fields: name, domain, and bounds are directly passed to the Pyomo ``Var`` constructor, with some minor checking. The guess field is passed to initialize. The description field is passed to the doc field in the Pyomo ``Var``. Units are passed directly with an additional check. -Non-scalar variables are constructed using Pyomo ``Sets``. Sets are constructed to be integer sets that fill the entire interval from lower bound to upper bound, ie a vector variable of length 5 would create a Pyomo ``Set`` with valid indices [0,1,2,3,4] with no skips. In this way, non-scalar constatants are slightly less flexible than general non-scalar Pyomo ``Params``. +Non-scalar variables are constructed using Pyomo ``Sets``. Sets are constructed to be integer sets that fill the entire interval from lower bound to upper bound, ie a vector variable of length 5 would create a Pyomo ``Set`` with valid indices [0,1,2,3,4] with no skips. In this way, non-scalar constants are slightly less flexible than general non-scalar Pyomo ``Params``. Examples diff --git a/pyomo/contrib/edi/README.md b/pyomo/contrib/edi/README.md index b829120e9f6..26f9976f490 100644 --- a/pyomo/contrib/edi/README.md +++ b/pyomo/contrib/edi/README.md @@ -24,7 +24,7 @@ pip install pint The core object in EDI is the `Formulation` object, which inherits from the `pyomo.environ.ConcreteModel`. Essentially, a `Formulation` is a Pyomo `Model` with some extra stuff, but can be treated exactly as if it were a Pyomo `Model`. However, an EDI `Formulation` has some additional features that can help simplify model construction. -Below is a simple example to get started, but additional resources can be found in the [examples](https://github.com/Pyomo/pyomo/tree/main/pyomo/contrib/edi/examples) folder or in the EDI [documentation](https://pyomo.readthedocs.io/en/stable/contributed_packages/edi/index.html) + ```python # ================= From ef135d1d20fe0428c2aeaf49d3136c6dbb6983f3 Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Tue, 20 Aug 2024 16:02:15 -0700 Subject: [PATCH 53/61] actualy removing links this time --- .../edi/blackboxconstraints.rst | 24 +++++++++---------- .../contributed_packages/edi/constants.rst | 2 +- .../contributed_packages/edi/constraints.rst | 6 ++--- .../contributed_packages/edi/objectives.rst | 4 ++-- pyomo/contrib/edi/README.md | 2 -- pyomo/contrib/edi/examples/aircraft_gp.py | 3 --- 6 files changed, 18 insertions(+), 23 deletions(-) diff --git a/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst b/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst index 60c8ab66465..4d23f419354 100644 --- a/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst +++ b/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst @@ -33,7 +33,7 @@ A simple example is shown below: :start-after: # BEGIN: RuntimeConstraints_Snippet_01 :end-before: # END: RuntimeConstraints_Snippet_01 -The inheriting classes can have any valid python name (in this case ``Parabola``) and have two methods ``__init__()`` and ``BlackBox()``. +The inheriting classes can have any valid Python name (in this case ``Parabola``) and must have two methods ``__init__()`` and ``BlackBox()``. The init method @@ -49,19 +49,19 @@ The ``__init__()`` function sets up the model, and has 5 distinct steps. First, In general, this line can be used verbatim. -Next, you must tell the model what its inputs are by appending them to the ``self.inputs`` attribute. These inputs exist entirely in the local namespace of the black-box model, and are **independent** of the namespace in the optimization model (ex, something called ``x`` in the optimization can be called ``y`` in the black-box model). Inputs must have a ``name`` and ``units``, and has optional arguments ``description``, and ``size``, all of which are defined the same way as EDI variables. (There are some advanced uses reserved for the advanced discussion). +Next, you must tell the model what its inputs are by appending them to the ``self.inputs`` attribute. These inputs exist entirely in the local namespace of the black-box model, and are **independent** of the namespace in the optimization model (e.g. something called ``x`` in the optimization model can be called ``y`` in the black-box model). Inputs must have a ``name`` and ``units`` and can optionally have a ``description``, and ``size``. .. py:function:: self.inputs.append(name, units, description='', size=0) Appends a variable to a black box input list - :param name: The name of the variable, any valid python string. **Does not** have to match the name in the optimization formulation + :param name: The name of the variable, any valid Python string. **Does not** have to match the name in the optimization formulation :type name: str - :param units: The units of the variable. Every entry in a vector variable must have the same units. Entries of '', ' ', '-', 'None', and 'dimensionless' all become units.dimensionless. The units **must** be convertible to the units used in the optimization formulation (ex, meters and feet), but **are not required** to be the same. Because of this, for example, a black-box can be written in imperial units, while an optimization formulation operates in metric. + :param units: The units of the variable. Every entry in a vector variable must have the same units. Entries of '', ' ', '-', 'None', and 'dimensionless' all become units.dimensionless. The units **must** be convertible to the units used in the optimization formulation (e.g., meters and feet), but **are not required** to be the same. Because of this, for example, a black-box can be written in imperial units, while an optimization formulation operates in metric. :type units: str or pyomo.core.base.units_container._PyomoUnit :param description: A description of the variable :type description: str - :param size: The size (or shape) of the variable. Entries of 0, 1, and None all correspond to scalar variables. Other integers correspond to vector variables. Matrix and tensor variable are declared using lists of ints, ex: [10,10]. Matrix and tensor variables with a dimension of 1 (ie, [10,10,1]) will be rejected as the extra dimension holds no meaningful value. + :param size: The size (or shape) of the variable. Entries of 0, 1, and None all correspond to scalar variables. Other integers correspond to vector variables. Matrix and tensor variable are declared using lists of ints, ex: [10,10]. Matrix and tensor variables with a dimension of 1 (i.e., [10,10,1]) will be rejected as the extra dimension holds no meaningful value. :type size: int or list @@ -81,13 +81,13 @@ Next, outputs must be added to the model. This is done identically to inputs, h Appends a variable to a black box output list - :param name: The name of the variable, any valid python string. **Does not** have to match the name in the optimization formulation + :param name: The name of the variable, any valid Python string. **Does not** have to match the name in the optimization formulation :type name: str :param units: The units of the variable. Every entry in a vector variable must have the same units. Entries of '', ' ', '-', 'None', and 'dimensionless' all become units.dimensionless. The units **must** be convertible to the units used in the optimization formulation (ex, meters and feet), but **are not required** to be the same. Because of this, for example, a black-box can be written in imperial units, while an optimization formulation operates in metric. :type units: str or pyomo.core.base.units_container._PyomoUnit :param description: A description of the variable :type description: str - :param size: The size (or shape) of the variable. Entries of 0, 1, and None all correspond to scalar variables. Other integers correspond to vector variables. Matrix and tensor variable are declared using lists of ints, ex: [10,10]. Matrix and tensor variables with a dimension of 1 (ie, [10,10,1]) will be rejected as the extra dimension holds no meaningful value. + :param size: The size (or shape) of the variable. Entries of 0, 1, and None all correspond to scalar variables. Other integers correspond to vector variables. Matrix and tensor variable are declared using lists of ints, ex: [10,10]. Matrix and tensor variables with a dimension of 1 (i.e., [10,10,1]) will be rejected as the extra dimension holds no meaningful value. :type size: int or list @@ -100,7 +100,7 @@ and similarly: :end-before: # END: RuntimeConstraints_Snippet_04 -Finally, the highest available derivative must be set. For models being used in optimization, this will most often be ``1``, ie first derivative, gradient, or Jacobian information. +Finally, the highest available derivative must be set. For models being used in optimization, this will most often be ``1``, i.e. first derivative, gradient, or Jacobian information. .. literalinclude:: ../../../../pyomo/contrib/edi/tests/test_docSnippets.py :language: python @@ -114,7 +114,7 @@ The BlackBox method The ``BlackBox`` is extremely flexible, but here we present standard usage for a typical user. -The ``BlackBox`` method assumes to take in the inputs as arguments in the order defined during the ``__init__()`` method. Note that the method assumes inputs **with units** and expects outputs **with units**. In general, the units on inputs and outputs need not be in any specific system, but should be convertible (ex, meters and feet) to whatever has been specified as the input units when defining in the ``__init__()`` function. +The ``BlackBox`` method should take in the inputs as arguments in the order defined during the ``__init__()`` method. Note that the method assumes inputs are passed in **with units** and outputs are returned **with units**. In general, the units on inputs and outputs need not be in any specific system, but should be convertible (e.g. meters and feet) to the units specified in the ``__init__()`` function. Various unpacking schemes are enabled by default via the ``parse_inputs`` function. Use of this function is not necessary, but provides for the parsing of index argumented lists (ex: ``function(x1, x2, x3)``) and keyword argumented dictionaries (ex: ``function({'x2':x2, 'x1':x1, 'x3',x3})``), along with a few other possibilities. @@ -396,10 +396,10 @@ Tips * Use the Pyomo ``tostr()`` function (``from pyomo.common.formatting import tostr``) to print the results of black-boxes for more meaningful printouts * Align input and output declarations just as is recommended for optimization variable and constant declarations * Declare an input/output all on one line, no matter what the style guides say -* This interface is really designed for subject matter experts who are not python users to have a simple, easy path to include their tools into a python based optimization architecture. Try to let them build their own models as a means of fostering trust in the optimization tools +* * This interface is designed for subject matter experts who are not Python users to have a simple, easy path to include their tools/models into a Python based optimization architecture. * Embrace units. They will save you so many times, it is well worth the minor additional overhead -* Pyomo units work slightly diffenrently than pint (for those with pint experience), but those differences should be hidden from the model creator for the most part -* It is common to use this framework to call to a piece of software external to python +* Pyomo units work slightly differently than pint (for those with pint experience), but those differences should be hidden from the model creator for the most part +* It is common to use this framework to call to a piece of software external to Python * A model summary can be printed by calling ``print(model_instance.summary)`` diff --git a/doc/OnlineDocs/contributed_packages/edi/constants.rst b/doc/OnlineDocs/contributed_packages/edi/constants.rst index 2a83d4de8a4..73e25234027 100644 --- a/doc/OnlineDocs/contributed_packages/edi/constants.rst +++ b/doc/OnlineDocs/contributed_packages/edi/constants.rst @@ -39,7 +39,7 @@ Relation to Pyomo Param The arguments ``name``, ``within``, and ``bounds`` are directly passed to the Pyomo ``Param`` constructor, with some minor checking. The ``value`` field is passed to the ``Param`` ``initialize`` field. The ``description`` field is passed to the ``doc`` field in the Pyomo ``Param``. Units are passed directly with an additional check. All Constants set the Pyomo ``Param`` ``mutable`` field to True. -Non-scalar constants are constructed using Pyomo ``Sets``. Sets are constructed to be integer sets that fill the entire interval from lower bound to upper bound, ie a vector constant of length 5 would create a Pyomo ``Set`` with valid indices [0,1,2,3,4] with no skips. In this way, non-scalar constatants are slightly less flexible than general non-scalar Pyomo ``Param``. +Non-scalar constants are constructed using Pyomo ``Sets``. Sets are constructed to be integer sets that fill the entire interval from lower bound to upper bound, ie a vector constant of length 5 would create a Pyomo ``Set`` with valid indices [0,1,2,3,4] with no skips. In this way, non-scalar constants are slightly less flexible than general non-scalar Pyomo ``Param``. Examples diff --git a/doc/OnlineDocs/contributed_packages/edi/constraints.rst b/doc/OnlineDocs/contributed_packages/edi/constraints.rst index 6217c7e8ea1..bf283f93d31 100644 --- a/doc/OnlineDocs/contributed_packages/edi/constraints.rst +++ b/doc/OnlineDocs/contributed_packages/edi/constraints.rst @@ -7,7 +7,7 @@ Overview Constraints are the mathematical representation of rules that are imposed on your decisions/variables when minimizing or maximizing. In engineering design, constraints are often imposed by physics or operational limits. -The Constraint constructor is a very thin wrapper on Pyomo ``Constraint``, and so experienced Pyomo users will not see any significant differences from base Pyomo. +The Constraint constructor is a very thin wrapper on Pyomo :py:class:`Constraint `, and so experienced Pyomo users will not see any significant differences from base Pyomo. Construction @@ -17,7 +17,7 @@ Constraints are constructed by creating an instance of a new constraint in a EDI .. py:function:: f.Constraint(expr) - Declares a constraint in a pyomo.edi.formulation + Declares a constraint in a pyomo.contrib.edi.formulation :param expr: The expression representing the constraint :type expr: pyomo expression @@ -30,7 +30,7 @@ However, the expected use case is the ``f.ConstraintList()`` function: .. py:function:: f.ConstraintList(conList) - Declares new constraints in a pyomo.edi.formulation from a list of inputs + Declares new constraints in a pyomo.contrib.edi.formulation from a list of inputs :param conList: The list of constraints to be generated. Entries will be Pyomo expressions, or lists/tuples/dicts that are used to create RuntimeConstraints (see :doc:`here <./blackboxconstraints>`) :type conList: list diff --git a/doc/OnlineDocs/contributed_packages/edi/objectives.rst b/doc/OnlineDocs/contributed_packages/edi/objectives.rst index ff4db17f40a..9144fbd42e5 100644 --- a/doc/OnlineDocs/contributed_packages/edi/objectives.rst +++ b/doc/OnlineDocs/contributed_packages/edi/objectives.rst @@ -6,7 +6,7 @@ Overview Objectives are the mathematical representation of what you wish to minimize or maximize. In engineering design, objectives are often minimizing cost, material, or time, or alternatively maximizing profit or utility. -The Objective constructor is a very thin wrapper on Pyomo ``Objective``, and so experienced Pyomo users will not see any significant differences from base Pyomo. +The Objective constructor is a very thin wrapper on Pyomo :py:class:`Objective `, and so experienced Pyomo users will not see any significant differences from base Pyomo. Construction @@ -16,7 +16,7 @@ Objectives are constructed by creating an instance of a new objective in a EDI F .. py:function:: f.Objective(expr, sense=minimize) - Declares an objective in a pyomo.edi.formulation + Declares an objective in a pyomo.contrib.edi.formulation :param expr: The expression to be optimized :type expr: pyomo expression diff --git a/pyomo/contrib/edi/README.md b/pyomo/contrib/edi/README.md index 26f9976f490..e335bf639ed 100644 --- a/pyomo/contrib/edi/README.md +++ b/pyomo/contrib/edi/README.md @@ -24,8 +24,6 @@ pip install pint The core object in EDI is the `Formulation` object, which inherits from the `pyomo.environ.ConcreteModel`. Essentially, a `Formulation` is a Pyomo `Model` with some extra stuff, but can be treated exactly as if it were a Pyomo `Model`. However, an EDI `Formulation` has some additional features that can help simplify model construction. - - ```python # ================= # Import Statements diff --git a/pyomo/contrib/edi/examples/aircraft_gp.py b/pyomo/contrib/edi/examples/aircraft_gp.py index 94e1db70083..7695a0a8c79 100644 --- a/pyomo/contrib/edi/examples/aircraft_gp.py +++ b/pyomo/contrib/edi/examples/aircraft_gp.py @@ -11,10 +11,7 @@ # Import Statements # ================= import numpy as np -import pyomo.environ as pyo -from pyomo.environ import units from pyomo.contrib.edi import Formulation -from pyomo.contrib.edi import BlackBoxFunctionModel # =================== # Declare Formulation From 1fb0c8d506a2f855305f8f2a45fb7bb16b8730b1 Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Fri, 27 Sep 2024 14:39:02 -0700 Subject: [PATCH 54/61] should be ready for reveiw? --- pyomo/contrib/edi/README.md | 6 ------ pyomo/contrib/edi/__init__.py | 10 +--------- 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/pyomo/contrib/edi/README.md b/pyomo/contrib/edi/README.md index e335bf639ed..ca369ac8df5 100644 --- a/pyomo/contrib/edi/README.md +++ b/pyomo/contrib/edi/README.md @@ -2,12 +2,6 @@ The Pyomo Engineering Design Interface (EDI) is a lightweight wrapper on the Pyomo language that is targeted at composing engineering design optimization problems. The language and interface have been designed to mimic many of the features found in [GPkit](https://github.com/convexengineering/gpkit) and [CVXPY](https://github.com/cvxpy/cvxpy) while also providing a simple, clean interface for black-box analysis codes that are common in engineering design applications. -## TODO -- Add unit tests for the BlackBox_Standardized method -- Add unit tests for the MultiCase method -- Add documentation for the MultiCase method -- Address Bethany's comments - ## Installation EDI is a part of the standard installation process for Pyomo: diff --git a/pyomo/contrib/edi/__init__.py b/pyomo/contrib/edi/__init__.py index 99ab37bc474..4f0093c51c4 100644 --- a/pyomo/contrib/edi/__init__.py +++ b/pyomo/contrib/edi/__init__.py @@ -36,15 +36,7 @@ # Import the black box modeling tools try: from pyomo.contrib.edi.blackBoxFunctionModel import BlackBoxFunctionModel - from pyomo.contrib.edi.blackBoxFunctionModel import ( - BlackBoxFunctionModel_Variable as BlackBoxVariable, - ) - from pyomo.contrib.edi.blackBoxFunctionModel import ( - BlackBoxFunctionModel_Variable as BBVariable, - ) - from pyomo.contrib.edi.blackBoxFunctionModel import ( - BlackBoxFunctionModel_Variable as BBV, - ) + except: pass # in this case, the dependencies are not installed, nothing will work From ac487b82b4bf78bb2a9f1124c258192f3772210f Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Fri, 27 Sep 2024 14:47:01 -0700 Subject: [PATCH 55/61] fixing dependency issue --- pyomo/contrib/edi/__init__.py | 44 ++++++++++++---------- pyomo/contrib/edi/blackBoxFunctionModel.py | 4 +- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/pyomo/contrib/edi/__init__.py b/pyomo/contrib/edi/__init__.py index 4f0093c51c4..8f5996eaaf0 100644 --- a/pyomo/contrib/edi/__init__.py +++ b/pyomo/contrib/edi/__init__.py @@ -47,28 +47,34 @@ from pyomo.core.base.units_container import _PyomoUnit from pyomo.core.expr.numeric_expr import NPV_ProductExpression, NPV_DivisionExpression from collections import namedtuple -import numpy as np +from pyomo.common.dependencies import numpy, numpy_available +if numpy_available: + np = numpy -def recursive_sub(x_in): - x = list(copy.deepcopy(x_in)) - for i in range(0, len(x)): - if isinstance(x[i], _PyomoUnit): - x[i] = '1.0*' + str(x[i]) - elif ( - isinstance( - x[i], (NPV_ProductExpression, NPV_DivisionExpression, np.float64) - ) - or x[i] is None - ): - if pyo.value(x[i]) == 1: + def recursive_sub(x_in): + x = list(copy.deepcopy(x_in)) + for i in range(0, len(x)): + if isinstance(x[i], _PyomoUnit): x[i] = '1.0*' + str(x[i]) + elif ( + isinstance( + x[i], (NPV_ProductExpression, NPV_DivisionExpression, np.float64) + ) + or x[i] is None + ): + if pyo.value(x[i]) == 1: + x[i] = '1.0*' + str(x[i]) + else: + x[i] = str(x[i]) else: - x[i] = str(x[i]) - else: - x[i] = recursive_sub(list(x[i])) - return x + x[i] = recursive_sub(list(x[i])) + return x -def ediprint(x): - print(recursive_sub(x)) + def ediprint(x): + print(recursive_sub(x)) + +else: + pass + # in this case, the dependencies are not installed, nothing will work diff --git a/pyomo/contrib/edi/blackBoxFunctionModel.py b/pyomo/contrib/edi/blackBoxFunctionModel.py index 61408ba87e8..0aacc49af7f 100644 --- a/pyomo/contrib/edi/blackBoxFunctionModel.py +++ b/pyomo/contrib/edi/blackBoxFunctionModel.py @@ -44,14 +44,14 @@ if numpy_available: - import numpy as np + np = numpy else: raise ImportError( "pyomo.contrib.edi requires numpy to enable black box capability, fix with 'pip install numpy' " ) if scipy_available: - import scipy.sparse as sps + sps = scipy.sparse else: raise ImportError( "pyomo.contrib.edi requires scipy to enable black box capability, fix with 'pip install scipy' " From 808483f23c9f8d03ece85302c1f729f0ede4d0e4 Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Fri, 27 Sep 2024 14:48:19 -0700 Subject: [PATCH 56/61] adding black --- pyomo/contrib/edi/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyomo/contrib/edi/__init__.py b/pyomo/contrib/edi/__init__.py index 8f5996eaaf0..4bddf791409 100644 --- a/pyomo/contrib/edi/__init__.py +++ b/pyomo/contrib/edi/__init__.py @@ -49,6 +49,7 @@ from collections import namedtuple from pyomo.common.dependencies import numpy, numpy_available + if numpy_available: np = numpy @@ -71,7 +72,6 @@ def recursive_sub(x_in): x[i] = recursive_sub(list(x[i])) return x - def ediprint(x): print(recursive_sub(x)) From 2342baa9736625c0ac47763c9ac0b6fff657a744 Mon Sep 17 00:00:00 2001 From: Cody Karcher Date: Fri, 27 Sep 2024 15:32:39 -0700 Subject: [PATCH 57/61] updating a python 3.8 compatibilty issue --- pyomo/contrib/edi/blackBoxFunctionModel.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pyomo/contrib/edi/blackBoxFunctionModel.py b/pyomo/contrib/edi/blackBoxFunctionModel.py index 0aacc49af7f..8d1bf62dee6 100644 --- a/pyomo/contrib/edi/blackBoxFunctionModel.py +++ b/pyomo/contrib/edi/blackBoxFunctionModel.py @@ -844,7 +844,13 @@ def MultiCase(self, *args, **kwargs): # first = [] # second = [] for rc in runCases: - ipt = rc | extras + try: + ipt = rc | extras + except: + # Allow for python 3.8 compatibility + ipt = copy.deepcopy(rc) + ipt.update(extras) + opt = self.BlackBox_Standardized(**ipt) outputList.append(opt) # values.append(opt[0]) From 0691ed49f9e63f3db91b6d2278b3f74aba36e649 Mon Sep 17 00:00:00 2001 From: blnicho Date: Mon, 4 Nov 2024 13:24:12 -0700 Subject: [PATCH 58/61] Updating location of EDI docs following the documentation reorg --- .../analysis}/edi/additionaltips.rst | 0 .../analysis}/edi/advancedruntimeconstraints.rst | 0 .../analysis}/edi/blackboxconstraints.rst | 0 .../analysis}/edi/blackboxobjectives.rst | 0 .../analysis}/edi/constants.rst | 0 .../analysis}/edi/constraints.rst | 0 .../analysis}/edi/examples.rst | 0 .../analysis}/edi/formulation.rst | 0 .../{contributed_packages => explanation/analysis}/edi/index.rst | 0 .../analysis}/edi/objectives.rst | 0 .../analysis}/edi/quickstart.rst | 0 .../analysis}/edi/variables.rst | 0 doc/OnlineDocs/explanation/analysis/index.rst | 1 + 13 files changed, 1 insertion(+) rename doc/OnlineDocs/{contributed_packages => explanation/analysis}/edi/additionaltips.rst (100%) rename doc/OnlineDocs/{contributed_packages => explanation/analysis}/edi/advancedruntimeconstraints.rst (100%) rename doc/OnlineDocs/{contributed_packages => explanation/analysis}/edi/blackboxconstraints.rst (100%) rename doc/OnlineDocs/{contributed_packages => explanation/analysis}/edi/blackboxobjectives.rst (100%) rename doc/OnlineDocs/{contributed_packages => explanation/analysis}/edi/constants.rst (100%) rename doc/OnlineDocs/{contributed_packages => explanation/analysis}/edi/constraints.rst (100%) rename doc/OnlineDocs/{contributed_packages => explanation/analysis}/edi/examples.rst (100%) rename doc/OnlineDocs/{contributed_packages => explanation/analysis}/edi/formulation.rst (100%) rename doc/OnlineDocs/{contributed_packages => explanation/analysis}/edi/index.rst (100%) rename doc/OnlineDocs/{contributed_packages => explanation/analysis}/edi/objectives.rst (100%) rename doc/OnlineDocs/{contributed_packages => explanation/analysis}/edi/quickstart.rst (100%) rename doc/OnlineDocs/{contributed_packages => explanation/analysis}/edi/variables.rst (100%) diff --git a/doc/OnlineDocs/contributed_packages/edi/additionaltips.rst b/doc/OnlineDocs/explanation/analysis/edi/additionaltips.rst similarity index 100% rename from doc/OnlineDocs/contributed_packages/edi/additionaltips.rst rename to doc/OnlineDocs/explanation/analysis/edi/additionaltips.rst diff --git a/doc/OnlineDocs/contributed_packages/edi/advancedruntimeconstraints.rst b/doc/OnlineDocs/explanation/analysis/edi/advancedruntimeconstraints.rst similarity index 100% rename from doc/OnlineDocs/contributed_packages/edi/advancedruntimeconstraints.rst rename to doc/OnlineDocs/explanation/analysis/edi/advancedruntimeconstraints.rst diff --git a/doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst b/doc/OnlineDocs/explanation/analysis/edi/blackboxconstraints.rst similarity index 100% rename from doc/OnlineDocs/contributed_packages/edi/blackboxconstraints.rst rename to doc/OnlineDocs/explanation/analysis/edi/blackboxconstraints.rst diff --git a/doc/OnlineDocs/contributed_packages/edi/blackboxobjectives.rst b/doc/OnlineDocs/explanation/analysis/edi/blackboxobjectives.rst similarity index 100% rename from doc/OnlineDocs/contributed_packages/edi/blackboxobjectives.rst rename to doc/OnlineDocs/explanation/analysis/edi/blackboxobjectives.rst diff --git a/doc/OnlineDocs/contributed_packages/edi/constants.rst b/doc/OnlineDocs/explanation/analysis/edi/constants.rst similarity index 100% rename from doc/OnlineDocs/contributed_packages/edi/constants.rst rename to doc/OnlineDocs/explanation/analysis/edi/constants.rst diff --git a/doc/OnlineDocs/contributed_packages/edi/constraints.rst b/doc/OnlineDocs/explanation/analysis/edi/constraints.rst similarity index 100% rename from doc/OnlineDocs/contributed_packages/edi/constraints.rst rename to doc/OnlineDocs/explanation/analysis/edi/constraints.rst diff --git a/doc/OnlineDocs/contributed_packages/edi/examples.rst b/doc/OnlineDocs/explanation/analysis/edi/examples.rst similarity index 100% rename from doc/OnlineDocs/contributed_packages/edi/examples.rst rename to doc/OnlineDocs/explanation/analysis/edi/examples.rst diff --git a/doc/OnlineDocs/contributed_packages/edi/formulation.rst b/doc/OnlineDocs/explanation/analysis/edi/formulation.rst similarity index 100% rename from doc/OnlineDocs/contributed_packages/edi/formulation.rst rename to doc/OnlineDocs/explanation/analysis/edi/formulation.rst diff --git a/doc/OnlineDocs/contributed_packages/edi/index.rst b/doc/OnlineDocs/explanation/analysis/edi/index.rst similarity index 100% rename from doc/OnlineDocs/contributed_packages/edi/index.rst rename to doc/OnlineDocs/explanation/analysis/edi/index.rst diff --git a/doc/OnlineDocs/contributed_packages/edi/objectives.rst b/doc/OnlineDocs/explanation/analysis/edi/objectives.rst similarity index 100% rename from doc/OnlineDocs/contributed_packages/edi/objectives.rst rename to doc/OnlineDocs/explanation/analysis/edi/objectives.rst diff --git a/doc/OnlineDocs/contributed_packages/edi/quickstart.rst b/doc/OnlineDocs/explanation/analysis/edi/quickstart.rst similarity index 100% rename from doc/OnlineDocs/contributed_packages/edi/quickstart.rst rename to doc/OnlineDocs/explanation/analysis/edi/quickstart.rst diff --git a/doc/OnlineDocs/contributed_packages/edi/variables.rst b/doc/OnlineDocs/explanation/analysis/edi/variables.rst similarity index 100% rename from doc/OnlineDocs/contributed_packages/edi/variables.rst rename to doc/OnlineDocs/explanation/analysis/edi/variables.rst diff --git a/doc/OnlineDocs/explanation/analysis/index.rst b/doc/OnlineDocs/explanation/analysis/index.rst index 0a8e3c3b416..a7899b29b4b 100644 --- a/doc/OnlineDocs/explanation/analysis/index.rst +++ b/doc/OnlineDocs/explanation/analysis/index.rst @@ -7,6 +7,7 @@ Analysis in Pyomo alternative_solutions community doe/doe + edi/index iis incidence/index mpc/index From 895fe8463505c8ca704fb13f55a3541cc2dd521f Mon Sep 17 00:00:00 2001 From: blnicho Date: Mon, 4 Nov 2024 13:38:40 -0700 Subject: [PATCH 59/61] NFC: removing file that was missed during the doc reorg --- doc/OnlineDocs/explanation/contrib_index.txt | 48 -------------------- 1 file changed, 48 deletions(-) delete mode 100644 doc/OnlineDocs/explanation/contrib_index.txt diff --git a/doc/OnlineDocs/explanation/contrib_index.txt b/doc/OnlineDocs/explanation/contrib_index.txt deleted file mode 100644 index cec80e9d965..00000000000 --- a/doc/OnlineDocs/explanation/contrib_index.txt +++ /dev/null @@ -1,48 +0,0 @@ -Third-Party Contributions -========================= - -Pyomo includes a variety of additional features and functionality -provided by third parties through the ``pyomo.contrib`` package. This -package includes both contributions included with the main Pyomo -distribution and wrappers for third-party packages that must be -installed separately. - -These packages are maintained by the original contributors and are -managed as *optional* Pyomo packages. - -Contributed packages distributed with Pyomo: - -.. toctree:: - :maxdepth: 1 - - alternative_solutions.rst - community.rst - doe/doe.rst - edi/index.rst - gdpopt.rst - iis.rst - incidence/index.rst - latex_printer.rst - mindtpy.rst - mpc/index.rst - multistart.rst - preprocessing.rst - parmest/index.rst - pynumero/index.rst - pyros.rst - sensitivity_toolbox.rst - trustregion.rst - -Contributed Pyomo interfaces to other packages: - -.. toctree:: - :maxdepth: 1 - - mcpp.rst - satsolver.rst - - -Contributed packages distributed independently of Pyomo, but accessible -through ``pyomo.contrib``: - -* `pyomo.contrib.simplemodel `_ From bd8420d2f68f4a00e5e1fb01590a950d6a7424c5 Mon Sep 17 00:00:00 2001 From: blnicho Date: Mon, 4 Nov 2024 16:57:06 -0700 Subject: [PATCH 60/61] Updating year in copyright statements and making sure copyright statement appears in every py file --- pyomo/contrib/edi/README.md | 16 ---------------- pyomo/contrib/edi/__init__.py | 2 +- pyomo/contrib/edi/blackBoxFunctionModel.py | 2 +- pyomo/contrib/edi/examples/__init__.py | 16 ++++++++++++++++ pyomo/contrib/edi/examples/aircraft_gp.py | 17 +++++++++++++++++ pyomo/contrib/edi/examples/readme_example.py | 17 +++++++++++++++++ pyomo/contrib/edi/formulation.py | 2 +- pyomo/contrib/edi/tests/__init__.py | 16 ++++++++++++++++ pyomo/contrib/edi/tests/test_blackbox.py | 2 +- pyomo/contrib/edi/tests/test_docSnippets.py | 2 +- pyomo/contrib/edi/tests/test_examples.py | 2 +- pyomo/contrib/edi/tests/test_formulation.py | 2 +- 12 files changed, 73 insertions(+), 23 deletions(-) diff --git a/pyomo/contrib/edi/README.md b/pyomo/contrib/edi/README.md index ca369ac8df5..7982834f4f6 100644 --- a/pyomo/contrib/edi/README.md +++ b/pyomo/contrib/edi/README.md @@ -99,19 +99,3 @@ f.ConstraintList( ] ) ``` - -## Acknowledgement - -Pyomo: Python Optimization Modeling Objects -Copyright (c) 2008-2023 -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. - -Development of this module was conducted as part of the Institute for -the Design of Advanced Energy Systems (IDAES) with support through the -Simulation-Based Engineering, Crosscutting Research Program within the -U.S. Department of Energy’s Office of Fossil Energy and Carbon Management. - -This software is distributed under the 3-clause BSD License. \ No newline at end of file diff --git a/pyomo/contrib/edi/__init__.py b/pyomo/contrib/edi/__init__.py index 4bddf791409..6c38af6220c 100644 --- a/pyomo/contrib/edi/__init__.py +++ b/pyomo/contrib/edi/__init__.py @@ -1,7 +1,7 @@ # ___________________________________________________________________________ # # Pyomo: Python Optimization Modeling Objects -# Copyright (c) 2008-2023 +# Copyright (c) 2008-2024 # 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 diff --git a/pyomo/contrib/edi/blackBoxFunctionModel.py b/pyomo/contrib/edi/blackBoxFunctionModel.py index 8d1bf62dee6..b8677bf406f 100644 --- a/pyomo/contrib/edi/blackBoxFunctionModel.py +++ b/pyomo/contrib/edi/blackBoxFunctionModel.py @@ -1,7 +1,7 @@ # ___________________________________________________________________________ # # Pyomo: Python Optimization Modeling Objects -# Copyright (c) 2008-2023 +# Copyright (c) 2008-2024 # 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 diff --git a/pyomo/contrib/edi/examples/__init__.py b/pyomo/contrib/edi/examples/__init__.py index e69de29bb2d..540b2561654 100644 --- a/pyomo/contrib/edi/examples/__init__.py +++ b/pyomo/contrib/edi/examples/__init__.py @@ -0,0 +1,16 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright (c) 2008-2024 +# 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. +# +# Development of this module was conducted as part of the Institute for +# the Design of Advanced Energy Systems (IDAES) with support through the +# Simulation-Based Engineering, Crosscutting Research Program within the +# U.S. Department of Energy’s Office of Fossil Energy and Carbon Management. +# +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ \ No newline at end of file diff --git a/pyomo/contrib/edi/examples/aircraft_gp.py b/pyomo/contrib/edi/examples/aircraft_gp.py index 7695a0a8c79..f23da219b17 100644 --- a/pyomo/contrib/edi/examples/aircraft_gp.py +++ b/pyomo/contrib/edi/examples/aircraft_gp.py @@ -1,3 +1,20 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright (c) 2008-2024 +# 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. +# +# Development of this module was conducted as part of the Institute for +# the Design of Advanced Energy Systems (IDAES) with support through the +# Simulation-Based Engineering, Crosscutting Research Program within the +# U.S. Department of Energy’s Office of Fossil Energy and Carbon Management. +# +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + # =========== # Description # =========== diff --git a/pyomo/contrib/edi/examples/readme_example.py b/pyomo/contrib/edi/examples/readme_example.py index 95e4fe42e9c..adf4799a569 100644 --- a/pyomo/contrib/edi/examples/readme_example.py +++ b/pyomo/contrib/edi/examples/readme_example.py @@ -1,3 +1,20 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright (c) 2008-2024 +# 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. +# +# Development of this module was conducted as part of the Institute for +# the Design of Advanced Energy Systems (IDAES) with support through the +# Simulation-Based Engineering, Crosscutting Research Program within the +# U.S. Department of Energy’s Office of Fossil Energy and Carbon Management. +# +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + # ================= # Import Statements # ================= diff --git a/pyomo/contrib/edi/formulation.py b/pyomo/contrib/edi/formulation.py index 897210b45cc..b1a2ae30f75 100644 --- a/pyomo/contrib/edi/formulation.py +++ b/pyomo/contrib/edi/formulation.py @@ -1,7 +1,7 @@ # ___________________________________________________________________________ # # Pyomo: Python Optimization Modeling Objects -# Copyright (c) 2008-2023 +# Copyright (c) 2008-2024 # 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 diff --git a/pyomo/contrib/edi/tests/__init__.py b/pyomo/contrib/edi/tests/__init__.py index e69de29bb2d..540b2561654 100644 --- a/pyomo/contrib/edi/tests/__init__.py +++ b/pyomo/contrib/edi/tests/__init__.py @@ -0,0 +1,16 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright (c) 2008-2024 +# 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. +# +# Development of this module was conducted as part of the Institute for +# the Design of Advanced Energy Systems (IDAES) with support through the +# Simulation-Based Engineering, Crosscutting Research Program within the +# U.S. Department of Energy’s Office of Fossil Energy and Carbon Management. +# +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ \ No newline at end of file diff --git a/pyomo/contrib/edi/tests/test_blackbox.py b/pyomo/contrib/edi/tests/test_blackbox.py index de98f4d7edd..7f273763317 100644 --- a/pyomo/contrib/edi/tests/test_blackbox.py +++ b/pyomo/contrib/edi/tests/test_blackbox.py @@ -1,7 +1,7 @@ # ___________________________________________________________________________ # # Pyomo: Python Optimization Modeling Objects -# Copyright (c) 2008-2023 +# Copyright (c) 2008-2024 # 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 diff --git a/pyomo/contrib/edi/tests/test_docSnippets.py b/pyomo/contrib/edi/tests/test_docSnippets.py index 7ae8d18cc16..09bc1fa3ab4 100644 --- a/pyomo/contrib/edi/tests/test_docSnippets.py +++ b/pyomo/contrib/edi/tests/test_docSnippets.py @@ -1,7 +1,7 @@ # ___________________________________________________________________________ # # Pyomo: Python Optimization Modeling Objects -# Copyright (c) 2008-2023 +# Copyright (c) 2008-2024 # 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 diff --git a/pyomo/contrib/edi/tests/test_examples.py b/pyomo/contrib/edi/tests/test_examples.py index 8fee03eb2b0..72eea036838 100644 --- a/pyomo/contrib/edi/tests/test_examples.py +++ b/pyomo/contrib/edi/tests/test_examples.py @@ -1,7 +1,7 @@ # ___________________________________________________________________________ # # Pyomo: Python Optimization Modeling Objects -# Copyright (c) 2008-2023 +# Copyright (c) 2008-2024 # 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 diff --git a/pyomo/contrib/edi/tests/test_formulation.py b/pyomo/contrib/edi/tests/test_formulation.py index 1a86610d7cc..37d7a9ae53e 100644 --- a/pyomo/contrib/edi/tests/test_formulation.py +++ b/pyomo/contrib/edi/tests/test_formulation.py @@ -1,7 +1,7 @@ # ___________________________________________________________________________ # # Pyomo: Python Optimization Modeling Objects -# Copyright (c) 2008-2023 +# Copyright (c) 2008-2024 # 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 From e73803ce9c577c407229123f8c2a34f367204f3d Mon Sep 17 00:00:00 2001 From: blnicho Date: Mon, 4 Nov 2024 17:18:34 -0700 Subject: [PATCH 61/61] Add missing newlines --- pyomo/contrib/edi/examples/__init__.py | 2 +- pyomo/contrib/edi/tests/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyomo/contrib/edi/examples/__init__.py b/pyomo/contrib/edi/examples/__init__.py index 540b2561654..38b30839be3 100644 --- a/pyomo/contrib/edi/examples/__init__.py +++ b/pyomo/contrib/edi/examples/__init__.py @@ -13,4 +13,4 @@ # U.S. Department of Energy’s Office of Fossil Energy and Carbon Management. # # This software is distributed under the 3-clause BSD License. -# ___________________________________________________________________________ \ No newline at end of file +# ___________________________________________________________________________ diff --git a/pyomo/contrib/edi/tests/__init__.py b/pyomo/contrib/edi/tests/__init__.py index 540b2561654..38b30839be3 100644 --- a/pyomo/contrib/edi/tests/__init__.py +++ b/pyomo/contrib/edi/tests/__init__.py @@ -13,4 +13,4 @@ # U.S. Department of Energy’s Office of Fossil Energy and Carbon Management. # # This software is distributed under the 3-clause BSD License. -# ___________________________________________________________________________ \ No newline at end of file +# ___________________________________________________________________________