Reputation: 857
My entire goal of engaging the sympy package is to take a function f
of d variables provided, evaluate its gradient, then given a new equation with 1 unknown variable lambda
substitute that into my initial function f
to get an equation with only the single variable lambda
. My progress has been quite promising, but left me wanting:
import simpy
# starting position
x0=(0,0)
# quadratic function provided; return sympy object
# f(x,y) = 3*x**2+x*y+y**2+x+2
f=3*x**2+x*y+y**2+x+2
# gradient of the function; return list
fgradient=[sympy.diff(f,var) for var in vars]
>> [6*x + y + 1, x + 2*y]
# stepping into algorithm
xk=x0
# evaluate the gradient at our initial point; return list
direction=[i.evalf(subs={'x':xk[0],’y’:xk[1]}) for i in fgradient]
>> [1.00000000000000, 0]
# substitute lambda in to get our next stepping direction; return list
# xk = x0 - lambda*direction
direction_lambda = [Symbol('lambda')*val for val in direction]
xk = [direction[i]-direction_lambda[i] for i in range(len(direction))]
>> [1.0*lambda, 0]
# substitute lambda into initial function component-wise; return updated sympy function
flambda = f.subs({x:xk[0], y:xk[1]})
>> -1.0*lambda + 3*(-1.0*lambda + 1.0)**2 + 3.0
So if you have followed the code all the way through this point, you will see that I have flambda exactly where I want it to do ... an expression of a single variable. However now is where things get funky. I want to substitute/evaluate it for lambda (as my independent variable), and all standard solutions fail.
flambda.evalf(subs={lambda:5})
>>TypeError: 'set' object has no attribute '__getitem__'
dflambda = flambda.diff() # works, but why cant i substitute a value?
>> 6.0*lambda - 7.0
In [285]: dflambda.evalf(subs={'lambda':5})
SympifyError: SympifyError: "could not parse u'lambda'"
Upvotes: 3
Views: 516
Reputation:
lambda
is a reserved word in Python. Don't use it as a variable. One typically uses lamda
instead if a reference to a Greek letter is needed.
Other changes I would suggest:
subs={'x':xk[0], 'y':xk[1]}
relies on conversion of strings to symbols, not recommended. Use
subs={x: xk[0], y: xk[1]}
Second,
xk = [direction[i]-direction_lambda[i] for i in range(len(direction))]
doesn't look right mathematically. You probably want to subtract from old value of xk here.
Third, there are too many evalf steps. Ideally only one is needed at the end. It's best to keep things symbolic until they have to become numeric all at one. So this is what I get.
import sympy
x0 = (0,0)
x, y, lamda = sympy.symbols('x y lamda')
f = 3*x**2+x*y+y**2+x+2
fgradient = [f.diff(var) for var in (x, y)] # calling diff as a method is convenient
xk = x0
direction = [i.subs({x: xk[0], y: xk[1]}) for i in fgradient]
xk = [xk[i] - lamda*direction[i] for i in range(len(direction))]
flamda = f.evalf(subs={x: xk[0], y: xk[1]})
But I suspect what you really want at the end is a Python callable computing flamda. That would be
flamda = sympy.lambdify(lamda, f.subs({x: xk[0], y: xk[1]}))
so you can use flamda(0.3)
for example. See lambdify.
Upvotes: 3