sympy .subs() built-in function doesn't work - python

I'm trying to implement the multivariate chain rule using python, when I try to substitute one of the elements using the built-in sympy function expr.subs() I won't get an answer.
The exercise goes as follows:
Compute df/dx of the following function using the chain rule:
a = x^2
b = exp(a)
c = a + b
d = log(c)
e = sin(c)
f = d + e
⁡
And this is the code:
a, b, c, d, e, f = sym.symbols('a b c d e f')
f = d + e
dfd = sym.diff(f, d)
dfe = sym.diff(f, e)
df = sym.Matrix([[dfd, dfe]])
d = sym.log(c)
e = sym.sin(c)
ddc = sym.diff(d)
dde = sym.diff(e)
dd = sym.Matrix([[ddc],[dde]])
dfdd = df#dd
c = a + b
dca = sym.diff(c, a)
dcb = sym.diff(c, b)
dc = sym. Matrix([[dca, dcb]])
dfdc = dfdd#dc
a = x**2
b = sym.exp(a)
result = dfdc.subs(c, (a + b))
result
The result the function .subs() doesn't substitute anything and I don't know why, I tried substituting it using other ways to write the function, like:
dfdc.subs({c : (a + b)})
dfdc.subs(c, a + b)
And even tried to just substitute it for an integer to see it that would work and neither does it.
What am I doing wrong?

The c in your dfdc expression was created before you set c=a+b. So it still appears as c in dfdc. However, by the time you want to do your subs, the c symbol is declared as c=a+b, and so your substitute really reads
dfdc.subs(a+b, a+b)
And that does nothing.
In order to really use c there, do
dfdc.subs(sym.Symbol('c'), a+b)

Related

Sympy: How can I store an equation in order to solve it later numerically?

Hello wonderful people,
I am building a physics model for some project. I have found a nice equation for my interest variable, but I would like to be able to solve the problem repeatedly with different parameters. What I would like to do is to save my equation as an object in a file (using pickle for example), then loading it at runtime and feed it the parameters it needs.
How would you achieve this?
With a simple example, the whole process would look like this:
(in a jupyter notebook)
import sympy as sp
import pickle
a, b, c = symbols("a b c")
eqn = sp.Eq(b + c, a) #for a real equation I would simplify it before using sympy
with open("eqn.txt") as f:
pickle.dump(eqn, f)
and then later in the app's code:
...
with open("eqn.txt") as f:
eqn = pickle.load(f)
b = 1
c = 2
#magic line to put b and c into the equation
a = sp.solve(eqn, a)
print(a) # 3
Implementing the whole equation directly in a function is probably not an option although I am considering how to implement it manually. It just looks really, really hard to do and if I could do it in two lines using simpy, that'd be great.
Thanks for your time!
with open("eqn.txt") as f:
eqn = pickle.load(f)
b, c = 1, 2 # b,c are python symbols here
reps = dict(zip(symbols('b c'), (b, c))) # keys are SymPy Symbols, values are 1,2
eqn = eqn.xreplace(reps) #magic line to put b and c into the equation
a = sp.solve(eqn, a)
print(a) # 3
It is important to keep in mind the distinction between b = 1 and b = Symbol('b'). The left hand side of the expressions are Python variables and on the right, an int or a SymPy Symbol, respectively. In a SymPy expression you might reference a Python variable and its value will be included in the equation:
>>> from sympy import *
>>> b = 1
>>> b + 1
2
>>> b = Symbol('b')
>>> b + 1
b + 1
>>> eq = _
>>> eq.subs(b,1)
2
>>> b=2 # assigning a new value to b does not change the object in eq
>>> eq
b + 1
>>> eq.subs(b, 2) # i.e., eq.subs(2,2) doesn't work -- no 2 in eq
b + 1
>>> eq.subs(Symbol('b'), 2) # replace the Symbol with value of 2 works
3
So in reps above, zipping the symbols to their corresponding values creates a mapping that can be used to do the replacement.
There is more discussion of such issues in the documentation gotchas file, but this should help with your current issue.

Linking multiple python functions together

