Skip to content

Commit 681747d

Browse files
authored
Merge pull request #31 from CLOCTools/v0.8.2
v0.9.0
2 parents ae14583 + 20f668b commit 681747d

File tree

20 files changed

+309
-139
lines changed

20 files changed

+309
-139
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ set(CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_SOURCE_DIR}/vcpkg/scripts/buildsystems/
1111
############ PROJECT SPECIFICATIONS
1212
################################################################################
1313
# set project name and version (MAJOR.minor.patch)
14-
project(ldsCtrlEst VERSION 0.8.1 LANGUAGES CXX C)
14+
project(ldsCtrlEst VERSION 0.9.0 LANGUAGES CXX C)
1515
include(CTest)
1616
# some name variants I will be using:
1717
set(CMAKE_PROJECT_NAME_CAP LDSCTRLEST)#all caps

include/ldsCtrlEst_h/lds_gaussian_sys.h

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,19 +86,19 @@ class System : public lds::System {
8686
}
8787
/// Set output noise covariance
8888
void set_R(const Matrix& R) {
89-
Reassign(R_,R);
89+
Reassign(R_, R);
9090
do_recurse_Ke_ = true;
9191
};
9292

9393
/// Set estimator gain
9494
void set_Ke(const Matrix& Ke) {
95-
Reassign(Ke_,Ke);
95+
Reassign(Ke_, Ke);
9696
// if users have set Ke, they must not want to calculate it online.
9797
do_recurse_Ke_ = false;
9898
};
9999
/// Set disturbance estimator gain
100100
void set_Ke_m(const Matrix& Ke_m) {
101-
Reassign(Ke_m_,Ke_m);
101+
Reassign(Ke_m_, Ke_m);
102102
// if users have set Ke, they must not want to calculate it online.
103103
do_recurse_Ke_ = false;
104104
};
@@ -113,13 +113,16 @@ class System : public lds::System {
113113
y_ = cx_ + d_;
114114
};
115115

116+
/// System output function: stateless
117+
Vector h_(Vector x) override { return C_ * x + d_; };
118+
116119
/// Recursively update estimator gain
117120
void RecurseKe() override;
118121

119122
// Gaussian-output-specific
120-
Matrix R_; ///< covariance of output noise
123+
Matrix R_; ///< covariance of output noise
121124
bool do_recurse_Ke_{}; ///< whether to recursively calculate estimator gain
122-
}; // System
125+
}; // System
123126
} // namespace gaussian
124127
} // namespace lds
125128

include/ldsCtrlEst_h/lds_poisson_sys.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ class System : public lds::System {
8383
diag_y_.diag() = y_;
8484
};
8585

86+
/// System output function: stateless
87+
Vector h_(Vector x) override { return exp(C_ * x + d_); };
88+
8689
/**
8790
* Recursively recalculate estimator gain (Ke).
8891
*
@@ -100,10 +103,10 @@ class System : public lds::System {
100103

101104
private:
102105
// Poisson-output-specific
103-
Matrix diag_y_; ///< diagonal matrix with elements y
106+
Matrix diag_y_; ///< diagonal matrix with elements y
104107
std::poisson_distribution<size_t>
105108
pd_; ///< poisson distribution for simulating data
106-
}; // System
109+
}; // System
107110
} // namespace poisson
108111
} // namespace lds
109112

include/ldsCtrlEst_h/lds_sys.h

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#define LDSCTRLEST_LDS_SYS_H
3131

3232
#include "lds.h"
33+
#include "lds_uniform_mats.h"
3334

3435
namespace lds {
3536
/// Linear Dynamical System Type
@@ -94,6 +95,13 @@ class System {
9495
*/
9596
virtual void h() = 0;
9697

