Approximation of 2-dimensional inverse Fourier transform with non-centred grid - python

I am trying to approximate the inverse of the Fourier transform in two dimensions. For the inverse Fourier transform, I use the definition,
$$
F^{-1}[\phi](x, y) = \dfrac{1}{4\pi} \int_{-\infty}^{\infty}\int_{-\infty}^{\infty} e^{-i (ux + wy} \phi(u, w) \text{d}u \text{d}w,
$$
for the inverse of the Fourier transform. The integral will be approximated by use of the trapezoidal integration rule. The grid structure is chosen as $x_j = x_0 + j\Delta_x$, $y_k = y_0 + k\Delta_y$, $u_n = \left(n - \frac{N}{2}\right) \Delta_u$ and $w_m = \left(m - \frac{N}{2}\right) \Delta_u$,
for $j, k, n, m, N \in \mathbb{N}$. By choosing the relations $\Delta_u \Delta_x = \dfrac{2\pi}{N}$ and $\Delta_w \Delta_y = \dfrac{2\pi}{N}$, the approximation of the two dimensional Fourier transform can be written in terms of the fast Fourier transform (FFT).
I implemented the results in Python, and tried to recover the bivariate normal density for different parameters $\sigma$ and $\mu$. As the normal density decays to zero, the grid can be constructed in terms of the mean and the variance.
Below, I have included the approximation in code:
from matplotlib import pyplot as plt
import matplotlib.patches as mpatches
import numpy as np
from scipy.fft import fft, fft2
from scipy.stats import multivariate_normal
i = complex(0.0, 1.0)
def chF(uu, ww, mu, sigma):
return np.exp(
i * (uu * mu[0] + ww * mu[1])
- 0.5 * (
uu**2 * sigma[0,0]
+ ww**2 * sigma[1,1]
+ uu * ww * (sigma[0,1] + sigma[1,0])
)
)
def pdf(xx, yy, mu, sigma):
norm = multivariate_normal(mu, sigma)
return norm.pdf(np.dstack([xx, yy]))
def powerSequence(a, N, offset=0):
# Returns a sequence of a ^ (n + offset) for n in N
return a ** (np.arange(N) + offset)
def powerSequence2d(a, N1, N2, offset=0):
# Returns a sequence of a ^ (n + j + offset) for n,j in N
return a ** (np.array([
j + np.arange(N2) for j in range(0, N1)]) + offset)
def createSimple1dGrid(c1, c2, c4=0, N:int=2**9, L:float=10):
x0 = c1 - L * np.sqrt(c2)
dx = 2 * L * np.sqrt(c2 + np.sqrt(c4)) / N
du = 2 * np.pi / (N * dx)
x = x0 + np.arange(N) * dx
u = (np.arange(N) - N / 2) * du
return x, u, du
def createSimpleGrid(c11, c21, c12, c22, c14=0, c24=0,
N:int=2**9, L1:float=10, L2:float=10):
x, u, du = createSimple1dGrid(c11, c12, c14, N=N, L=L1)
y, w , dw = createSimple1dGrid(c21, c22, c24, N=N, L=L2)
return x, y, u, w, du, dw
def inverseTransform(x, y, u, w, du, dw, phinn, N):
# Compute edges of the trapezoidal integration rule for 2-dimensional integrals.
e1 = np.exp(-i * (u[0] * x + w[0] * y)) * phinn[0, 0]
e2 = np.exp(-i * (u[0] * x + w[N-1] * y)) * phinn[0, N-1]
e3 = np.exp(-i * (u[N-1] * x + w[0] * y)) * phinn[N-1, 0]
e4 = np.exp(-i * (u[N-1] * x + w[N-1] * y)) * phinn[N-1, N-1]
e = 0.25 * (e1 + e2 + e3 + e4)
# Compute boundaries of the trapezoidal integration rule
# for 2-dimensional integrals. These can be written in
# one-dimensional Fourier transforms.
b1 = np.exp(-i * w[0] * y) * powerSequence(-1, N)\
* fft(phinn[:, 0] * np.exp(-i * x[0] * u))
b2 = np.exp(-i * w[N-1] * y) * powerSequence(-1, N)\
* fft(phinn[:, N-1] * np.exp(-i * x[0] * u))
b3 = np.exp(-i * u[0] * x) * powerSequence(-1, N)\
* fft(phinn[0, :] * np.exp(-i * y[0] * w))
b4 = np.exp(-i * u[N-1] * x) * powerSequence(-1, N)\
* fft(phinn[N-1, :] * np.exp(-i * y[0] * w))
b = 0.5 * (b1 + b2 + b3 + b4)
# Compute IFFT-2d
func = phinn * np.exp(-i *(x[0] * u + y[0] * w))
invFour2d = powerSequence2d(-1, N, N)\
* fft2(func)
invTransform = 1 / (4 * np.pi**2) * du * dw * (invFour2d - b + e)
return invTransform
def test_recover_normal_density_off_2d_non_centred():
# Initialize parameters of the two-dimensional normal density
s1, s2 = 4.0, 10.0
mu, sigma = np.array([10, 10]), np.array([
[s1, 0.0],
[0.0, s2]
])
# Create the Fourier grid
N, L = 2**9, 10
x, y, u, w, du, dw = createSimpleGrid(mu[0], mu[1], s1, s2, N=N, L1=L, L2=L)
uu, ww = np.meshgrid(u, w, indexing="ij")
xx, yy = np.meshgrid(x, y, indexing="ij")
phi = chF(uu, ww, mu, sigma)
expected = pdf(xx, yy, mu,sigma)
# Compute the inverse transform
result = inverseTransform(x, y, u, w, du, dw, phi, N)
# Plot results
_, ax = plt.subplots(subplot_kw={"projection": "3d"})
ax.plot_surface(xx, yy, result)
ax.plot_wireframe(xx, yy, expected, color="r", rstride=10, cstride=10)
# manually make legend for plot
col1_patch = mpatches.Patch(color="b", label="approximation")
col2_patch = mpatches.Patch(color="r", label="expected")
legends = [col1_patch, col2_patch]
ax.legend(handles=legends)
plt.show()
The resulting plot is given as:
bivariate normal approximation
The approximation is correct, however, misplaced on the grid. I thought this had something to do with the way the fft algorithm places the origin. However, trying different shifts from scipy.fft import fftshift, ifftshift only worsen the results.
Using different values for $\mu$ yielded results that were closer to the approximation or sometimes even further off.
How do I get the approximation result to coincide with the expected plot of the bivariate normal?

Related

I would like to make a colormap from a matrix, with making difference, which way we have got the negative values

We have two terms, two parameters (h, h0) and a variable T. We can define a certain value of T, where the difference of the two terms divided by L is equal to zero.
With this T value, we can calculate g0, gc and gs operators, and then witness1 and witness2.
Then we make this whole calculation with running parameters (from 0.03 to 3): h0=0.03, h=0.03; h0=0.06, h=0.03; ...; h0=3, h=0.03; h0=0.03, h=0.06; ...; h0=3, h=3
Then we select the witness with the lower value, and put it to the proper place in the matrix.
The values can be both positive and negative. We are interested in where will be the result negative, and from which witness we got the result. I have plotted the results from the matrix, the negative values with yellow, and the positive with white. Now I would like to plot the negative numbers from witness1 with blue, and the negative numbers from witness2 with yellow. Then I would like to norm the graph from 0-100 to 0-3.
How can I make this?
import math
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import colors
def g0operator(T, gamma, h, L):
k = -math.pi
d=2*math.pi/L
g0=0
while k <= math.pi:
g0 = g0 + (((math.tanh(math.sqrt(((gamma * math.sin(k)) ** 2) + (h - math.cos(k)) ** 2) / T))) * (
1 / (math.sqrt(
((gamma * math.sin(k)) ** 2) + (h - math.cos(k)) ** 2))) * ((h - math.cos(k))))
k = k + d
g0 = g0 / L
return g0
def gsoperator(T, gamma, h, L):
gs=0
k = -math.pi
d=2*math.pi/L
while k <= math.pi:
gs = gs + ((math.tanh(math.sqrt(((gamma * math.sin(k)) ** 2) + (h - math.cos(k)) ** 2) / T)) * (1 /
(math.sqrt(((gamma * math.sin(k)) ** 2) + (h - math.cos(k)) ** 2))) * (
((math.sin(k)) ** 2)))
k = k + d
gs = -(gs / L) * gamma
return gs
def gcoperator(T, gamma, h, L):
d=2*math.pi/L
gc=0
k = -math.pi
while k <= math.pi:
gc = gc + ((math.tanh(np.sqrt(((gamma * np.sin(k)) ** 2) + (h - np.cos(k)) ** 2) / T)) * (1 /
np.sqrt(((gamma * np.sin(k)) ** 2) + (h - np.cos(k)) ** 2))) * (np.cos(k) * ((np.cos(k) - h)))
k = k + d
gc = gc / L
return gc
def first_term(gamma, h, T, L):
c=-math.pi
d=2*math.pi/L
first=0
while c <= math.pi:
first = first + (math.tanh(math.sqrt(((gamma * math.sin(c)) ** 2) + (h - math.cos(c)) ** 2) / T) * math.sqrt(
((gamma * math.sin(c))
** 2) + (h - math.cos(c)) ** 2))
c=c+d
return first
def second_term(h, h0, gamma, L):
second=0
c=-math.pi
d=2*math.pi/L
while c<=math.pi:
second = second + abs(((math.cos(c) - h0) * (math.cos(c) - h) + ((gamma * math.sin(c)) ** 2)) / (
math.sqrt(((gamma * math.sin(c)) ** 2) + (h0 - math.cos(c)) ** 2)))
c = c + d
return second
def witness1(gs, g0, gc):
W = (-1 / 4) * (2 * abs(gs) + ((g0) ** 2) - ((gc) ** 2) + ((gs) ** 2) - 1)
return W
def witness2(gs, g0, gc):
w=-(1/4)*(-(g0**2)+(gc**2)-(gs**2)-1+2*math.sqrt((g0**2)+(gc**2)))
return w
h=0.03
h0=0.03
T=2.5
gamma=0.6
K=2.5
L=100000
matrix=np.zeros((100,100))
for i in range(100):
for b in range(100):
K = 2.5
T = 2.5
result=1
while abs(result) > 10 ** (-12):
c = -math.pi
first = first_term(gamma, h, T, L)
second = second_term(h, h0, gamma, L)
result = (first - second) / L
if result > 0:
T = T + K / 2
elif result < 0:
T = T - K / 2
K = K / 2
gc = gcoperator(T, gamma, h, L)
gs = gsoperator(T, gamma, h, L)
g0 = g0operator(T, gamma, h, L)
W = witness1(gs, g0, gc)
w = witness2(gs, g0, gc)
if W < w:
matrix[i, b] = W
else:
matrix[i, b] = w
h0=h0+0.03
h=h+0.03
h0=0.03
np.savetxt('matrixgamma=0.6.txt', matrix)
a=np.loadtxt('matrixgamma=0.6.txt')
cmap=colors.ListedColormap(['yellow', 'white'])
bounds=[-2, 0, 2]
norm=colors.BoundaryNorm(bounds, cmap.N)
img=plt.imshow(a, interpolation='nearest', origin='lower', cmap=cmap, norm=norm)
plt.colorbar(img, cmap=cmap, norm=norm, boundaries=bounds)
plt.show()
I'm sorry but I have simplified a little bit your witnessN functions, but I hope that you can adapt my solution, in which the key points are
to save the values for the grid evaluations of witnessN
to use Numpy's logical functions
to use the fact that Booleans can be used as 0 or 1 integers,
With respect to plotting, you can omit the plt.contourf statement, but I like the additional lines subdividing the fields.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import BoundaryNorm, ListedColormap
witness1 = lambda X, Y: np.sin(X-Y/2)
witness2 = lambda X, Y: np.cos(X/2+2*Y)
x = np.linspace(-6, 6, 241)
X, Y = np.meshgrid(x, x)
W = witness1(X, Y)
w = witness2(X, Y)
Wp, wp, Wgtw = W>0, w>0, W>w
values = np.where(Wgtw, W, w)
colors = np.where(np.logical_and(1-Wgtw, 1-wp), 0, 0)
colors += np.where(np.logical_and( Wgtw, 1-Wp), 1, 0)
colors += np.where(np.logical_and( Wgtw, Wp), 2, 0)
colors += np.where(np.logical_and(1-Wgtw, wp), 3, 0)
# change colors to your taste
# w<0, W<0, W>0, w>0
cm = ListedColormap([ 'y', 'b', 'r', 'g'])
norm=BoundaryNorm([-0.5, 0.5, 1.5, 2.5, 3.5], 4)
pc = plt.pcolor(X, Y, colors, norm=norm, cmap=cm)
ct = plt.contour(X, Y, values, [-.9, -0.1, 0.1, 0.9], colors='k')
lb = plt.clabel(ct)
cf = plt.contourf(X, Y, colors, norm=norm, cmap=cm, alpha=0.6)
cb = plt.colorbar(pc)
cb.set_ticks([0, 1, 2, 3])
cb.set_ticklabels(['w<0', 'W<0', 'W>0', 'w>0'])
plt.show()

Fitting curve with conditions

I'm trying to simulate an exoplanet transit and to determine its orbital characteristics with curve fitting. However, the intersection area between two circles needs to distinguish two cases: if the center of the smallest circle is in the biggest or not. This is a problem for scipy with the function curve_fit, calling an array in my function cacl_aire. The function transit simulates the smallest disc's evolution with time.
Here's my code:
import numpy as np
from matplotlib import pyplot as plt
from scipy.optimize import curve_fit
import xlrd
dt = 0.1
Vx = 0.08
Vy = 0
X0 = -5
Y0 = 0
R = 2
r = 0.7
X = X0
Y = Y0
doc = xlrd.open_workbook("transit data.xlsx")
feuille_1 = doc.sheet_by_index(0)
mag = [feuille_1.cell_value(rowx=k, colx=4) for k in range(115)]
T = [feuille_1.cell_value(rowx=k, colx=3) for k in range(115)]
def calc_aire(r, x, y):
D2 = x * x + y * y
if D2 >= (r + R)**2:
return 0
d = (r**2 - R**2 + D2) / (2 * (D2**0.5))
d2 = D2**0.5 - d
if abs(d) >= r:
return min([r * r * np.pi, R * R * np.pi])
H = (r * r - d * d)**0.5
As = np.arccos(d / r) * r * r - d * H
As2 = R * R * np.arccos(d2 / R) - d2 * H
return As + As2
def transit(t, r, X0, Y0, Vx, Vy):
return -calc_aire(r, X0 + Vx * t, Y0 + Vy * t)
best_vals = curve_fit(transit, T, mag)[0]
print('best_vals: {}'.format(best_vals))
plt.figure()
plt.plot(T, mag)
plt.draw()
I have the following error :
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all() with the line 28 :
if D2 >= (r + R)**2:
Here is my database:
https://drive.google.com/file/d/1SP12rrHGjjpHfKBQ0l3nVMJDIRCPlkuf/view?usp=sharing
I don't see any trick to solve my problem.

How can I plot a 3D graph of a multivariate integral function, and find its global minima

I have a cost function f(r, Q), which is obtained in the code below. The cost function f(r, Q) is a function of two variables r and Q. I want to plot the values of the cost function for all values of r and Q in the range given below and also find the global minimum value of f(r, Q).
The range of r and Q are respectively :
0 < r < 5000
5000 < Q < 15000
The plot should be in r, Q and f(r,Q) axis.
Code for the cost function:
from numpy import sqrt, pi, exp
from scipy import optimize
from scipy.integrate import quad
import numpy as np
mean, std = 295, 250
l = 7
m = 30
p = 15
w = 7
K = 100
c = 5
h = 0.001 # per unit per day
# defining Cumulative distribution function
def cdf(x):
cdf_eqn = lambda t: (1 / (std * sqrt(2 * pi))) * exp(-(((t - mean) ** 2) / (2 * std ** 2)))
cdf = quad(cdf_eqn, -np.inf, x)[0]
return cdf
# defining Probability density function
def pdf(x):
return (1 / (std * sqrt(2 * pi))) * exp(-(((x - mean) ** 2) / (2 * std ** 2)))
# getting the equation in place
def G(r, Q):
return K + c * Q \
+ w * (quad(cdf, 0, Q)[0] + quad(lambda x: cdf(r + Q - x) * cdf(x), 0, r)[0]) \
+ p * (mean * l - r + quad(cdf, 0, r)[0])
def CL(r, Q):
return (Q - r + mean * l - quad(cdf, 0, Q)[0]
- quad(lambda x: cdf(r + Q - x) * cdf(x), 0, r)[0]
+ quad(cdf, 0, r)[0]) / mean
def I(r, Q):
return h * (Q + r - mean * l - quad(cdf, 0, Q)[0]
- quad(lambda x: cdf(r + Q - x) * cdf(x), 0, r)[0]
+ quad(cdf, 0, r)[0]) / 2
def f(params):
r, Q = params
TC = G(r, Q)/CL(r, Q) + I(r, Q)
return TC
How to plot this function f(r,Q) in a 3D plot and also get the global minima or minimas and values of r and Q at that particular point.
Additionally, I already tried using scipy.optimize.minimize to minimise the cost function f(r, Q) but the problem I am facing is that, it outputs the results - almost same as the initial guess given in the parameters for optimize.minimize. Here is the code for minimizing the function:
initial_guess = [2500., 10000.]
result = optimize.minimize(f, initial_guess, bounds=[(1, 5000), (5000, 15000)], tol=1e-3)
print(result)
Output:
fun: 2712.7698818644253
hess_inv: <2x2 LbfgsInvHessProduct with dtype=float64>
jac: array([-0.01195986, -0.01273293])
message: b'CONVERGENCE: REL_REDUCTION_OF_F_<=_FACTR*EPSMCH'
nfev: 6
nit: 1
status: 0
success: True
x: array([ 2500.01209628, 10000.0127784 ])
The output x: array([ 2500.01209628, 10000.0127784 ]) - Which I doubt is the real answer and also it is almost same as the initial guess provided. Am I doing anything wrong in minimizing or is there any other way to do it? So I want to plot the cost function and look around for myself.
It could be great if I can have an interactive plot to play around with
My answer is concerned only with plotting but in the end I'll comment on the issue of minimax.
For what you need a 3D surface plot is, imho, overkill, I'll show you instead show the use of contourf and contour to have a good idea of what is going on with your function.
First, the code — key points:
your code, as is, cannot be executed in a vector context, so I wrote an explicit loop to compute the values,
due to Matplotib design, the x axis of matrix data is associated on columns, this has to be accounted for,
the results of the countour and contourf must be saved because they are needed for the labels and the color bar, respectively,
no labels or legends because I don't know what you are doing.
That said, here it is the code
import matplotlib.pyplot as plt
import numpy as np
from numpy import sqrt, pi, exp
from scipy.integrate import quad
mean, std = 295, 250
l, m, p = 7, 30, 15
w, K, c = 7, 100, 5
h = 0.001 # per unit per day
# defining Cumulative distribution function
def cdf(x):
cdf_eqn = lambda t: (1 / (std * sqrt(2 * pi))) * exp(-(((t - mean) ** 2) / (2 * std ** 2)))
cdf = quad(cdf_eqn, -np.inf, x)[0]
return cdf
# defining Probability density function
def pdf(x):
return (1 / (std * sqrt(2 * pi))) * exp(-(((x - mean) ** 2) / (2 * std ** 2)))
# getting the equation in place
def G(r, Q):
return K + c * Q \
+ w * (quad(cdf, 0, Q)[0] + quad(lambda x: cdf(r + Q - x) * cdf(x), 0, r)[0]) \
+ p * (mean * l - r + quad(cdf, 0, r)[0])
def CL(r, Q):
return (Q - r + mean * l - quad(cdf, 0, Q)[0]
- quad(lambda x: cdf(r + Q - x) * cdf(x), 0, r)[0]
+ quad(cdf, 0, r)[0]) / mean
def I(r, Q):
return h * (Q + r - mean * l - quad(cdf, 0, Q)[0]
- quad(lambda x: cdf(r + Q - x) * cdf(x), 0, r)[0]
+ quad(cdf, 0, r)[0]) / 2
# pulling it all together
def f(r, Q):
TC = G(r, Q)/CL(r, Q) + I(r, Q)
return TC
nr, nQ = 6, 11
r = np.linspace(0, 5000, nr)
Q = np.linspace(5000, 15000, nQ)
z = np.zeros((nr, nQ)) # r ←→ y, Q ←→ x
for i, ir in enumerate(r):
for j, jQ in enumerate(Q):
z[i, j] = f(ir, jQ)
print('%2d: '%i, ','.join('%8.3f'%v for v in z[i]))
fig, ax = plt.subplots()
cf = plt.contourf(Q, r, z)
cc = plt.contour( Q, r, z, colors='k')
plt.clabel(cc)
plt.colorbar(cf, orientation='horizontal')
ax.set_aspect(1)
plt.show()
and here the results of its execution
$ python cost.py
0: 4093.654,3661.777,3363.220,3120.073,2939.119,2794.255,2675.692,2576.880,2493.283,2426.111,2359.601
1: 4072.865,3621.468,3315.193,3068.710,2887.306,2743.229,2626.065,2528.934,2447.123,2381.802,2316.991
2: 4073.852,3622.443,3316.163,3069.679,2888.275,2744.198,2627.035,2529.905,2448.095,2382.775,2317.965
3: 4015.328,3514.874,3191.722,2939.397,2758.876,2618.292,2505.746,2413.632,2336.870,2276.570,2216.304
4: 3881.198,3290.628,2947.273,2694.213,2522.845,2394.095,2293.867,2213.651,2148.026,2098.173,2047.140
5: 3616.675,2919.726,2581.890,2352.015,2208.814,2106.289,2029.319,1969.438,1921.555,1887.398,1849.850
$
I can add that global minimum and global maximum are in the corners, while there are two sub-horizontal lines of local minima (lower line) and local maxima (upper line) in the approximate regions r ≈ 1000 and r ≈ 2000.

Solving and animating a Lorenz system using RK4

I'm working on a project which revolves around chaotic oscillators.
Now, I've just coded a Lorenz Attractor in Python using a Runge-Kutta of fourth order:
'''
Created on 19 feb. 2018
#author: judvuyst
'''
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.animation as animation
#Differential equations of a Lorenz System
def X(x, y, s):
return s * (y - x)
def Y(x, y, z, r):
return (-x) * z + r * x - y
def Z(x, y, z, b):
return x * y - b * z
#RK4 for the differential equations
def RK4(x, y, z, s, r, b, h):
k_1 = X(x, y, s)
l_1 = Y(x, y, z, r)
m_1 = Z(x, y, z, b)
k_2 = X((x + k_1 * h * 0.5), (y + l_1 * h * 0.5), s)
l_2 = Y((x + k_1 * h * 0.5), (y + l_1 * h * 0.5), (z + m_1 * h * 0.5), r)
m_2 = Z((x + k_1 * h * 0.5), (y + l_1 * h * 0.5), (z + m_1 * h * 0.5), b)
k_3 = X((x + k_2 * h * 0.5), (y + l_2 * h * 0.5), s)
l_3 = Y((x + k_2 * h * 0.5), (y + l_2 * h * 0.5), (z + m_2 * h * 0.5), r)
m_3 = Z((x + k_2 * h * 0.5), (y + l_2 * h * 0.5), (z + m_2 * h * 0.5), b)
k_4 = X((x + k_3 * h), (y + l_3 * h), s)
l_4 = Y((x + k_3 * h), (y + l_3 * h), (z + m_3 * h), r)
m_4 = Z((x + k_3 * h), (y + l_3 * h), (z + m_3 * h), b)
x += (k_1 + 2 * k_2 + 2 * k_3 + k_4) * h * (1/6)
y += (l_1 + 2 * l_2 + 2 * l_3 + l_4) * h * (1/6)
z += (m_1 + 2 * m_2 + 2 * m_3 + m_4) * h * (1/6)
return (x, y, z)
#Initial values and Parameters
x_0, y_0, z_0 = 1, 1, 1
s, r, b = 10, 28, 8/3
#RK4 iteration
x_list = [x_0]
y_list = [y_0]
z_list = [z_0]
h = 0
i = 0
while h < 0.15:
x = x_list[i]
y = y_list[i]
z = z_list[i]
position = RK4(x, y, z, s, r, b, h)
x_list.append(position[0])
y_list.append(position[1])
z_list.append(position[2])
h += 0.0000005
i += 1
#plotten
fig = plt.figure()
ax = fig.gca(projection = '3d')
x_array = np.array(x_list)
y_array = np.array(y_list)
z_array = np.array(z_list)
ax.plot(x_array, y_array, z_array, ',')
plt.show()
My question is: can I use this code to make a simulation of the attractor? Some sort of dot and line which follows these equations and leave behind a trail which at the end is the full attractor image.
I'm not really familiar with the animation module of matplotlib.
Thx in advance!
Kind regards
Julian

Create scipy curve fitting definitions for fourier series dynamically

I'd like to achieve a fourier series development for a x-y-dataset using numpy and scipy.
At first I want to fit my data with the first 8 cosines and plot additionally only the first harmonic. So I wrote the following two function defintions:
# fourier series defintions
tau = 0.045
def fourier8(x, a1, a2, a3, a4, a5, a6, a7, a8):
return a1 * np.cos(1 * np.pi / tau * x) + \
a2 * np.cos(2 * np.pi / tau * x) + \
a3 * np.cos(3 * np.pi / tau * x) + \
a4 * np.cos(4 * np.pi / tau * x) + \
a5 * np.cos(5 * np.pi / tau * x) + \
a6 * np.cos(6 * np.pi / tau * x) + \
a7 * np.cos(7 * np.pi / tau * x) + \
a8 * np.cos(8 * np.pi / tau * x)
def fourier1(x, a1):
return a1 * np.cos(1 * np.pi / tau * x)
Then I use them to fit my data:
# import and filename
filename = 'data.txt'
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
z, Ua = np.loadtxt(filename,delimiter=',', unpack=True)
tau = 0.045
# plot data
fig = plt.figure()
ax1 = fig.add_subplot(111)
p1, = plt.plot(z,Ua)
# fits
popt, pcov = curve_fit(fourier8, z, Ua)
# further plots
Ua_fit8 = fourier8(z,*popt)
Ua_fit1 = fourier1(z,popt[0])
p2, = plt.plot(z,Ua_fit8)
p3, = plt.plot(z,Ua_fit1)
plt.show()
which works as desired:
But know I got stuck making it generic for arbitary orders of harmonics, e.g. I want to fit my data with the first fifteen harmonics and plot the first three harmonics.
How could I achieve that without defining fourier1, fourier2, fourier3 ... , fourier15?
Here is the example text file data.txt to play with it:
1.000000000000000021e-03,2.794682735905079767e+02
2.000000000000000042e-03,2.792294526290349950e+02
2.999999999999999629e-03,2.779794770690260179e+02
4.000000000000000083e-03,2.757183469104809888e+02
5.000000000000000104e-03,2.734572167519349932e+02
5.999999999999999258e-03,2.711960865933900209e+02
7.000000000000000146e-03,2.689349564348440254e+02
8.000000000000000167e-03,2.714324329982829909e+02
8.999999999999999320e-03,2.739299095617229796e+02
1.000000000000000021e-02,2.764273861251620019e+02
1.100000000000000110e-02,2.789248626886010243e+02
1.199999999999999852e-02,2.799669443683339978e+02
1.299999999999999940e-02,2.795536311643609793e+02
1.400000000000000029e-02,2.791403179603880176e+02
1.499999999999999944e-02,2.787270047564149991e+02
1.600000000000000033e-02,2.783136915524419805e+02
1.699999999999999775e-02,2.673604939531239779e+02
1.799999999999999864e-02,2.564072963538059753e+02
1.899999999999999953e-02,2.454540987544889958e+02
2.000000000000000042e-02,2.345009011551709932e+02
2.099999999999999784e-02,1.781413355804160119e+02
2.200000000000000219e-02,7.637540203022649621e+01
2.299999999999999961e-02,-2.539053151996269975e+01
2.399999999999999703e-02,-1.271564650701519952e+02
2.500000000000000139e-02,-2.289223986203409993e+02
2.599999999999999881e-02,-2.399383538664330047e+02
2.700000000000000316e-02,-2.509543091125239869e+02
2.800000000000000058e-02,-2.619702643586149975e+02
2.899999999999999800e-02,-2.729862196047059797e+02
2.999999999999999889e-02,-2.786861050144170235e+02
3.099999999999999978e-02,-2.790699205877460258e+02
3.200000000000000067e-02,-2.794537361610759945e+02
3.300000000000000155e-02,-2.798375517344049968e+02
3.399999999999999550e-02,-2.802213673077350222e+02
3.500000000000000333e-02,-2.776516459805940258e+02
3.599999999999999728e-02,-2.750819246534539957e+02
3.700000000000000511e-02,-2.725122033263129993e+02
3.799999999999999906e-02,-2.699424819991720028e+02
3.899999999999999994e-02,-2.698567311502329744e+02
4.000000000000000083e-02,-2.722549507794930150e+02
4.100000000000000172e-02,-2.746531704087540220e+02
4.199999999999999567e-02,-2.770513900380149721e+02
4.299999999999999656e-02,-2.794496096672759791e+02
4.400000000000000439e-02,-2.800761105821779893e+02
4.499999999999999833e-02,-2.800761105821779893e+02
4.599999999999999922e-02,-2.800761105821779893e+02
4.700000000000000011e-02,-2.800761105821779893e+02
4.799999999999999406e-02,-2.788333722531979788e+02
4.900000000000000189e-02,-2.763478955952380147e+02
5.000000000000000278e-02,-2.738624189372779938e+02
5.100000000000000366e-02,-2.713769422793179729e+02
5.199999999999999761e-02,-2.688914656213580088e+02
5.299999999999999850e-02,-2.715383673199499981e+02
5.400000000000000633e-02,-2.741852690185419874e+02
5.499999999999999334e-02,-2.768321707171339767e+02
5.600000000000000117e-02,-2.794790724157260229e+02
5.700000000000000205e-02,-2.804776351435970128e+02
5.799999999999999600e-02,-2.798278589007459800e+02
5.899999999999999689e-02,-2.791780826578950041e+02
5.999999999999999778e-02,-2.785283064150449945e+02
6.100000000000000561e-02,-2.778785301721940186e+02
6.199999999999999956e-02,-2.670252067497989970e+02
6.300000000000000044e-02,-2.561718833274049985e+02
6.400000000000000133e-02,-2.453185599050100052e+02
6.500000000000000222e-02,-2.344652364826150119e+02
6.600000000000000311e-02,-1.780224826854309867e+02
6.700000000000000400e-02,-7.599029851345700592e+01
6.799999999999999101e-02,2.604188565851649884e+01
6.900000000000000577e-02,1.280740698304900036e+02
7.000000000000000666e-02,2.301062540024639986e+02
7.100000000000000755e-02,2.404921248105050040e+02
7.199999999999999456e-02,2.508779956185460094e+02
7.299999999999999545e-02,2.612638664265870148e+02
7.400000000000001021e-02,2.716497372346279917e+02
7.499999999999999722e-02,2.773051723900500178e+02
7.599999999999999811e-02,2.782301718928520131e+02
7.699999999999999900e-02,2.791551713956549747e+02
7.799999999999999989e-02,2.800801708984579932e+02
7.900000000000001465e-02,2.810051704012610116e+02
8.000000000000000167e-02,2.785107135689390248e+02
8.099999999999998868e-02,2.760162567366169810e+02
8.200000000000000344e-02,2.735217999042949941e+02
8.300000000000000433e-02,2.710273430719730072e+02
8.399999999999999134e-02,2.706544464035359852e+02
8.500000000000000611e-02,2.724031098989830184e+02
8.599999999999999312e-02,2.741517733944299948e+02
8.699999999999999400e-02,2.759004368898779944e+02
8.800000000000000877e-02,2.776491003853250277e+02
8.899999999999999578e-02,2.783445666445250026e+02
8.999999999999999667e-02,2.790400329037249776e+02
I would approach this by defining a single function fourier that accepts a variable number of arguments, using a loop through the cosines to evaluate the function for each of your input points:
def fourier(x, *a):
ret = a[0] * np.cos(np.pi / tau * x)
for deg in range(1, len(a)):
ret += a[deg] * np.cos((deg+1) * np.pi / tau * x)
return ret
Because this function takes a variable number of arguments, you need to provide a starting position of the correct dimensionality as a hint to the curve_fit function so it knows the number of cosines to use. In the example you provide with 8 cosines:
popt, pcov = curve_fit(fourier, z, Ua, [1.0] * 8)
Now, the use case you're asking about (fitting with 15 harmonics and plotting the first three) can be accomplished with:
# Fit with 15 harmonics
popt, pcov = curve_fit(fourier, z, Ua, [1.0] * 15)
# Plot data, 15 harmonics, and first 3 harmonics
fig = plt.figure()
ax1 = fig.add_subplot(111)
p1, = plt.plot(z,Ua)
p2, = plt.plot(z, fourier(z, *popt))
p3, = plt.plot(z, fourier(z, popt[0], popt[1], popt[2]))
plt.show()
based on this thread.
Passing additional arguments using scipy.optimize.curve_fit?
# import and filename
filename = 'data.txt'
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
z, Ua = np.loadtxt(filename,delimiter=',', unpack=True)
tau = 0.045
def make_fourier(na, nb):
def fourier(x, *a):
ret = 0.0
for deg in range(0, na):
ret += a[deg] * np.cos((deg+1) * np.pi / tau * x)
for deg in range(na, na+nb):
ret += a[deg] * np.sin((deg+1) * np.pi / tau * x)
return ret
return fourier
popt, pcov = curve_fit(make_fourier(15,15), z, Ua, [0.0]*30)
# Plot data, 15 harmonics, and first 3 harmonics
fig = plt.figure()
ax1 = fig.add_subplot(111)
p1, = plt.plot(z,Ua)
p2, = plt.plot(z, (make_fourier(15,15))(z, *popt))
#p3, = plt.plot(z, (make_fourier(8,8))(z, popt[0], popt[1], popt[2]))
plt.show()

Categories