Defining the adjusting error as
$$
E(\theta_0,\omega) = \sum_{k=1}^n\left(\theta_k-\theta_0\cos(\omega t_k)\right)^2
$$
the error stationary points are solutions for
$$
\nabla E = \begin{cases}
E_{\theta_0} = \sum_{k=1}^n\left(\theta_k-\theta_0\cos(\omega t_k)\right)\cos(\omega t_k) = 0\\
E_{\omega} = \sum_{k=1}^n\left(\theta_k-\theta_0\cos(\omega t_k)\right)\sin(\omega t_k) = 0
\end{cases}
$$
Those two nonlinear equations should be solved regarding $\theta_0,\omega$ using an iterative procedure like Newton's. This procedure can be described as
$$
(\theta_0,\omega)_{k+1} = (\theta_0,\omega)_k-J_k^{-1}\cdot (E_{\theta_0},E_{\omega})_k
$$
with
$$
J(\theta_0,\omega) = \nabla^2 E(\theta_0,\omega)
$$
Follows a basic python script which implements the procedure
import numpy as np
import random as rd
import matplotlib as mp
n = 100
sigma = 0.5
theta = 1
omega = 3
tkmax = 10.0
niter = 50
error = 0.0001
def noise(sigma):
return (rd.random()-0.5)*sigma
thetak = []
tk = list(np.linspace(0.0,tkmax,n))
for k in range(n):
thetak.append(theta*np.cos(omega*tk[k])+noise(sigma))
mp.pyplot.plot(tk,thetak,'o', color = 'Red')
"""
Data is contained in tk and thetak. This signal is shown in red
"""
def dE(tk,thetak,theta,omega):
dEtheta = 0.0
dEomega = 0.0
for k in range(n):
wk = omega*tk[k]
dEtheta += thetak[k]*np.cos(wk)-0.5*theta*(np.cos(2*wk)+1)
dEomega += thetak[k]*np.sin(wk)-0.5*theta*np.sin(2*wk)
return np.array([dEtheta,dEomega])
def d2E(tk,thetak,theta,omega):
dEthetaomega = 0
dEtheta2 = 0
dEomega2 = 0
for k in range(n):
wk = omega*tk[k]
dEthetaomega += (thetak[k]*np.sin(wk)-theta*np.sin(2*wk))
dEtheta2 += (np.cos(2*wk)+1)
dEomega2 += (thetak[k]*np.cos(wk)-theta*np.cos(2*wk))
a11 = -0.5*dEtheta2
a12 = omega*dEthetaomega
a21 = a12
a22 = omega*dEomega2
return np.array([[a11,a12],[a21,a22]])
"""
Main iterative procedure
"""
eka = np.array([theta+noise(sigma),omega+noise(sigma)])
print("initial values theta0 = ",eka[0], " omega = ", eka[1])
for j in range(niter):
invd2E = np.linalg.inv(d2E(tk,thetak,eka[0],eka[1]))
dEk = dE(tk,thetak,eka[0],eka[1])
dek = np.dot(invd2E,dEk)
if max(abs(dek)) < error:
break
ek = eka - dek
eka = ek
print("final values theta0 = ",eka[0], " omega = ", eka[1])
print(" in ", j," iterations within an error ", error)
theta0 = []
for j in range(n):
theta0.append(ek[0]*np.cos(ek[1]*tk[j]))
"""
The adjusted function y = eka[0]*cos(eka[1]*tk) is shown in green
"""
mp.pyplot.plot(tk,theta0,'o-',color='Black')
Follow a plot showing in red the data points and in black the adjusting solution.

`
NOTE
Depending on the noise added, for large sigma, strange solutions may appear due to the aliasing phenomena. This problem can be handled by introducing suitable terms into the error definition.