Python – A function that converts an expression of multiple variables into a single variable by substitution

A function that converts an expression of multiple variables into a single variable by substitution… here is a solution to the problem.

A function that converts an expression of multiple variables into a single variable by substitution

My whole goal with the Sympy package is to take the provided function f of d variables, evaluate its gradient, and then give a new equation with 1 unknown variable lambda and substitute it into my initial function f to get an equation containing only a single variable lambda. My progress has been promising, but makes me want to:

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 follow the code from start to finish, you’ll see that I used flambda exactly where I want it to execute… An expression for a single variable. But now things are getting weird. I want to replace/evaluate it with lambda (as my argument) 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'"

Solution

lambda is a reserved word in Python. Do not use it as a variable. If you need to quote the Greek alphabet, lamda is usually used.

Other changes I’m proposing:

subs={'x':xk[0], 'y':xk[1]}

Relies on string-to-symbol conversion, not recommended. Use

subs={x: xk[0], y: xk[1]}

Secondly,

xk = [direction[i]-direction_lambda[i] for i in range(len(direction))]

Doesn’t look mathematically correct. You may want to subtract from the 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 the symbols until they all have to be numbers. That’s 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 in the end is a Python callable compute flamda. That would be

flamda = sympy.lambdify(lamda, f.subs({x: xk[0], y: xk[1]}))

For example, you can use flamda(0.3). See also lambdify

Related Problems and Solutions