SymPy: How to implement summation of indexed coefficients times indexed functons? - python

Update:
I want to run a generate_function(3) for example to have an output of:
c_0 * F(0) + c_1 * F(1) + c_2 * F(2) + c_3 * F(3)
where c_i is just a symbol, while F(i) is a function or an object that I can use later for the rest of code.
I simply want to use SymPy to implement the summation:
summation (from i = 0 to n) c_i * f(i)
where c_i is indexed constant (symbol) and f is a function with argument of i.
I tried many times and failed.
def generate(n):
coeff = sym.symbols('c0:{}'.format(n))
def f(i):
return i
return sym.Sum(coeff(i) * f(i),(i,0,n))
I got: 'tuple' object is not callable
Thanks for help

It's not completely clear what you want but maybe this is it:
In [31]: C = IndexedBase('C')
In [32]: f = Function('f')
In [33]: i, n = symbols('i, n')
In [34]: s = Sum(C[i] * f(i), (i, 0, n))
In [35]: s
Out[35]:
n
___
╲
╲
╱ f(i)⋅C[i]
╱
‾‾‾
i = 0

You created a tuple of symbols:
In [8]: coeff = symbols("c0:{}".format(n))
In [9]: coeff
Out[9]: (c0a, c0b, c0c, c0d, c0e, c0f, c0g, c0h, c0i, c0j, c0k, c0l, c0m, c0n)
Treating such a tuple as though it were a function, as in f(i), does not work. That's basic Python! While the indexing is a nice short to making many symbols, the result is no different from doing symbols('x y z')
In [10]: coeff(0)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Input In [10], in <module>
----> 1 coeff(0)
TypeError: 'tuple' object is not callable
You can index the tuple
In [11]: coeff[0]
Out[11]: c0a
You could use a list comprehension to make a list of expressions:
In [14]: [coeff[i] * f(i) for i in range(5)]
Out[14]: [0, c0b, 2⋅c0c, 3⋅c0d, 4⋅c0e]
and even apply the base sum function to create a sympy.Add expression:
In [16]: sum([coeff[i] * f(i) for i in range(5)])
Out[16]: c0b + 2⋅c0c + 3⋅c0d + 4⋅c0e

Related

Function vectorization says there is a 0-dimensional argument while the argument is an array

I'm implementing this equation and using it for the set of frequencies nos:
The non vectorized code works:
import numpy as np
h = np.array([1,2,3])
nos = np.array([4, 5, 6, 7])
func = lambda h, no: np.sum([hk * np.exp(-1j * no * k) for k, hk in enumerate(h)])
# Not vectorized
resps = np.zeros(nos.shape[0], dtype='complex')
for i, no in enumerate(nos):
resps[i] = func(h, no)
print(resps)
> Out: array([-0.74378734-1.45446975j,
> -0.94989022+3.54991188j,
> 5.45190245+2.16854975j,
> 2.91801616-4.28579526j])
I'd like to vectorize the call in order to pass nos at once instead of explicitly iterating:
H = np.vectorize(func, excluded={'h'}, signature='(k),(n)->(n)')
resps = H(h, nos)
When calling H:
Error: ValueError: 0-dimensional argument does not have enough dimensions for all core dimensions ('n',)
I'm using the signature parameter but I'm not sure I use it in the correct way. Without this parameter there is an error in func:
TypeError: 'numpy.int32' object is not iterable
I don't understand where the problem is.
A list comprehension version of your loop:
In [15]: np.array([func(h,n) for n in nos])
Out[15]:
array([-0.74378734-1.45446975j, -0.94989022+3.54991188j,
5.45190245+2.16854975j, 2.91801616-4.28579526j])
vectorize - excluding the first argument (by position, not name), and scalar iteration on second.
In [16]: f=np.vectorize(func, excluded=[0])
In [17]: f(h,nos)
Out[17]:
array([-0.74378734-1.45446975j, -0.94989022+3.54991188j,
5.45190245+2.16854975j, 2.91801616-4.28579526j])
No need to use signature.
With true numpy vectorization (not the pseudo np.vectorize):
In [23]: np.sum(h * np.exp(-1j * nos[:,None] * np.arange(len(h))), axis=1)
Out[23]:
array([-0.74378734-1.45446975j, -0.94989022+3.54991188j,
5.45190245+2.16854975j, 2.91801616-4.28579526j])

