-
Notifications
You must be signed in to change notification settings - Fork 42
Reuse solver residual in calibrator #231
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
430cf55
027df26
04ab418
eb80bf1
27326ea
1c9a670
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -26,6 +26,11 @@ LevenbergMarquardtOptimizer::LevenbergMarquardtOptimizer( | |||||
| mat = Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic>(num_active, | ||||||
| num_active); | ||||||
| vec = Eigen::Matrix<double, Eigen::Dynamic, 1>::Zero(num_active); | ||||||
|
|
||||||
| // Set up the solver system so that its residual assembly can be reused for a | ||||||
| // single observation at a time. | ||||||
| system = SparseSystem(num_vars); | ||||||
| system.reserve(model); | ||||||
| } | ||||||
|
|
||||||
| Eigen::Matrix<double, Eigen::Dynamic, 1> LevenbergMarquardtOptimizer::run( | ||||||
|
|
@@ -68,14 +73,46 @@ void LevenbergMarquardtOptimizer::update_gradient( | |||||
| jacobian.setZero(); | ||||||
| residual.setZero(); | ||||||
|
|
||||||
| // Push the current parameter vector into the model so that the solver's | ||||||
| // residual assembly uses it. | ||||||
| for (size_t k = 0; k < num_params; k++) { | ||||||
| model->update_parameter_value(k, alpha[k]); | ||||||
| } | ||||||
|
|
||||||
| // Assemble the parameter-dependent constant system contributions (E and F) | ||||||
| // once for the current parameters. | ||||||
| model->update_constant(system); | ||||||
|
|
||||||
| Eigen::Matrix<double, Eigen::Dynamic, 1> y_dpoint(num_vars); | ||||||
| Eigen::Matrix<double, Eigen::Dynamic, 1> dy_dpoint(num_vars); | ||||||
|
|
||||||
| // Assemble gradient and residual | ||||||
| for (size_t i = 0; i < num_obs; i++) { | ||||||
| // Copy the observation for this datapoint into Eigen vectors. | ||||||
| for (size_t k = 0; k < num_vars; k++) { | ||||||
| y_dpoint[k] = y_obs[i][k]; | ||||||
| dy_dpoint[k] = dy_obs[i][k]; | ||||||
| } | ||||||
|
|
||||||
| // Reuse the residual of the solver: r = -C - E*ydot - F*y. This is the | ||||||
| // same residual the solver assembles, so it does not need to be redefined | ||||||
| // here. | ||||||
| model->update_solution(system, y_dpoint, dy_dpoint); | ||||||
| system.update_residual(y_dpoint, dy_dpoint); | ||||||
| residual.segment(num_eqns * i, num_eqns) = system.residual; | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
system.residual has max(num_vars, num_eqns) slots (see comment at line 32), which is num_vars in the calibrator, since there are no boundary condition blocks. However, only the first num_eqns slots ever get filled in, the rest stay zero. .head(num_eqns) keeps just those first num_eqns slots so the result fits into the num_eqns-sized segment. |
||||||
|
|
||||||
| // Assemble the Jacobian of the residual with respect to the parameters. | ||||||
| for (size_t j = 0; j < model->get_num_blocks(true); j++) { | ||||||
| auto block = model->get_block(j); | ||||||
| // Blocks without calibration parameters do not contribute to the | ||||||
| // Jacobian (e.g. a normal junction). | ||||||
| if (block->global_param_ids.empty()) { | ||||||
| continue; | ||||||
| } | ||||||
| for (size_t l = 0; l < block->global_eqn_ids.size(); l++) { | ||||||
| block->global_eqn_ids[l] += num_eqns * i; | ||||||
| } | ||||||
| block->update_gradient(jacobian, residual, alpha, y_obs[i], dy_obs[i]); | ||||||
| block->update_gradient(jacobian, alpha, y_obs[i], dy_obs[i]); | ||||||
| for (size_t l = 0; l < block->global_eqn_ids.size(); l++) { | ||||||
| block->global_eqn_ids[l] -= num_eqns * i; | ||||||
| } | ||||||
|
|
||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The calibrator's model omits boundary condition blocks (their values come from the observations instead), so num_eqns and num_vars need not match here as they do for a full simulation. Size the system by the larger of the two so that both equation-row and variable-column indices fit.