pythonsympycalculushessian-matrix

Substitute values for variables in the result of derive_by_array in SymPy


I'm currently working through some exercises on multivariable function calculus and thought I would have a go at making my own function to determine gradient and hessian at a defined point for any function. I'm currently having issues when attempting to substitute the resulting matrices with coordinate values for an arbitrary function. I've already managed to solve specific examples, but my attempt to make a function to solve a user defined function isn't working correctly.

def multivariable_function(function, variables, substitute=(0,0)):
"""Determines Gradient and Hessian vectors for multivariable function.

  Args:
    function: Enter the multivariable function
    variables: Enter list of variable names
    substitute: Default = (0,0)

  Returns:
    gradient/hessian matrices for given coordinate 

  To do:
    Include sympy symbol() generation within function
  """

  #derive_by_array returns a gradient matrix for multivariable function
  Gradient = simplify(derive_by_array(function, variables))

  #derive_by_array returns a Hessian matrix for multivariable function                   
  Hessian = simplify(derive_by_array(derive_by_array(function, variables), variables))

  #Line currently isn't doing anything
  Gradient.subs(zip(variables, substitute))

  return Gradient, Hessian

This is the basic function so far in operation.

multivariable_function((x**2)*(y**3) + exp(2*x + x*y - 1) - (x**3 + 3*y**2)**2, (x,y))`

which yields the following result, I am however aiming to substitute the desired values into the gradient and hessian matrices to achieve the following desired result. I managed to achieved the desired result using the following.

from sympy import *

x, y, z, K, T, r, σ, h, a, f, μ, c, t, m, x1, x2, x3 = symbols('x, y, z, K, T, r, σ, h, a, f, μ, c, t, m, x1, x2, x3') # Variables used must be defined in sympy.
init_printing(use_unicode=False) #Print the answers in unicode characters

function = (x**2)*(y**3) + exp(2*x + x*y - 1) - (x**3 + 3*y**2)**2

Gradient_1 = simplify(derive_by_array(function, (x, y)))
Hessian_1 = simplify(derive_by_array(derive_by_array(function, (x, y)), (x, y)))

Gradient_1.subs(x, 0).subs(y,0), Hessian_1.subs(x,0).subs(y,0)

After viewing the issue raised here, it seems zipping the two lists should enable the subs() function to work, but it currently isn't for me. I attempted to loop through 'variables and 'substitute' to sequentially apply .subs(), however I'm finding the function only works if the method is chained for all replacement variables, as in the example above.

Does anyone know how I can apply the .subs() n times for a given coordinate to yield the relevant gradient/hessian matrices?


Solution

  • The variable Gradient is of type

    sympy.tensor.array.dense_ndim_array.ImmutableDenseNDimArray
    

    Like almost all SymPy objects, with exception of mutable matrices, it is immutable. The method subs does not modify it in place; it returns a new object, which needs to be assigned.

      Gradient = Gradient.subs(zip(variables, substitute))
      Hessian = Hessian.subs(zip(variables, substitute))
    

    Then the function works as expected, returning

    ([2*exp(-1), 0], [[4*exp(-1), exp(-1)], [exp(-1), 0]])
    

    But I suggest not passing generators to subs; there are outstanding issues involving that. Convert to a list or a dict first, to be safe. (There is also a difference there: should substitutions be consecutive or simultaneous, although this does not matter when substituting numbers for symbols.)

      subs_dict = dict(zip(variables, substitute))
      Gradient = Gradient.subs(subs_dict)
      Hessian = Hessian.subs(subs_dict)