def _conduct_FIRuncFilter_and_MonteCarlo(Ub, b1, blow, kind, runs, sigma_noise, x): y, Uy = FIRuncFilter(x, sigma_noise, b1, Ub, blow=blow, kind=kind) # apply uncertain FIR filter (GUM formula) yMC, UyMC = MC(x, sigma_noise, b1, np.ones(1), Ub, runs=runs, blow=blow) # apply uncertain FIR filter (Monte Carlo) return Uy, UyMC, y, yMC
def test_FIRuncFilter_MC_uncertainty_comparison(filters, signals, lowpasses): # Check output for thinkable permutations of input parameters against a Monte Carlo approach. # run method y_fir, Uy_fir = FIRuncFilter(**filters, **signals, **lowpasses, return_full_covariance=True) # run Monte Carlo simulation of an FIR ## adjust input to match conventions of MC x = signals["y"] ux = signals["sigma_noise"] b = filters["theta"] a = [1.0] if isinstance(filters["Utheta"], np.ndarray): Uab = filters["Utheta"] else: # Utheta == None Uab = np.zeros( (len(b), len(b))) # MC-method cant deal with Utheta = None blow = lowpasses["blow"] if isinstance(blow, np.ndarray): n_blow = len(blow) else: n_blow = 0 ## run FIR with MC and extract diagonal of returned covariance y_mc, Uy_mc = MC(x, ux, b, a, Uab, blow=blow, runs=2000) # HACK for visualization during debugging # import matplotlib.pyplot as plt # fig, ax = plt.subplots(nrows=1, ncols=3) # ax[0].plot(y_fir, label="fir") # ax[0].plot(y_mc, label="mc") # ax[0].set_title("filter: {0}, signal: {1}".format(len(b), len(x))) # ax[0].legend() # ax[1].imshow(Uy_fir) # ax[1].set_title("FIR") # ax[2].imshow(Uy_mc) # ax[2].set_title("MC") # plt.show() # /HACK # check basic properties assert np.all(np.diag(Uy_fir) >= 0) assert np.all(np.diag(Uy_mc) >= 0) assert Uy_fir.shape == Uy_mc.shape # approximative comparison after swing-in of MC-result # (which is after the combined length of blow and b) assert np.allclose( Uy_fir[len(b) + n_blow:, len(b) + n_blow:], Uy_mc[len(b) + n_blow:, len(b) + n_blow:], atol=2e-1 * Uy_fir.max(), # very broad check, increase runs for better fit rtol=1e-1, )
def test_FIRuncFilter_float(): # input signal + run methods x = rect(time,100*Ts,250*Ts,1.0,noise=sigma_noise) # apply uncertain FIR filter (GUM formula) for blow in [None, b2]: y, Uy = FIRuncFilter(x, sigma_noise, b1, Ub, blow=blow, kind="float") assert len(y) == len(x) assert len(Uy) == len(x)
def test_FIRuncFilter_diag(): sigma_diag = sigma_noise * ( 1 + np.heaviside(np.arange(len(time)) - len(time)//2,0) ) # std doubles after half of the time noise = sigma_diag * white_gaussian(len(time)) # input signal + run methods x = rect(time,100*Ts,250*Ts,1.0,noise=noise) # apply uncertain FIR filter (GUM formula) for blow in [None, b2]: y, Uy = FIRuncFilter(x, sigma_diag, b1, Ub, blow=blow, kind="diag") assert len(y) == len(x) assert len(Uy) == len(x)
def test_FIRuncFilter_legacy_comparison(filters, signals, lowpasses): # Compare output of both functions for thinkable permutations of input parameters. y, Uy = legacy_FIRuncFilter(**filters, **signals, **lowpasses) y2, Uy2 = FIRuncFilter(**filters, **signals, **lowpasses) # check output dimensions assert len(y2) == len(signals["y"]) assert Uy2.shape == (len(signals["y"]), ) # check value identity assert np.allclose(y, y2) assert np.allclose(Uy, Uy2)
def test_FIR_IIR_identity(kind, fir_filter, input_signal): # run signal through both implementations y_iir, Uy_iir, _ = IIRuncFilter(*input_signal.values(), **fir_filter, kind=kind) y_fir, Uy_fir = FIRuncFilter( *input_signal.values(), theta=fir_filter["b"], Utheta=fir_filter["Uab"], kind=kind, ) assert_allclose(y_fir, y_iir) assert_allclose(Uy_fir, Uy_iir)
def test_FIRuncFilter_corr(): # get an instance of noise, the covariance and the covariance-matrix with the specified color color = "white" noise = power_law_noise(N=nTime, color_value=color, std=sigma_noise) Ux = power_law_acf(nTime, color_value=color, std=sigma_noise) # input signal x = rect(time,100*Ts,250*Ts,1.0,noise=noise) # apply uncertain FIR filter (GUM formula) for blow in [None, b2]: y, Uy = FIRuncFilter(x, Ux, b1, Ub, blow=blow, kind="corr") assert len(y) == len(x) assert len(Uy) == len(x)
def test_FIRuncFilter_equality(equal_filters, equal_signals): # Check expected output for being identical across different equivalent input # parameter cases. all_y = [] all_uy = [] # run all combinations of filter and signals for (f, s) in itertools.product(equal_filters, equal_signals): y, uy = FIRuncFilter(**f, **s) all_y.append(y) all_uy.append(uy) # check that all have the same output, as they are supposed to represent equal cases for a, b in itertools.combinations(all_y, 2): assert np.allclose(a, b) for a, b in itertools.combinations(all_uy, 2): assert np.allclose(a, b)
def evaluate(): if covariance_case == "full": U_theta = np.diag(np.full_like(theta, sigma_noise)) elif covariance_case == "zero": len_theta = len(theta) U_theta = np.zeros((len_theta, len_theta)) elif covariance_case == "none": U_theta = None else: raise NotImplementedError # measure runtime (timeit.timeit is unable to use both local+global variables) start_time = time.time() current_y, current_Uy = FIRuncFilter(signal, sigma_noise, theta, U_theta, blow=None) end_time = time.time() return end_time - start_time, current_y, current_Uy
def apply_filter(self, b, a=1, filter_uncertainty=None, MonteCarloRuns=None): """Apply digital filter (b,a) to the signal values and propagate the uncertainty associated with the signal Parameters ---------- b: np.ndarray filter numerator coefficients a: np.ndarray filter denominator coefficients, use a=1 for FIR-type filter filter_uncertainty: np.ndarray covariance matrix associated with filter coefficients, Uab=None if no uncertainty associated with filter MonteCarloRuns: int number of Monte Carlo runs, if `None` then GUM linearization will be used Returns ------- no return variables """ if isinstance(a, list): a = np.array(a) if not (isinstance(a, np.ndarray)): # FIR type filter if len(self.uncertainty.shape) == 1: if not isinstance(MonteCarloRuns, int): self.values, self.uncertainty = \ FIRuncFilter(self.values, self.uncertainty, b, Utheta = filter_uncertainty) else: self.values, self.uncertainty = \ MC(self.values, self.uncertainty, b, a, filter_uncertainty, runs=MonteCarloRuns) else: if not isinstance(MonteCarloRuns, int): MonteCarloRuns = 10000 self.values, self.uncertainty = \ MC(self.values, self.uncertainty, b, a, filter_uncertainty, runs=MonteCarloRuns) else: # IIR-type filter if not isinstance(MonteCarloRuns, int): MonteCarloRuns = 10000 self.values, self.uncertainty = \ MC(self.values, self.uncertainty, b, a, filter_uncertainty, runs=MonteCarloRuns)
# uncertain knowledge: cutoff between 19.5kHz and 20.5kHz runs = 1000 FC = fcut + (2 * np.random.rand(runs) - 1) * 0.5e3 B = np.zeros((runs, L + 1)) for k in range(runs): B[k, :] = kaiser_lowpass(L, FC[k], Fs)[0] Ub = make_semiposdef(np.cov(B, rowvar=0)) # simulate input and output signals time = np.arange(0, 499 * Ts, Ts) noise = 1e-3 x = rect(time, 100 * Ts, 250 * Ts, 1.0, noise=noise) y, Uy = FIRuncFilter(x, noise, b, Ub) yMC, UyMC = MC.MC(x, noise, b, [1.0], Ub, runs=10000) yMC2, UyMC2 = MC.SMC(x, noise, b, [1.0], Ub, runs=10000) plt.figure(1) plt.cla() plt.plot(time, col_hstack([x, y])) plt.legend(('input', 'output')) plt.figure(3) plt.cla() plt.plot(time, col_hstack([Uy, UyMC, UyMC2])) plt.title('Uncertainty of filter output signal') plt.legend(('FIR formula', 'Monte Carlo', 'Sequential Monte Carlo')) plt.show()
bF, UbF = model_est.invLSFIR_unc( H, UH, N, tau, f, Fs) # Calculation of FIR deconvolution filter and its assoc. unc. CbF = UbF / (np.tile(np.sqrt(np.diag(UbF))[:, np.newaxis], (1, N + 1)) * np.tile(np.sqrt(np.diag(UbF))[:, np.newaxis].T, (N + 1, 1))) # correlation of filter coefficients # Deconvolution Step1: lowpass filter for noise attenuation fcut = f0 + 20e3 low_order = 100 # cut-off frequency and filter order blow, lshift = kaiser_lowpass(low_order, fcut, Fs) # FIR low pass filter coefficients shift = tau + lshift # delay of low-pass plus that of the FIR deconv filter # Deconvolution Step2: Application of deconvolution filter xhat, Uxhat = FIRuncFilter(yn, noise, bF, UbF, shift, blow) # apply low-pass and FIR deconv filter # Plot of results fplot = np.linspace(0, 80e3, 1000) Hc = dsp.freqz(b, a, 2 * np.pi * fplot / Fs)[1] Hif = dsp.freqz(bF, 1.0, 2 * np.pi * fplot / Fs)[1] Hl = dsp.freqz(blow, 1.0, 2 * np.pi * fplot / Fs)[1] plt.figure(1) plt.clf() plt.plot(fplot * 1e-3, db(Hc), fplot * 1e-3, db(Hif * Hl), fplot * 1e-3, db(Hc * Hif * Hl)) plt.legend( ('System freq. resp.', 'compensation filter', 'compensation result')) # plt.title('Amplitude of frequency responses') plt.xlabel('frequency / kHz', fontsize=22)
# uncertain knowledge: cutoff between 19.5kHz and 20.5kHz runs = 1000 FC = fcut + (2 * np.random.rand(runs) - 1) * 0.5e3 B = np.zeros((runs, L + 1)) for k in range(runs): # Monte Carlo for filter coefficients of low-pass filter B[k, :] = kaiser_lowpass(L, FC[k], Fs)[0] Ub = make_semiposdef(np.cov(B, rowvar=False)) # covariance matrix of MC result # simulate input and output signals time = np.arange(0, 499 * Ts, Ts) # time values noise = 1e-5 # std of white noise x = rect(time, 100 * Ts, 250 * Ts, 1.0, noise=noise) # input signal y, Uy = FIRuncFilter(x, noise, b, Ub, blow=b) # apply uncertain FIR filter (GUM formula) yMC, UyMC = MC(x, noise, b, np.ones(1), Ub, runs=1000, blow=b) # apply uncertain FIR filter (Monte Carlo) plt.figure(1) plt.cla() plt.plot(time, x, label="input") plt.plot(time, y, label="output") plt.xlabel("time / au") plt.ylabel("signal amplitude / au") plt.legend() plt.figure(2) plt.cla() plt.plot(time, Uy, label="FIR formula") plt.plot(time, np.sqrt(np.diag(UyMC)), label="Monte Carlo")
# different cases sigma_noise = 1e-2 # 1e-5 for kind in ["float", "corr", "diag"]: for blow in [None, b2]: print(kind, type(blow)) if kind == "float": # input signal + run methods x = rect(time, 100 * Ts, 250 * Ts, 1.0, noise=sigma_noise) y, Uy = FIRuncFilter( x, sigma_noise, b1, Ub, blow=blow, kind=kind) # apply uncertain FIR filter (GUM formula) yMC, UyMC = MC( x, sigma_noise, b1, [1.0], Ub, runs=runs, blow=blow) # apply uncertain FIR filter (Monte Carlo) elif kind == "corr": # get an instance of noise, the covariance and the covariance-matrix with the specified color color = "red" noise = power_law_noise(N=nTime, color_value=color, std=sigma_noise) Ux = power_law_acf(nTime, color_value=color, std=sigma_noise)
UH = np.cov(np.c_[np.real(HMC), np.imag(HMC)], rowvar=0) UH = make_semiposdef(UH) # Calculation of FIR deconvolution filter and its assoc. unc. bF, UbF = deconv.LSFIR_unc(H, UH, N, tau, f, Fs) # correlation of filter coefficients CbF = UbF / (np.tile(np.sqrt(np.diag(UbF))[:, np.newaxis], (1, N + 1)) * np.tile(np.sqrt(np.diag(UbF))[:, np.newaxis].T, (N + 1, 1))) # Deconvolution Step1: lowpass filter for noise attenuation fcut = f0 + 20e3 low_order = 100 blow, lshift = kaiser_lowpass(low_order, fcut, Fs) shift = -tau - lshift # Deconvolution Step2: Application of deconvolution filter xhat, Uxhat = FIRuncFilter(yn, noise, bF, UbF, shift, blow) # Plot of results fplot = np.linspace(0, 80e3, 1000) Hc = dsp.freqz(b, a, 2 * np.pi * fplot / Fs)[1] Hif = dsp.freqz(bF, 1.0, 2 * np.pi * fplot / Fs)[1] Hl = dsp.freqz(blow, 1.0, 2 * np.pi * fplot / Fs)[1] plt.figure(1) plt.clf() plt.plot(fplot * 1e-3, db(Hc), fplot * 1e-3, db(Hif * Hl), fplot * 1e-3, db(Hc * Hif * Hl)) plt.legend( ('System freq. resp.', 'compensation filter', 'compensation result')) # plt.title('Amplitude of frequency responses') plt.xlabel('frequency / kHz', fontsize=22)
def conduct_validation_of_FIRuncFilter(): # parameters of simulated measurement Fs = 100e3 # sampling frequency (in Hz) Ts = 1 / Fs # sampling interval length (in s) # nominal system parameters fcut = 20e3 # low-pass filter cut-off frequency (6 dB) L = 100 # filter order b1 = kaiser_lowpass(L, fcut, Fs)[0] b2 = kaiser_lowpass(L - 20, fcut, Fs)[0] # uncertain knowledge: cutoff between 19.5kHz and 20.5kHz runs = 1000 FC = fcut + (2 * np.random.rand(runs) - 1) * 0.5e3 B = np.zeros((runs, L + 1)) for k in range( runs): # Monte Carlo for filter coefficients of low-pass filter B[k, :] = kaiser_lowpass(L, FC[k], Fs)[0] Ub = make_semiposdef(np.cov( B, rowvar=False)) # covariance matrix of MC result # simulate input and output signals nTime = 500 time = np.arange(nTime) * Ts # time values # different cases sigma_noise = 1e-2 # 1e-5 for kind in ["float", "corr", "diag"]: for blow in [None, b2]: print(kind, type(blow)) if kind == "float": # input signal + run methods x = rect(time, 100 * Ts, 250 * Ts, 1.0, noise=sigma_noise) Uy, UyMC, y, yMC = _conduct_FIRuncFilter_and_MonteCarlo( Ub, b1, blow, kind, runs, sigma_noise, x) elif kind == "corr": # get an instance of noise, the covariance and the covariance-matrix # with # the specified color color = "red" noise = power_law_noise(N=nTime, color_value=color, std=sigma_noise) Ux = power_law_acf(nTime, color_value=color, std=sigma_noise) # input signal x = rect(time, 100 * Ts, 250 * Ts, 1.0, noise=noise) # build Ux_matrix from autocorrelation Ux Ux_matrix = toeplitz(trimOrPad(Ux, nTime)) # run methods y, Uy = FIRuncFilter( x, Ux, b1, Ub, blow=blow, kind=kind) # apply uncertain FIR filter (GUM formula) yMC, UyMC = MC( x, Ux_matrix, b1, np.ones(1), Ub, runs=runs, blow=blow) # apply uncertain FIR filter (Monte Carlo) elif kind == "diag": sigma_diag = sigma_noise * ( 1 + np.heaviside(np.arange(len(time)) - len(time) // 2.5, 0) ) # std doubles after half of the time noise = sigma_diag * white_gaussian(len(time)) # input signal + run methods x = rect(time, 100 * Ts, 250 * Ts, 1.0, noise=noise) Uy, UyMC, y, yMC = _conduct_FIRuncFilter_and_MonteCarlo( Ub, b1, blow, kind, runs, sigma_diag, x) # compare FIR and MC results plt.figure(1) plt.cla() plt.plot(time, x, label="input") plt.plot(time, y, label="output FIR direct") plt.plot(time, yMC, label="output FIR MC") plt.xlabel("time [s]") plt.ylabel("signal amplitude [1]") plt.legend() plt.figure(2) plt.cla() plt.plot(time, Uy, label="FIR formula") plt.plot(time, np.sqrt(np.diag(UyMC)), label="Monte Carlo") plt.xlabel("time [s]") plt.ylabel("signal uncertainty [1]") plt.legend() plt.show()
from PyDynamic.uncertainty.propagate_filter import FIRuncFilter def evaluate(signal, filter, case="full/zero/none"): # different covariance matrices if case == "full": U_theta = 1e-2 * np.eye(filter_length) elif case == "zero": U_theta = np.zeros((filter_length, filter_length)) elif case == "none": U_theta = None else: raise NotImplementedError # measure runtime (timeit.timeit is unable to use both local+global variables) start = time.time() y, Uy = FIRuncFilter(signal, sigma_noise, theta, U_theta, blow=None) end = time.time() runtime = end - start return runtime, y, Uy filter_lengths = [10, 50, 100, 200, 500, 1000] cases = ["full", "zero", "none"] results = pd.DataFrame(index=filter_lengths, columns=cases) # prepare signal signal_length = 2000 sigma_noise = 1e-2 # 1e-5 signal = sigma_noise * np.random.randn(signal_length)
def test(fir_unc_filter_input): def legacy_FIRuncFilter(y, sigma_noise, theta, Utheta=None, shift=0, blow=None, kind="corr"): """Uncertainty propagation for signal y and uncertain FIR filter theta A preceding FIR low-pass filter with coefficients `blow` can be provided optionally. Parameters ---------- y : np.ndarray filter input signal sigma_noise : float or np.ndarray float: standard deviation of white noise in y 1D-array: interpretation depends on kind theta : np.ndarray FIR filter coefficients Utheta : np.ndarray, optional covariance matrix associated with theta if the filter is fully certain, use `Utheta = None` (default) to make use of more efficient calculations. See also the comparison given in <examples/Digital filtering/FIRuncFilter_runtime_comparison.py> shift : int, optional time delay of filter output signal (in samples) (defaults to 0) blow : np.ndarray, optional optional FIR low-pass filter kind : string only meaningful in combination with sigma_noise a 1D numpy array "diag": point-wise standard uncertainties of non-stationary white noise "corr": single sided autocovariance of stationary (colored/correlated) noise (default) Returns ------- x : np.ndarray FIR filter output signal ux : np.ndarray point-wise standard uncertainties associated with x References ---------- * Elster and Link 2008 [Elster2008]_ .. seealso:: :mod:`PyDynamic.deconvolution.fit_filter` """ Ntheta = len(theta) # FIR filter size # check which case of sigma_noise is necessary if isinstance(sigma_noise, float): sigma2 = sigma_noise**2 elif isinstance(sigma_noise, np.ndarray) and len( sigma_noise.shape) == 1: if kind == "diag": sigma2 = sigma_noise**2 elif kind == "corr": sigma2 = sigma_noise else: raise ValueError("unknown kind of sigma_noise") else: raise ValueError( f"FIRuncFilter: Uncertainty sigma_noise associated " f"with input signal is expected to be either a float or a 1D array but " f"is of shape {sigma_noise.shape}. Please check documentation for " f"input " f"parameters sigma_noise and kind for more information.") if isinstance( blow, np.ndarray ): # calculate low-pass filtered signal and propagate noise if isinstance(sigma2, float): Bcorr = np.correlate(blow, blow, "full") # len(Bcorr) == 2*Ntheta - 1 ycorr = (sigma2 * Bcorr[len(blow) - 1:] ) # only the upper half of the correlation is needed # trim / pad to length Ntheta ycorr = trimOrPad(ycorr, Ntheta) Ulow = toeplitz(ycorr) elif isinstance(sigma2, np.ndarray): if kind == "diag": # [Leeuw1994](Covariance matrix of ARMA errors in closed form) # can be # used, to derive this formula # The given "blow" corresponds to a MA(q)-process. # Going through the calculations of Leeuw, but assuming # that E(vv^T) is a diagonal matrix with non-identical elements, # the covariance matrix V becomes (see Leeuw:corollary1) # V = N * SP * N^T + M * S * M^T # N, M are defined as in the paper # and SP is the covariance of input-noise prior to the observed # time-interval (SP needs be available len(blow)-time steps into the # past. Here it is assumed, that SP is constant with the first value # of sigma2) # V needs to be extended to cover Ntheta-1 time steps more into # the past sigma2_extended = np.append( sigma2[0] * np.ones((Ntheta - 1)), sigma2) N = toeplitz(blow[1:][::-1], np.zeros_like(sigma2_extended)).T M = toeplitz( trimOrPad(blow, len(sigma2_extended)), np.zeros_like(sigma2_extended), ) SP = np.diag(sigma2[0] * np.ones_like(blow[1:])) S = np.diag(sigma2_extended) # Ulow is to be sliced from V, see below V = N.dot(SP).dot(N.T) + M.dot(S).dot(M.T) elif kind == "corr": # adjust the lengths sigma2 to fit blow and theta # this either crops (unused) information or appends zero-information # note1: this is the reason, why Ulow will have dimension # (Ntheta x Ntheta) without further ado # calculate Bcorr Bcorr = np.correlate(blow, blow, "full") # pad or crop length of sigma2, then reflect some part to the # left and # invert the order # [0 1 2 3 4 5 6 7] --> [0 0 0 7 6 5 4 3 2 1 0 1 2 3] sigma2 = trimOrPad(sigma2, len(blow) + Ntheta - 1) sigma2_reflect = np.pad(sigma2, (len(blow) - 1, 0), mode="reflect") ycorr = np.correlate( sigma2_reflect, Bcorr, mode="valid" ) # used convolve in a earlier version, should make no # difference as # Bcorr is symmetric Ulow = toeplitz(ycorr) xlow, _ = lfilter(blow, 1.0, y, zi=y[0] * lfilter_zi(blow, 1.0)) else: # if blow is not provided if isinstance(sigma2, float): Ulow = np.eye(Ntheta) * sigma2 elif isinstance(sigma2, np.ndarray): if kind == "diag": # V needs to be extended to cover Ntheta time steps more into the # past sigma2_extended = np.append( sigma2[0] * np.ones((Ntheta - 1)), sigma2) # Ulow is to be sliced from V, see below V = np.diag( sigma2_extended ) # this is not Ulow, same thing as in the case of a provided blow # (see above) elif kind == "corr": Ulow = toeplitz(trimOrPad(sigma2, Ntheta)) xlow = y # apply FIR filter to calculate best estimate in accordance with GUM x, _ = lfilter(theta, 1.0, xlow, zi=xlow[0] * lfilter_zi(theta, 1.0)) x = np.roll(x, -int(shift)) # add dimension to theta, otherwise transpose won't work if len(theta.shape) == 1: theta = theta[:, np.newaxis] # NOTE: In the code below wherever `theta` or `Utheta` get used, they need to be # flipped. This is necessary to take the time-order of both variables into # account. (Which is descending for `theta` and `Utheta` but ascending for # `Ulow`.) # # Further details and illustrations showing the effect of not-flipping # can be found at https://github.com/PTB-M4D/PyDynamic/issues/183 # handle diag-case, where Ulow needs to be sliced from V if kind == "diag": # UncCov needs to be calculated inside in its own for-loop # V has dimension (len(sigma2) + Ntheta) * (len(sigma2) + Ntheta) --> # slice a # fitting Ulow of dimension (Ntheta x Ntheta) UncCov = np.zeros((len(sigma2))) if isinstance(Utheta, np.ndarray): for k in range(len(sigma2)): Ulow = V[k:k + Ntheta, k:k + Ntheta] UncCov[k] = np.squeeze( np.flip(theta).T.dot(Ulow.dot(np.flip(theta))) + np.abs(np.trace(Ulow.dot( np.flip(Utheta))))) # static part of uncertainty else: for k in range(len(sigma2)): Ulow = V[k:k + Ntheta, k:k + Ntheta] UncCov[k] = np.squeeze( np.flip(theta).T.dot(Ulow.dot( np.flip(theta)))) # static part of uncertainty else: if isinstance(Utheta, np.ndarray): UncCov = np.flip(theta).T.dot(Ulow.dot( np.flip(theta))) + np.abs( np.trace(Ulow.dot( np.flip(Utheta)))) # static part of uncertainty else: UncCov = np.flip(theta).T.dot(Ulow.dot( np.flip(theta))) # static part of uncertainty if isinstance(Utheta, np.ndarray): unc = np.empty_like(y) # use extended signal to match assumption of stationary signal prior to # first # entry xlow_extended = np.append(np.full(Ntheta - 1, xlow[0]), xlow) for m in range(len(xlow)): # extract necessary part from input signal XL = xlow_extended[m:m + Ntheta, np.newaxis] unc[m] = XL.T.dot( np.flip(Utheta).dot(XL)) # apply formula from paper else: unc = np.zeros_like(y) ux = np.sqrt(np.abs(UncCov + unc)) ux = np.roll(ux, -int(shift)) # correct for delay return x, ux.flatten() # flatten in case that we still have 2D array legacy_y, legacy_Uy = legacy_FIRuncFilter(**fir_unc_filter_input) current_y, current_Uy = FIRuncFilter(**fir_unc_filter_input) assert_allclose( legacy_y, current_y, atol=1e-15, ) assert_allclose( legacy_Uy, current_Uy, atol=np.max((np.max(current_Uy) * 1e-7, 1e-7)), rtol=3e-6, )
def test_FIRuncFilter(filters, signals, lowpasses): # Check expected output for thinkable permutations of input parameters. y, Uy = FIRuncFilter(**filters, **signals, **lowpasses) assert len(y) == len(signals["y"]) assert len(Uy) == len(signals["y"])