Manipulating sympy expression trees - python-3.x

Lets say, we have a sympy function cos(x). Every function can be
presented by a tree, e.g. like the image here https://reference.wolfram.com/language/tutorial/ExpressionsAsTrees.html
I want to insert a parameter into every node of this expression tree, that means
cos(x) -> a*cos(b*x)
For more complicated expression, it should look like
(exp(x)+cos(x)*x)/(x) -> h*(b*exp(a*x)+f*(c*cos(d*x)*e*x))/(j*x)
where a,b,c,d,e,f,g,h,j are parameters, that I want to fit.
A helpful source could be https://docs.sympy.org/latest/tutorial/manipulation.html in the chapter "walking the tree". I tried to replace parts of the
expr.args
tuple, but it is not possible.
This is the expression:
from simply import symbols, exp, cos
x, y = symbols('x y')
expr = (exp(x)+cos(x)*y)/(x)

This might get you started:
>>> s = numbered_symbols('C')
>>> cform = ((exp(x)+cos(x)*x)/(x)).replace(
... lambda x:not x.is_Number,
... lambda x:x*next(s))
>>> cform
C1*C8*C9*(C2*C4*C5*x*cos(C3*x) + C7*exp(C6*x))/(C0*x)
>>> from sympy.solvers.ode import constantsimp, constant_renumber
>>> constantsimp(cform, [i for i in cform.atoms(Symbol) if i.name.startswith('C')])
C0*(C2*x*cos(C3*x) + C7*exp(C6*x))/x
>>> constant_renumber(_)
C1*(C2*x*cos(C3*x) + C4*exp(C5*x))/x
>>> eq = _
>>> cons = ordered(i for i in eq.atoms(Symbol) if i.name.startswith('C'))
>>> eq.xreplace(dict(zip(cons, var('a:z'))))
a*(b*x*cos(c*x) + d*exp(e*x))/x

Related

python 'concatenate' requires extra parentheses

I'm trying to concatenate 3 lists. When I try to use concatenate, like so, I get an error (TypeError: 'list' object cannot be interpreted as an integer):
import numpy as np
a = [1]
b = [2]
c = [3]
z = np.concatenate(a, b, c)
But if I put "extra" parentheses, it works like so:
z = np.concatenate((a, b, c))
Why?
I am not sure what library you are using (concatenate is not a built-in python 3.x function). However, I'll explain what I think is going on.
When you call concatenate(a, b, c), the function concatenate is sent three parameters: a, b, and c. concatenate then performs some logic that is (presumably) not the desired behavior.
When you call concatenate((a, b, c)), a tuple (effectively a list that cannot be changed) is created with a value of (a, b, c), which is evaluated to ([1], [2], [3]). Then this tuple is passed to the concatenate function. The following code is actually equivalent to your second code snippet:
a = [1]
b = [2]
c = [3]
y = (a, b, c) # This evaluates to ([1], [2], [3]).
z = concatenate(y)
I hope I've explained this clearly enough. Here's an article that explains tuples in more depth, if I haven't: https://www.w3schools.com/python/python_tuples.asp
EDIT: Thanks for including the library. Here's the code for what you're probably trying to do:
import numpy as np
a = [1]
b = [2]
c = [3]
z = np.array(a + b + c) # Lists can be concatenated using the `+` operator. Then, to make a numpy array, just call the constructor

Is there a way to pass an equation symbolically into a function for sympy?

