Defining a range for a symbol in Sympy - 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.

Related

Substitute compound expression in SymPy

In sympy how can I make a substitution of a compound expression for a single variable as in the following example that only works for one of the instances of the common factor?
from sympy import *
x, y, z = symbols('x y z')
eq = Eq(2*(x+y) + 3*(x+y)**2, 0)
print(eq)
eq1 = Eq(z, x+y)
print(eq1)
eq2 = eq.subs(eq1.rhs, eq1.lhs)
print(eq2)
Output
Eq(2*x + 2*y + 3*(x + y)**2, 0)
Eq(z, x + y)
Eq(2*x + 2*y + 3*z**2, 0)
Desired output for last line
Eq(2*z + 3*z**2, 0)
Thanks to Oscar Benjamin's comment. I've solved the case I was actually interested in:
from sympy import *
t, L, C0, R, a, w0, h = symbols('t L C_0 R alpha omega_0 h')
Q = Function('Q')
ex0 = L*Q(t).diff(t, t) + R*Q(t).diff(t) + Q(t)*(1/(C0/(1+h*cos(a*t))))
print(ex0)
ex1 = ex0/L
ex1 = ex1.collect(Q(t)).expand()
print(ex1)
# substitute the following compound expression
ex2 = Eq(w0*w0, 1/(L*C0))
print(ex2)
ex3 = ex1.subs(L*C0, 1/(w0*w0))
ex4 = ex3.collect(Q(t))
print(ex4)
Output:
L*Derivative(Q(t), (t, 2)) + R*Derivative(Q(t), t) + (h*cos(alpha*t) + 1)*Q(t)/C_0
Derivative(Q(t), (t, 2)) + R*Derivative(Q(t), t)/L + h*Q(t)*cos(alpha*t)/(C_0*L) + Q(t)/(C_0*L)
Eq(omega_0**2, 1/(C_0*L))
(h*omega_0**2*cos(alpha*t) + omega_0**2)*Q(t) + Derivative(Q(t), (t, 2)) + R*Derivative(Q(t), t)/L
The substitution fails because subs does not encounter any argument x + y in the (sub)expression 2*(x + y): that expression automatically expands to 2*x + 2*y. So one solution is to do as Oscar suggested: make an algebraic substitution. I often follow this up with a restoration step to handle anything that didn't change as I expected. The other thing you can do is to use a helper function that groups together terms that are in the multi-term old object that you desire to replace:
def mvsubs(eq, old, new):
from sympy.core.exprtools import factor_terms
if not old.is_Add:
return eq.subs(old, new)
Add = old.func
free = old.free_symbols
for i in eq.atoms(Add):
reps = {}
for i in i.args:
if not all(i.has(x) for x in free):
reps.setdefault(i, Dummy())
eq = eq.subs(reps).subs(Add(*reps.values()),
factor_terms(Add(*reps.keys()))).subs(
old, new).xreplace({v:k for k,v in reps.items()})
return eq
>>> mvsubs(eq, x+y, z)
Eq(3*z**2 + 2*z, 0)

How could I generate random coefficients for polynomials using Sum( f(x), (x,0,b) )?

from sympy import Sum, Eq
from sympy.abc import n,x
import random
def polynomial(x):
i = 0
def random_value(i):
return random.choice([i for i in range(-10,10) if i not in [0]])
eq = Sum(random_value(i)*x**n, (n,0,random_value(i)))
display(Eq(eq,eq.doit(), evaluate=False))
polynomial(x)
polynomial(x)
With this code, the coefficients are always the same.
Also, I am not sure if the algebra evaluations are correct for b < 0 .
One way is to use IndexedBase to generate symbolic-placeholder coefficients, and then substitute them with numerical coefficients.
from sympy import Sum, Eq, Matrix, IndexedBase
from sympy.abc import n, x
import random
def polynomial(x):
# n will go from zero to this positive value
to = random.randint(0, 10)
# generate random coefficients
# It is important for them to be a sympy Matrix or Tuple,
# otherwise the substitution (later step) won't work
coeff = Matrix([random.randint(-10, 10) for i in range(to + 1)])
c = IndexedBase("c")
eq = Sum(c[n]*x**n, (n, 0, to)).doit()
eq = eq.subs(c, coeff)
return eq
display(polynomial(x))
display(polynomial(x))
Another ways is to avoid using Sum, relying instead on list-comprehension syntax and builtin sum:
def polynomial(x):
to = random.randint(0, 10)
coeff = [random.randint(-10, 10) for i in range(to + 1)]
return sum([c * x**n for c, n in zip(coeff, range(to + 1))])
display(polynomial(x))
display(polynomial(x))
You can pass a list of coefficients (with highest order coefficient first and constant last) directly to Poly and then convert that to an expression:
>>> from sympy import Poly
>>> from sympy.abc import x
>>> Poly([1,2,3,4], x)
Poly(x**3 + 2*x**2 + 3*x + 4, x, domain='ZZ')
>>> _.as_expr()
x**3 + 2*x**2 + 3*x + 4
>>> from random import randint, choice
>>> Poly([choice((-1,1))*randint(1,10) for i in range(randint(0, 10))], x).as_expr()
-3*x**4 + 3*x**3 - x**2 - 6*x + 2

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 : How is it possible to simplify power of sum?

Considering an expression of this form:
x,y,n=sp.symbols("x y n",positive=True,real=True)
sp.Pow(x+y+x**2,n+1)*sp.Pow(x+2*y+4*y**3,-n-1)
how is it possible to simplify it to have a common power ?
(i.e. sp.Pow((x+y+x**2)/(x+2*y+y**3),n+1) )
This is the same general problem as here
>>> var('z', positiv=True)
z
>>> expr = sp.Pow(x+y+x**2,n+1)*sp.Pow(x+2*y+4*y**3,-n-1)
>>> powsimp(expr.subs(n + 1, var('z',positive=1))).subs(z, n + 1)
((x**2 + x + y)/(x + 4*y**3 + 2*y))**(n + 1)

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.)