sympy: post order traversal over expression tree: skips top-level operation - sympy

I am trying to loop post order through a sympy-expression.
It works fine except for the fact that it skips the last addition (im my example case the addition of sympy.sin(x * y) and z**2).
import sympy
def post(expr):
if not expr:
return
for arg in expr.args:
post(arg)
print(f'arg {arg}')
print(f'arg.func: {arg.func}')
print(f'arg.args: {arg.args}')
x, y, z = sympy.symbols('x,y,z')
expr = sympy.sin(x * y) + z**2
post(expr)
I think it's because of the for arg in expr.args part yet if I ditch the .args and
loop over the whole expression
import sympy
def post(expr):
if not expr:
return
for arg in expr:
post(arg)
print(f'arg {arg}')
print(f'arg.func: {arg.func}')
print(f'arg.args: {arg.args}')
x, y, z = sympy.symbols('x,y,z')
expr = sympy.sin(x * y) + z**2
post(expr)
I get TypeError: 'Add' object is not iterable.
Can I somehow fix this such that it loops over all functions of the expression and doesn't skip the top-level function?

This battery is already included if you want to use it:
from sympy.core.traversal import postorder_traversal
for i in postorder_traversal(expr):print(i)
gives
z
2
z**2
x
y
x*y
sin(x*y)
z**2 + sin(x*y)
Otherwise, consider this modification for your function:
def post(expr):
for arg in expr.args:
post(arg)
print(f'arg {expr}')
print(f'arg.func: {expr.func}')
print(f'arg.args: {expr.args}')

Related

How to divide by variable that belongs to y in Sympy

I am making an separable differential equation solver. In order to make an expression that separated by x and y variables I have to divide expression on the right by every variable that belong to s such as sin(y), e**y, y**2, ...
I am using Sympy
def equationseparator(diffeq):
x, y, z, e= sym.symbols("x y z e")
separateddiff, separatedeq = diffeq.split("=")
variables_of_eq = re.split('[(|)]', separatedeq)
eq = sym.parse_expr(separatedeq)
variables_of_eq_ordered = []
variables_of_eq_ord_var = []
for var in variables_of_eq:
if var == " * " or var == "":
pass
else:
variables_of_eq_ordered.append(var)
for var in variables_of_eq_ordered:
var = sym.Symbol(var)
variables_of_eq_ord_var.append(var)
print(sym.simplify(separatedeq))
print(variables_of_eq_ordered)
print(variables_of_eq_ord_var)
equationseparator("dy/dx=(6 * x) * (y) * (e**y)")
By using variables_of_eq_ord_var I get all the variables and append to the list. And I want to choose all the expressions that belong to y. But I couldn't make it. Thanks in advance!
Since you are using SymPy, why not use its solver for such equations?
>>> from sympy import S, Function
>>> from sympy.abc import x
>>> f = Function('f')
>>> S('dydx-6*x*y*exp(y)').subs(y,f(x)).subs('dydx',f(x).diff(x))
-6*x*f(x)*exp(f(x)) + Derivative(f(x), x)
>>> dsolve(_)
Eq(Ei(exp_polar(I*pi)*f(x)), C1 + 3*x**2)
Else, if you have a product of factors and want those that contain a certain symbol you can just use as_independent to separate them:
>>> nony, withy = (x*y*exp(y)).as_independent(y); (nony, withy)
(x, y*exp(y))

Sympy container objects that respond to simplify(...)

I'm trying to create a sympy container object with multiple sympy expressions inside it, representing the x, y and z observables of a qubit.
I started by creating a class that extends sympy.Basic. I was hoping that pushing x, y and z into the sympy args would result in an object that would respond to simplify(qubit) by returning an object with the x, y, and z args simplified. When that didn't work naturally, I redefined the simplify(self, **kwargs) method.
Is there a recommended way to get simplify(qubit) working rather than qubit.simplify()?
from sympy import Symbol, cos, sin, Basic
aa = Symbol('aa')
# simplifies to 1
expression_to_simplify = sin(aa)**2 + cos(aa)**2
class Qubit(Basic):
def __new__(cls, qubitnumber, x, y, z):
obj = super().__new__(cls, qubitnumber, x, y, z)
obj.qubitnumber = qubitnumber
return obj
def simplify(self, **kwargs):
from sympy.simplify import simplify
return Qubit(
self.args[0],
simplify(self.args[1]),
simplify(self.args[2]),
simplify(self.args[3])
)
qb = Qubit(1, expression_to_simplify, expression_to_simplify, expression_to_simplify)
print(qb.simplify())
from sympy.simplify import simplify
print(simplify(qb))
Gives output:
Qubit(1, 1, 1, 1)
Qubit(1, sin(aa)**2 + cos(aa)**2, sin(aa)**2 + cos(aa)**2, sin(aa)**2 + cos(aa)**2)
simplify is not written to provide support for simplifying the contents of containers, but you can make it so (or only do so for Qubit as follows):
>>> from sympy import simplify as _simplify
>>> def simplify(x, *y, **k):
... if isinstance(x, Expr): return _simplify(x, *y, **k)
... elif isinstance(x, Qubit): return Qubit(*[_simplify(i, *y, **k) for i in x.args])
... raise NotImplementedError
This then gives Qubit(1, 1, 1, 1) for simplify(qb). The trick is in using the original simplify as _simplify inside the new simplify.

