Source code for pyomo.core.expr.calculus.diff_with_sympy

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

from pyomo.core.expr.sympy_tools import (
    sympy_available,
    sympyify_expression,
    sympy2pyomo_expression,
)

# A "public" attribute indicating that differentiate() can be called
# ... this provides a bit of future-proofing for alternative approaches
# to symbolic differentiation.
differentiate_available = sympy_available


[docs] def differentiate(expr, wrt=None, wrt_list=None): """Return derivative of expression. This function returns an expression or list of expression objects corresponding to the derivative of the passed expression 'expr' with respect to a variable 'wrt' or list of variables 'wrt_list' Args: expr (Expression): Pyomo expression wrt (Var): Pyomo variable wrt_list (list): list of Pyomo variables Returns: Expression or list of Expression objects """ if not sympy_available: raise RuntimeError( "The sympy module is not available.\n\t" "Cannot perform automatic symbolic differentiation." ) if not ((wrt is None) ^ (wrt_list is None)): raise ValueError( "differentiate(): Must specify exactly one of wrt and wrt_list" ) import sympy # # Convert the Pyomo expression to a sympy expression # objectMap, sympy_expr = sympyify_expression(expr) # # The partial_derivs dict holds intermediate sympy expressions that # we can re-use. We will prepopulate it with None for all vars that # appear in the expression (so that we can detect wrt combinations # that are, by definition, 0) # partial_derivs = {x: None for x in objectMap.sympyVars()} # # Setup the WRT list # if wrt is not None: wrt_list = [wrt] else: # Copy the list because we will normalize things in place below wrt_list = list(wrt_list) # # Convert WRT vars into sympy vars # ans = [None] * len(wrt_list) for i, target in enumerate(wrt_list): if target.__class__ is not tuple: target = (target,) wrt_list[i] = tuple(objectMap.getSympySymbol(x) for x in target) for x in wrt_list[i]: if x not in partial_derivs: ans[i] = 0.0 break # # We assume that users will not request duplicate derivatives. We # will only cache up to the next-to last partial, and if a user # requests the exact same derivative twice, then we will just # re-calculate it. # last_partial_idx = max(len(x) for x in wrt_list) - 1 # # Calculate all the derivatives # for i, target in enumerate(wrt_list): if ans[i] is not None: continue part = sympy_expr for j, wrt_var in enumerate(target): if j == last_partial_idx: part = sympy.diff(part, wrt_var) else: partial_target = target[: j + 1] if partial_target in partial_derivs: part = partial_derivs[partial_target] else: part = sympy.diff(part, wrt_var) partial_derivs[partial_target] = part ans[i] = sympy2pyomo_expression(part, objectMap) # # Return the answer # return ans if wrt is None else ans[0]