Simple question on how to link (or string together) multiple functions that depend on each other. I have the following example function (in Jupyter):
### First function
def function_one():
## process one
a = "one" + "two"
print(a)
## process two
b = "red" + "blue"
print(b)
## process three
c = "this" + "sucks"
print(c)
return a, b, c
### Second function
def function_two(a, b, c):
## process four
d = a + b
print(d)
## process five
e = b + c
print(e)
## process six
f = a + c
print(f)
return d, e, f
### Third function
def function_three():
g = a + b + c + d + e + f
print(g)
return g
### Calling functions
initial = function_one()
second = function_two(initial)
third = ... #I can't get past the following error to even link this function in
The first function works when called, but when I try to send that data downstream to the second function, I get this error:
onetwo
redblue
thissucks
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-10-7c5562b97c86> in <module>
1 initial = function_one()
----> 2 second = function_two(initial)
TypeError: function_two() missing 2 required positional arguments: 'b' and 'c'
How do I remedy this?
When returning multiple objects, you are actually returning a tuple. Ex:
>>> def foo():
>>> return 1, 2, 3
>>> type(foo())
<class 'tuple'>
So, right now the whole tuple is treated as argument a and hence b and c are missing. In order to pass that on as 3 separate arguments, you need to unpack that tuple:
initial = function_one()
second = function_two(*initial)
third = function_three(*initial, *second)
Assign the return values to variables then pass it to the second function:
a, b, c = function_one()
function_two(a, b, c)
For your third function you need it to accept parameters as well
def function_three(a, b, c, d, e, f):
g = a + b + c + d + e + f
print(g)
return g
Then putting it all together:
a, b, c = function_one()
d, e, f = function_two(a, b, c)
g = function_three(a, b, c, d, e, f)

How to test all possible values ​for all variables to get the maximum result for the function

I have three variables called a, b and c, each of these can assume a different value defined in a range. I'd like to create a function that tests every possible variable value and gives me their best combination for the output 'f'.
a = list(range(1, 10, 2))
b = list(range(5, 8, 1))
c = list(range(1, 3, 1))
def all_combinations (a, b, c):
#something
f = a + (b * a) - (c*(a ^ b))
return BEST a, b, c for my f
it's possible to do it ? what is the best way to do it?
You can use itertools.product() to get all the possible combinations of a, b, and c.
Then calculate your formula for each unique combination of a b c, keep track of the result, and if the result is better than the previous best, save the current values of a b c.
import itertools
def all_combinations (alist, blist, clist):
best_a = 0
best_b = 0
best_c = 0
best_f = 0
for a,b,c in itertools.product(alist, blist, clist):
f = a + (b * a) - (c*(a ^ b))
if f > best_f: # use your own definition of "better"
best_a = a
best_b = b
best_c = c
best_f = f
return best_a, best_b, best_c
First of all, you said I have three variables called a, b and c, each of these can assume a different value defined in a range. Note that the variables in your code are actually equal to three lists of integers, not three integers.
The naive algorithm to test all possible combinations is 3 nested for loops. Here I assume that by "best" you mean "maximum value":
def all_combinations (list1, list2, list3):
best_f, best_a, best_b, best_c = None, None, None, None
for a in list1:
for b in list2:
for c in list3:
f = a + (b * a) - (c*(a ^ b))
# here you have to define what f being "better" than best_f means:
if not f or f > best_f:
best_f = f
best_a = a
best_b = b
best_c = c
return best_a, best_b, best_c
If you're sure those are the only values you want to test, then the following will work. Otherwise you might want to look into scipy.optimize.
from itertools import product
import numpy as np
parameters = list(product(a, b, c))
results = [my_fun(*x) for x in parameters]
print(parameters[np.argmax(results)])
obviously replace np.argmax with np.argmin if you want to minimize the function

Plotting with SymPy

