У мене виникають проблеми з генерацією набору стаціонарних кольорових часових рядів, враховуючи їх матрицю коваріації (їх спектральні щільності потужності (PSD) та спектральні щільності спектру потужності (CSD)).
Я знаю, що, враховуючи два часові ряди та , я можу оцінити їх спектральні щільності потужності (PSD) та перехресні спектральні щільності (CSD), використовуючи багато широко доступних підпрограм, таких як і функції в Matlab тощо. PSD і CSD складають матрицю коваріації:
psd()
csd()
Що станеться, якщо я хочу зробити зворотний? З огляду на матрицю коваріації, як я можу генерувати реалізацію та ?
Будь ласка, включіть будь-яку фонову теорію або вкажіть будь-які існуючі інструменти, які це роблять (все, що в Python було б чудово).
Моя спроба
Нижче описується те, що я спробував, і проблеми, які я помітив. Це трохи прочитане, і вибачте, якщо він містить терміни, які були неправильно використані. Якщо те, що є помилковим, можна вказати, це було б дуже корисно. Але моє питання - це жирним шрифтом вище
- PSD і CSD можуть бути записані як величина очікування (або середня кількість ансамблів) продуктів перетворень Фур'є часового ряду. Отже, матрицю коваріації можна записати у вигляді:
де
- Коваріаційна матриця - це ермітова матриця, яка має реальні власні значення, що є або нульовими, або позитивними. Отже, його можна розкласти на
де - діагональна матриця, ненульові елементи якої є квадратними коренями власних значень ; - матриця, стовпці якої є ортонормальними власними векторами ;- матриця ідентичності.
- Матриця ідентичності записується як
де
і - некорельовані та складні частотні ряди з нульовою середньою та одиничною дисперсією.
- Використовуючи 3. в 2., а потім порівняйте з 1. Перетвореннями Фур'є часового ряду є:
- Тоді ряд може бути отриманий за допомогою таких процедур, як зворотне швидке перетворення Фур'є.
Я написав процедуру в Python для цього:
def get_noise_freq_domain_CovarMatrix( comatrix , df , inittime , parityN , seed='none' , N_previous_draws=0 ) :
"""
returns the noise time-series given their covariance matrix
INPUT:
comatrix --- covariance matrix, Nts x Nts x Nf numpy array
( Nts = number of time-series. Nf number of positive and non-Nyquist frequencies )
df --- frequency resolution
inittime --- initial time of the noise time-series
parityN --- is the length of the time-series 'Odd' or 'Even'
seed --- seed for the random number generator
N_previous_draws --- number of random number draws to discard first
OUPUT:
t --- time [s]
n --- noise time-series, Nts x N numpy array
"""
if len( comatrix.shape ) != 3 :
raise InputError , 'Input Covariance matrices must be a 3-D numpy array!'
if comatrix.shape[0] != comatrix.shape[1] :
raise InputError , 'Covariance matrix must be square at each frequency!'
Nts , Nf = comatrix.shape[0] , comatrix.shape[2]
if parityN == 'Odd' :
N = 2 * Nf + 1
elif parityN == 'Even' :
N = 2 * ( Nf + 1 )
else :
raise InputError , "parityN must be either 'Odd' or 'Even'!"
stime = 1 / ( N*df )
t = inittime + stime * np.arange( N )
if seed == 'none' :
print 'Not setting the seed for np.random.standard_normal()'
pass
elif seed == 'random' :
np.random.seed( None )
else :
np.random.seed( int( seed ) )
print N_previous_draws
np.random.standard_normal( N_previous_draws ) ;
zs = np.array( [ ( np.random.standard_normal((Nf,)) + 1j * np.random.standard_normal((Nf,)) ) / np.sqrt(2)
for i in range( Nts ) ] )
ntilde_p = np.zeros( ( Nts , Nf ) , dtype=complex )
for k in range( Nf ) :
C = comatrix[ :,:,k ]
if not np.allclose( C , np.conj( np.transpose( C ) ) ) :
print "Covariance matrix NOT Hermitian! Unphysical."
w , V = sp_linalg.eigh( C )
for m in range( w.shape[0] ) :
w[m] = np.real( w[m] )
if np.abs(w[m]) / np.max(w) < 1e-10 :
w[m] = 0
if w[m] < 0 :
print 'Negative eigenvalue! Simulating unpysical signal...'
ntilde_p[ :,k ] = np.conj( np.sqrt( N / (2*stime) ) * np.dot( V , np.dot( np.sqrt( np.diag( w ) ) , zs[ :,k ] ) ) )
zerofill = np.zeros( ( Nts , 1 ) )
if N % 2 == 0 :
ntilde = np.concatenate( ( zerofill , ntilde_p , zerofill , np.conj(np.fliplr(ntilde_p)) ) , axis = 1 )
else :
ntilde = np.concatenate( ( zerofill , ntilde_p , np.conj(np.fliplr(ntilde_p)) ) , axis = 1 )
n = np.real( sp.ifft( ntilde , axis = 1 ) )
return t , n
Я застосував цю процедуру до PSD та CSD, аналітичні вирази яких були отримані з моделювання деякого детектора, з яким я працюю. Важливо те, що на всіх частотах вони складають коваріаційну матрицю (ну принаймні вони передають усі ці if
твердження в рутину). Коваріаційна матриця дорівнює 3х3. Три часові серії були сформовані приблизно в 9000 разів, а розрахункові PSD та CSD, усереднені за всіма цими реалізаціями, наведені нижче з аналітичними. Незважаючи на те, що загальні форми узгоджуються, на певних частотах у CSD помітні шумні особливості (рис. 2). Після крупного плану навколо піків у PSD (рис.3) я помітив, що PSD насправді недооцінені, а також те, що шумні особливості в центральних дисках виникають приблизно на тих же частотах, що і піки в PSD. Я не думаю, що це збіг обставин, і що якимось чином витікає сила з PSD в ЦДД. Я б очікував, що криві лежать одна над одною, маючи це безліч реалізацій даних.