def _test_scale_dependence(std_t_nd0, std_t_d0, mu0, scale0, N0, th): """Nondimensional: independent* -- Dimensional: std_t ~ scale. *Nondimensional breaks down for low scales (<~3), where freq-domain wavelet is trimmed (even beyond mode), deviating center frequency from continuous-time counterpart. Particularly large `th` per default `(min_decay, max_mult) = (1e6, 2)` in `time_resolution`, which reasonably limits the extended wavelet duration in time-domain in (paddded) CWT (but this limitation might be undue; I'm unsure. https://dsp.stackexchange.com/q/70810/50076). _nd here is just an extra division by 'peak' center_frequency. """ scales = 2**(np.arange(16, 81) / 8) # [4, ..., 1024] errs = np.zeros((2, len(scales))) wavelet = Wavelet(('morlet', {'mu': mu0})) kw = dict(wavelet=wavelet, N=N0, force_int=False) for i, scale in enumerate(scales): std_t_nd = time_resolution(**kw, scale=scale, nondim=True) std_t_d = time_resolution(**kw, scale=scale, nondim=False) errs[0, i] = abs(std_t_nd - std_t_nd0) errs[1, i] = abs((std_t_d / std_t_d0) - (scale / scale0)) _assert_and_viz(th, errs, 2*[np.log2(scales)], 'log2(scale)', "Time resolution, morlet")
def test_bounds(): wavelet = Wavelet(('morlet', {'mu': 6})) for N in (4096, 2048, 1024, 512, 256, 128, 64): try: cwt_scalebounds(wavelet, N=N) except Exception as e: raise Exception(f"N={N} failed; errmsg:\n{e}")
def _test_N_dependence(wc0, mu0, scale0, N0, th): """Independent""" Ns = (np.array([.25, .5, 2, 4, 9]) * N0).astype('int64') errs = np.zeros(len(Ns)) wavelet = Wavelet(('morlet', {'mu': mu0, 'dtype': 'float64'})) for i, N in enumerate(Ns): wc = center_frequency(wavelet, scale=scale0, N=N, kind='energy') errs[i] = abs(wc - wc0) _assert_and_viz(th, errs, Ns, 'N', "Center frequency (energy), morlet")
def _test_mu_dependence(wc0, mu0, scale0, N0, th): """wc ~ mu""" mus = np.arange(mu0 + 1, 21) errs = np.zeros(len(mus)) for i, mu in enumerate(mus): wavelet = Wavelet(('morlet', {'mu': mu})) wc = center_frequency(wavelet, scale=scale0, N=N0, kind='energy') errs[i] = abs((wc / wc0) - (mu / mu0)) _assert_and_viz(th, errs, mus, 'mu', "Center frequency (energy), morlet")
def _test_mu_dependence(std_t_nd0, std_t_d0, mu0, scale0, N0, th): """Nondimensional: std_t ~ 1/mu -- Dimensional: independent""" mus = np.arange(mu0 + 1, 21) errs = np.zeros((2, len(mus))) for i, mu in enumerate(mus): wavelet = Wavelet(('morlet', {'mu': mu, 'dtype': 'float64'})) std_t_nd = time_resolution(wavelet, scale0, N0, nondim=True) std_t_d = time_resolution(wavelet, scale0, N0, nondim=False) errs[0, i] = abs((std_t_nd / std_t_nd0) - (mu / mu0)) errs[1, i] = abs(std_t_d - std_t_d0) _assert_and_viz(th, errs, 2 * [mus], 'mu', "Time resolution, morlet")
def _test_N_dependence(std_w_nd0, std_w_d0, mu0, scale0, N0, th): """Independent""" Ns = (np.array([.25, .5, 2, 4, 9]) * N0).astype('int64') errs = np.zeros((2, len(Ns))) wavelet = Wavelet(('morlet', {'mu': mu0})) for i, N in enumerate(Ns): std_w_nd = freq_resolution(wavelet, scale0, N, nondim=True) std_w_d = freq_resolution(wavelet, scale0, N, nondim=False) errs[0, i] = abs(std_w_nd - std_w_nd0) errs[1, i] = abs(std_w_d - std_w_d0) _assert_and_viz(th, errs, 2*[Ns], 'N', "Frequency resolution, morlet")
def _test_mu_dependence(std_w_nd0, std_w_d0, mu0, scale0, N0, th): """Nondimensional: std_w ~ mu -- Dimensional: independent""" mus = np.arange(mu0 + 1, 21) errs = np.zeros((2, len(mus))) for i, mu in enumerate(mus): wavelet = Wavelet(('morlet', {'mu': mu})) std_w_nd = freq_resolution(wavelet, scale0, N0, nondim=True) std_w_d = freq_resolution(wavelet, scale0, N0, nondim=False) errs[0, i] = abs((std_w_nd / std_w_nd0) - (mu0 / mu)) errs[1, i] = abs(std_w_d - std_w_d0) _assert_and_viz(th, errs, 2*[mus], 'mu', "Frequency resolution, morlet")
def _test_scale_dependence_high(wc0, mu0, scale0, N0, th): """wc ~ 1/scale High `scale` subject to more significant discretization error in frequency domain. """ scales = 2**(np.arange(53, 81) / 8) # [90.5, ..., 1024] errs = np.zeros(len(scales)) wavelet = Wavelet(('morlet', {'mu': mu0})) for i, scale in enumerate(scales): wc = center_frequency(wavelet, scale=scale, N=N0, kind='energy') errs[i] = abs((wc / wc0) - (scale0 / scale)) _assert_and_viz(th, errs, np.log2(scales), 'log2(scale)', "Center frequency (energy), morlet | High scales")
def _test_scale_dependence(wc0, mu0, scale0, N0, th): """wc ~ 1/scale For small `scale`, the bell is trimmed and the (energy) center frequency is no longer at mode/peak (both of which are also trimmed), but is less; we don't test these to keep code clean. """ scales = 2**(np.arange(16, 53) / 8) # [4, ..., 90.5] errs = np.zeros(len(scales)) wavelet = Wavelet(('morlet', {'mu': mu0})) for i, scale in enumerate(scales): wc = center_frequency(wavelet, scale=scale, N=N0, kind='energy') errs[i] = abs((wc / wc0) - (scale0 / scale)) _assert_and_viz(th, errs, np.log2(scales), 'log2(scale)', "Center frequency (energy), morlet")
def _test_N_dependence(std_t_nd0, std_t_d0, mu0, scale0, N0, th): """Independent `th` can be 1e-14 if dropping odd-sampled case where time-domain wavelet has suboptimal decay: https://github.com/jonathanlilly/jLab/issues/13 (also dropping low-sampled case) """ Ns = (np.array([.1, 1 / 3, .5, 2, 4, 9]) * N0).astype('int64') errs = np.zeros((2, len(Ns))) wavelet = Wavelet(('morlet', {'mu': mu0, 'dtype': 'float64'})) for i, N in enumerate(Ns): std_t_nd = time_resolution(wavelet, scale0, N, nondim=True) std_t_d = time_resolution(wavelet, scale0, N, nondim=False) errs[0, i] = abs(std_t_nd - std_t_nd0) errs[1, i] = abs(std_t_d - std_t_d0) _assert_and_viz(th, errs, [Ns, Ns], 'N', "Time resolution, morlet")
def _test_scale_dependence(std_w_nd0, std_w_d0, mu0, scale0, N0, th): """Nondimensional: independent* -- Dimensional: std_w ~ 1/scale Particularly large `th` per nontrivial discretization finite-precision error for large scales, with small number of samples representing the non-zero region. We don't "fix" this to match continuous-time behavior again since it's more accurate per our CWT. """ scales = 2**(np.arange(16, 81) / 8) # [4, ..., 1024] errs = np.zeros((2, len(scales))) wavelet = Wavelet(('morlet', {'mu': mu0})) for i, scale in enumerate(scales): std_w_nd = freq_resolution(wavelet, scale, N0, nondim=True) std_w_d = freq_resolution(wavelet, scale, N0, nondim=False) errs[0, i] = abs(std_w_nd - std_w_nd0) errs[1, i] = abs((std_w_d / std_w_d0) - (scale0 / scale)) _assert_and_viz(th, errs, 2*[np.log2(scales)], 'log2(scale)', "Frequency resolution, morlet")
def test_utils(): _ = buffer(np.random.randn(20), 4, 1) wavelet = Wavelet(('morlet', {'mu': 6})) _ = center_frequency(wavelet, viz=1) _ = freq_resolution(wavelet, viz=1, scale=3, force_int=0) _ = time_resolution(wavelet, viz=1) xh = np.random.randn(128) xhs = np.zeros(xh.size) _aifftshift_even(xh, xhs) _afftshift_even(xh, xhs) _ = padsignal(xh, padlength=len(xh) * 2, padtype='symmetric') _ = padsignal(xh, padlength=len(xh) * 2, padtype='wrap') g = np.ones((128, 200)) unbuffer(g, xh, 1, n_fft=len(xh), N=None, win_exp=0) unbuffer(g, xh, 1, n_fft=len(xh), N=g.shape[1], win_exp=2) #### errors / warnings ################################################### _pass_on_error(find_max_scale, 1, 1, -1, -1) _pass_on_error(cwt_scalebounds, 1, 1, preset='etc', min_cutoff=0) _pass_on_error(cwt_scalebounds, 1, 1, min_cutoff=-1) _pass_on_error(cwt_scalebounds, 1, 1, min_cutoff=.2, max_cutoff=.1) _pass_on_error(cwt_scalebounds, 1, 1, cutoff=0) _pass_on_error(_assert_positive_integer, -1, 'w') _pass_on_error(_infer_scaletype, 1) _pass_on_error(_infer_scaletype, np.array([1])) _pass_on_error(_infer_scaletype, np.array([1., 2., 5.])) _pass_on_error(_process_fs_and_t, 1, np.array([1]), 2) _pass_on_error(_process_fs_and_t, 1, np.array([1., 2, 4]), 3) _pass_on_error(_process_fs_and_t, -1, None, 1) _pass_on_error(make_scales, 128, scaletype='banana')
def test_energy_center_frequency(): """If kind='energy' passes, long shot for 'peak' to fail, so just test former; would still be interesting to investigate 'peak' vs 'energy', esp. at scale extrema. """ def _test_mu_dependence(wc0, mu0, scale0, N0, th): """wc ~ mu""" mus = np.arange(mu0 + 1, 21) errs = np.zeros(len(mus)) for i, mu in enumerate(mus): wavelet = Wavelet(('morlet', {'mu': mu})) wc = center_frequency(wavelet, scale=scale0, N=N0, kind='energy') errs[i] = abs((wc / wc0) - (mu / mu0)) _assert_and_viz(th, errs, mus, 'mu', "Center frequency (energy), morlet") def _test_scale_dependence(wc0, mu0, scale0, N0, th): """wc ~ 1/scale For small `scale`, the bell is trimmed and the (energy) center frequency is no longer at mode/peak (both of which are also trimmed), but is less; we don't test these to keep code clean. """ scales = 2**(np.arange(16, 53) / 8) # [4, ..., 90.5] errs = np.zeros(len(scales)) wavelet = Wavelet(('morlet', {'mu': mu0})) for i, scale in enumerate(scales): wc = center_frequency(wavelet, scale=scale, N=N0, kind='energy') errs[i] = abs((wc / wc0) - (scale0 / scale)) _assert_and_viz(th, errs, np.log2(scales), 'log2(scale)', "Center frequency (energy), morlet") def _test_scale_dependence_high(wc0, mu0, scale0, N0, th): """wc ~ 1/scale High `scale` subject to more significant discretization error in frequency domain. """ scales = 2**(np.arange(53, 81) / 8) # [90.5, ..., 1024] errs = np.zeros(len(scales)) wavelet = Wavelet(('morlet', {'mu': mu0})) for i, scale in enumerate(scales): wc = center_frequency(wavelet, scale=scale, N=N0, kind='energy') errs[i] = abs((wc / wc0) - (scale0 / scale)) _assert_and_viz(th, errs, np.log2(scales), 'log2(scale)', "Center frequency (energy), morlet | High scales") def _test_N_dependence(wc0, mu0, scale0, N0, th): """Independent""" Ns = (np.array([.25, .5, 2, 4, 9]) * N0).astype('int64') errs = np.zeros(len(Ns)) wavelet = Wavelet(('morlet', {'mu': mu0})) for i, N in enumerate(Ns): wc = center_frequency(wavelet, scale=scale0, N=N, kind='energy') errs[i] = abs(wc - wc0) _assert_and_viz(th, errs, Ns, 'N', "Center frequency (energy), morlet") mu0 = 5 scale0 = 10 N0 = 1024 wavelet0 = Wavelet(('morlet', {'mu': mu0})) wc0 = center_frequency(wavelet0, scale=scale0, N=N0, kind='energy') args = (wc0, mu0, scale0, N0) _test_mu_dependence( *args, th=1e-7) _test_scale_dependence( *args, th=1e-14) _test_scale_dependence_high(*args, th=1e-1) _test_N_dependence( *args, th=1e-14)
def test_anim(): # bare minimally (still takes long, but covers many lines of code) wavelet = Wavelet(('morlet', {'mu': 6})) wavelet.viz('anim:time-frequency', N=8, scales=np.linspace(10, 20, 3))
def test_time_resolution(): """Some thresholds are quite large per center_frequency(, kind='peak') as opposed to 'energy', with force_int=True, especially for large scales. These are per great deviations from continuous-time counterparts, but this is simply a reflection of discretization limitations (trimmed or unsmooth bell) and finite precision error. """ def _test_mu_dependence(std_t_nd0, std_t_d0, mu0, scale0, N0, th): """Nondimensional: std_t ~ 1/mu -- Dimensional: independent""" mus = np.arange(mu0 + 1, 21) errs = np.zeros((2, len(mus))) for i, mu in enumerate(mus): wavelet = Wavelet(('morlet', {'mu': mu})) std_t_nd = time_resolution(wavelet, scale0, N0, nondim=True) std_t_d = time_resolution(wavelet, scale0, N0, nondim=False) errs[0, i] = abs((std_t_nd / std_t_nd0) - (mu / mu0)) errs[1, i] = abs(std_t_d - std_t_d0) _assert_and_viz(th, errs, 2*[mus], 'mu', "Time resolution, morlet") def _test_scale_dependence(std_t_nd0, std_t_d0, mu0, scale0, N0, th): """Nondimensional: independent* -- Dimensional: std_t ~ scale. *Nondimensional breaks down for low scales (<~3), where freq-domain wavelet is trimmed (even beyond mode), deviating center frequency from continuous-time counterpart. Particularly large `th` per default `(min_decay, max_mult) = (1e6, 2)` in `time_resolution`, which reasonably limits the extended wavelet duration in time-domain in (paddded) CWT (but this limitation might be undue; I'm unsure. https://dsp.stackexchange.com/q/70810/50076). _nd here is just an extra division by 'peak' center_frequency. """ scales = 2**(np.arange(16, 81) / 8) # [4, ..., 1024] errs = np.zeros((2, len(scales))) wavelet = Wavelet(('morlet', {'mu': mu0})) kw = dict(wavelet=wavelet, N=N0, force_int=False) for i, scale in enumerate(scales): std_t_nd = time_resolution(**kw, scale=scale, nondim=True) std_t_d = time_resolution(**kw, scale=scale, nondim=False) errs[0, i] = abs(std_t_nd - std_t_nd0) errs[1, i] = abs((std_t_d / std_t_d0) - (scale / scale0)) _assert_and_viz(th, errs, 2*[np.log2(scales)], 'log2(scale)', "Time resolution, morlet") def _test_N_dependence(std_t_nd0, std_t_d0, mu0, scale0, N0, th): """Independent `th` can be 1e-14 if dropping odd-sampled case where time-domain wavelet has suboptimal decay: https://github.com/jonathanlilly/jLab/issues/13 (also dropping low-sampled case) """ Ns = (np.array([.1, 1/3, .5, 2, 4, 9]) * N0).astype('int64') errs = np.zeros((2, len(Ns))) wavelet = Wavelet(('morlet', {'mu': mu0})) for i, N in enumerate(Ns): std_t_nd = time_resolution(wavelet, scale0, N, nondim=True) std_t_d = time_resolution(wavelet, scale0, N, nondim=False) errs[0, i] = abs(std_t_nd - std_t_nd0) errs[1, i] = abs(std_t_d - std_t_d0) _assert_and_viz(th, errs, [Ns, Ns], 'N', "Time resolution, morlet") mu0 = 5 scale0 = 10 N0 = 1024 wavelet0 = Wavelet(('morlet', {'mu': mu0})) std_t_nd0 = time_resolution(wavelet0, scale0, N0, nondim=True) std_t_d0 = time_resolution(wavelet0, scale0, N0, nondim=False) args = (std_t_nd0, std_t_d0, mu0, scale0, N0) _test_mu_dependence( *args, th=[1e-1, 1e-6]) _test_scale_dependence(*args, th=[2e-0, 1e-1]) _test_N_dependence( *args, th=[1e-1, 1e-8])
def test_freq_resolution(): def _test_mu_dependence(std_w_nd0, std_w_d0, mu0, scale0, N0, th): """Nondimensional: std_w ~ mu -- Dimensional: independent""" mus = np.arange(mu0 + 1, 21) errs = np.zeros((2, len(mus))) for i, mu in enumerate(mus): wavelet = Wavelet(('morlet', {'mu': mu})) std_w_nd = freq_resolution(wavelet, scale0, N0, nondim=True) std_w_d = freq_resolution(wavelet, scale0, N0, nondim=False) errs[0, i] = abs((std_w_nd / std_w_nd0) - (mu0 / mu)) errs[1, i] = abs(std_w_d - std_w_d0) _assert_and_viz(th, errs, 2*[mus], 'mu', "Frequency resolution, morlet") def _test_scale_dependence(std_w_nd0, std_w_d0, mu0, scale0, N0, th): """Nondimensional: independent* -- Dimensional: std_w ~ 1/scale Particularly large `th` per nontrivial discretization finite-precision error for large scales, with small number of samples representing the non-zero region. We don't "fix" this to match continuous-time behavior again since it's more accurate per our CWT. """ scales = 2**(np.arange(16, 81) / 8) # [4, ..., 1024] errs = np.zeros((2, len(scales))) wavelet = Wavelet(('morlet', {'mu': mu0})) for i, scale in enumerate(scales): std_w_nd = freq_resolution(wavelet, scale, N0, nondim=True) std_w_d = freq_resolution(wavelet, scale, N0, nondim=False) errs[0, i] = abs(std_w_nd - std_w_nd0) errs[1, i] = abs((std_w_d / std_w_d0) - (scale0 / scale)) _assert_and_viz(th, errs, 2*[np.log2(scales)], 'log2(scale)', "Frequency resolution, morlet") def _test_N_dependence(std_w_nd0, std_w_d0, mu0, scale0, N0, th): """Independent""" Ns = (np.array([.25, .5, 2, 4, 9]) * N0).astype('int64') errs = np.zeros((2, len(Ns))) wavelet = Wavelet(('morlet', {'mu': mu0})) for i, N in enumerate(Ns): std_w_nd = freq_resolution(wavelet, scale0, N, nondim=True) std_w_d = freq_resolution(wavelet, scale0, N, nondim=False) errs[0, i] = abs(std_w_nd - std_w_nd0) errs[1, i] = abs(std_w_d - std_w_d0) _assert_and_viz(th, errs, 2*[Ns], 'N', "Frequency resolution, morlet") mu0 = 5 scale0 = 10 N0 = 1024 wavelet0 = Wavelet(('morlet', {'mu': mu0})) std_w_nd0 = freq_resolution(wavelet0, scale0, N0, nondim=True) std_w_d0 = freq_resolution(wavelet0, scale0, N0, nondim=False) args = (std_w_nd0, std_w_d0, mu0, scale0, N0) _test_mu_dependence( *args, th=(1e-2, 1e-6)) _test_scale_dependence(*args, th=(6e-1, 1e-1)) _test_N_dependence( *args, th=(1e-2, 1e-13))
def test_wavelet(): """Test that `gmw` is a valid `Wavelet`.""" os.environ['SSQ_GPU'] = '0' wavelet = Wavelet('gmw') wavelet.info() wavelet.viz() wavelet = Wavelet(('gmw', { 'gamma': 3, 'beta': 60, 'norm': 'energy', 'centered_scale': True })) wavelet.info() wavelet.viz()
def test_wavelets(): for wavelet in ('morlet', ('morlet', {'mu': 4}), 'bump'): wavelet = Wavelet(wavelet) wavelet = Wavelet(('morlet', {'mu': 5})) wavelet.viz(name='overview') wavelet.info(nondim=1) wavelet.info(nondim=0) #### Visuals ############################################################# for name in wavelet.VISUALS: if 'anim:' in name: # heavy-duty computations, skip animating kw = {'testing': True} else: kw = {} try: wavelet.viz(name, N=256, **kw) except TypeError as e: if "positional argument" not in str(e): raise TypeError(e) try: wavelet.viz(name, scale=10, N=256, **kw) except TypeError as e: if "positional argument" not in str(e): raise TypeError(e) wavelet.viz(name, scales='log', N=256, **kw) _ = cwt_scalebounds(wavelet, N=512, viz=3) #### misc ################################################################ wavelet = Wavelet(lambda x: x) _ = _xifn(scale=10, N=128)