Sympy "global" substitution - python

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.

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.

Sympy evalf for real symbol

This Sympy code works as I expect:
>>> x = sp.Symbol("x")
>>> y = sp.Symbol("y")
>>> z = sp.Symbol("z")
>>> (x+y+z).evalf(subs={"x":1, "y":2, "z":3})
6.0
However, if I use real-valued Symbols instead, the expression isn't simplified:
>>> x = sp.Symbol("x", real=True)
>>> y = sp.Symbol("y", real=True)
>>> z = sp.Symbol("z", real=True)
>>> (x+y+z).evalf(subs={"x":1, "y":2, "z":3})
x + y + z
I was unable to find an explanation for this by searching with keywords like sympy symbol real evalf - I only get unrelated results.
Why isn't the expression simplified in the second case? How can I substitute in values for real-valued Symbols and evaluate the expression?
Use the symbols as keys for the dictionary of substitutions, rather than string names:
>>> (x + y + z).evalf(subs={x: 1, y: 2, z: 3})
6.00000000000000
There appears to be an inconsistency between how complex and real symbols are treated:
>>> x = sp.Symbol('x')
>>> x.subs(x, 1)
1
>>> x.subs('x', 1)
1
>>> x = sp.Symbol('x', real=True)
>>> x.subs(x, 1)
1
>>> x.subs('x', 1)
x
I can't find anything relevant about this in the documentation, and the built-in help text isn't useful either. My best guess is that a string 'x' is naively converted using sp.Symbol, and the resulting symbol is always a complex-valued symbol that doesn't match the real-valued one with the same name.
I would consider this behaviour a bug and file a bug report (or look for an existing report). IMO, if a string is usable at all, it should match any symbol with that name; and an expression shouldn't be able to contain two different variables with the same name and different types; and trying to substitute in a variable with a matching name and incompatible type should probably raise an exception:
>>> x = sp.Symbol('x')
>>> # why allow this?
>>> broken = sp.Symbol('x', real=True) + x
>>> broken # if the types matched, it would simplify to 2*x
x + x
>>> # surely 2 is the only value that makes sense?
>>> broken.subs('x', 1)
x + 1
>>> x.subs('x', 1)
1
>>> # If this is allowed at all, surely the result should be 1?
>>> x.subs(sp.Symbol('x', real=True), 1)
x

How can make a axiom in Z3Py correctly?

I am working in this axiom ForAll X( f(x) > 0 -> b == True) .
How I could to do it in Z3Py? I try to do this:
from z3 import *
Z = IntSort()
f = Function('f', Z, Z)
g = Function('g', Z, Z)
a, n, x = Ints('a n x')
b = BoolSort()
solve(ForAll(x,Implies(f(x) > 0,b ==True)))
but Python return me AttributeError: 'bool' object has no attribute 'ast'
You can declare b as b = Bool('b'). Then it works.
By the way, you can replace b == True by just b as in
solve(ForAll(x,Implies(f(x) > 0, b ))).
BoolSort etc. are meant for parameters to Z3Py functions, not for variables.
What's your axiom trying to state? Note that in the formula ForAll X. (f(x) > 0 -> b == True), b is a free variable. So, this doesn't really seem to correspond to anything logical. But, if that's what you really want to say, this is how you'd code it:
from z3 import *
Z = IntSort()
f = Function('f', Z, Z)
g = Function('g', Z, Z)
a, n, x = Ints('a n x')
b = Bool('b')
solve(ForAll(x,Implies(f(x) > 0, b)))
And we get:
$ python a.py
[b = False, f = [else -> 0]]
What's z3 telling us? It says, OK, I'll pick f to be a function that maps everything to 0. So, your implication will have a antecedent 0 > 0, which is always false, and thus the implication is always true. (False implies anything.) The choice for False for b in the model is really irrelevant.
So, z3 did find you a model for f and b that satisfied your quantified assertion. Just like you asked. But I suspect this wasn't really the formula you were trying to assert. Axioms are typically closed: That is, they have no free variables, other than the uninterpreted functions symbols they include. Of course, this all depends on what exactly you're trying to do.

Get min/max of returned object of solver / sympy [Python]

