2025-07-13 08:55:18 +08:00

155 lines
5.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- coding: utf-8 -*-
"""
Created on Wed Feb 20 19:24:58 2019
@author: Vinícius Rezende Carvalho
"""
import numpy as np
def VMD(f, alpha, tau, K, DC, init, tol):
"""
u,u_hat,omega = VMD(f, alpha, tau, K, DC, init, tol)
Variational mode decomposition
Python implementation by Vinícius Rezende Carvalho - vrcarva@gmail.com
code based on Dominique Zosso's MATLAB code, available at:
https://www.mathworks.com/matlabcentral/fileexchange/44765-variational-mode-decomposition
Original paper:
Dragomiretskiy, K. and Zosso, D. (2014) Variational Mode Decomposition,
IEEE Transactions on Signal Processing, 62(3), pp. 531544. doi: 10.1109/TSP.2013.2288675.
Input and Parameters:
---------------------
f - the time domain signal (1D) to be decomposed
alpha - the balancing parameter of the data-fidelity constraint
tau - time-step of the dual ascent ( pick 0 for noise-slack )
K - the number of modes to be recovered
DC - true if the first mode is put and kept at DC (0-freq)
init - 0 = all omegas start at 0
1 = all omegas start uniformly distributed
2 = all omegas initialized randomly
tol - tolerance of convergence criterion; typically around 1e-6
Output:
-------
u - the collection of decomposed modes
u_hat - spectra of the modes
omega - estimated mode center-frequencies
"""
if len(f)%2:
f = f[:-1]
# Period and sampling frequency of input signal
fs = 1./len(f)
ltemp = len(f)//2
fMirr = np.append(np.flip(f[:ltemp],axis = 0),f)
fMirr = np.append(fMirr,np.flip(f[-ltemp:],axis = 0))
# Time Domain 0 to T (of mirrored signal)
T = len(fMirr)
t = np.arange(1,T+1)/T
# Spectral Domain discretization
freqs = t-0.5-(1/T)
# Maximum number of iterations (if not converged yet, then it won't anyway)
Niter = 500
# For future generalizations: individual alpha for each mode
Alpha = alpha*np.ones(K)
# Construct and center f_hat
f_hat = np.fft.fftshift((np.fft.fft(fMirr)))
f_hat_plus = np.copy(f_hat) #copy f_hat
f_hat_plus[:T//2] = 0
# Initialization of omega_k
omega_plus = np.zeros([Niter, K])
if init == 1:
for i in range(K):
omega_plus[0,i] = (0.5/K)*(i)
elif init == 2:
omega_plus[0,:] = np.sort(np.exp(np.log(fs) + (np.log(0.5)-np.log(fs))*np.random.rand(1,K)))
else:
omega_plus[0,:] = 0
# if DC mode imposed, set its omega to 0
if DC:
omega_plus[0,0] = 0
# start with empty dual variables
lambda_hat = np.zeros([Niter, len(freqs)], dtype = complex)
# other inits
uDiff = tol+np.spacing(1) # update step
n = 0 # loop counter
sum_uk = 0 # accumulator
# matrix keeping track of every iterant // could be discarded for mem
u_hat_plus = np.zeros([Niter, len(freqs), K],dtype=complex)
#*** Main loop for iterative updates***
while ( uDiff > tol and n < Niter-1 ): # not converged and below iterations limit
# update first mode accumulator
k = 0
sum_uk = u_hat_plus[n,:,K-1] + sum_uk - u_hat_plus[n,:,0]
# update spectrum of first mode through Wiener filter of residuals
u_hat_plus[n+1,:,k] = (f_hat_plus - sum_uk - lambda_hat[n,:]/2)/(1.+Alpha[k]*(freqs - omega_plus[n,k])**2)
# update first omega if not held at 0
if not(DC):
omega_plus[n+1,k] = np.dot(freqs[T//2:T],(abs(u_hat_plus[n+1, T//2:T, k])**2))/np.sum(abs(u_hat_plus[n+1,T//2:T,k])**2)
# update of any other mode
for k in np.arange(1,K):
#accumulator
sum_uk = u_hat_plus[n+1,:,k-1] + sum_uk - u_hat_plus[n,:,k]
# mode spectrum
u_hat_plus[n+1,:,k] = (f_hat_plus - sum_uk - lambda_hat[n,:]/2)/(1+Alpha[k]*(freqs - omega_plus[n,k])**2)
# center frequencies
omega_plus[n+1,k] = np.dot(freqs[T//2:T],(abs(u_hat_plus[n+1, T//2:T, k])**2))/np.sum(abs(u_hat_plus[n+1,T//2:T,k])**2)
# Dual ascent
lambda_hat[n+1,:] = lambda_hat[n,:] + tau*(np.sum(u_hat_plus[n+1,:,:],axis = 1) - f_hat_plus)
# loop counter
n = n+1
# converged yet?
uDiff = np.spacing(1)
for i in range(K):
uDiff = uDiff + (1/T)*np.dot((u_hat_plus[n,:,i]-u_hat_plus[n-1,:,i]),np.conj((u_hat_plus[n,:,i]-u_hat_plus[n-1,:,i])))
uDiff = np.abs(uDiff)
#Postprocessing and cleanup
#discard empty space if converged early
Niter = np.min([Niter,n])
omega = omega_plus[:Niter,:]
idxs = np.flip(np.arange(1,T//2+1),axis = 0)
# Signal reconstruction
u_hat = np.zeros([T, K],dtype = complex)
u_hat[T//2:T,:] = u_hat_plus[Niter-1,T//2:T,:]
u_hat[idxs,:] = np.conj(u_hat_plus[Niter-1,T//2:T,:])
u_hat[0,:] = np.conj(u_hat[-1,:])
u = np.zeros([K,len(t)])
for k in range(K):
u[k,:] = np.real(np.fft.ifft(np.fft.ifftshift(u_hat[:,k])))
# remove mirror part
u = u[:,T//4:3*T//4]
# recompute spectrum
u_hat = np.zeros([u.shape[1],K],dtype = complex)
for k in range(K):
u_hat[:,k]=np.fft.fftshift(np.fft.fft(u[k,:]))
return u, u_hat, omega