Updating variables with sympy

I have a problem with sympy where it won't update my values. So I'm trying to do an iterative method for solving $\sigma_x$ and when I try to put numerical values into my expression it won't update.
I have imported sympy as sy
So I first have this code:
q,b,E,t,nu,L,z,x,y = sy.symbols("q,b,E,t,nu,L,z,x,y")
D = (E*t**3)/(12*(1-nu**2))
q_0 = 4*q/(sy.pi*b) * sy.sin(sy.pi/2)*(1-sy.cos(sy.pi))
D2 = (sy.pi**2 / L**2) + (sy.pi**2 / b**2)
w = q_0/(D* D2**2) * sy.sin(sy.pi*x/L) * sy.sin(sy.pi * y / b)
M = 4
N = 4
w_iterert = 0
for m in range(1,M+1):
for n in range(N+1):
q_iterert = 4*q/(sy.pi*b*m)*sy.sin(sy.pi*n/2)*(1-sy.cos(sy.pi*m))
w_mn = q_iterert/(D*((sy.pi**2*m**2 / L**2) + (sy.pi**2 * n**2 / b**2))**2)
w_iterert += w_mn*sy.sin(m*pi*x/L)*sy.sin(n*pi*y/b)
Then I plot the analytical expression:
w_iterert
And now I use formulas to find my sigma_x:
w_xx_iter = sy.diff(w_iterert,x,2)
w_yy_iter = sy.diff(w_iterert,y,2)
sigma_x_iter = - z*E/(1-nu**2)*(w_xx_iter+nu*w_yy_iter)
Here is where I get it wrong. now I do this:
E = 210000
pi = sy.pi
q = 20
nu = 0.3
L = 4000
b = 1000
t = 10
x = 2
z = t/2
y = b/2
sigma_x_iter
And I would expect this to update the values and give me the numerical value. Instead I just get the same analytical expression. How can I update my values?
I tried everything, I just wrote and tried to copy the text into another notebook which obviously worked. But then I can't change the M N values and get a different result unless I do it automatically.
I had to edit your code several times to get something that I could copy-n-paste and run.
What I meant by subs is:
In [38]: sigma_x_iter.subs({E:210000, q:20,nu:0.3,L:4000,b:1000,t:10,x:2,z:10/2,
...: y:1000/2})
Out[38]:
⎛ ⎛ π ⎞ ⎛3⋅π ⎞⎞
⎜654766080000000000⋅sin⎜────⎟ 9844326400000000⋅sin⎜────⎟⎟
⎜ ⎝2000⎠ ⎝2000⎠⎟
2.0e-10⋅⎜──────────────────────────── + ──────────────────────────⎟ 9.6e-10⋅
⎝ 243049 2601 ⎠
─────────────────────────────────────────────────────────────────── + ────────
3
π
⎛ ⎛3⋅π ⎞ ⎛ π ⎞⎞
⎜1321369600000000⋅sin⎜────⎟ 725790720000000000⋅sin⎜────⎟⎟
⎜ ⎝2000⎠ ⎝2000⎠⎟
⎜────────────────────────── + ────────────────────────────⎟
⎝ 2601 243049 ⎠
───────────────────────────────────────────────────────────
3
π
In [39]: _38.n()
Out[39]: 0.361692509661739
In sympy you need to keep a clear distinction between a symbol, and the variable that references it. sympy works within the Python environment, without changing syntax. You may need to study the documented gotachas some more.
E as defined is a symbol:
In [42]: type(E)
Out[42]: sympy.core.symbol.Symbol
This assignment assigns an int to E, breaking any connection it had with the symbol. The symbol still exists in the various expressions, but you can no longer reference it with the variable E. This assignment does not touch any of the expressions.
In [43]: E = 210000
In [44]: type(E)
Out[44]: int

TypeError: loop of ufunc does not support argument 0 of type Add which has no callable sin method

