0

I am working on an optimization problem using 2D Fourier series, basically I am trying to implement this formula:

2D Fourier series of order m

and I wanted to have it implemented in my script as a function of the order m, in the sense that provided m I can fit some data with a m-order series and obtain the best coefficients of the series (except for C_00 that I already know)

Anyway I am struggling when it comes to use the curve_fit function from Scipy with a model with a variable number of parameters (eg. if m=1 I have 8 coefficients + 2 angular velocities, if m=2 I have 24 coefficients + 2 angular velocities and so on)

What I have written so far is the following:

c00=50
def fourier(x, v, w1, w2):
   f=c00
   k=0
   for i in range(1, m+1):
       f=f+v[k]*np.cos(i*w1*x)+v[k+1]*np.sin(i*w1*x)
       k+=2
   for j in range(1, m+1):
      for i in range(-m, m+1):
           f=f+v[k]*np.cos(i*w1*x+j*w2*x)+v[k+1]*np.sin(i*w1*x+j*w2*x)
           k+=2
return f

time = [] #some x data I get from a txt file
energy = [] # some y data I get from a txt file
popt, pcov = curve_fit(fourier, time, energy)

But when I run the script it raises the following error:

File "/home/arcalinux/Desktop/fourier_series.py", line 63, in fourier f=f+v[k]np.cos(iw1x)+v[k+1]np.sin(iw1x) IndexError: invalid index to scalar variable.

I also tried this other function:

def fourier2(args):
   f=c00
   k=3
   for i in range(1, m+1):
       f=f+args[k]*np.cos(i*args[1]*args[0])+args[k+1]*np.sin(i*args[1]*args[0])
       k+=2
   for j in range(1, m+1):
       for i in range(-m, m+1):
          f=f+args[k]*np.cos(i*args[0]*x+j*args[1]*x)+
                          +args[k+1]*np.sin(i*args[0]*x+j*args[1]*x)
          k*=2
   return f                                                 

but when I run curve_fit I have:

ValueError: Unable to determine number of fit parameters.

please notice that the fourier series function works correctly, for example (random numbers):

v=[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
m=2
x=2
w1=3
w2=4
print(fourier(x,v,w1,w2))

49.136961626348295

And the same goes for fourier2

How can I solve this problem? Thank you!

the whole code is here:

#importing libraries
import numpy as np
import math
from scipy.optimize import curve_fit
from statistics import mean
import matplotlib.pyplot as plt


m=2 #order of series
number_of_coeff=(2*m+1)**2 - 1 
N=number_of_coeff
#build series
coefficients=[]

#first part, j=0

for i in range(1,m+1):
   coefficients.append('c'+str(i)+'0')
   coefficients.append('s'+str(i)+'0')

#second part

for j in range(1, m+1):
   for i in range(-m, 0):
       coefficients.append('g'+str(-i)+str(j))
       coefficients.append('t'+str(-i)+str(j))
   for i in range(0, m+1):
       coefficients.append('c'+str(i)+str(j))
       coefficients.append('s'+str(i)+str(j))
print(len(coefficients))
v=np.ones(N)
m=2
x=2
w1=3
w2=4
arg=[2,3,4,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
arg3=[3,4,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]

c00=50
def fourier(x, v, w1, w2):
    print(type(v))
    print('v is ',v)
    f=c00
    k=0
    print(v[0])
    for i in range(1, m+1):
        f=f+v[k]*np.cos(i*w1*x)+v[k+1]*np.sin(i*w1*x)
    
        k+=2
    for j in range(1, m+1):
        for i in range(-m, m+1):
            f=f+v[k]*np.cos(i*w1*x+j*w2*x)+
                v[k+1]*np.sin(i*w1*x+j*w2*x)
            k+=2
return f
def fourier2(args):
    f=c00
    k=3
    for i in range(1, m+1):
        f=f+args[k]*np.cos(i*args[1]*args[0])+
            args[k+1]*np.sin(i*args[1]*args[0])
        k+=2
    for j in range(1, m+1):
        for i in range(-m, m+1):
            f=f+args[k]*np.cos(i*args[1]*args[0]+
                j*args[2]*args[0])+
                args[k+1]*np.sin(i*args[1]*args[0]+
                j*args[2]*args[0])
        k+=2
#print('number of args =',len(args))
return f
def fourier3(x,args):
    f=c00
    k=2
    for i in range(1, m+1):
        f=f+args[k]*np.cos(i*args[0]*x)+
            args[k+1]*np.sin(i*args[0]*x)
        k+=2
    for j in range(1, m+1):
        for i in range(-m, m+1):
            f=f+args[k]*np.cos(i*args[0]*x+j*args[1]*x)+
                args[k+1]*np.sin(i*args[0]*x+j*args[1]*x)
            k+=2
#print('number of args =',len(args))
return f
#print(fourier2(arg))
#print(fourier3(x,arg3))
#print(fourier(2,v,3,4))

#getting data

time = [] 
energy = [] 
filepath=''
filename=''
with open(filepath+filename, 'r') as f:
    for line in f: # iterate through the file
        if not line: continue 
        t, e = line.split() 
        time.append(float(t)),
        energy.append(float(e)) # add a value
c00=mean(energy)
time=np.array(time)
popt, pcov = curve_fit(fourier, time, energy)
print(popt)
Gilberto
  • 3
  • 3
  • Hello, thank you first of all. if I only run the script it says v is and v is 1.0 if I add also 'print(fourier(x,v,w1,w2))' it says v is and v is [1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.], so it seems like it is not getting v as an input vector as I'd like but as a scalar? – Gilberto Jul 15 '22 at 14:31
  • No problem! Can you share your whole script so I can help you find what's going wrong ? – Pauuuuuuul Jul 15 '22 at 14:36
  • @Pauuuuuuul sure, how can I share it with you? Sorry but I'm new to stackoverflow as an active user – Gilberto Jul 15 '22 at 14:38
  • If it's not too long, you can copy paste it in your question (click on the "edit" button) – Pauuuuuuul Jul 15 '22 at 14:40
  • @Pauuuuuuul I just did it, there could be some typo due to the indentation I had to do manually when pasting, but I hope it's ok! thank you! as you can see there are a few tries... – Gilberto Jul 15 '22 at 14:51

1 Answers1

0

According to scipy documentation, you can provide to the curve_fit function the p0 parameter, which is your initial guess to your function's parameters (here, v, w1 and w2).

If you don't, the function will assume an initial value of 1 for your parameters, which is why you get a scalar instead of an array for v.

So what you could do before calling curve_fit is:

initial_v = ...  # Replace with value (an array)
initial_w1 = ... # Replace with value (a scalar)
initial_w2 = ... # Replace with value (a scalar)

popt, pcov = curve_fit(fourier, time, energy, p0=[initial_v, initial_w1, initial_w2])
Pauuuuuuul
  • 253
  • 3
  • 12
  • 1
    thank you very much, I saw this [link](https://stackoverflow.com/questions/36620025/pass-array-as-argument-in-python) and I rearranged the function a little bit (actually using the model from _fourier3_ in the original code), then I fixed the issue regarding the number parameters just as you said, using the _p0_ value (actually I'm just giving a vector of ones of the adequate lenght as _p0_, which I think it's the same as your suggestion). Now it seems to work for different values of _m_ ! Thank you very much! – Gilberto Jul 15 '22 at 15:42