@@ -57,7 +57,7 @@ template <typename VariationalSolver> class fpca_power_iteration_impl {
5757 V = std::move (svd.matrixV ());
5858 } else {
5959 Eigen::JacobiSVD<matrix_t > svd (X, Eigen::ComputeThinU | Eigen::ComputeThinV);
60- V = std::move (svd.matrixV ());
60+ V = std::move (svd.matrixV ());
6161 }
6262 // allocate memory
6363 f_.resize (n_dofs_, rank);
@@ -67,17 +67,17 @@ template <typename VariationalSolver> class fpca_power_iteration_impl {
6767
6868 int calibration = (flag & 0b11110 ); // detect calibration strategy
6969 for (int i = 0 ; i < rank; ++i) {
70- // select optimal smoothing level for i-th component
70+ // select optimal smoothing level for i-th component
7171 std::array<double , n_lambda> opt_lambda;
7272 switch (calibration) {
7373 case 0 : { // no calibration
7474 fdapde_assert (lambda_grid.size () == n_lambda);
75- std::copy (lambda_grid.begin (), lambda_grid.end (), opt_lambda.begin ());
75+ std::copy (lambda_grid.begin (), lambda_grid.end (), opt_lambda.begin ());
7676 } break ;
7777 case OptimizeGCV: {
7878 auto gcv_functor = [&](auto lambda) { return gcv_ (X, lambda, V.col (i)); };
7979 // GridOptimizer<n_lambda> optimizer;
80- GridOptimizer<n_lambda> optimizer;
80+ GridOptimizer<n_lambda> optimizer;
8181 opt_lambda = optimizer.optimize (gcv_functor, lambda_grid);
8282 } break ;
8383 case OptimizeMSRE: {
@@ -188,7 +188,7 @@ template <typename VariationalSolver> class fpca_subspace_iteration_impl {
188188 f_norm_.resize (rank);
189189 lambda_.resize (rank, n_lambda);
190190
191- int calibration = (flag & 0b11110 ); // detect calibration strategy
191+ int calibration = (flag & 0b11110 ); // detect calibration strategy
192192 std::array<double , n_lambda> opt_lambda;
193193 switch (calibration) {
194194 case 0 : { // no calibration
@@ -216,7 +216,7 @@ template <typename VariationalSolver> class fpca_subspace_iteration_impl {
216216 f_.col (i) = F.col (i) / f_norm_[i];
217217 s_.col (i) = S.col (i) * f_norm_[i];
218218 }
219- return std::tie (f_, s_);
219+ return std::tie (f_, s_);
220220 }
221221 // observers
222222 const matrix_t & scores () const { return s_; }
@@ -258,13 +258,23 @@ template <typename VariationalSolver> class fpca_subspace_iteration_impl {
258258 }
259259 return std::make_pair (F, S);
260260 }
261+ // finds vectors s, f minimizing \norm{X - s * f^\top}_F^2 + P_{\lambda}(f) and returns the GCV index
262+ template <typename LambdaT>
263+ requires (internals::is_subscriptable<LambdaT, int >)
264+ double gcv_ (const matrix_t & X, int rank, const LambdaT lambda, const matrix_t F0) {
265+ const auto & [F, S] = solve_ (X, rank, lambda, F0);
266+ // evaluate GCV index at convergence
267+ int dor = n_locs_ - smoother_->edf (lambda);
268+ return (n_locs_ / std::pow (dor, 2 )) * (X.transpose () * S - (smoother_->Psi () * F)).squaredNorm ();
269+ }
270+
261271 int n_locs_ = 0 , n_units_ = 0 , n_dofs_ = 0 ;
262272 smoother_t * smoother_; // smoothing variational solver
263273 matrix_t f_; // PCs expansion coefficient vector
264274 matrix_t s_; // PCs scores
265275 std::vector<double > f_norm_; // L^2 norm of estimated PCs
266276 matrix_t lambda_; // selected PCs smoothing level
267-
277+
268278 // subspace iteration algorithm parameters
269279 double tol_ = 1e-6 ;
270280 int max_iter_ = 20 ;
@@ -327,6 +337,7 @@ template <typename VariationalSolver> class fpca_direct_impl {
327337 const matrix_t & scores () const { return s_; }
328338 const matrix_t & loading () const { return f_; }
329339 const std::vector<double >& loadings_norm () const { return f_norm_; }
340+ const matrix_t & lambda () const { return lambda_; }
330341 const smoother_t * smoother () const { return smoother_; }
331342 private:
332343 // finds vectors s, f minimizing \norm{X - s * f^\top}_F^2 + P_{\lambda}(f)
@@ -360,8 +371,8 @@ template <typename VariationalSolver> class fpca_direct_impl {
360371 requires (internals::is_subscriptable<LambdaT, int >)
361372 double gcv_ (const matrix_t & X, int rank, const LambdaT lambda, int flag) {
362373 const auto & [F, S] = solve_ (X, rank, lambda, flag);
363- // evaluate GCV index at convergence
364- int dor = n_locs_ - smoother_-> edf (lambda );
374+ // evaluate GCV index at convergence (note that Tr[S] = \|D^(-1)\|_F^2)
375+ int dor = n_locs_ - invD_. squaredNorm ( );
365376 return (n_locs_ / std::pow (dor, 2 )) * (X.transpose () * S - (smoother_->Psi () * F)).squaredNorm ();
366377 }
367378 int n_locs_ = 0 , n_units_ = 0 , n_dofs_ = 0 ;
@@ -468,7 +479,7 @@ template <typename fPCASolver> class fpca_na_impl {
468479
469480 // MM scheme parameters
470481 double tol_ = 1e-6 ;
471- int max_iter_ = 100 ;
482+ int max_iter_ = 100 ;
472483};
473484
474485} // namespace internals
@@ -565,11 +576,13 @@ template <typename VariationalSolver> class fPCA {
565576 s_ = std::move (s);
566577 f_norm_ = solver_.loadings_norm ();
567578 }
579+ lambda_ = solver_.lambda ();
568580 return std::tie (f_, s_);
569581 }
570582 // observers
571- const matrix_t & scores () const { return s_; }
572- const matrix_t & loading () const { return f_; }
583+ const matrix_t & S () const { return s_; } // scoring matrix
584+ const matrix_t & F () const { return f_; } // loading matrix
585+ matrix_t Fn () const { return smoother_.Psi () * f_; }
573586 const std::vector<double >& loadings_norm () const { return f_norm_; }
574587 const matrix_t & lambda () const { return lambda_; }
575588 private:
0 commit comments