import math
import numpy as np
import matplotlib.pyplot as plt
import simpy as sp
init_printing()
var('x,y,z')
a = math.sqrt(10/7)
c = (y-math.sqrt(18/7)-1)
b = np.sin(math.pi/2*c)-z
f = lambda x,y,z: x**2+y**2+z**2-4
g = lambda x, y, z: (9/2)*(x**2+z**2)+y**2-9
h = lambda y, z: np.sqrt(10/7) * np.sin((np.pi/2) * y - np.sqrt(18/7)-1)- z
F = [[f(x, y, z)],
[g(x, y, z)],
[h(y, z)]]
As I try to define the fuction h(x) I get this:
AttributeError Traceback (most recent call last)
AttributeError: 'Add' object has no attribute 'sin'
The above exception was the direct cause of the following exception:
TypeError Traceback (most recent call last)
<ipython-input-68-da3a7e28861c> in <module>
10 a = math.sqrt(10/7)
11 c = (y-math.sqrt(18/7)-1)
---> 12 b = np.sin((math.pi/2*c))-z
13
14
TypeError: loop of ufunc does not support argument 0 of type Add which has no callable sin method
I tried to separete the function in parts a, b and c so the error could be more easy to find. Now I know I have some problem with the var('x, y, z') and the np.sin()/math.sin(), but I can't find how to fix it.
Pay close attention to the type of the variables. When in doubt, check, don't just guess or assume
var('x,y,z')
these are sympy symbols (though here I'm guessing what var is doing)
a = math.sqrt(10/7)
this must be a python float, produced by the math.sqrt function.
c = (y-math.sqrt(18/7)-1)
Assuming y is sympy, then c itself is a sympy expression, probably an Add.
b = np.sin(math.pi/2*c)-z
math.pi/2*c is sympy expression.
np.sin is a numpy ufunc, and only works with numpy arrays. That means it first does
np.array(math.pi/2*c)
making a single element object dtype array.
np.sin given an object dtype array, passes the task to the sin method of that object. But noone defines a sin method. It's always a function.
Using np.sin, or many other numpy functions on sympy expressions just does not work. Don't mix sympy and numpy until you know what you are doing!
In [5]: sp.var('x,y,z')
Out[5]: (x, y, z)
In [6]: y
Out[6]: y
In [7]: a = math.sqrt(10/7)
...: c = (y-math.sqrt(18/7)-1)
In [8]: a
Out[8]: 1.1952286093343936
In [9]: c
Out[9]: y - 2.60356745147455
In [10]: type(c)
Out[10]: sympy.core.add.Add
In [11]: math.pi/2*c
Out[11]: 1.5707963267949*y - 4.08967418933897
In [12]: np.array(_)
Out[12]: array(1.5707963267949*y - 4.08967418933897, dtype=object)
In [13]: np.sin(_)
AttributeError: 'Add' object has no attribute 'sin'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "<ipython-input-13-2b409a55b2a2>", line 1, in <module>
np.sin(_)
TypeError: loop of ufunc does not support argument 0 of type Add which has no callable sin method
pure sympy
In [14]: a = sp.sqrt(10/7)
...: c = (y-sp.sqrt(18/7)-1)
In [15]: c
Out[15]: y - 2.60356745147455
In [16]: sp.pi/2*c
Out[16]: pi*(y - 2.60356745147455)/2
In [17]: sp.sin(sp.pi/2*c)
Out[17]: sin(pi*(y/2 - 1.30178372573727))

How to pass function expression to lambda (sympy)

I don't know how to pass an expression to lambda parameter.
I think this is a problem of type with sympy.core.power.pow type.
Here is the code :
import sympy
def trapezoid(f, a, b, n):
h = float(b - a) / n
result = 0.5 * f(a) + 0.5 * f(b)
for i in range(1, n):
result += f(a + i * h)
result *= h
return result
if __name__ == "__main__":
x = sympy.Symbol('x')
fonction = sympy.Pow(x,2)
print(fonction) # x**2
print(type(fonction)) # <class 'sympy.core.power.Pow'>
print("Expected Result : ", trapezoid(lambda x: x ** 2, 1, 5, 100))
print("Not expected result : ",trapezoid(lambda x:fonction, 1, 5, 100)) # Won't give me expected result
Have you guys any idea what is the correct syntax I need to use ?
I couldn't find an example of that even though I searched through several subjects.
Updating my answer based on the changes in your question -
Sympy lets you create expressions that can be utilized across multiple languages but you have to evaluate them when trying to run them. Your code's issue was that you were passing a sympy expression (which is NOT callable directly) to your trapezoid function. I have modified your trapezoid function to evaluate the 'fonction' you have defined.
from sympy import *
def trapezoid(f, a, b, n):
h = float(b - a) / n
result = 0.5 * f.evalf(subs={x: a}) + 0.5 * f.evalf(subs={x: b})
for i in range(1, n):
result += f.evalf(subs={x: (a + i * h)})
result *= h
return result
if __name__ == "__main__":
x = Symbol('x')
fonction = Pow(x,2)
print(fonction) # x**2
print("Expected result : ",trapezoid(fonction, 1, 5, 100))
x**2
Expected result : 41.3344000000000
Check this post out. Here you can find different ways of evaluating your sympy expressions in python.
https://www.w3schools.com/python/python_lambda.asp
The example below of how to create a lambda function and to call it.
my_func = lambda x: x**2;
print(my_func(3))
In your example:
def test(function):
print(function)
print(type(function))
print(function(3))
Calling it:
>>> test(lambda x:x**2)
<function <lambda> at 0x7f49d24d7d50>
<type 'function'>
9

For loop and 'numpy.float64' object is not iterable error

I have a simple for loop to calculate RMS(root mean square) which is defined in sigma summation form:
for i in range(int(N-(n*periyot/delta)), N+1):
sum = np.sqrt((1 / N) * (sum((Cl[i]**2))))
Then I got this error:
TypeError: 'numpy.float64' object is not iterable
Here are some information about my definitons:
N=40000, n=10.0, periyot=6.451290, delta=0.005
Cl=[-21.91969 -12.452671 -7.928303 ..., -0.0833991 -0.0579686
-0.0823822]
Remove that sum, each element of Cl is a float so you can't possibly call sum on them:
>>> sum(2.4)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'float' object is not iterable
If you intend to invoke numpy's broadcasting to perform the power operation then you don't need to index the array.
The problem is that you overwrite sum function with sum variable. Try something like this:
my_sum = 0
for i in range(int(N-(n*periyot/delta)), N+1):
my_sum += np.sqrt((1 / N) * (sum((Cl[i]**2))))
Replicating your calculation, a bit simplified:
In [1]: Cl = np.array([-21.91969 , -12.452671 , -7.928303 , -0.0833991,-0.0579686,-0.0823822])
To calculate a sum in a loop, initial a value, and add to it at each iteration:
In [2]: res = 0
In [3]: for i in range(len(Cl)):
...: res += np.sqrt((1/3)*Cl[i]**2)
...:
In [4]: res
Out[4]: 24.551481812296061
Letting numpy calculate everything (slightly different)
In [5]: np.sqrt((1/3)*Cl**2).sum()
Out[5]: 24.551481812296064
Your range is a little more complicated, but I think that can be accommodated with:
s, e = int(N-(n*periyot/delta)), N+1 # start, end of range
for i in range(s, e): ....
or
np.sqrt((1/N) * Cl[s:e]**2).sum()
But I wonder why you started with that sum((Cl[i]**2))). Where you hoping to square a range of Cl values and then sum them? And repeat that for multiple ranges?
=============
There's a np.sum and a Python sum. Python sum works nicely with a list of numbers, such as those generated by a list comprehension:
In [6]: [np.sqrt((1/3)*Cl[i]**2) for i in range(len(Cl))]
Out[6]:
[12.655338922053147,
7.1895529539798462,
4.5774078712669173,
0.048150492835172518,
0.03346818681454574,
0.047563385346433583]
In [7]: sum([np.sqrt((1/3)*Cl[i]**2) for i in range(len(Cl))])
Out[7]: 24.551481812296061
The errors that result from trying to apply sum to a single value:
In [9]: sum(Cl[0])
....
TypeError: 'numpy.float64' object is not iterable
In [10]: sum(12.234)
...
TypeError: 'float' object is not iterable
In [11]: sum(Cl[:3]) # sum of several items
Out[11]: -42.300663999999998
==========
RMS = ( (1 / N ) * (Cl[1]^2 + Cl[2]^2 + Cl[3]^2 + ... Cl[N]^2) ) ^0.5
is expressed, for lists as:
rms = (1/n) * math.sqrt(sum([Cl[1]**2, Cl[2]**2, ....]))
rms = (1/n) * math.sqrt(sum([Cl[i]**2 for i in range(len(Cl))]))
rms = (1/n) * math.sqrt(sum([c**2 for c in Cl])) # iterate on Cl directly
rms = (1/n) * np.sqrt(np.sum(Cl**2)) # for array Cl

Categories

Resources