How to solve this differential equation in sympy?

I want to solve this differential equation in sympy:
f'(x) = f(x+1)
I try this:
from sympy import *
x = symbols("x")
f = Function("f")
f_ = Derivative(f,x)
dsolve(f_(x) - f(x+1), f(x))
but get an error: "'Derivative' object is not callable".
When I replace "f_(x)" by "f_", I get a different error: "TypeError: doit() missing 1 required positional argument: 'self'".
What is the correct syntax for this?
You have to differentiate after providing an argument.
The following works for me:
from sympy import *
x = symbols("x")
f = Function("f")
f_ = Derivative(f(x),x)
dsolve(f_ - f(x+1), f(x))
Sidenote: Solution to your actual problem
What you have is essentially a DDE, just with the time pointing in the wrong direction. The typical form of the DDE would be g'(t) = −g(t−1). With this module of mine, we can solve this numerically:
from jitcdde import y, t, jitcdde
from numpy import arange
f = [-y(0,t-1)]
DDE = jitcdde(f)
DDE.constant_past([1.0])
DDE.step_on_discontinuities()
times = arange(0,1000,0.1) + DDE.t
solution = [(time,DDE.integrate(time)[0]) for time in times]
It seems that no matter how we initialise the past, the solutions eventually converge to something of the form exp(a·t)·sin(b·t) with some constants a and b specified below. In fact if instead of DDE.constant_past([1.0]) we use
a = -0.318131477176434
b = 1.33723563936212
DDE.past_from_function([exp(a*t)*sin(b*t)])
the solution matches exp(a·t)·sin(b·t) extremely well.
Something tells me we're on a hiding to nowhere. This is not a useful answer.
>>> from sympy import *
>>> f = Function('f')
>>> var('x')
x
>>> Eq(f(x).diff(x,x)-f(x+1))
Eq(-f(x + 1) + Derivative(f(x), x, x), 0)
>>> dsolve(_,f(x))
Eq(f(x), C1 + x*(C2 + Integral(f(x + 1), x)) - Integral(x*f(x + 1), x))
>>> latex(_)
'f{\\left (x \\right )} = C_{1} + x \\left(C_{2} + \\int f{\\left (x + 1 \\right )}\\, dx\\right) - \\int x f{\\left (x + 1 \\right )}\\, dx'
As a graphic (having tried various ways of putting the mathematical representation here.)

Defining a range for a symbol in Sympy

In Sympy it is possible to define constraints on what values a symbol may take
x = symbols('x', real=True)
Is it possible to say that a symbol should take values only in a certain range, say -1 < x < 1? The reason why I am interested in this is because I am trying to get sympy to automatically simplify expressions like the one below
expr = sqrt(1+x) * sqrt((1-x)*(1+x)) / sqrt(1-x)
Running simplify(expr) yields no simplification, whereas when -1<x<1 the simplified result should be 1+x. How do I get sympy to simplify expressions like the one above?
Although a single symbol can't hold that assumption, an expression can. Let's define an expression that has the desired range:
>>> p = Symbol('p', positive=True)
>>> neg1to1 = (p - 1)/(p + 1)
Now replace x with that value and simplify
>>> asp = expr.subs(x, neg1to1).simplify(); asp
2*p/(p + 1)
Now restore x from the relationship between it and neg1to1:
>>> p_as_x = solve(neg1to1 - x, p)[0]
>>> asp.subs(p, p_as_x).simplify()
x + 1
You could turn this into a function to allow for any range for x:
>>> def simplify_assuming_range(expr, x, lo, hi):
... from sympy import Dummy, solve
... p = Dummy(positive=True)
... xr = (p - 1)/(p + 1)*(hi - lo) + lo
... rx = solve(xr - x, p)[0]
... return expr.subs(x, xr).simplify().subs(p, rx).simplify()
...
>>> simplify_assuming_range(expr,x,-1,1)
x + 1
Using targeted expansion with force can help:
>>> expand(expr, power=True, force=True, mul=False)
x + 1
The expand docstring will tell about each of those options.

Returning np.array or np.matrix objects in a theano function

I have to do something like this.
import theano as th
import theano.tensor as T
x, y = T.dscalars('x', 'y')
z = np.matrix([[x*y, x-y], [x/y, x**2/(2*y)]])
f = th.function([x, y], z) # causes error
# next comes calculations like f(2, 1)*f(3, 2)*some_matrix
I know the last line is not a valid code as th.function doesn't support returning these objects. Is there an efficient way to do this without returning all elements of matrix and casting it as an np.matrix?
The problem with your approach is that z needs to be a list of theano variables not a numpy matrix.
You can achieve the same result using:
z1,z2,z3,z4 = x*y,x-y,x/y,x**2/(2*y)
f = th.function([x, y], [z1,z2,z3,z4])
def createz(z1,z2,z3,z4) :
return np.matrix([[z1,z2],[z3,z4]])
print(createz(*f(1,2)))