<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Creating a function from a symbolic expression
In SymPy there is a function to create a Python function which evaluates (usually numerically) an expression. SymPy allows the user to define the signature of this function (which is convenient when working with e.g. a numerical solver in scipy).
Step1:  We will look at an arbitrary expression $f(x, y)$
Step2:  One way to evaluate above expression numerically is to invoke the subs method followed by the evalf method
Step3:  However, if we need to do this repeatedly it can be quite slow
Step4:  even compared to a simple lambda function
Step5:  SymPy can also create a function analogous to f above. The function for doing so is called lambdify
Step6:  Note how we specified modules above
Step7:  NumPy's broadcasting (handling of different shapes) then works as expected
Step8:  Behind the scenes lambdify constructs a string representation of the Python code and uses Python's eval function to compile the function.
Let's now look at how we can get a specific function signature from lambdify
Step9:  Exercise
Step10:  Use either the %exercise or %load magic to get the exercise / solution respecitvely
Step11:  Replace ??? with the correct expression above.
<ASSISTANT_TASK:>
Python Code:
import sympy as sym
sym.init_printing()
Explanation: Creating a function from a symbolic expression
In SymPy there is a function to create a Python function which evaluates (usually numerically) an expression. SymPy allows the user to define the signature of this function (which is convenient when working with e.g. a numerical solver in scipy).
End of explanation
x, y = sym.symbols('x y')
expr = 3*x**2 + sym.log(x**2 + y**2 + 1)
expr
Explanation: We will look at an arbitrary expression $f(x, y)$:
$$
f(x, y) = 3 x^{2} + \log{\left (x^{2} + y^{2} + 1 \right )}
$$
End of explanation
expr.subs({x: 17, y: 42}).evalf()
Explanation: One way to evaluate above expression numerically is to invoke the subs method followed by the evalf method:
End of explanation
%timeit expr.subs({x: 17, y: 42}).evalf()
Explanation: However, if we need to do this repeatedly it can be quite slow:
End of explanation
import math
f = lambda x, y: 3*x**2 + math.log(x**2 + y**2 + 1)
f(17, 42)
%timeit f(17, 42)
Explanation: even compared to a simple lambda function:
End of explanation
g = sym.lambdify([x, y], expr, modules=['math'])
g(17, 42)
%timeit g(17, 42)
Explanation: SymPy can also create a function analogous to f above. The function for doing so is called lambdify:
End of explanation
import numpy as np
xarr = np.linspace(17, 18, 5)
h = sym.lambdify([x, y], expr)
out = h(xarr, 42)
out.shape
Explanation: Note how we specified modules above: it tells lambdify to use math.log, if we don't specify modules SymPy will (since v1.1) use numpy by default. This can be useful when dealing with arrays in the input:
End of explanation
yarr = np.linspace(42, 43, 7).reshape((1, 7))
out2 = h(xarr.reshape((5, 1)), yarr)  # if we would try to use g() here, it would fail
out2.shape
Explanation: NumPy's broadcasting (handling of different shapes) then works as expected:
End of explanation
z = z1, z2, z3 = sym.symbols('z:3')
expr2 = x*y*(z1 + z2 + z3)
func2 = sym.lambdify([x, y, z], expr2)
func2(1, 2, (3, 4, 5))
Explanation: Behind the scenes lambdify constructs a string representation of the Python code and uses Python's eval function to compile the function.
Let's now look at how we can get a specific function signature from lambdify:
End of explanation
xplot = np.outer(np.linspace(-5, 5, 100), np.ones(100))
yplot = xplot.T
%load_ext scipy2017codegen.exercise
Explanation: Exercise: Create a function from a symbolic expression
Plot $z(x, y) = \frac{\partial^2 f(x,y)}{\partial x \partial y}$ from above ($f(x, y)$ is available as expr) as a surface plot for $-5 < x < 5, -5 < y < 5$.
End of explanation
%exercise exercise_lambdify_expr.py
Explanation: Use either the %exercise or %load magic to get the exercise / solution respecitvely
End of explanation
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
%matplotlib inline
fig = plt.figure(figsize=(15, 13))
ax = plt.axes(projection='3d')
ax.plot_surface(xplot, yplot, zplot, cmap=plt.cm.coolwarm)
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('$%s$' % sym.latex(d2fdxdy))
Explanation: Replace ??? with the correct expression above.
End of explanation
<END_TASK>