98+
/**
99+
* @brief system output function (stateless)
100+
* @param x_t state at time t
101+
* @return predicted state at time t + 1
102+
*/
103+
virtual Vector h_(Vector x) = 0;
104+
97105
/// Get number of inputs
98106
size_t n_u() const { return n_u_; };
99107
/// Get number of states
@@ -149,7 +157,7 @@ class System {
149157
/// Set input matrix
150158
void set_B(const Matrix& B) { Reassign(B_, B); };
151159
/// Set process disturbance
152-
void set_m(const Vector& m, bool do_force_assign=false) {
160+
void set_m(const Vector& m, bool do_force_assign = false) {
153161
Reassign(m0_, m);
154162
if ((!do_adapt_m) || do_force_assign) {
155163
Reassign(m_, m);
@@ -180,6 +188,10 @@ class System {
180188
/// Reset system variables
181189
void Reset();
182190

191+
std::vector<UniformMatrixList<kMatFreeDim2>> nstep_pred_block(
192+
UniformMatrixList<kMatFreeDim2> u, UniformMatrixList<kMatFreeDim2> z,
193+
size_t n_pred = 1);
194+
183195
/// Print system variables to stdout
184196
void Print();
185197

@@ -220,7 +232,7 @@ class System {
220232

221233
Matrix Ke_; ///< estimator gain
222234
Matrix Ke_m_; ///< estimator gain for process disturbance
223-
}; // System
235+
}; // System
224236

225237
} // namespace lds
226238

include/ldsCtrlEst_h/lds_uniform_mats.h

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,15 @@ class UniformMatrixList : public std::vector<Matrix> {
3838
private:
3939
// TODO(mfbolus): would rather *uncomment* the below for sake of conversion
4040
// using std::vector<Matrix>::vector;
41+
// don't allow push_back to be used since it doesn't check dims
42+
using std::vector<Matrix>::push_back;
43+
44+
public:
4145
using std::vector<Matrix>::operator=;
4246
using std::vector<Matrix>::operator[];
4347
using std::vector<Matrix>::begin;
4448
using std::vector<Matrix>::end;
4549
using std::vector<Matrix>::size;
46-
47-
public:
4850
using std::vector<Matrix>::at;
4951
/**
5052
* @brief Constructs a new UniformMatrixList.
@@ -139,6 +141,12 @@ class UniformMatrixList : public std::vector<Matrix> {
139141
* @return reference to object
140142
*/
141143
UniformMatrixList<D>& operator=(UniformMatrixList<D>&& that) noexcept;
144+
/**
145+
* @brief appends a matrix to the list
146+
*
147+
* @param mat input matrix
148+
*/
149+
void append(const Matrix& mat);
142150

143151
private:
144152
void CheckDimensions(std::array<size_t, 2> dim);
@@ -167,9 +175,11 @@ inline void UniformMatrixList<D>::Swap(Matrix& that, size_t n) {
167175
return;
168176
}
169177
// if checks pass, perform swap
170-
Matrix tmp = std::move((*this)[n]);
171-
(*this)[n] = std::move(that);
172-
that = std::move(tmp);
178+
// not moving, since it causes memory issues.
179+
// so this method isn't a memory-saver as designed for now
180+
Matrix tmp = (*this)[n];
181+
(*this)[n] = that;
182+
that = tmp;
173183

174184
if (D == kMatFreeDim1) {
175185
this->dim_[n][0] = (*this)[n].n_rows;
@@ -179,6 +189,14 @@ inline void UniformMatrixList<D>::Swap(Matrix& that, size_t n) {
179189
}
180190
}
181191

192+
template <MatrixListFreeDim D>
193+
void UniformMatrixList<D>::append(const Matrix& mat) {
194+
std::array<size_t, 2> dim({mat.n_rows, mat.n_cols});
195+
CheckDimensions(dim);
196+
std::vector<Matrix>::push_back(mat);
197+
dim_.push_back(dim);
198+
}
199+
182200
template <MatrixListFreeDim D>
183201
inline UniformMatrixList<D>& UniformMatrixList<D>::operator=(
184202
const UniformMatrixList<D>& that) {

python/MANIFEST.in

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
include ldsctrlest/*.so
2+
include ldsctrlest/*.pyd
3+
include ldsctrlest/*.lib
4+
include ldsctrlest/*.pdb
5+
include ldsctrlest/*.dylib
6+
include ldsctrlest/*.dll

python/examples/eg_glds_fit.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,6 @@
7878

7979
# compare fit to original without state noise
8080
sys_hat = glds.System(fit)
81-
sys_hat.Q = np.zeros_like(sys_hat.Q)
8281
y_hat, x_hat, _ = sys_hat.simulate_block(u)
8382
y_imp_hat = sys_hat.simulate_imp(n_samp_imp)
8483

@@ -96,7 +95,6 @@
9695
axs[1].legend([l1[0], l2[0]], ["ground truth", "estimated"])
9796
axs[1].set(ylabel="Impulse Response (a.u.)", xlabel="Time (s)")
9897
fig.tight_layout()
99-
fig
10098

10199
# %%
102100
# SSID plot var explained

python/examples/eg_plds_fit.py

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,12 @@
5353
for trial in range(n_trials):
5454
u.append(np.zeros((n_u, n_samp)))
5555
for k in range(1, n_samp):
56-
u_t = 0.975*u[trial][:,k-1] + 1e-1*np.random.normal(size=n_u)
56+
u_t = 0.975 * u[trial][:, k - 1] + 1e-1 * np.random.normal(size=n_u)
5757
u[trial][:, k] = u_t
5858

5959
y, x, z = sys.simulate_block(u)
6060

61-
n_samp_imp = int(np.ceil(0.1/dt))
61+
n_samp_imp = int(np.ceil(0.1 / dt))
6262
y_imp = sys.simulate_imp(n_samp_imp)
6363
t_imp = np.arange(0, n_samp_imp * dt, dt)
6464

@@ -77,7 +77,6 @@
7777

7878
# compare fit to original without state noise
7979
sys_hat = plds.System(fit)
80-
sys_hat.Q = np.zeros_like(sys_hat.Q)
8180
y_hat, x_hat, _ = sys_hat.simulate_block(u)
8281
y_imp_hat = sys_hat.simulate_imp(n_samp_imp)
8382

@@ -87,7 +86,7 @@
8786

8887
fig, axs = plt.subplots(1, 2)
8988
axs[0].semilogy(sing_vals[:n_h], "-o", color=[0.5, 0.5, 0.5])
90-
axs[0].semilogy(sing_vals[:n_h], color='k', linewidth=2)
89+
axs[0].semilogy(sing_vals[:n_h], color="k", linewidth=2)
9190
axs[0].set(ylabel="Singular Values", xlabel="Singular Value Index")
9291

9392
l1 = axs[1].plot(t_imp, y_imp[0].T, "-", c="k", linewidth=2)
@@ -112,13 +111,21 @@
112111
axs[0].plot(t, y[eg_trial][0, :] / dt, "k-")
113112
axs[0].plot(t, y_hat[eg_trial][0, :] / dt, "-", c="gray", linewidth=2)
114113
axs[0].legend(["measurement", "fit"])
115-
axs[0].set(ylabel="Output 1 (events/s)", xlabel="Time (s)", title=f"proportion var explained (training): {pve[0]:0.3f}")
114+
axs[0].set(
115+
ylabel="Output 1 (events/s)",
116+
xlabel="Time (s)",
117+
title=f"proportion var explained (training): {pve[0]:0.3f}",
118+
)
116119

117120
axs[1].plot(t, y[eg_trial][1, :] / dt, "k-")
118121
axs[1].plot(t, y_hat[eg_trial][1, :] / dt, "-", c="gray", linewidth=2)
119-
axs[1].set(ylabel="Output 2 (events/s)", xlabel="Time (s)", title=f"proportion var explained (training): {pve[1]:0.3f}")
122+
axs[1].set(
123+
ylabel="Output 2 (events/s)",
124+
xlabel="Time (s)",
125+
title=f"proportion var explained (training): {pve[1]:0.3f}",
126+
)
120127

121-
axs[2].plot(t, u[eg_trial].T, 'k')
128+
axs[2].plot(t, u[eg_trial].T, "k")
122129
axs[2].set(ylabel="Input (a.u.)", xlabel="Time (s)")
123130

124131
fig.tight_layout()
@@ -131,18 +138,20 @@
131138

132139
# %%
133140
# Refit by E-M
134-
calc_dynamics = True #calculate dynamics (A, B mats)
135-
calc_Q = True #calculate process noise cov (Q)
136-
calc_init = True #calculate initial conditions
137-
calc_output = True #calculate output (C)
138-
calc_measurement = True #calculate output noise (R)
141+
calc_dynamics = True # calculate dynamics (A, B mats)
142+
calc_Q = True # calculate process noise cov (Q)
143+
calc_init = True # calculate initial conditions
144+
calc_output = True # calculate output (C)
145+
calc_measurement = True # calculate output noise (R)
139146
max_iter = 50
140147
tol = 1e-2
141148

142149
em = plds.FitEM(fit, u_train, z_train)
143150

144151
start = time.perf_counter()
145-
fit_em = em.Run(calc_dynamics, calc_Q, calc_init, calc_output, calc_measurement, max_iter, tol)
152+
fit_em = em.Run(
153+
calc_dynamics, calc_Q, calc_init, calc_output, calc_measurement, max_iter, tol
154+
)
146155
stop = time.perf_counter()
147156
print(f"Finished EM fit in {(stop-start)*1000} ms.")
148157

@@ -162,9 +171,9 @@
162171
axs[0].legend(["measurement", "EM re-estimated"])
163172
axs[0].set(ylabel="Output (events/s)")
164173

165-
axs[1].plot(t, z[eg_trial][0, :], 'k')
174+
axs[1].plot(t, z[eg_trial][0, :], "k")
166175

167-
axs[2].plot(t, u[eg_trial].T, 'k')
176+
axs[2].plot(t, u[eg_trial].T, "k")
168177
axs[2].set(ylabel="Input (a.u.)", xlabel="Time (s)")
169178

170179
fig.tight_layout()
@@ -175,7 +184,7 @@
175184
fig, axs = plt.subplots(1, 2)
176185

177186
l1 = axs[1].plot(t_imp, y_imp[0].T, "-", c="k", linewidth=2)
178-
l2 = axs[1].plot(t_imp, y_imp_hat_em[0].T, "-", c='gray', linewidth=2)
187+
l2 = axs[1].plot(t_imp, y_imp_hat_em[0].T, "-", c="gray", linewidth=2)
179188
axs[1].legend([l1[0], l2[0]], ["ground truth", "EM re-estimated"])
180189
axs[1].set(ylabel="Impulse Response (a.u.)", xlabel="Time (s)")
181190
fig.tight_layout()
@@ -192,13 +201,21 @@
192201
axs[0].plot(t, y[eg_trial][0, :] / dt, "k-")
193202
axs[0].plot(t, y_hat_em[eg_trial][0, :] / dt, "-", c="gray", linewidth=2)
194203
axs[0].legend(["measurement", "EM re-estimated"])
195-
axs[0].set(ylabel="Output 1 (a.u.)", xlabel="Time (s)", title=f"EM-refit proportion var explained (training): {pve_em[0]:0.3f}")
204+
axs[0].set(
205+
ylabel="Output 1 (a.u.)",
206+
xlabel="Time (s)",
207+
title=f"EM-refit proportion var explained (training): {pve_em[0]:0.3f}",
208+
)
196209

197210
axs[1].plot(t, y[eg_trial][1, :] / dt, "k-")
198211
axs[1].plot(t, y_hat_em[eg_trial][1, :] / dt, "-", c="gray", linewidth=2)
199-
axs[1].set(ylabel="Output 2 (a.u.)", xlabel="Time (s)", title=f"EM-refit proportion var explained (training): {pve_em[1]:0.3f}")
212+
axs[1].set(
213+
ylabel="Output 2 (a.u.)",
214+
xlabel="Time (s)",
215+
title=f"EM-refit proportion var explained (training): {pve_em[1]:0.3f}",
216+
)
200217

201-
axs[2].plot(t, u[eg_trial].T, 'k')
218+
axs[2].plot(t, u[eg_trial].T, "k")
202219
axs[2].set(ylabel="Input (a.u.)", xlabel="Time (s)")
203220

204221
fig.tight_layout()

0 commit comments

Comments
 (0)