From this
import sympy as sp
x,y,z = sp.symbols("x y z")
sp.Ep(x,y/z)
To this
#varibles = array
#equation = ????
def solver(variables,equation):
#Looping through variables array and converting variables to sympy objects
for var in variables:
var = sp.symbols(var)
#Generate sympy Equation
equation = sp.Ep(equation)
variables = [x,y,z]
equation = x,y/z #invalid code
solver(variables,equation)
I'm creating a function that is able to take in an equation with x amount of variables and x-1 number of values then solve for the missing variable symbolically then return a numerical answer using the values provided.
I only included a small portion of code where I'm having trouble understanding how to pass through an equation. Any solutions or pointers would be greatly appericated. Thanks.
There are several layers of potential confusion here concerning Python variables and SymPy objects (Symbols) used for variables.
Here is an example of what you are saying:
# 3 variables
syms = x, y, z = var('x:z')
# 2 values
vals = {x:1, y:2}
# an equations
eq = Eq(x, y/z)
# solve for the missing value symbolically
missing = set(syms) - set(vals) # == {z}
solve(eq, missing)
[y/x]
# solve for the missing value after substituting in the known values
solve(eq.subs(vals))
[2]
You could make a solver to accept an equation and then specified values and figure out the missing one and return that value by doing something like this:
>>> def solver(eq, **vals):
... from sympy.core.containers import Dict
... from sympy.solvers.solvers import solve
... free = eq.free_symbols
... vals = Dict(vals)
... x = free - set(vals)
... if len(x) != 1:
... raise ValueError('specify all but one of the values for %s' % free)
... x = x.pop()
... return solve(eq.subs(vals), x, dict=True)
...
>>> solver(eq, x=1, z=2)
[{y: 2}]
Does that give you some ideas of how to continue?

Unexplained behavior in terminal with is operator [duplicate]