With SymPy, I can plot a function with:
f, a = symbols('f a')
f = a + 10
plot(f)
However, if I define the function as:
f, a, b = symbols('f a b')
f = a + b
b = 10
plot(f)
Then I get an error stating:
ValueError: The same variable should be used in all univariate
expressions being plotted.
How can I plot f if I define f = a + b, considering that b is assigned a constant value before plotting the function?
The lines
f, a, b = symbols('f a b')
f = a + b
b = 10
don't change b in the expression. If you print f you'll see that it is still defined as a + b.
You are confusing Python variables with SymPy symbols. In the first line, the Python variable b points to a SymPy symbol named b (in fact, they need not be the same name; you could have also written x = Symbol('b') and y = a + x). In the second line, the variable f points to a SymPy expression containing the symbol b. In the third line, the variable b points to the integer 10. This doesn't not change any previous lines that used the variable b, since they have already been run. It's no different than if you ran
a = 1
b = 1
c = a + b
b = 2
You would expect the value of c at the end to be 2, not 3. Similarly, when b points to a Symbol, expressions you create with it use a Symbol, but if you change it to point to a number, it doesn't affect previous lines from when it was a Symbol.
The recommended way to deal with this in SymPy is to avoid assigning the same variable to a symbol and then later to a non-symbol (it's worth pointing out that your definition of f in the first line is completely useless, since you immediately redefine it in the second line). To replace a symbol in an expression, use subs:
a, b = symbols('a b')
f = a + b
f1 = f.subs(b, 10)
Note that subs does not change the original f. It returns a new expression.
This document may also help clear this confusion up.
If you didn't want to use substitution as in the other answer, you could make f an actual function of course
def f(a, b):
return a + b
a = symbols('a')
b = 10
plot(f(a,b))
You must substitute b into f:
plot(f.subs('b', b))

python generators/ variable [duplicate]

This question already has answers here:
Multiple assignment and evaluation order in Python
(11 answers)
Closed 9 years ago.
2 questions...
1) I am trying to wrap my brain around this...
I am to understand that variables can take values using such code syntax as this:
a ,b = 2, 3
and that this would be the same as coding:
a = 2
b = 3
I hope this is correct.
So here is my puzzlement. I have the following code using a generator:
def fibonacci_generator() :
a = b = 1
while True :
yield a
a , b = b , a + b
fib = fibonacci_generator()
for i in fib :
if i > 100 :
break
else :
print ('Generated: ', i)
print (next(fib))
(yes this is code from a python learning book)
If I were to rewrite this code and instead assign my a and b variables like so:
yield a
a = b
b = a + b
then I get different returns for a.
I am not understanding why this is??? Super frustrated about it!
2) When I run the code as written the first time above, I get the number 233 printed at the end. I also cannot figure out why??!!
In this code:
a, b = b, a + b
a is set to b, and b is set to a+b.
In this code:
a = b
b = a + b
a is set to b, and b is afterwards set to a+b. But since a is already set to b, then b is in fact set to b+b.
a , b = b , a + b
is not the same as
a = b
b = a + b
Because, when you say
a, b = b, a + b
It will first prepare the values on the right side b, a + b and assign them to the variables on the left.
Python computes the right hand side first and then assigns the value (or unpacks it) on the left hand side. So, in the example:
a, b = b, a+b
compared to:
a = b
b = a + b
You have different values for a when you go to compute a + b. In the second example, when you compute a + b it is equivalent to computing b + b!
You are probably missing the flow of data.
a = b ...eqI
b = a+b ...eqII
here, before executing b eqII, a has already stored bas a value of itself. Now when yow try execute b of eqII it comes like b=b+b. Because after executing eqI when it comes to eqII, a is bnow.
But in python you can avoid this conflict if you try a, b = b, a+b.
For your second question:
I am not sure about your code but this one will work fine in the sense of your code...
a = b = 1
while True :
a , b = b , a + b
if a and b > 100:
break
else: print a, b
try it !!
In an assignment statement, the right-hand side is always evaluated fully before doing the actual setting of variables. Because that you get different results
a, b = b, a+b
This line computes b and a+b before performing any assignment. Strictly speaking, it computes a tuple (b, a+b) and then unpacks the elements of the tuple to assign them to a and b.
a = b
b = a+b
This assigns a, then computes a+b using the new value of a.

Categories

Resources