I use sympy in a Python script to get the solutions of an inequality.
I would then like to get the minimum and maximum value among all the possible values returned but cannot find out how.
The type of the returned object (x_sol) is 'And'.
x = Symbol("x", real=True)
a = 1
b = 2
c = 3
d = 4
e = 5
CM = Matrix([ [0,1,1,1,1], [1,0,a,b,c], [1,a,0,d,e], [1,b,d,0,x], [1,c,e,x,0] ])
x_sol = solve_univariate_inequality( det(CM) >= 0, x, S.Reals )
You could use xsol.as_set().boundary:
import sympy as sym
x = sym.Symbol("x", real=True)
a, b, c, d, e = 1, 2, 3, 4, 5
CM = sym.Matrix([ [0,1,1,1,1], [1,0,a,b,c], [1,a,0,d,e], [1,b,d,0,x], [1,c,e,x,0] ])
x_sol = sym.solve_univariate_inequality( sym.det(CM) >= 0, x, sym.S.Reals )
x_set = x_sol.as_set()
x_min, x_max = x_set.boundary
print('{}, {}'.format(x_min, x_max))
prints
-sqrt(77)/2 + 9/2, sqrt(77)/2 + 9/2
Knowing how people find answers is often more interesting than the answer itself.
So here is how I found the answer above.
IPython has an extremely useful
tab-completion feature. By typing x_sol. and pressing TAB,
In [129]: x_sol.[TAB]
IPython shows all the attributes of xsol:
x_sol.args x_sol.as_content_primitive
x_sol.as_poly x_sol.as_set
x_sol.assumptions0 x_sol.atoms
...
Typing x_sol.as_set? gives documentation about the attribute or method:
In [129]: x_sol.as_set?
Signature: x_sol.as_set()
Docstring:
Rewrite logic operators and relationals in terms of real sets.
Examples
========
>>> from sympy import And, Symbol
>>> x = Symbol('x', real=True)
>>> And(x<2, x>-2).as_set()
(-2, 2)
File: ~/.virtualenvs/muffy/lib/python3.4/site-packages/sympy/logic/boolalg.py
Type: method
Simply by using IPython to explore the available attributes, it was not hard to
discover that as_set and boundary yield the desired values.
Hopefully, knowing this trick will help you discover solutions to other problems more quickly in the future.

Sympy: working with equalities manually

I'm currently doing a maths course where my aim is to understand the concepts and process rather than crunch through problem sets as fast as possible. When solving equations, I'd like to be able to poke at them myself rather than have them solved for me.
Let's say we have the very simple equation z + 1 = 4- if I were to solve this myself, I would obviously subtract 1 from both sides, but I can't figure out if sympy provides a simple way to do this. At the moment the best solution I can come up with is:
from sympy import *
z = symbols('z')
eq1 = Eq(z + 1, 4)
Eq(eq1.lhs - 1, eq1.rhs - 1)
# Output:
# z == 3
Where the more obvious expression eq1 - 1 only subtracts from the left-hand side. How can I use sympy to work through equalities step-by-step like this (i.e. without getting the solve() method to just given me the answer)? Any pointers to the manipulations that are actually possible with sympy equalities would be appreciated.
There is a "do" method and discussion at https://github.com/sympy/sympy/issues/5031#issuecomment-36996878 that would allow you to "do" operations to both sides of an Equality. It's not been accepted as an addition to SymPy but it is a simple add-on that you can use. It is pasted here for convenience:
def do(self, e, i=None, doit=False):
"""Return a new Eq using function given or a model
model expression in which a variable represents each
side of the expression.
Examples
========
>>> from sympy import Eq
>>> from sympy.abc import i, x, y, z
>>> eq = Eq(x, y)
When the argument passed is an expression with one
free symbol that symbol is used to indicate a "side"
in the Eq and an Eq will be returned with the sides
from self replaced in that expression. For example, to
add 2 to both sides:
>>> eq.do(i + 2)
Eq(x + 2, y + 2)
To add x to both sides:
>>> eq.do(i + x)
Eq(2*x, x + y)
In the preceding it was actually ambiguous whether x or i
was to be added but the rule is that any symbol that are
already in the expression are not to be interpreted as the
dummy variable. If we try to add z to each side, however, an
error is raised because now it is unclear whether i or z is being
added:
>>> eq.do(i + z)
Traceback (most recent call last):
...
ValueError: not sure what symbol is being used to represent a side
The ambiguity must be resolved by indicating with another parameter
which is the dummy variable representing a side:
>>> eq.do(i + z, i)
Eq(x + z, y + z)
Alternatively, if only one Dummy symbol appears in the expression then
it will be automatically used to represent a side of the Eq.
>>> eq.do(2*Dummy() + z)
Eq(2*x + z, 2*y + z)
Operations like differentiation must be passed as a
lambda:
>>> Eq(x, y).do(lambda i: i.diff(x))
Eq(1, 0)
Because doit=False by default, the result is not evaluated. to
evaluate it, either use the doit method or pass doit=True.
>>> _.doit == Eq(x, y).do(lambda i: i.diff(x), doit=True)
True
"""
if not isinstance(e, (FunctionClass, Lambda, type(lambda:1))):
e = S(e)
imaybe = e.free_symbols - self.free_symbols
if not imaybe:
raise ValueError('expecting a symbol')
if imaybe and i and i not in imaybe:
raise ValueError('indicated i not in given expression')
if len(imaybe) != 1 and not i:
d = [i for i in imaybe if isinstance(i, Dummy)]
if len(d) != 1:
raise ValueError(
'not sure what symbol is being used to represent a side')
i = set(d)
else:
i = imaybe
i = i.pop()
f = lambda side: e.subs(i, side)
else:
f = e
return self.func(*[f(side) for side in self.args], evaluate=doit)
from sympy.core.relational import Equality
Equality.do = do

Categories

Resources