def check_spatial_accuracy(self, dmd, kwargs, X, freqs, rtol): dmd = DMD(**kwargs) dmd.fit(X) #sort: highest power first f, P = dmd.spectrum(sort='power', sort_modes=True, plotfig=False) # two modes for each actual signal last_expected_mode = 2 * len(freqs) original_channels = X.shape[0] Phi = np.abs(dmd.Phi[:original_channels]) #check majority power within expected modes assert (np.isclose(np.sum(Phi), np.sum(Phi[:, -last_expected_mode:]), rtol=.01)) #assert each chan increasing power like input signal for i in range(last_expected_mode): last = 0 for val in Phi[:, -(i + 1)]: #check values are nondecreasing for chans assert (val >= last) last = val
def dmd(self, request): """ Setup dmd fit on sine wave before any method of this class""" dt = .01 kwargs = { 'dt': dt, 'stack_factor': 'estimate', 'scale_modes': True, 'use_optimal_SVHT': True } dmd = DMD(**kwargs) yield dmd print("Tear down dmd instance") del dmd
def dmd_fitted(self, request): """ Setup dmd fit on sine wave before any method of this class""" N = 1000 t = np.linspace(0, 10, N) freqs = [3.3] size = 1 dt = .01 x = buildX(freqs, t, size=size) kwargs = { 'dt': dt, 'stack_factor': 'estimate', 'scale_modes': True, 'use_optimal_SVHT': True } dmd = DMD(**kwargs).fit(x) yield dmd print("Tear down dmd instance") del dmd
def check_spectrum_elements(self, dmd, kwargs, X, freqs, rtol): dmd = DMD(**kwargs) dmd.fit(X) #sort: highest power first f, P = dmd.spectrum(sort='power', plotfig=False) # two modes for each actual signal last_expected_mode = 2 * len(freqs) #check that most dominant signals # were contained in original signal for f_ind in f[-last_expected_mode:]: freqs_idx = dmd._find_nearest_idx(np.asarray(freqs), f_ind) assert (np.isclose(f_ind, freqs[freqs_idx], rtol=rtol)) #check each original signal contained in final for freq in freqs: f_idx = dmd._find_nearest_idx(np.asarray(f), freq) assert (np.isclose(freq, f[f_idx], rtol=rtol))
def check_frequency_dynamics(self, dmd, kwargs, X, freqs, rtol, growth_decay_list): """Test growth and decay of modes given signal of known dynamics""" dmd = DMD(**kwargs) dmd.fit(X) f, P = dmd.spectrum(sort='power', sort_modes=True) last_expected_mode = 2 * len(freqs) #examine only dominant modes lambdas = dmd.lambdas[-last_expected_mode:] P = P[-last_expected_mode:] f = f[-last_expected_mode:] for freq, growth_decay in zip(freqs, growth_decay_list): f_idx = dmd._find_nearest_idx(np.asarray(f), freq) lambda_i = lambdas[f_idx] if growth_decay is 'stable': assert (np.isclose(abs(lambda_i), 1.0)) elif growth_decay is 'decay': assert (abs(lambda_i) < 1.0) elif growth_decay is 'growth': assert (abs(lambda_i) > 1.0)
import seaborn as sns from matplotlib import pyplot as plt done = False Delta = system("pystorm Delta") Xub_extreme, Xlb_extreme = Delta.stateBounds() Uub, Ulb = Delta.inputBounds() t = 0 tmulti = 50 n_basis = 6 n = Xub_extreme.size m = Uub.size n0 = 20 #n0 > nk + m KPmodel = Koopman(Xub_extreme, Xlb_extreme, Uub, Ulb, n_basis) KCmodel = Koopman2(Xub_extreme, Xlb_extreme, Uub, Ulb, n_basis) DMDmodel = DMD(Xub_extreme, Xlb_extreme, Uub, Ulb, n_basis) NARXmodel = NARX(Xub_extreme, Xlb_extreme, Uub, Ulb, n_basis) MAmodel = MovingAnchor(Xub_extreme, Xlb_extreme, Uub, Ulb, n_basis) Xub_scaled = KPmodel.scale(Xub_extreme) Xlb_scaled = KPmodel.scale(Xlb_extreme) Uub_scaled = KPmodel.scale(Uub, state_scale=False) Ulb_scaled = KPmodel.scale(Ulb, state_scale=False) KMPC = MPC(Uub_scaled, Ulb_scaled, Xub_soft=Xub_scaled, Xlb_soft=Xlb_scaled) u = [] x0 = 0 * np.ones(n) actions_north3 = [] actions_north2 = [] actions_north1 = [] actions_central = [] actions_south = [] settings = np.ones(5)
def __init__(self, kwargs, method='frequency_bin', subsample=False, L=10, cutoff_mode_thresh=None, freq_space='default', freq_gradation='default', plot_levels=False, subtraction=False, excess_reconstruction=False, power_denoise=False, power_denoise_thresh=.05, lower_lim=0.0, linear_freq_bins=False, l2_norm=False): """ Multi-Resolution Dynamic Mode Decomposition (mrDMD). Applies DMD in windowed fashion similar in implementation to Short-Time FFT. Yields time-evolving spatial modes with corresponding growth and frequency characteristics. Parameters -------- kwargs : dict The dictionary of keyword arguments for the dmd instance to be run at each MRDMD window. Note: 'dt' of MRDMD is determined by the value 'kwargs['dt']' since these values must match between DMD and MRDMD. method : string Determines the methodology MRDMD via passing one of the following values: 'slow_cutoff' : 'frequency_bin' : 'power_cutoff : subsample : boolean Each level of MRDMD where DMD is applied is only sensitive to a subset of the frequency range. The higher limit of a level's frequency range is often much lower than the Nyquist frequency of the input raw data. When subsample is set to True and the upper limit of a given level is below Nyquist of the data's sampling frequency, the data is subsampled where possible. cutoff_mode_thresh : int Manually set the number of modes to take for each level. This only applies to the methods of 'slow_cutoff' and 'power_cutoff' freq_space : array-like The list of frequencies to discretize modes into. The continuos frequency computed is rounded to nearest value in this list. freq_gradation : int When freq_space is 'default', 'freq_gradation' determines the number of frequencies to split the frequency space into between 0 and the maximum frequency. plot_levels : boolean Print a plot of frequencies and modes recorded at each window. subtracton : boolean Each level records a set of modes. When 'subtraction' is True, the reconstruction of the time window from the recorded modes is subtracted from the raw data over the time window such that higher levels ideally do not record the same modes again. In practice, this strategy tends to amplify noise at the higher levels. excess_reconstruction : boolean As an alternative to subtracting the recorded modes which may amplify noisy or erroneous modes via subtraction on the data, this strategy makes a reconstruction of the remaining non-recorded modes for higher level time windows. power_denoise : boolean 'power_denoise' seeks to filter out noise via the estimation of power associated with each mode. If 'power_denoise' is True, only modes with power representing a percentage of the total power above 'power_denoise_thresh' will be recorded. power_denoise_thresh : float The threshold percentage of total power to keep modes when 'power_denoise' is set. See 'power_denoise'. lower_lim : float The lower frequency of the first level. linear_freq_bins : boolean In the 'frequency_bin' method, each level only records modes with associated frequencies within a designated frequency range. When 'linear_freq_bins' is True, this frequency range is divided linearly between each level. Otherwise it is divided logarithmically, since the length of the time window(s) of each level is half as long as the preceding window. Returns -------- mrDMD : object The blank instance of mrDMD with given parameters. See Also -------- class: `DMD` """ #user input handling assert (method is 'slow_cutoff' or method is 'frequency_bin' or method is 'power_cutoff') if method is 'frequency_bin': if cutoff_mode_thresh is not None: warnings.warn('cutoff_mode_thresh does not apply with' +\ 'frequency_bin method') else: if cutoff_mode_thresh is None: #when X is 1 channel stack_factor determines the number #of modes to extract at each level. Each mode is a #pair (complex, real), the default behavior below #thus takes a cutoff at half of the total modes #in single channel examples. if ('stack_factor' in kwargs) and \ (kwargs['stack_factor'] == int): cutoff_mode_thresh = int((kwargs['stack_factor'] / 2) / 2) else: if not (0.0 < cutoff_mode_thresh <= 1.0): raise ( '`cutoff_mode_thresh` must be 0.0 < cutoff_mode_thresh <= 1.0' ) if subsample: warnings.warn('subsample does not apply with method %s' % method) subsample = False if ('stack_factor' in kwargs) and \ (kwargs['stack_factor'] == int) and \ (kwargs['stack_factor'] < 2): warnings.warn( 'stack_factor must always be at least 2 or greater to capture frequency content' ) if ('stack_factor' in kwargs) and \ (kwargs['stack_factor'] == int): self.stack_factor = kwargs['stack_factor'] else: self.stack_factor = 'estimate' if not ('dt' in kwargs): warnings.warn( 'dt unspecified in kwargs. Using default dt = 1 second') #highest frequency to record from self.max_freq = 200 self.freq_space = freq_space self.linear_freq_bins = linear_freq_bins self.freq_gradation = freq_gradation self.cutoff_mode_thresh = cutoff_mode_thresh self.dmd = DMD(**kwargs) self.dt = self.dmd.dt self.subsample = subsample self.L = L self.excess_reconstruction = excess_reconstruction self.method = method self.spec = None #The list of the lower and upper frequency bounds of each 'L' levels self.freq_bins = np.zeros((L, 2)) #the lower frequency bound of the first level #all recorded modes are therefore above this frequency self.lower_lim = lower_lim self.time_resolution = None self.time_steps = None self.levelSet = set() self.plot_levels = plot_levels self.subtraction = subtraction self.power_denoise = power_denoise self.power_denoise_thresh = power_denoise_thresh self.Phi_modes = None #freq by time by feature self.original_channels = None self.original_samples = None self.l2_norm = l2_norm self.baseline_power_denoise = None
class mrDMD: def __init__(self, kwargs, method='frequency_bin', subsample=False, L=10, cutoff_mode_thresh=None, freq_space='default', freq_gradation='default', plot_levels=False, subtraction=False, excess_reconstruction=False, power_denoise=False, power_denoise_thresh=.05, lower_lim=0.0, linear_freq_bins=False, l2_norm=False): """ Multi-Resolution Dynamic Mode Decomposition (mrDMD). Applies DMD in windowed fashion similar in implementation to Short-Time FFT. Yields time-evolving spatial modes with corresponding growth and frequency characteristics. Parameters -------- kwargs : dict The dictionary of keyword arguments for the dmd instance to be run at each MRDMD window. Note: 'dt' of MRDMD is determined by the value 'kwargs['dt']' since these values must match between DMD and MRDMD. method : string Determines the methodology MRDMD via passing one of the following values: 'slow_cutoff' : 'frequency_bin' : 'power_cutoff : subsample : boolean Each level of MRDMD where DMD is applied is only sensitive to a subset of the frequency range. The higher limit of a level's frequency range is often much lower than the Nyquist frequency of the input raw data. When subsample is set to True and the upper limit of a given level is below Nyquist of the data's sampling frequency, the data is subsampled where possible. cutoff_mode_thresh : int Manually set the number of modes to take for each level. This only applies to the methods of 'slow_cutoff' and 'power_cutoff' freq_space : array-like The list of frequencies to discretize modes into. The continuos frequency computed is rounded to nearest value in this list. freq_gradation : int When freq_space is 'default', 'freq_gradation' determines the number of frequencies to split the frequency space into between 0 and the maximum frequency. plot_levels : boolean Print a plot of frequencies and modes recorded at each window. subtracton : boolean Each level records a set of modes. When 'subtraction' is True, the reconstruction of the time window from the recorded modes is subtracted from the raw data over the time window such that higher levels ideally do not record the same modes again. In practice, this strategy tends to amplify noise at the higher levels. excess_reconstruction : boolean As an alternative to subtracting the recorded modes which may amplify noisy or erroneous modes via subtraction on the data, this strategy makes a reconstruction of the remaining non-recorded modes for higher level time windows. power_denoise : boolean 'power_denoise' seeks to filter out noise via the estimation of power associated with each mode. If 'power_denoise' is True, only modes with power representing a percentage of the total power above 'power_denoise_thresh' will be recorded. power_denoise_thresh : float The threshold percentage of total power to keep modes when 'power_denoise' is set. See 'power_denoise'. lower_lim : float The lower frequency of the first level. linear_freq_bins : boolean In the 'frequency_bin' method, each level only records modes with associated frequencies within a designated frequency range. When 'linear_freq_bins' is True, this frequency range is divided linearly between each level. Otherwise it is divided logarithmically, since the length of the time window(s) of each level is half as long as the preceding window. Returns -------- mrDMD : object The blank instance of mrDMD with given parameters. See Also -------- class: `DMD` """ #user input handling assert (method is 'slow_cutoff' or method is 'frequency_bin' or method is 'power_cutoff') if method is 'frequency_bin': if cutoff_mode_thresh is not None: warnings.warn('cutoff_mode_thresh does not apply with' +\ 'frequency_bin method') else: if cutoff_mode_thresh is None: #when X is 1 channel stack_factor determines the number #of modes to extract at each level. Each mode is a #pair (complex, real), the default behavior below #thus takes a cutoff at half of the total modes #in single channel examples. if ('stack_factor' in kwargs) and \ (kwargs['stack_factor'] == int): cutoff_mode_thresh = int((kwargs['stack_factor'] / 2) / 2) else: if not (0.0 < cutoff_mode_thresh <= 1.0): raise ( '`cutoff_mode_thresh` must be 0.0 < cutoff_mode_thresh <= 1.0' ) if subsample: warnings.warn('subsample does not apply with method %s' % method) subsample = False if ('stack_factor' in kwargs) and \ (kwargs['stack_factor'] == int) and \ (kwargs['stack_factor'] < 2): warnings.warn( 'stack_factor must always be at least 2 or greater to capture frequency content' ) if ('stack_factor' in kwargs) and \ (kwargs['stack_factor'] == int): self.stack_factor = kwargs['stack_factor'] else: self.stack_factor = 'estimate' if not ('dt' in kwargs): warnings.warn( 'dt unspecified in kwargs. Using default dt = 1 second') #highest frequency to record from self.max_freq = 200 self.freq_space = freq_space self.linear_freq_bins = linear_freq_bins self.freq_gradation = freq_gradation self.cutoff_mode_thresh = cutoff_mode_thresh self.dmd = DMD(**kwargs) self.dt = self.dmd.dt self.subsample = subsample self.L = L self.excess_reconstruction = excess_reconstruction self.method = method self.spec = None #The list of the lower and upper frequency bounds of each 'L' levels self.freq_bins = np.zeros((L, 2)) #the lower frequency bound of the first level #all recorded modes are therefore above this frequency self.lower_lim = lower_lim self.time_resolution = None self.time_steps = None self.levelSet = set() self.plot_levels = plot_levels self.subtraction = subtraction self.power_denoise = power_denoise self.power_denoise_thresh = power_denoise_thresh self.Phi_modes = None #freq by time by feature self.original_channels = None self.original_samples = None self.l2_norm = l2_norm self.baseline_power_denoise = None def _in_range(self, x, lim): """ Return the indices of the x vector where the values are within the range of lim. Parameters -------- x : vector-like The list or vector lim : vector-like The list or vector of lower and upper limit respect. The lower limit is inclusive, the upper exclusive such that no frequency is overlapping in separate mrDMD levels (`l`). Returns -------- indices : vector-like The indices corresponding to such values of x. """ return [i for i, v in enumerate(x) if (v >= \ lim[0]) and (v < lim[1])] def _scalar_add(self, array, value): """ Element-wise addition to an array, list, iterable """ return [x + value for x in array] def fit(self, X): """ Public call to fit mrDMD to a data sample X. Parameters -------- X : matrix-like The data matrix Returns -------- self.spec : matrix-like The spectrogram of shape : (len(self.freq_space), self.time_steps) See Also -------- :class:`mrDMD._fit` : private call on `fit` """ if self.method == 'frequency_bin': # Assign frequency bins for each level if self.linear_freq_bins: #deprecated if self.freq_gradation is 'default': self.freq_space = np.linspace(0, self.max_freq, self.max_freq + 1) else: self.freq_space = np.linspace(0, self.max_freq, self.freq_gradation) self.freq_bin_width = len(self.freq_space) // self.L for l in range(self.L): #get the indices of freq_space limits lim_inds = [ l * self.freq_bin_width, (l + 1) * self.freq_bin_width ] if l == (self.L - 1): # include all frequencies lim_inds[1] = len(self.freq_space) - 1 #get the values of the lower and upper discretized freqs lim = (self.freq_space[lim_inds[0]], self.freq_space[lim_inds[1]]) self.freq_bins[l, :] = lim # save freq lim information else: #assign frequency bins based off time of window for l in range(self.L): if l == 0: time_of_window = X.shape[1] * self.dt else: time_of_window /= 2 lower_lim = (2 * float(time_of_window))**-1 upper_lim = (float(time_of_window))**-1 #get the values of the lower and upper discretized freqs lim = (lower_lim, upper_lim) self.freq_bins[l, :] = lim # save freq lim information # Assign discretized frequency space if self.freq_space is 'default': if self.freq_gradation is 'default': self.freq_space = np.linspace(0, self.max_freq, self.max_freq + 1) else: #self.freq_space = np.linspace(0, np.max(self.freq_bins), (((2 ** L) + 1) // freq_gradation)) self.freq_space = np.linspace(0, self.max_freq, self.freq_gradation) sample_resolution = X.shape[1] * (2**(-self.L)) self.time_steps = int(np.ceil(X.shape[1] / sample_resolution)) self.time_resolution = sample_resolution * self.dt self.levelSet = set() self.original_channels = X.shape[0] self.original_samples = X.shape[1] self.spec = np.zeros((len(self.freq_space), self.time_steps)) #freq by time by feature self.Phi_modes = np.zeros( (len(self.freq_space), X.shape[0], self.time_steps)) time_idx = range(self.time_steps) l = 0 half = 'left' self.spec = self._fit(X, l, time_idx, half) if self.method is 'frequency_bin': self.summed_Phi_modes = np.zeros( (self.L, self.Phi_modes.shape[1], self.Phi_modes.shape[2])) return self.spec def _fit(self, X, l, time_idx, half, nyquist_factor=12): """ Private recursive method for fitting mrDMD to a data sample. Parameters -------- X : matrix-like The data matrix l : scalar The local level of the mrDMD. l is in [0, L) time_idx : list The indices detailing the position of the windowed X relative to the initial full X matrix passed to mrDMD.fit() Returns -------- self.spec : matrix-like The spectrogram See Also -------- :class:`mrDMD.fit` : public call on `fit` """ if l < self.L: #make sure all changes to dt are contained via hard reset self.dmd.dt = self.dt #record the original samples before subsampling local_t_shots = X.shape[1] if self.subsample: #subsample compress each window upper_lim = self.freq_bins[l, 1] upper_dt = 1.0 / (nyquist_factor * upper_lim ) #dictated by Nyquist stride = int(upper_dt / self.dt) if stride < 1: #leave dt, stride at its non subsampled value stride = 1 warnings.warn('Levels %d and higher are not subsampled' % l) self.dmd.dt *= stride #adjust dt according to M Xsub = X[:, ::stride].copy() else: Xsub = X try: if not Xsub.any(): warnings.warn('X is a zero vector. mrDMD stopped' +\ 'with X shape %d by %d at level %d\n' % (X.shape[0], X.shape[1], l)) return self.spec #catch and add nothing if Xsub.size == 0: warnings.warn('X is an empty vector. mrDMD stopped' +\ 'with X shape %d by %d at level %d\n' % (X.shape[0], X.shape[1], l)) return self.spec #catch and add nothing if self.stack_factor == 'estimate': local_stack_factor = self.dmd._estimate_stack_factor( *Xsub.shape) else: local_stack_factor = self.stack_factor if (Xsub.shape[1] - local_stack_factor - 1) < 2: warnings.warn('X is too small of a vector. mrDMD stopped' +\ 'with X shape %d by %d at level %d\n' % (X.shape[0], X.shape[1], l)) return self.spec #catch and add nothing self.dmd.stack_factor = self.stack_factor #must explicitly reset stack_factor for each fit self.dmd.fit(Xsub) if self.method is 'slow_cutoff' or self.method is 'frequency_bin': #any sorting of f, P also sorts the corresponding self.dmd.Phi f, P = self.dmd.spectrum(freq_space=self.freq_space, sort='frequencies', sort_modes=True) elif self.method is 'power_cutoff': #any sorting of f, P also sorts the corresponding self.dmd.Phi f, P = self.dmd.spectrum(freq_space=self.freq_space, sort='power', sort_modes=True) except LA.LinAlgError as err: ##dmd will fail on eig of Ahat when passed a zero vector if err.message == "SVD did not converge": warnings.warn( "DMD failed with X shape %d by %d at level %d\n %s\n" % (X.shape[0], X.shape[1], l, err.message)) else: warnings.warn( "DMD failed with X shape %d by %d at level %d\n X may be a zero vector\n %s\n" % (X.shape[0], X.shape[1], l, err.message)) return self.spec #catch and add nothing #except ValueError as err: ##dmd will fail on svd when data matrices are too large ##caused by some unknown failure in lapack #warnings.warn("DMD failed with X shape %d by %d at level %d\n X may be a zero vector" #% (X.shape[0], X.shape[1], l)) ##import pdb; pdb.set_trace() #return self.spec #catch and add nothing except AssertionError: #dmd will fail when the vector length < stack_factor warnings.warn( "DMD failed with X shape %d by %d at level %d.\n Vector length may be < stack_factor" % (X.shape[0], X.shape[1], l) + "\nConsider lowering L or dmd.stack_factor") return self.spec #catch and add nothing #except ValueError as err: ##print(err.args) #warnings.warn("Lambdas contain no complex components") #return self.spec #catch and add nothing #except: ##dmd will fail when the vector length < stack_factor #warnings.warn("DMD failed with X shape %d by %d at level %d.\n CVXPY SolverError" #% (X.shape[0], X.shape[1], l) #+ "\nConsider lowering L or dmd.stack_factor") #return self.spec #catch and add nothing X_next_l = self._reconstruct(X, l, half, time_idx, local_t_shots, f, P) split_ind_time = local_t_shots // 2 split_ind = len(time_idx) // 2 first_idx = range(split_ind) second_idx = range(split_ind, len(time_idx)) #import pdb; pdb.set_trace() first_idx = self._scalar_add(first_idx, time_idx[0]) second_idx = self._scalar_add(second_idx, time_idx[0]) #import pdb; pdb.set_trace() self.spec = self._fit(X_next_l[:, :split_ind_time], l + 1, first_idx, half='left') self.spec = self._fit(X_next_l[:, split_ind_time:], l + 1, second_idx, half='right') return self.spec def _record_power_and_phi(self, fP, time_idx, Phi, selected_mode_idx): """ Summate spectral power content to self.spec and record Phi weightings (the spatial modes) at the given time interval. Parameters -------- fP : array-like The arrary or tuple of frequency followed by its respective power to be recorded time_idx : list The indices detailing the position of the windowed X relative to the initial full X matrix passed to mrDMD.fit() Phi : matrix-like Spatial weightings selected_mode_idx : list Contains list of indices into the modes of Phi to include """ for i in selected_mode_idx: fP_tup = fP[i] idx = self.dmd._find_nearest_idx(self.freq_space, fP_tup[0]) self.spec[idx, time_idx] += fP_tup[1] #record Phi if self.l2_norm: self.Phi_modes[idx, :, time_idx] += preprocessing.normalize( np.abs(Phi[:self.original_channels, i].reshape(1, -1)), norm='l2').flatten() else: self.Phi_modes[idx, :, time_idx] += \ np.abs(Phi[:self.original_channels, i].reshape(1, -1)) def _reconstruct(self, X, l, half, time_idx, local_t_shots, f, P, savefig=False, verbose=True): """ Reconstruct X based on method of 'slow_cutoff' or 'frequency_bin'. 'slow_cutoff' method takes the first number of modes specified by the `slow_cutoff` parameter passed during initialization of mrDMD. In the 'frequency_bin' method, each level of mrDMD will only extract frequencies within a certain range according to level and it's frequency bounds specified in 'self.freq_bins'. The range is linearly determined by the window level of mrDMD. The frequencies from the subtracted modes are recorded (added) to the spectogram. Parameters ---------- X : matrix-like The data matrix l : scalar The local level of the mrDMD. l is in [0, L) time_idx : list The indices detailing the position of the windowed X relative to the initial full X matrix passed to mrDMD.fit() local_t_shots : scalar Number of (before subsampled) timeshots of the current data window f : vector-like Frequencies P : vector-like Power at each frequency savefig : boolean Saves figure in current directory with title given verbose : boolean Prints descriptive reconstruction behavior Returns -------- X_next_l : matrix-like The new X matrix reconstructed from the remaining higher frequency modes. This is the matrix to be passed onto the lower (shorter time period) windows. """ #total number of modes total_modes = self.dmd.Phi.shape[1] if self.cutoff_mode_thresh: cutoff_mode = int(total_modes * self.cutoff_mode_thresh) if cutoff_mode > total_modes: warnings.warn('Cutoff mode %d exceeds number of modes' %\ cutoff_mode) cutoff_mode = total_modes else: cutoff_mode = total_modes assert (len(f) == total_modes) fP = np.array(list(zip(f, P))) if 'slow_cutoff' == self.method: #with any cutoff method selected_mode_idx = range(cutoff_mode) excess_mode_idx = range(cutoff_mode, total_modes) assert (len(selected_mode_idx) == cutoff_mode) elif self.method == 'power_cutoff': #modes sorted in nondecreasing order according to power first_mode = total_modes - cutoff_mode #excess_mode_idx = range(first_mode) #selected_mode_idx = range(first_mode, total_modes) selected_mode_idx = range(cutoff_mode) excess_mode_idx = range(cutoff_mode, total_modes) assert (len(selected_mode_idx) == cutoff_mode) elif self.method == 'frequency_bin': lim = self.freq_bins[l, :].copy() #get the freq lim information selected_mode_idx = self._in_range(f, lim) #Denoise: do not reconstruct or record modes #with power below threshold if self.power_denoise: tot_energy = sum(P) keep_ind = [ ind for ind, p in enumerate(P) if (ind in selected_mode_idx) and ( (p / tot_energy) > self.power_denoise_thresh) ] selected_mode_idx = keep_ind #reupdate the high modes #only record modes with powers above some baseline if self.baseline_power_denoise is not None: #take only modes with higher P than average rest, at corr. f selected_mode_idx = [ i for i in selected_mode_idx if P[i] > self.baseline_power_denoise[ self.dmd._find_nearest_idx(self.freq_space, f[i])] ] select_mode_set = set(selected_mode_idx) excess_mode_idx = [ i for i in range(total_modes) if i not in select_mode_set ] if selected_mode_idx == []: #if no modes selected, return if self.plot_levels: print("No power recorded with X shape %d by %d at level %d" % (X.shape[0], X.shape[1], l)) return X #subtract nothing add no power self.dmd.dt = self.dt # hard reset for reconstruction if self.subtraction: #make a reconstruction for the time window #using on the selected low modes or modes within #the frequency bounds Xhatlow = self.dmd.transform(timesteps=local_t_shots, keep_modes=selected_mode_idx) Xhathigh = None assert (X.shape == Xhatlow.shape) #subtract this reconstruction from the window #to create the data for lower levels X_next_l = X - Xhatlow else: X_next_l = X Xhathigh = None Xhatlow = None if self.excess_reconstruction: #make a reconstruction based off of all #unselected modes used #to create the data window for all lower levels Xhathigh = self.dmd.transform(local_t_shots, keep_modes=excess_mode_idx) Xhatlow = None X_next_l = Xhathigh #FIXME #selected_mode_idx = selected_mode_idx[::2] #record only one of the real imag pair fP_record = fP[selected_mode_idx] #record only the power of the modes being subtracted self._record_power_and_phi(fP, time_idx, self.dmd.Phi, selected_mode_idx) #show plots for levels) if self.plot_levels and (l not in self.levelSet) and (half == 'left'): self._draw_levels(f, P, l, savefig, time_idx, Xhatlow, Xhathigh, fP_record, X, X_next_l, verbose) return X_next_l def _scale1(self, arr, mval=None): """ Scale a vector by its maximum value or by some value specified by mval. Parameters -------- arr : array-like The data matrix mval : scalar (Optional) The scalar value to divide by. Default is None which searches the array for the maximum value to divide by. Returns -------- arr : array-like The new scaled array. """ if mval is None: mval = np.max(arr) return np.array([i / mval for i in arr]) def _day_plot(self, time_idx, x, title='', step=1, color='k', savefig=False): """ Draw a day plot (lines for each row of x separated by distance `step`) representation of matrix `x` Parameters ---------- time_idx : array-like array of time indices for the current window x : matrix The data to display (typically timeseries data) title : string Title to be printed to plot. Also name of saved file step : float Distance on y-axis to separate each time varying channel color : string Color of lines savefig : boolean Saves figure to pdf """ plt.figure() plt.rc('text', usetex=True) for ri, row in enumerate(x): current_step = ri * step y = x[ri, :] scaled = self._scale1(y) shifted = scaled + current_step if (x.shape[1] == len(time_idx)): plt.plot(time_idx, shifted, color) else: plt.plot(shifted, color) plt.ylim([-1, x.shape[0] * step]) plt.ylabel('Channels') plt.xlabel('Time (s)') plt.title(title) plt.show() if savefig: plt.savefig('%s.pdf' % title) plt.close() def _draw_levels(self, f, P, l, savefig, time_idx, Xhatlow, Xhathigh, fP_record, X, X_next_l, verbose): """ Draw descriptive plots at each level of mrDMD showing the spatial modes and spectral components recorded and/or extracted. Parameters ---------- f : array-like Frequencies P : array-like Corresponding power l : int The level of mrDMD currently on savefig : boolean Saves figure time_idx : array_like The array of indices associated with current window Xhatlow : matrix The matrix representation of the unselected matrices Xhathigh : matrix The matrix representation of the selected matrices fP_record : list The list of (f, P) tuple pairs recorded X : matrix The raw data matrix X_next_l : The data matrix to be passed to lower levels verbose : boolean Explicit printing of f and P recorded """ self.levelSet.add(l) #only plot once per level if verbose: print('\n\nLevel: %d' % l) #Frequency vs. scalings of DMD plt.figure() plt.rc('text', usetex=True) plt.stem(f, P, 'k') plt.title(r'Level: %d' % l) title = r'SpectrumLevel:%d' % l plt.xlabel(r'Frequency') plt.ylabel(r'DMD scaling') plt.show() if savefig: plt.savefig('%s.pdf' % title) plt.close() if verbose: print('Power recorded [frequency, Power]:') print(fP_record) print('Added to time_idx %d - %d' % (time_idx[0], time_idx[-1])) #show the original time window of X in black, then #show the subtracted reconstruction or the high mode #reconstruction in red if self.original_channels == 1: plt.figure() plt.rc('text', usetex=True) plt.plot(time_idx, X[0, :], 'k', label=r'\left| X \right|') if self.subtraction: plt.plot(time_idx, Xhatlow[0, :], 'r', label=r'\left| \hat{X} \right|') else: plt.plot(time_idx, Xhathigh[0, :], 'r', label=r'\left| \hat{X} \right| left over modes') plt.legend() title = r'Reconstruction level: %d' % l plt.title(title) plt.show() else: self._day_plot(time_idx, X, title=r'\left| X \right|, level: %d' % l, savefig=savefig) if self.subtraction: title_end = r'\left| \hat{X} \right|, level: %d' % l self._day_plot(time_idx, Xhatlow, color='r', title=title_end, savefig=savefig) elif self.excess_reconstruction: title_end = r'\left| \hat{X} \right| left over modes, level: %d' % l self._day_plot(time_idx, Xhathigh, color='r', title=title_end, savefig=savefig) #show the reconstruction for the next levels only for #subtraction constructed via: x - xhat if self.subtraction: if self.original_channels == 1: plt.figure() plt.rc('text', usetex=True) plt.plot(time_idx, X_next_l[0, :], 'b', label=r'\left| X \right|') title = r'\left| X - \hat{X} \right| level: %d' % l plt.title(title) plt.show() if savefig: plt.savefig('%s.pdf' % title) plt.close() else: title = r'\left| X - \hat{X} \right| level: %d' % l self._day_plot(time_idx, X_next_l, color='b', title=title, savefig=savefig) #show heatmap so far self.heatmap(title='Level %d' % l) def heatmap(self, title='', norm=False, savefig=False, xticks_loc='default', xticks_label='default', yticks_interval='default'): #xticks_loc=np.linspace(0,3000, 7), #xticks_label=np.arange(0, 35, 5), yticks_interval=5): """ Draw a heatmap of the spectral information returned by mrDMD.fit() Parameters -------- title : string Titles the plot and names the png file norm : boolean The local level of the mrDMD. l is in [0, L) savefig : boolean Saves figure in current directory with title given xticks_loc : array-like Locations of xticks (time axis) xticks_label : array-like Labels at xticks (time axis) yticks_interval : int Skip interval for yticks (frequency axis) """ if xticks_loc is 'default': t = range(self.time_steps) * np.tile(self.time_resolution, self.time_steps) plt.figure() if norm: spec = self.spec / np.max(self.spec) else: spec = self.spec plt.pcolor(t, self.freq_space, spec, cmap='hot') #plt.yticks(np.arange(len(self.freq_space) - #1)[::yticks_interval], [int(i) for i in #self.freq_space[::yticks_interval]]) #plt.xticks(xticks_loc, xticks_label) plt.ylabel('Frequency (Hz)') plt.xlabel('Time (s)') plt.title(title) #plt.colorbar() if savefig: plt.savefig('%s.png' % title.replace(' ', ''))
sys.exit('The input snapshots does not match!!!') Snapshots = np.vstack((Snapshots, TempFrame[var])) DataFrame += TempFrame Snapshots = Snapshots.T Snapshots = Snapshots[ind, :] Snapshots = Snapshots*fa m, n = np.shape(Snapshots) AveFlow = DataFrame/np.size(dirs) meanflow = AveFlow.query("x>=-5.0 & x<=20.0 & y>=-3.0 & y<=5.0") # %% DMD Snapshots1 = Snapshots[:, :-1] if np.size(dirs) != np.size(timepoints): sys.exit("The NO of snapshots are not equal to the NO of timespoints!!!") predmd = DMD(Snapshots) with timer("DMD computing"): eigval, phi = predmd.dmd_standard(fluc=True) # %% coeff = predmd.dmd_amplitude(opt='spdmd') dynamics = predmd.dmd_dynamics(timepoints) residual = predmd.dmd_residual print("The residuals of DMD is ", residual) with timer("Precompute SPDMD amplitudes"): predmd.spdmd_amplitude() # %% SPDMD gamma = [200, 300] # np.logspace(2.7, 3.0, 5) # around the value of snapshots NO with timer("SPDMD computing"): ans = predmd.compute_spdmd(gamma=gamma)
marker_detection=True, event_dict={ 'Rest:EC': 1, 'Rest:EO': 2 }, sr_new=200, ref_new=['EXG5', 'EXG6'], filter_freqs=[4, 30], ICA=True, Autoreject=False).run() X = data.epochs.get_data() * (1e6) #scale to microvolt channels = data.epochs.info['ch_names'][:32] labels = data.epochs.events[:, -1] with open(output_dir + participant + '_cleanRest.pkl', 'wb') as handle: pickle.dump(data, handle, protocol=pickle.HIGHEST_PROTOCOL) ######################### Dynamic Mode Decomposition############################ dmd = DMD(X, labels, channels=channels, dt=1 / 200, win_size=100, overlap=50, stacking_factor=4, truncation=True).DMD_win() with open(output_dir + participant + 'Rest_dmd.pkl', 'wb') as handle: pickle.dump(dmd, handle, protocol=pickle.HIGHEST_PROTOCOL)
from DMD import DMD from PinLayout import PinLayout layout = PinLayout(37, 38, 23, 35, 24) dmd = DMD(7, 1, 32, 16, layout) dmd.draw_line(0,0,223,15) dmd.draw_line(0,15,223,0) while True: dmd.scan_full()