Sympy: Is it possible use function collect() to IndexedBase variables? - sympy

I'm trying to use the function collect() to simplify mi expression . My desired result is
My code:
from sympy import *
#index
i = symbols('i' , integer = True )
#constants
a = symbols( 'a' )
#variables
alpha = IndexedBase('alpha', positive=True, domain=QQ)
index = (i, 1, 3)
rho = symbols( 'rho')
U = product( alpha[i]**(1/(rho-1)) , index )
U
:
My solution attempt:
U = U.subs(1/(rho-1),a)
collect(U,rho, evaluate=False)[1]
:
What I'm doing wrong?

You must be using a fairly old version of SymPy because in recent versions the form that you wanted arises automatically. In any case you should be able to use powsimp:
In [9]: U
Out[9]:
a a a
alpha[1] ⋅alpha[2] ⋅alpha[3]
In [10]: powsimp(U, force=True)
Out[10]:
a
(alpha[1]⋅alpha[2]⋅alpha[3])
https://docs.sympy.org/latest/tutorials/intro-tutorial/simplification.html#powsimp

Related

Replace values in sympy NDimArray

I would like to replace values in a sympy NDimArray.
I have the following code
import sympy as sp
import numpy as np
e = sp.MatrixSymbol('e',3,3)
E = sp.Matrix(e)
# Make E symmetric
E[1,0] = E[0,1]
E[2,0] = E[0,2]
E[2,1] = E[1,2]
result = sp.tensorproduct(E,E)
E_tst = np.random.rand(3,3)
E_tst[1,0] = E_tst[0,1]
E_tst[2,0] = E_tst[0,2]
E_tst[2,1] = E_tst[1,2]
resultNumeric = np.tensordot(E_tst,E_tst,axes=0)
check = resultNumeric - result.as_mutable().subs({E:sym.Matrix(E_tst)})
I get the error AttributeError: 'MutableDenseNDimArray' object has no attribute 'subs'.
How can I replace the symbols in a NDimArray?
Best Regards
Unfortunately, MutableDenseNDimArray doesn't inherit from Basic, whereas ImmutableDenseNDimArray does, therefore some attributes are not available. Don't ask me about this design decision.
However, you can achieve the same result by creating a substitution dictionary:
# substitution dictionary
d = {k: v for k, v in zip(list(E), list(Matrix(E_tst)))}
check = resultNumeric - result.subs(d)

SymPy `subs` Not Doing Anything

I have a differential equation for which I use sympy.solvers.ode.dsolve to solve, I get out
___________ ___________
-x⋅╲╱ E - V_max x⋅╲╱ E - V_max
ψ(x) = C₁⋅ℯ + C₂⋅ℯ
From (I put some of the code to I used to generate this equation at the end):
2
d
-ψ(x)⋅E + ψ(x)⋅V_max + ───(ψ(x)) = 0
2
dx
This is all well and good, the problem comes when I know that C₁ and C₂ happen to be equal and want to substitute one for the other. So I try something like
psi_high.subs( sp.Symbol( "C_2" ), sp.Symbol( "C_1" ) )
However it just comes out the same as before
___________ ___________
-x⋅╲╱ E - V_max x⋅╲╱ E - V_max
ψ(x) = C₁⋅ℯ + C₂⋅ℯ
I am thinking this may be a memory issue, that a reference to a sympy.Symbol object must refer to not only a sympy.Symbol object with the same value/symbol but which also must be the same underlying object.
This is only speculation (but I can say, do psi_high.subs( x, 0 ) and it works), but my question is how do I resolve it?
Curiously, it seems to work here (I did try this using the sympy.symbols function and by enclosing the symbol references in a tuple and list like shown in the question)
Thanks!
well_length = sq.Quantity( 'L' )
highest_potential = sq.Quantity( "V_max" )
x = sp.Symbol( 'x' )
m = sq.Quantity( 'm' )
hbar = sq.Quantity( "hbar" )
total_energy = sq.Quantity( 'E' )
inverse_total_energy = 1.0 / total_energy
psi_symbol = ud.lookup( "GREEK SMALL LETTER PSI" )
psi = sp.Function( "psi" )
second_derivative = sp.Derivative( psi( x ), x, 2 )
make_shrodinger_left = lambda potential, psi_parameter : ( second_derivative + ( psi( psi_parameter ) * potential ) )
make_shrodinger_right = lambda psi_parameter : total_energy * psi( psi_parameter )
make_psi_equal = lambda input_value, value : sp.Eq( psi( sp.Eq( x, input_value ) ), value )
set_equal = lambda to_set, value : sp.Eq( to_set, value )
shrodinger_left_high = sp.simplify( make_shrodinger_left( highest_potential, x ) )
shrodinger_right = make_shrodinger_right( x )
high_diff = sp.simplify( set_equal( shrodinger_left_high - shrodinger_right, 0 ) )
Here's a simpler example:
In [3]: eq = Eq(f(x).diff(x, 2), 0)
In [4]: eq
Out[4]:
2
d
───(f(x)) = 0
2
dx
In [5]: sol = dsolve(eq)
In [7]: sol
Out[7]: f(x) = C₁ + C₂⋅x
We can inspect these symbols:
In [8]: sol.free_symbols
Out[8]: {C₁, C₂, x}
In [9]: [s.name for s in sol.free_symbols]
Out[9]: ['C2', 'C1', 'x']
Note that there are no underscores in the symbol names. What we want to do then is:
In [10]: sol.subs(Symbol("C1"), Symbol("C2"))
Out[10]: f(x) = C₂⋅x + C₂

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

Lambdifying a function with conditon statements using sympy

Im trying to lambdify this function
def f(x):
if ceil(x)%2 == 0:
return -1
else :
return +1
a = sympy.lambdify(x,f(x))
Im getting an error when i try to do that.
I also tried piecewise , but it is not giving me the desired outcome
y = lambdify(x,(Piecewise((1, ceil(x)%2 == 0), (-1,True))))
Please help
Thanks in advance
You need to pass a symbolic expression to lambdify so a Python function is no good. Also you need to use symbolic sympy functions and sympy's ceil function is actually called ceiling. Finally == compares if two expressions are the same which is not the same as constructing a symbolic Boolean. For that you need Eq:
That gives
In [19]: p = Piecewise((1, Eq(ceiling(x)%2, 0)), (-1,True))
In [20]: p
Out[20]:
⎧1 for ⌈x⌉ mod 2 = 0
⎨
⎩-1 otherwise
In [21]: y = lambdify(x, p)
In [22]: y([1, 2, 3])
Out[22]: array([-1., 1., -1.])
References:
https://docs.sympy.org/latest/modules/functions/elementary.html#ceiling
https://docs.sympy.org/latest/tutorial/gotchas.html#equals-signs

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.