def __init__(self, mu=0, C=0, M=None): """Init allowing for shortcut notation.""" if isinstance(mu, CovMat): raise TypeError("Got a covariance paramter as mu. " + "Use kword syntax (C=...) ?") # Set mu mu = np.atleast_1d(mu) assert mu.ndim == 1 if len(mu) > 1: if M is None: M = len(mu) else: assert len(mu) == M else: if M is not None: mu = np.ones(M) * mu # Set C if isinstance(C, CovMat): if M is None: M = C.M else: if np.isscalar(C) and C == 0: pass # Assign as pure 0! else: if np.isscalar(C): M = len(mu) C = CovMat(C * np.ones(M), 'diag') else: C = CovMat(C) if M is None: M = C.M # Validation if len(mu) not in (1, M): raise TypeError("Inconsistent shapes of (M,mu,C)") if M is None: raise TypeError("Could not deduce the value of M") try: if M != C.M: raise TypeError("Inconsistent shapes of (M,mu,C)") except AttributeError: pass # Assign self.M = M self.mu = mu self.C = C
def assimilate(self, HMM, xx, yy): muC = np.mean(xx, 0) AC = xx - muC PC = CovMat(AC, 'A') self.stats.assess(0, mu=muC, Cov=PC) self.stats.trHK[:] = 0 for k, ko, _, _ in progbar(HMM.tseq.ticker): fau = 'u' if ko is None else 'fau' self.stats.assess(k, ko, fau, mu=muC, Cov=PC)
def assimilate(self, HMM, xx, yy): chrono, stats = HMM.t, self.stats muC = np.mean(xx, 0) AC = xx - muC PC = CovMat(AC, 'A') stats.assess(0, mu=muC, Cov=PC) stats.trHK[:] = 0 for k, kObs, _, _ in progbar(chrono.ticker): fau = 'u' if kObs is None else 'fau' stats.assess(k, kObs, fau, mu=muC, Cov=PC)
def assimilate(self, HMM, xx, yy): Dyn, Obs, chrono, X0, stats = HMM.Dyn, HMM.Obs, HMM.t, HMM.X0, self.stats R, KObs = HMM.Obs.noise.C, HMM.t.KObs Rm12 = R.sym_sqrt_inv Nx = Dyn.M # Set background covariance. Note that it is static (compare to iEnKS). if self.B in (None, 'clim'): # Use climatological cov, ... B = np.cov(xx.T) # ... estimated from truth elif self.B == 'eye': B = np.eye(Nx) else: B = self.B B *= self.xB B12 = CovMat(B).sym_sqrt # Init x = X0.mu stats.assess(0, mu=x, Cov=B) # Loop over DA windows (DAW). for kObs in progbar(np.arange(-1, KObs + self.Lag + 1)): kLag = kObs - self.Lag DAW = range(max(0, kLag + 1), min(kObs, KObs) + 1) # Assimilation (if ∃ "not-fully-assimlated" obs). if 0 <= kObs <= KObs: # Init iterations. w = np.zeros(Nx) # Control vector for the mean state. x0 = x.copy() # Increment reference. for iteration in np.arange(self.nIter): # Reconstruct smoothed state. x = x0 + B12 @ w X = B12 # Aggregate composite TLMs onto B12 # Forecast. for kCycle in DAW: for k, t, dt in chrono.cycle(kCycle): # noqa X = Dyn.linear(x, t - dt, dt) @ X x = Dyn(x, t - dt, dt) # Assess forecast stats if iteration == 0: stats.assess(k, kObs, 'f', mu=x, Cov=X @ X.T) # Observe. Y = Obs.linear(x, t) @ X xo = Obs(x, t) # Analysis prep. y = yy[kObs] # Get current obs. dy = Rm12 @ (y - xo) # Transform obs space. Y = Rm12 @ Y # Transform obs space. V, s, UT = svd0(Y.T) # Decomp for lin-alg update comps. # Post. cov (approx) of w, # estimated at current iteration, raised to power. Cow1 = (V * (pad0(s**2, Nx) + 1)**-1.0) @ V.T # Compute analysis update. grad = Y.T @ dy - w # Cost function gradient dw = Cow1 @ grad # Gauss-Newton step w += dw # Step if dw @ dw < self.wtol * Nx: break # Assess (analysis) stats. final_increment = X @ dw stats.assess(k, kObs, 'a', mu=x + final_increment, Cov=X @ Cow1 @ X.T) stats.iters[kObs] = iteration + 1 # Final (smoothed) estimate at [kLag]. x = x0 + B12 @ w X = B12 # Slide/shift DAW by propagating smoothed ('s') state from [kLag]. if -1 <= kLag < KObs: if kLag >= 0: stats.assess(chrono.kkObs[kLag], kLag, 's', mu=x, Cov=X @ Cow1 @ X.T) for k, t, dt in chrono.cycle(kLag + 1): stats.assess(k - 1, None, 'u', mu=x, Cov=Y @ Y.T) X = Dyn.linear(x, t - dt, dt) @ X x = Dyn(x, t - dt, dt) stats.assess(k, KObs, 'us', mu=x, Cov=X @ Cow1 @ X.T)