I have a huge symbolic sympy expression on the form
expression = factor * (f1*a + f2*b + f3*c + f4*d + f5*e)
where all of the factors a through e all consists of several terms. I.e:
a = exp(2x) + exp(3x) + sin(Ix).
I want to create en array on the form
array = factor * [a,b,c,d,e]
But dont see a cleaver way to do this. I´ve tried to use the factor function, but it only gives me the expression on the form of "expression" above.
Until now i have used
print(expression)
and then done some brute force copy paste of the factors a through e. Since I am going to get expressions with way more terms than in this example, I want to do this without the copy paste routine. Any ideas?
Here's a simple example you can extrapolate for more terms
import sympy as sp
x = sp.var('x')
f1, f2 = sp.symbols('f1:3')
factor = sp.symbols('factor')
a = x**2 + sp.sin(x) + sp.exp(sp.I * x)
b = sp.log(x)/(x+1)**2
# example expression:
expression = (factor * (f1 * a + f2 * b)).expand()
print(expression)
# collect coefficients of f1 and f2
coeffs = sp.collect(expression.expand(),[f1,f2], evaluate=False)
print(coeffs)
# show the coefficients w/o the factor factor
[(coeffs[f]/factor).simplify() for f in (f1,f2)]
f1*factor*x**2 + f1*factor*exp(I*x) + f1*factor*sin(x) + f2*factor*log(x)/(x**2 + 2*x + 1)
{f2: factor*log(x)/(x**2 + 2*x + 1), f1: factor*x**2 + factor*exp(I*x) + factor*sin(x)}
[x**2 + exp(I*x) + sin(x), log(x)/(x**2 + 2*x + 1)]
Related
Say I have an equation:
eq = sym.Eq(5*x**2 + 2*x + 5*y + f_1(y) + f_2(x), 0)
Is there a way in Sympy to strip the function of x portion and the function of y portion from the above expression?
My goal is something like:
sym.strip(eq, x)
>>>5*x**2 + 2*x + f_2(x)
sym.strip(eq, y)
>>>5*y + f_1(y)
I'm not 100% sure if this will work with the equation you defined, but this seemed to work for me for polynomials. Try this or an adaptation of it.
def strip_sym(expr, sym):
newExpr = 0
for term in expr.args:
newTerm = term if sym in term.free_symbols else 0
newExpr = newExpr + newTerm
return newExpr
I am just looking at the Python module SymPy and try, as a simple (useless) example the fit of a function f(x) by a function set g_i(x) in a given interval.
import sympy as sym
def functionFit(f, funcset, interval):
N = len(funcset) - 1
A = sym.zeros(N+1, N+1)
b = sym.zeros(N+1, 1)
x = sym.Symbol('x')
for i in range(N+1):
for j in range(i, N+1):
A[i,j] = sym.integrate(funcset[i]*funcset[j],
(x, interval[0], interval[1]))
A[j,i] = A[i,j]
b[i,0] = sym.integrate(funcset[i]*f, (x, interval[0], interval[1]))
c = A.LUsolve(b)
u = 0
for i in range(len(funcset)):
u += c[i,0]*funcset[i]
return u, c
x = sym.Symbol('x')
f = 10*sym.cos(x)+3*sym.sin(x)
fooset=(sym.sin(x), sym.cos(x))
interval = (1,2)
print("function to approximate:", f)
print("Basic functions:")
for foo in fooset:
print(" - ", foo)
u,c = functionFit(f, fooset, interval)
print()
print("simplified u:")
print(sym.simplify(u))
print()
print("simplified c:")
print(sym.simplify(c))
The result is the fit function u(x), to be returned, together with the coefficients by functionFit.
In my case
f(x) = 10 * sym.cos(x) + 3 * sym.sin(x)
and I want to fit it according to a linear combination of sin(x), cos(x).
So the coefficients should be 3 and 10.
The result is OK, but for u(x) I get
u(x) = (12*sin(2)**2*sin(4)*sin(x) + 3*sin(8)*sin(x) + 12*sin(2)*sin(x) + 40*sin(2)**2*sin(4)*cos(x) + 10*sin(8)*cos(x) + 40*sin(2)*cos(x))/(2*(sin(4) + 2*sin(2))) :
Function to approximate: 3*sin(x) + 10*cos(x)
Basic functions:
- sin(x)
- cos(x)
Simplified u: (12*sin(2)**2*sin(4)*sin(x) + 3*sin(8)*sin(x) + 12*sin(2)*sin(x) + 40*sin(2)**2*sin(4)*cos(x) + 10*sin(8)*cos(x) + 40*sin(2)*cos(x))/(2*(sin(4) + 2*sin(2)))
Simplified c: Matrix([[3], [10]])
which is indeed the same as 10 * cos(x) + 3 * sin(x).
However I wonder why it is not simplified to that expression. I tried several simplifying function available, but none of it gives the expected result.
Is there something wrong in my code or are my expectations to high?
Don't know if this is a solution for you, but I'd simply use the .evalf method of every Sympy expression
In [26]: u.simplify()
Out[26]: (12*sin(2)**2*sin(4)*sin(x) + 3*sin(8)*sin(x) + 12*sin(2)*sin(x) + 40*sin(2)**2*sin(4)*cos(x) + 10*sin(8)*cos(x) + 40*sin(2)*cos(x))/(2*(sin(4) + 2*sin(2)))
In [27]: u.evalf()
Out[27]: 3.0*sin(x) + 10.0*cos(x)
In [28]:
I want to use power series to approximate some PDEs. The first step I need to generate symbolic multivariate polynomials, given a numpy ndarray.
Consider the polynomial below:
I want to take a m dimensional ndarray of D=[d1,...,dm] where djs are non-negative integers, and generate a symbolic multivariate polynomial in the form of symbolic expression. The symbolic expression consists of monomials of the form:
Fo example if D=[2,3] the output should be
For this specific case I could nest two for loops and add the expressions. But I don't know what to do for Ds with arbitrary length. If I could generate the D dimensional ndarrays of A and X without using for loops, then I could use np.sum(np.multiply(A,X)) as Frobenius inner product to get what I need.
I would use symarray and itertools.product for this:
from sympy import *
import itertools
D = (3, 4, 2, 3)
a = symarray("a", D)
x = symarray("x", len(D))
prod_iterator = itertools.product(*map(range, D))
result = Add(*[a[p]*Mul(*[v**d for v, d in zip(x, p)]) for p in prod_iterator])
The result being
a_0_0_0_0 + a_0_0_0_1*x_3 + a_0_0_0_2*x_3**2 + a_0_0_1_0*x_2 + a_0_0_1_1*x_2*x_3 + a_0_0_1_2*x_2*x_3**2 + a_0_1_0_0*x_1 + a_0_1_0_1*x_1*x_3 + a_0_1_0_2*x_1*x_3**2 + a_0_1_1_0*x_1*x_2 + a_0_1_1_1*x_1*x_2*x_3 + a_0_1_1_2*x_1*x_2*x_3**2 + a_0_2_0_0*x_1**2 + a_0_2_0_1*x_1**2*x_3 + a_0_2_0_2*x_1**2*x_3**2 + a_0_2_1_0*x_1**2*x_2 + a_0_2_1_1*x_1**2*x_2*x_3 + a_0_2_1_2*x_1**2*x_2*x_3**2 + a_0_3_0_0*x_1**3 + a_0_3_0_1*x_1**3*x_3 + a_0_3_0_2*x_1**3*x_3**2 + a_0_3_1_0*x_1**3*x_2 + a_0_3_1_1*x_1**3*x_2*x_3 + a_0_3_1_2*x_1**3*x_2*x_3**2 + a_1_0_0_0*x_0 + a_1_0_0_1*x_0*x_3 + a_1_0_0_2*x_0*x_3**2 + a_1_0_1_0*x_0*x_2 + a_1_0_1_1*x_0*x_2*x_3 + a_1_0_1_2*x_0*x_2*x_3**2 + a_1_1_0_0*x_0*x_1 + a_1_1_0_1*x_0*x_1*x_3 + a_1_1_0_2*x_0*x_1*x_3**2 + a_1_1_1_0*x_0*x_1*x_2 + a_1_1_1_1*x_0*x_1*x_2*x_3 + a_1_1_1_2*x_0*x_1*x_2*x_3**2 + a_1_2_0_0*x_0*x_1**2 + a_1_2_0_1*x_0*x_1**2*x_3 + a_1_2_0_2*x_0*x_1**2*x_3**2 + a_1_2_1_0*x_0*x_1**2*x_2 + a_1_2_1_1*x_0*x_1**2*x_2*x_3 + a_1_2_1_2*x_0*x_1**2*x_2*x_3**2 + a_1_3_0_0*x_0*x_1**3 + a_1_3_0_1*x_0*x_1**3*x_3 + a_1_3_0_2*x_0*x_1**3*x_3**2 + a_1_3_1_0*x_0*x_1**3*x_2 + a_1_3_1_1*x_0*x_1**3*x_2*x_3 + a_1_3_1_2*x_0*x_1**3*x_2*x_3**2 + a_2_0_0_0*x_0**2 + a_2_0_0_1*x_0**2*x_3 + a_2_0_0_2*x_0**2*x_3**2 + a_2_0_1_0*x_0**2*x_2 + a_2_0_1_1*x_0**2*x_2*x_3 + a_2_0_1_2*x_0**2*x_2*x_3**2 + a_2_1_0_0*x_0**2*x_1 + a_2_1_0_1*x_0**2*x_1*x_3 + a_2_1_0_2*x_0**2*x_1*x_3**2 + a_2_1_1_0*x_0**2*x_1*x_2 + a_2_1_1_1*x_0**2*x_1*x_2*x_3 + a_2_1_1_2*x_0**2*x_1*x_2*x_3**2 + a_2_2_0_0*x_0**2*x_1**2 + a_2_2_0_1*x_0**2*x_1**2*x_3 + a_2_2_0_2*x_0**2*x_1**2*x_3**2 + a_2_2_1_0*x_0**2*x_1**2*x_2 + a_2_2_1_1*x_0**2*x_1**2*x_2*x_3 + a_2_2_1_2*x_0**2*x_1**2*x_2*x_3**2 + a_2_3_0_0*x_0**2*x_1**3 + a_2_3_0_1*x_0**2*x_1**3*x_3 + a_2_3_0_2*x_0**2*x_1**3*x_3**2 + a_2_3_1_0*x_0**2*x_1**3*x_2 + a_2_3_1_1*x_0**2*x_1**3*x_2*x_3 + a_2_3_1_2*x_0**2*x_1**3*x_2*x_3**2
Remarks:
symarray depends on NumPy, but this does not seem to be an issue for you. If it was, I would create symbols one by one using itertools.product
The format Add(*[...]) is more efficient than sum([...]) for forming symbolic sums with a large number of terms, see SymPy issue 13945.
I need to find and substitute subexpression with a symbol, doing an "inverse" substitution of sorts.
Here is direct substitution example:
(simplify and collect added to make the resulting expression have the form that I need to work with)
In [1]: from sympy.abc import a, b, x, y, z
...: expr = (1 + b) * z + (1 + b) * y
...: z_expr = a / (1 + b) + x
...: subs_expr = expr.subs(z, z_expr).simplify().collect(1+b)
...: print(expr)
...: print(z_expr)
...: print(subs_expr)
y*(b + 1) + z*(b + 1)
a/(b + 1) + x
a + (b + 1)*(x + y)
Now I want to go back, and subs does not do anything:
In [2]: orig_expr = subs_expr.subs(z_expr, z)
...: print(orig_expr)
a + (b + 1)*(x + y)
How can I get back to y*(b + 1) + z*(b + 1)?
The substitution attempt fails because subs_expr does not actually contain z_expr in its expression tree. "Substitute for an expression that isn't there" is not really a well-defined goal. A well-defined goal would be "eliminate a using the relation z = z_expr". That can be done as follows:
var('orig_expr')
orig_expr = solve([orig_expr - subs_expr, z - z_expr], [orig_expr, a])[orig_expr]
Now orig_expr is equal to b*y + b*z + y + z
Using Sympy, say we have an expression f, which is a polynomial of the Symbol "x" (and of potentially other symbols).
I would like to know what if there is an efficient way to drop all terms in f of order greater than some integer n.
As a special case I have a very complicated function but i want to only keep terms up to 2nd order in x. What's the efficient way to do this?
The obvious, not-very-efficient way to do it would be for each m less than n, take m derivatives and set x to 0 to obtain the coefficient of x^m. We obtain each coefficient this way then reconstruct the polynomial. But taking derivatives is not the most efficient thing.
An easy way to do this is to add O(x**n) to the expression, like
In [23]: x + x**2 + x**4 + x**10 + O(x**3)
Out[23]:
2 ⎛ 3⎞
x + x + O⎝x ⎠
If you want to later remove it, use the removeO method
In [24]: (x + x**2 + x**4 + x**10 + O(x**3)).removeO()
Out[24]:
2
x + x
You can also use series to take the series expansion of the expression. The difference here is the behavior if a non-polynomial term ends up in the expression:
In [25]: x + sin(x) + O(x**3)
Out[25]:
⎛ 3⎞
sin(x) + x + O⎝x ⎠
In [26]: (x + sin(x)).series(x, 0, 3)
Out[26]:
⎛ 3⎞
2⋅x + O⎝x ⎠
If you take a look at the polynomial module docs:
http://docs.sympy.org/latest/modules/polys/reference.html
there will be plenty of ways to go about it, depending on the specifics of your situation. A couple different ways that would work:
Using .coeffs():
>>> f = 3 * x**3 + 2 * x**2 + x * y + y**3 + 1
>>> order = 2
>>> coeffs = Poly(f, x).coeffs()
>>> f_new = sum(x**n * coeffs[-(n+1)] for n in range(order+1)) # the +1 is to get 0th order
>>> f_new
2*x**2 + x*y + y**3 + 1
Alternatively, you could iterate over items in .all_terms():
>>> all_terms = Poly(f, x).all_terms()
>>> sum(x**n * term for (n,), term in all_terms() if n <= order)
There are plenty of manipulation functions in the module that you should be able to work with the expression directly rather than doing calculations/taking derivatives/etc.