1# ___________________________________________________________________________ 2# 3# Pyomo: Python Optimization Modeling Objects 4# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC 5# Under the terms of Contract DE-NA0003525 with National Technology and 6# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain 7# rights in this software. 8# This software is distributed under the 3-clause BSD License. 9# ___________________________________________________________________________ 10 11import enum 12from .diff_with_sympy import differentiate as sympy_diff 13from .diff_with_pyomo import reverse_sd, reverse_ad 14 15 16class Modes(str, enum.Enum): 17 sympy='sympy' 18 reverse_symbolic='reverse_symbolic' 19 reverse_numeric='reverse_numeric' 20 21 # Overloading __str__ is needed to match the behavior of the old 22 # pyutilib.enum class (removed June 2020). There are spots in the 23 # code base that expect the string representation for items in the 24 # enum to not include the class name. New uses of enum shouldn't 25 # need to do this. 26 def __str__(self): 27 return self.value 28 29 30def differentiate(expr, wrt=None, wrt_list=None, mode=Modes.reverse_numeric): 31 """Return derivative of expression. 32 33 This function returns the derivative of expr with respect to one or 34 more variables. The type of the return value depends on the 35 arguments wrt, wrt_list, and mode. See below for details. 36 37 Parameters 38 ---------- 39 expr: pyomo.core.expr.numeric_expr.ExpressionBase 40 The expression to differentiate 41 wrt: pyomo.core.base.var._GeneralVarData 42 If specified, this function will return the derivative with 43 respect to wrt. wrt is normally a _GeneralVarData, but could 44 also be a _ParamData. wrt and wrt_list cannot both be specified. 45 wrt_list: list of pyomo.core.base.var._GeneralVarData 46 If specified, this function will return the derivative with 47 respect to each element in wrt_list. A list will be returned 48 where the values are the derivatives with respect to the 49 corresponding entry in wrt_list. 50 mode: pyomo.core.expr.calculus.derivatives.Modes 51 Specifies the method to use for differentiation. Should be one 52 of the members of the Modes enum: 53 54 Modes.sympy: 55 The pyomo expression will be converted to a sympy 56 expression. Differentiation will then be done with 57 sympy, and the result will be converted back to a pyomo 58 expression. The sympy mode only does symbolic 59 differentiation. The sympy mode requires exactly one of 60 wrt and wrt_list to be specified. 61 Modes.reverse_symbolic: 62 Symbolic differentiation will be performed directly with 63 the pyomo expression in reverse mode. If neither wrt nor 64 wrt_list are specified, then a ComponentMap is returned 65 where there will be a key for each node in the 66 expression tree, and the values will be the symbolic 67 derivatives. 68 Modes.reverse_numeric: 69 Numeric differentiation will be performed directly with 70 the pyomo expression in reverse mode. If neither wrt nor 71 wrt_list are specified, then a ComponentMap is returned 72 where there will be a key for each node in the 73 expression tree, and the values will be the floating 74 point values of the derivatives at the current values of 75 the variables. 76 77 Returns 78 ------- 79 res: float, :py:class:`ExpressionBase`, :py:class:`ComponentMap`, or list 80 The value or expression of the derivative(s) 81 82 """ 83 84 if mode == Modes.reverse_numeric or mode == Modes.reverse_symbolic: 85 if mode == Modes.reverse_numeric: 86 res = reverse_ad(expr=expr) 87 else: 88 res = reverse_sd(expr=expr) 89 90 if wrt is not None: 91 if wrt_list is not None: 92 raise ValueError( 93 'differentiate(): Cannot specify both wrt and wrt_list.') 94 if wrt in res: 95 res = res[wrt] 96 else: 97 res = 0 98 elif wrt_list is not None: 99 _res = list() 100 for _wrt in wrt_list: 101 if _wrt in res: 102 _res.append(res[_wrt]) 103 else: 104 _res.append(0) 105 res = _res 106 elif mode is Modes.sympy: 107 res = sympy_diff(expr=expr, wrt=wrt, wrt_list=wrt_list) 108 else: 109 raise ValueError( 110 'differentiate(): Unrecognized differentiation mode: {0}'.format( 111 mode)) 112 113 return res 114 115 116differentiate.Modes = Modes 117