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))
Related
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.
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)
x=10, y=20
This is really short code, but when I execute this code in python, "can't assign to literal" error appears. Of course I know that this can't be executed in pyhon just intuitively, and it's out of question. This code should be changed to
x,y=10,20
or
x=10
y=20
like this. but I can't explain WHY the first code is error logically. please help me!
Your error is that you think x=10, y=20 means x=10; y=20 when in fact it means x=(10, y)=20. This is because the comma creates a tuple, and you can't assign to a tuple which contains a literal (in this case 10).
What python does with this:
A, B = C, D
It assigns the first value to the first variable, and the second value to the second variable:
A = C
B = D
This works because python internally makes "tuples" with your values delimited by a comma:
(A, B) = (C, D)
When you do
A = C, B = D
Python believes that you are doing:
A = (C, B) = D
Or:
(C, B) = D # Which is C = D[0] and B = D[1]
A = (C, B)
But C in you case is a number, not a variable, so:
x = 10, y = 20
Is:
x = (10, y) = 20
Or:
(10, y) = 20
x = (10, y)
Which is not possible. You can't assign something to a number (10). Doing 10 = 'something' will give you SyntaxError: can't assign to literal.
To make simpler, just execute in your python console:
10 = 1
And you will have the same error.
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.
I have a number of symbolic expressions in sympy, and I may come to realize that one of the coefficients is zero. I would think, perhaps because I am used to mathematica, that the following makes sense:
from sympy import Symbol
x = Symbol('x')
y = Symbol('y')
f = x + y
x = 0
f
Surprisingly, what is returned is x + y. Is there any way, aside from explicitly calling "subs" on every equation, for f to return just y?
I think subs is the only way to do this. It looks like a sympy expression is something unto itself. It does not reference the pieces that made it up. That is f only has the expression x+y, but doesn't know it has any link back to the python objects x and y. Consider the code below:
from sympy import Symbol
x = Symbol('x')
y = Symbol('y')
z = Symbol('z')
f1 = x + y
f2 = z + f1
f1 = f1.subs(x,0)
print(f1)
print(f2)
The output from this is
y
x + y + z
So even though f1 has changed f2 hasn't. To my knowledge subs is the only way to get done what you want.
I don't think there is a way to do that automatically (or at least no without modifying SymPy).
The following question from SymPy's FAQ explains why:
Why doesn't changing one variable change another that depends it?
The short answer is "because it doesn't depend on it." :-) Even though
you are working with equations, you are still working with Python
objects. The equations you are typing use the values present at the
time of creation to "fill in" values, just like regular python
definitions. They are not altered by changes made afterwards. Consider
the following:
>>> a = Symbol('a') # create an object with name 'a' for variable a to point to
>>> b = a + 1; b # create another object that refers to what 'a' refers to
a + 1
>>> a = 4; a # a now points to the literal integer 4, not Symbol('a')
4
>>> b # but b is still pointing at Symbol('a')
a + 1
Changing quantity a does not change b; you are not working with a set
of simultaneous equations. It might be helpful to remember that the
string that gets printed when you print a variable refering to a sympy
object is the string that was give to it when it was created; that
string does not have to be the same as the variable that you assign it
to:
>>> r, t, d = symbols('rate time short_life')
>>> d = r*t; d
rate*time
>>> r=80; t=2; d # we haven't changed d, only r and t
rate*time
>>> d=r*t; d # now d is using the current values of r and t
160
Maybe this is not what you're looking for (as it was already explained by others), but this is my solution to substitute several values at once.
def GlobalSubs(exprNames, varNames, values=[]):
if ( len(values) == 0 ): # Get the values from the
for varName in varNames: # variables when not defined
values.append( eval(varName) ) # as argument.
# End for.
# End if.
for exprName in exprNames: # Create a temp copy
expr = eval(exprName) # of each expression
for i in range(len(varNames)): # and substitute
expr = expr.subs(varNames[i], values[i]) # each variable.
# End for.
yield expr # Return each expression.
# End for.
It works even for matrices!
>>> x, y, h, k = symbols('x, y, h, k')
>>> A = Matrix([[ x, -h],
... [ h, x]])
>>> B = Matrix([[ y, k],
... [-k, y]])
>>> x = 2; y = 4; h = 1; k = 3
>>> A, B = GlobalSubs(['A', 'B'], ['x', 'h', 'y', 'k'])
>>> A
Matrix([
[2, -1],
[1, 2]])
>>> B
Matrix([
[ 4, 3],
[-3, 4]])
But don't try to make a module with this. It won't work. This will only work when the expressions, the variables and the function are defined into the same file, so everything is global for the function and it can access them.