This question already has answers here:
Compare if two variables reference the same object in python
(6 answers)
Closed 5 months ago.
The is operator does not match the values of the variables, but the
instances themselves.
What does it really mean?
I declared two variables named x and y assigning the same values in both variables, but it returns false when I use the is operator.
I need a clarification. Here is my code.
x = [1, 2, 3]
y = [1, 2, 3]
print(x is y) # It prints false!
You misunderstood what the is operator tests. It tests if two variables point the same object, not if two variables have the same value.
From the documentation for the is operator:
The operators is and is not test for object identity: x is y is true if and only if x and y are the same object.
Use the == operator instead:
print(x == y)
This prints True. x and y are two separate lists:
x[0] = 4
print(y) # prints [1, 2, 3]
print(x == y) # prints False
If you use the id() function you'll see that x and y have different identifiers:
>>> id(x)
4401064560
>>> id(y)
4401098192
but if you were to assign y to x then both point to the same object:
>>> x = y
>>> id(x)
4401064560
>>> id(y)
4401064560
>>> x is y
True
and is shows both are the same object, it returns True.
Remember that in Python, names are just labels referencing values; you can have multiple names point to the same object. is tells you if two names point to one and the same object. == tells you if two names refer to objects that have the same value.
Another duplicate was asking why two equal strings are generally not identical, which isn't really answered here:
>>> x = 'a'
>>> x += 'bc'
>>> y = 'abc'
>>> x == y
True
>>> x is y
False
So, why aren't they the same string? Especially given this:
>>> z = 'abc'
>>> w = 'abc'
>>> z is w
True
Let's put off the second part for a bit. How could the first one be true?
The interpreter would have to have an "interning table", a table mapping string values to string objects, so every time you try to create a new string with the contents 'abc', you get back the same object. Wikipedia has a more detailed discussion on how interning works.
And Python has a string interning table; you can manually intern strings with the sys.intern method.
In fact, Python is allowed to automatically intern any immutable types, but not required to do so. Different implementations will intern different values.
CPython (the implementation you're using if you don't know which implementation you're using) auto-interns small integers and some special singletons like False, but not strings (or large integers, or small tuples, or anything else). You can see this pretty easily:
>>> a = 0
>>> a += 1
>>> b = 1
>>> a is b
True
>>> a = False
>>> a = not a
>>> b = True
a is b
True
>>> a = 1000
>>> a += 1
>>> b = 1001
>>> a is b
False
OK, but why were z and w identical?
That's not the interpreter automatically interning, that's the compiler folding values.
If the same compile-time string appears twice in the same module (what exactly this means is hard to define—it's not the same thing as a string literal, because r'abc', 'abc', and 'a' 'b' 'c' are all different literals but the same string—but easy to understand intuitively), the compiler will only create one instance of the string, with two references.
In fact, the compiler can go even further: 'ab' + 'c' can be converted to 'abc' by the optimizer, in which case it can be folded together with an 'abc' constant in the same module.
Again, this is something Python is allowed but not required to do. But in this case, CPython always folds small strings (and also, e.g., small tuples). (Although the interactive interpreter's statement-by-statement compiler doesn't run the same optimization as the module-at-a-time compiler, so you won't see exactly the same results interactively.)
So, what should you do about this as a programmer?
Well… nothing. You almost never have any reason to care if two immutable values are identical. If you want to know when you can use a is b instead of a == b, you're asking the wrong question. Just always use a == b except in two cases:
For more readable comparisons to the singleton values like x is None.
For mutable values, when you need to know whether mutating x will affect the y.
is only returns true if they're actually the same object. If they were the same, a change to one would also show up in the other. Here's an example of the difference.
>>> x = [1, 2, 3]
>>> y = [1, 2, 3]
>>> print x is y
False
>>> z = y
>>> print y is z
True
>>> print x is z
False
>>> y[0] = 5
>>> print z
[5, 2, 3]
Prompted by a duplicate question, this analogy might work:
# - Darling, I want some pudding!
# - There is some in the fridge.
pudding_to_eat = fridge_pudding
pudding_to_eat is fridge_pudding
# => True
# - Honey, what's with all the dirty dishes?
# - I wanted to eat pudding so I made some. Sorry about the mess, Darling.
# - But there was already some in the fridge.
pudding_to_eat = make_pudding(ingredients)
pudding_to_eat is fridge_pudding
# => False
is and is not are the two identity operators in Python. is operator does not compare the values of the variables, but compares the identities of the variables. Consider this:
>>> a = [1,2,3]
>>> b = [1,2,3]
>>> hex(id(a))
'0x1079b1440'
>>> hex(id(b))
'0x107960878'
>>> a is b
False
>>> a == b
True
>>>
The above example shows you that the identity (can also be the memory address in Cpython) is different for both a and b (even though their values are the same). That is why when you say a is b it returns false due to the mismatch in the identities of both the operands. However when you say a == b, it returns true because the == operation only verifies if both the operands have the same value assigned to them.
Interesting example (for the extra grade):
>>> del a
>>> del b
>>> a = 132
>>> b = 132
>>> hex(id(a))
'0x7faa2b609738'
>>> hex(id(b))
'0x7faa2b609738'
>>> a is b
True
>>> a == b
True
>>>
In the above example, even though a and b are two different variables, a is b returned True. This is because the type of a is int which is an immutable object. So python (I guess to save memory) allocated the same object to b when it was created with the same value. So in this case, the identities of the variables matched and a is b turned out to be True.
This will apply for all immutable objects:
>>> del a
>>> del b
>>> a = "asd"
>>> b = "asd"
>>> hex(id(a))
'0x1079b05a8'
>>> hex(id(b))
'0x1079b05a8'
>>> a is b
True
>>> a == b
True
>>>
Hope that helps.
x is y is same as id(x) == id(y), comparing identity of objects.
As #tomasz-kurgan pointed out in the comment below is operator behaves unusually with certain objects.
E.g.
>>> class A(object):
... def foo(self):
... pass
...
>>> a = A()
>>> a.foo is a.foo
False
>>> id(a.foo) == id(a.foo)
True
Ref;
https://docs.python.org/2/reference/expressions.html#is-not
https://docs.python.org/2/reference/expressions.html#id24
As you can check here to a small integers. Numbers above 257 are not an small ints, so it is calculated as a different object.
It is better to use == instead in this case.
Further information is here: http://docs.python.org/2/c-api/int.html
X points to an array, Y points to a different array. Those arrays are identical, but the is operator will look at those pointers, which are not identical.
It compares object identity, that is, whether the variables refer to the same object in memory. It's like the == in Java or C (when comparing pointers).
A simple example with fruits
fruitlist = [" apple ", " banana ", " cherry ", " durian "]
newfruitlist = fruitlist
verynewfruitlist = fruitlist [:]
print ( fruitlist is newfruitlist )
print ( fruitlist is verynewfruitlist )
print ( newfruitlist is verynewfruitlist )
Output:
True
False
False
If you try
fruitlist = [" apple ", " banana ", " cherry ", " durian "]
newfruitlist = fruitlist
verynewfruitlist = fruitlist [:]
print ( fruitlist == newfruitlist )
print ( fruitlist == verynewfruitlist )
print ( newfruitlist == verynewfruitlist )
The output is different:
True
True
True
That's because the == operator compares just the content of the variable. To compare the identities of 2 variable use the is operator
To print the identification number:
print ( id( variable ) )
The is operator is nothing but an English version of ==.
Because the IDs of the two lists are different so the answer is false.
You can try:
a=[1,2,3]
b=a
print(b is a )#True
*Because the IDs of both the list would be same

Lambdify A Parametric Integral

I have the following issue: I want to lambdify a sympy expression containing parametric integrals like Integral(tanh(a*x),(x,0,1)). I tried to do a manual implementation like here.
What we want is essentially that the integral gets converted to something like:
lambda theta: quad(lambda x: g(x,theta), a,b)[0]
where
g = sp.lambdify((x,param), f, modules='numpy'))
Consider the following MWE:
import sympy as sp
import numpy as np
from scipy.integrate import quad
def integral_as_quad(function, limits):
x, a, b = limits
param = function.free_symbols - {x}
f = sp.lambdify( (x,*param), function, modules='numpy')
return lambda y: quad(lambda x: f(x,y), a,b)[0]
a, x = sp.symbols('a,x')
I = sp.Integral(sp.tanh(a*x),(x,0,1))
K = integral_as_quad(sp.tanh(a*x),(x,0,1))
L = sp.lambdify(a, I, modules=['numpy', {'Integral':integral_as_quad}] )
Then calling for example K(1) returns the correct value. However L(1) gives
AttributeError: 'Mul' object has no attribute 'tanh'
Does anyone have an idea how to fix this?
NOTE: Doing it manually is no option, since the expressions I deal with are way more complicated and may contain several different integrals. So I really need to get the lambdify working.
I think returning a lambda from integral_as_quad cannot work, because this lambda will never be called, as the Integral object in SymPy is not callable. Instead, the parameter tuple can be passed to quad via its args argument. Another change I made is in the outer lambdification, replacing
modules=['numpy', {'Integral':integral_as_quad}]
with
modules=[{'Integral': integral_as_quad}, 'sympy']
The idea is that at this stage we don't need NumPy functions yet, we just want to replace the Integral by our callable. The order of modules list matters: the dictionary comes first to prevent SymPy from keeping Integral as an Integral.
Now L(1) returns the correct amount.
import sympy as sp
import numpy as np
from scipy.integrate import quad
def integral_as_quad(function, limits):
x, a, b = limits
param = tuple(function.free_symbols - {x})
f = sp.lambdify((x, *param), function, modules=['numpy'])
return quad(f, a, b, args=param)[0]
a, x = sp.symbols('a,x')
I = sp.Integral(sp.tanh(a*x), (x,0,1))
L = sp.lambdify(a, I, modules=[{'Integral': integral_as_quad}, 'sympy'])
So one possible workaround I have found, but I am unhappy with because it is too slow for my application, is the following:
def to_lambda(expr, param):
# Preprocessing
expr = expr.evalf()
f = sp.lambdify([param], expr, modules='sympy')
fun = lambda x: np.array(np.array(f(x).evalf()), dtype='float64')
return fun
So first, expr gets cast into a lambda function using sympy-functions, e.g. we have
f = lambda a: Integral(tanh(a*x),(x,0,1))
and then we use sympy's internal integrator via evalf() (slow!).
Also, don't ask me why theres the double np.array, if one puts the dtype='float64' into the first one, then it returns TypeError: __array__() takes 1 positional argument but 2 were given

List comprehension in typing.Union

Rather than write out all the combinations, I've tried the following:
from typing import Union, Tuple
from itertools import product as prod
class A: pass
v = (A, None)
annot = Union[Tuple[x, y] for x, y in prod(v, v)] # breaks ofc
Obviously this breaks because I'm trying to treat it like a list comprehension. Doing the following (which is similar to the above anyway) breaks also:
X = [Tuple[x, y] for x, y in prod(v, v)]
Union[X] # breaks
Union[*X] # also breaks
Union[*[X]] # breaks again
But is there some way of doing this?
Just for clarity, the end result I want is:
Union[Tuple[A, A], Tuple[A, NoneType], Tuple[NoneType, A], Tuple[NoneType, NoneType]]

Resources