Expose Population proportion_female with default 0.5#1494
Conversation
|
Thank you for contributing to FIMS and opening your first PR here! We are happy to have your contributions. Please ensure that the PR is made to the dev branch and let us know if you need any help! Also, we encourage you to introduce yourself to the community on the introduction thread in our Discussions. |
🎨 Chore: code formatting workflowOur automated workflows cannot run on forks because of permission issues, and thus, we ask that you run the following code locally and push any changes that are created to your feature branch. You will only be reminded of this once per PR. Thank you! Format C++ code
Format R code
styler::style_pkg() # Style R code
roxygen2::roxygenise() # Update documentation
styler::style_pkg() # Style R code again
roxygen2::roxygenise() # Update documentation again
usethis::use_tidy_description() # Style DESCRIPTION filePush changes
|
|
Hi @kellijohnson-NOAA, @nathanvaughan-NOAA, @Andrea-Havron-NOAA. I ran into some implementation questions when implementing this feature and I was hoping if you can point me in the correct direction: The issue I hit is that Question: For a scalar
I am still getting familiar with the TMB architecture and FIMS primitive data types, so please correct me if I am misunderstanding anything. I would appreciate any guidance on which of these approaches best fits the intended design, or whether there is another approach you would prefer. Thanks! |
|
Hey @szu-yun-ko looks like you are making good progress digging into the architecture. Changing proportion_female to a ParameterVector to match the other inputs seems like the best approach. I would suggest setting it up similar to other parameters that default to a scalar value using but can be extended to be time-varying. This shouldn't be any more difficult than using year instead of 1 when referencing the value because fims vectors include a get_force_scalar function to recycle scalar values for vectors. If you set a default 0.5 value that will enable the user to not specify the value if they choose and also allow all the existing examples/tests to function the same. You can look at the setup for log_f_multiplier which is filled with default values if you hit any snags, I'm not sure if we have any other parameters with internal default values. |
6911a5b to
b223631
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1494 +/- ##
==========================================
- Coverage 87.53% 87.33% -0.20%
==========================================
Files 97 97
Lines 8749 8817 +68
Branches 523 520 -3
==========================================
+ Hits 7658 7700 +42
- Misses 1054 1079 +25
- Partials 37 38 +1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
@kellijohnson-NOAA @awilnoaa I think this is ready for review now. At one point I ran into an issue that seemed to be the root cause of most failing CIs, which I want to briefly document in this comment: On main, this->proportion_female[0].initial_value_m = static_cast<double>(0.5);
this->proportion_female[0].estimation_type_m.set("constant");That gives integration tests and manual builds a safe fallback (0.5) without reintroducing the Please let me know if there's anything I'm missing to address, and thanks for the review! |
878e9c9 to
db3808e
Compare
| population->proportion_female.resize(this->proportion_female.size()); | ||
| } | ||
|
|
||
| for (size_t i = 0; i < this->proportion_female.size(); i++) { |
There was a problem hiding this comment.
Correct me if I am wrong, but should proportion_female be bounded to [0, 1] here or earlier in the wrapper? I think with this setup users can pass values outside of [0, 1]?
There was a problem hiding this comment.
Hi @nathanvaughan-NOAA, I was reviewing this and wanted to get your thoughts. If proportion_female can support fixed and random effects, should it also be bounded to [0,1] throughout estimation, in addition to checking the initial value?
There was a problem hiding this comment.
I think we want to error out if a user assigns it to a fixed effect or a random effect because we have no data to estimate the value right now.
There was a problem hiding this comment.
Hey @awilnoaa the [0,1] bounding should be accomplished by using a logit transform of the input value, the same way we use a log transform for things like F_mort to constrain them as positive. @kellijohnson I feel like it's probably more future-proof to leave the potential for estimability than to set a rigid error, there are plenty of things like M or the length at age matrix that aren't practically estimable but are still technically allowed to be estimated.
There was a problem hiding this comment.
I've replaced the hard-errors with warnings for proportion_female's bounding range.
awilnoaa
left a comment
There was a problem hiding this comment.
Eveything looks good, I left two comments on rcpp_population.hpp. I also wanted to ask if proportion_female should be added to rcpp_models.hpp so it shows up in standard output for users to inspect after fitting?
c306f71 to
a7c95ce
Compare
kellijohnson-NOAA
left a comment
There was a problem hiding this comment.
I added some more comments. Additionally, I think we need a test in the testthat tests, demonstrating that if we reduce the proportion female that spawning biomass decreases compared to using a higher value or the default.
| */ | ||
| ParameterVector log_init_naa; | ||
| /** | ||
| * @brief Proportion female in the population. |
There was a problem hiding this comment.
More information here would be great.
| population->proportion_female.resize(this->proportion_female.size()); | ||
| } | ||
|
|
||
| for (size_t i = 0; i < this->proportion_female.size(); i++) { |
There was a problem hiding this comment.
I think we want to error out if a user assigns it to a fixed effect or a random effect because we have no data to estimate the value right now.
wire it through default parameters and initialize_module, and broadcast to the per-age C++ vector at TMB build time.
Switch from a plain double to ParameterVector on PopulationInterface so values stay in sync with live_objects on copy. Wire through set_param_vector, fill a scalar default when unset, and use get_force_scalar(age) in spawning biomass calculations to support scalar or per-age inputs.
Accept the expected default parameter table after adding the proportion_female Population row with default 0.5.
Broadcast a constant length-1 R value to n_ages at TMB build time, default empty vectors to n_ages in Initialize(), and initialize proportion_female in gtest fixtures now that Prepare() no longer resets it.
Follow reviewer guidance: keep length-1 or n_ages on the model side, default missing values in add_to_fims like log_f_multiplier, and use get_force_scalar(age) instead of broadcasting constants to n_ages.
Manual methods::new(Population) builds left proportion_female at Parameter()'s initial_value_m of 0. Integration tests use that path and no longer rely on main's Prepare() loop to force 0.5 each eval.
2314214 to
261bf4e
Compare
|
There are two failed tests in the testthat tests, see the output here. Let us know if you need help fixing them. |
|
Thanks for the review! I should be able to address the comments and implement fixes to pass the test before next Monday. |
|
I think this is ready for review again! I've made some changes to address the comments, and the |
What is the feature?
How have you implemented the solution?
User / R layer
create_default_parameters(): added a singlePopulationrow (proportion_female = 0.5,estimation_type = "constant") so users can edit it with the usual dplyr/tidyr workflow.initialize_module():proportion_femaleis initialized via the standardset_param_vector()path, likelog_Mandlog_init_naa. Unlikelog_f_multiplier, it is not excluded frommodule_fieldsand does not require manual setup in the standardinitialize_fims()workflow.proportion_femaleon the Population Rcpp module (src/fims_modules.hpp). Direct access uses theParameterVectorAPI:pop$proportion_female[1]$value <- 0.35.Rcpp
proportion_femaleas aParameterVectoronPopulationInterface. This resolves the live-object copy sync issue seen when it was a plaindouble.[0]to0.5withestimation_type = "constant", so integration tests and manual builds that skip the tibble do not inheritParameter()'s default of 0.add_to_fims_tmb_internal(), ifproportion_femaleis unset or has invalid size, it is filled with a default scalar value of 0.5 (similar tolog_f_multiplierdefaulting to zeros). Valid sizes are 1 (scalar, recycled across ages) orn_ages(per-age values). Values are copied to the TMBPopulationvector without broadcasting ton_ages.C++ layer
proportion_femaleremains afims::Vector<Type>onfims_popdy::Population, defaulting to length 1 with value 0.5.get_force_scalar(age)so a length-1 vector is recycled across all ages, while a length-n_agesvector supports per-age values.Prepare()loop that forcedproportion_female[age] = 0.5on every run, which previously overwrote user-provided values.Initialize()no longer resizesproportion_femaleton_ages; it only ensures a default exists if the vector is empty.Testing this PR
End-to-end test: verify that the value reaches the TMB model, not only the R interface.
Does the PR impact any other area of the project, maybe another repo?
Instructions for code reviewer
👋Hello reviewer👋, thank you for taking the time to review this PR!
nit:(for nitpicking) as the comment type. For example,nit:I prefer using adata.frame()instead of amatrixbecause ...This PR is now ready to be merged.Checklist