Skip to content

Commit

Permalink
start the state weighted eDMD
Browse files Browse the repository at this point in the history
  • Loading branch information
EthanJamesLew committed Apr 7, 2024
1 parent 0ee18bf commit 81dda39
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 4 deletions.
2 changes: 1 addition & 1 deletion autokoopman/core/trajectory.py
Original file line number Diff line number Diff line change
Expand Up @@ -626,7 +626,7 @@ def n_step_matrices(
if weights is None:
W = None
else:
W = np.hstack([weights[idx].flatten()[1:] for idx, _ in items])
W = np.vstack([weights[idx][:-nstep:nstep, :] for idx, _ in items])

return X, Xp, U, W

Expand Down
43 changes: 40 additions & 3 deletions autokoopman/estimator/koopman.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,32 @@ def wdmdc(X, Xp, U, r, W):
return Atilde[:, :state_size], Atilde[:, state_size:]


def swdmdc(X, Xp, U, r, Js, W):
"""State Weighted Dynamic Mode Decomposition with Control (wDMDC)"""
assert len(W.shape) == 2, "weights must be 2D for snapshot x state"

if U is not None:
Y = np.hstack((X, U)).T
else:
Y = X.T
Yp = Xp.T

# compute observables weights from state weights
Wy = np.vstack([(np.abs(J) @ np.atleast_2d(w).T).T for J, w in zip(Js, W)])

# apply weights element-wise
Y, Yp = Wy.T * Y, Wy.T * Yp
state_size = Yp.shape[0]

# compute Atilde
U, Sigma, V = np.linalg.svd(Y, False)
U, Sigma, V = U[:, :r], np.diag(Sigma)[:r, :r], V.conj().T[:, :r]

# get the transformation
Atilde = Yp @ V @ np.linalg.inv(Sigma) @ U.conj().T
return Atilde[:, :state_size], Atilde[:, state_size:]


class KoopmanDiscEstimator(kest.NextStepEstimator):
"""Koopman Discrete Estimator
Expand Down Expand Up @@ -96,9 +122,20 @@ def fit_next_step(
if weights is None:
self._A, self._B = dmdc(G, Gp, U.T if U is not None else U, self.rank)
else:
self._A, self._B = wdmdc(
G, Gp, U.T if U is not None else U, self.rank, weights
)
# TODO: change this condition to be more accurate
if False: # len(weights[0].shape) == 1:
self._A, self._B = wdmdc(
G, Gp, U.T if U is not None else U, self.rank, weights
)
else:
self._A, self._B = swdmdc(
G,
Gp,
U.T if U is not None else U,
self.rank,
[self.obs.obs_grad(xi) for xi in X.T],
weights,
)
self._has_input = U is not None

@property
Expand Down

0 comments on commit 81dda39

Please sign in to comment.