Conversation
Bumps [actions/cache](https://github.com/actions/cache) from 4 to 5. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](actions/cache@v4...v5) --- updated-dependencies: - dependency-name: actions/cache dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com>
WardBrian
left a comment
There was a problem hiding this comment.
A few comments/questions, but overall I like the look of the code!
Responding to your comments in the PR:
- Runset being left out of the result entirely makes sense. We will have to see how this feels when we tackle something with more moving parts like the MCMC class, but for now I agree.
- Repr changes make sense.
- Stdout file is good. We may want to also do the same thing with diagnostic files and profiling files, which gets us closer to just including the whole Runset again... maybe we have a small dataclass that is like a cut-down runset and stores the relevant files, can be re-used across all of them?
- I agree, it makes sense to replace
csvwithoutputacross the board, both in the save functions and thefrom_csvfunction. This would make sense going forward anyway, c.f. stan-dev/design-docs#58 - Yep, totally agree that InferenceMetadata might want to take a hike when all is said and done. The header could be stored in each class, or in the cut-down-Runset idea ("
FileInfo"?)
|
@WardBrian Working through some of the other methods. For I see we could maybe parse that info out of the |
|
Hm, that is indeed tricky. I think we should add the piece of information that currently ends up in stdout ( Looking at the code, it looks like the only place we actually check the return codes is in model.py, before we call the CmdStanMLE constructor, so maybe it is fine? |
|
Yeah. In my local implementation, I take It does seem like the kind of thing that should be available in the CSV? Like a |
This is true for the existing |
|
Yeah good point, this is the snippet from for i in range(len(runset._retcodes)):
runset._set_retcode(i, 0)
return CmdStanMLE(runset)Looks like in recreating the runset, we just manually set all return codes to 0, which is just assuming convergence I believe. |
|
Yeah, so we won't be doing any worse, at least! |
|
@WardBrian Is there an equivalent to saving the metric json for the variational method? Looking at CmdStanVB now and we parse the variational eta value from the stancsv comments, but afaik we don't have an alternative way to source it for now. |
|
There's no preconditioning in our two variational inference implementations, ADVI and Pathfinder, so there's no equivalent of a metric/mass matrix/preconditioner. |
|
@bob-carpenter I believe @amas0 was asking just if we wrote the metadata from the algorithm as a json, similar to the metric in sampling, not if there was actually a preconditioner Unfortunately to my knowledge the answer to this is no. It's a shame to have to keep all the comment-reading machinery around for something as rarely-needed as the final eta value from ADVI... |
|
Sorry for the confusion. Keeping things around for A better approach for |
|
Yeah, in particular, I was referring to this section that appears in the output CSV if you run the variational method: lp__,log_p__,log_g__,theta
# Stepsize adaptation complete.
# eta = 0.1
0,0,0,0.5838444I just meant that we output the algorithm stepsize info to the CSV in the same way that we write the sampler adaptation output (as comment lines after the column headers). But in the sample case, we can output equivalent info to a metric JSON file, but that doesn't seem to be the case for the variational method here. So, I guess the question is what do we want to do with this in the near term? I think it makes sense to have it be an additional column as Bob suggested? I suppose the question is whether it is important enough to maintain the comment parsing in the near term or should we drop keeping track of it for now until an alternative source is available? |
|
I'm not sure it makes sense as a column, since the advi output is only the one row of the varational parameters, in which eta has already been adapted, and then forward sampling iterations Github code search doesn't seem to find any public code which actually calls Maybe we preserve a dumber version of comment parsing just for that, i.e. a function that just does for line in file:
if line.startswith("# eta ="):
return float(line.split('=')[1])Rather than all the present machinery? We can also try to add a structured/json output like for the metric, but that would take longer to thread through the cmdstan code and we'd still need something in the meantime |
|
Ah, because we now pass the names we’d like as a comma separated list? That’s nice |
|
Yeah, it makes it so that if we run a model via cmdstanpy then we get a pretty clean output set. I think it was also necessary to maintain the naming convention discussed in #835 now that we have the config in the mix? The underlying tension here I think is between whether the output files were generated by cmdstan directly or indirectly via cmdstanpy. In the latter case, we get a good amount of control to have a clean output to work with. In the former case, it's trickier. |
|
How about:
Reasonable? |
|
I'm good with that. |
WardBrian
left a comment
There was a problem hiding this comment.
Ok, I'm up to 96/103 'viewed' on this, which means I mostly have only the big files like GQ, MCMC, and base left. A few questions in the mean time
This establishes two primary pathways for loading CmdStan fits into cmdstanpy: discovery based on expected file naming formats or explicit file listing for non-standard cases.
|
Okay I implemented a refactor of the To start, I finally the loading from file logic into its own
This diagram shows visually the valid paths of how one can build a fit via this function: flowchart TD
A["from_output_files"]
B["Single path provided"]
C["All files explicitly passed"]
A --> B
A --> C
D["Directory"]
E["Config JSON"]
F["Stan CSV"]
G["Find sibling config JSON"]
B --> D
B --> E
B --> F
F --> G
H["Build FitFiles manifest"]
E --> H
G --> H
I["Find all valid Configs"]
D --> I
J["One unique valid config (multiple configs from the same sample fit produce the same config)"]
K["Two unique valid configs (Laplace edge case with mode fit)"]
I --> J
I --> K
J --> H
K --> H
L["Classify each input file by kind"]
C --> L
M["Verify minimally valid set of files included (no discovery)"]
L --> M
M --> H
N["Construct StanFit from FitFiles"]
H --> N
If one wishes to be more explicit, of course, one can always call a particular StanFit's |
|
Okay I think I addressed the comments you had. There's a section of the docs that needs to be rewritten if we decide to go through with these changes, so I'll get to that in a bit. |
WardBrian
left a comment
There was a problem hiding this comment.
Sorry for my delay! I'm quite happy with how things are working
There was a problem hiding this comment.
It feels like this could be simplified even more using the new base class. For example, should _draws_start be in the base classes instead? (I think it's more or less equivalent to the new _draws_for_inits?)
There was a problem hiding this comment.
I gave a shot at looking at what _draws_start in the base class would look like, but I didn't feel it was much of an improvement.
That being said, I did make a handful of changes that I think are cleanups of some of the longstanding code in here (also fixed up some bugs that AI flagged).
Let me know if you think there are any other pathways you wanna go here.
There was a problem hiding this comment.
This file is a lot of code but it all looks pretty reasonable. I do wonder if it should be split up or if the parts that are e.g. super-Laplace specific be moved to the laplace.py file
There was a problem hiding this comment.
I did another pass on this file -- some of the laplace stuff made sense to move out to laplace.py. The file probably could be broken up further, but I wasn't sure if splitting this logic across multiple files was better than just a ~700 line file? If you have a particular suggestion, I can see how that might play out.
|
As a side note, I made an update to the docsrc conf.py to remove a check on a nearly 10 year old sphinx version? I think we can probably assume that we'll have newer versions than that? |
|
That seems like a safe bet… I haven’t forgotten about this PR, just had a bunch of other stuff to do with the 2.40 release. Hopefully next week I will have enough time to get this review done and the PR merged |
|
No worries! I figure if it took me the better part of a year to get this together, what's a few more weeks. Appreciate all the comments. |
PR intending to close #785 and implementing ideas discussed in #848.
Okay. I'll start by saying that the line diff is highly inflated due to needing to replace/generate large numbers of test files to be compatible with the new output files that are being consumed. The actual diff in the core code is:
So still a major PR, but not as insane as the top-line numbers look.
This is a major refactor of how stanfit objects are structured and constructed. The major points are:
etavalue is the only survivor). Each method now has a typed Pydantic object to validate/parse the config JSON and is stored infit.config.RunSets. The stanfit objects are all now dataclasses constructed directly from the output files of the cmdstan process. This relegates RunSet's role to constructing the cmdstan argument/output file name patterns.CmdStanMLEtakes aconvergedargument on construction sourced from the retcodes of the process (assumed true when from files).StanFitbase class which collects a good chunk of code that was getting repeated. This breaks down further intoSingleFileFit(MLE/VB/Laplace/Pathfinder) andMultiChainFit(Sample/GQ).from_csvis nowfrom_output_filessave_csvfileis nowsave_output_filesfit.runsetis gonefrom_output_files, we require config JSONs next to the CSV filesAlso, since we are on version 2.39 now it seemed a good time to bump the minimum version to 2.37 (this simplified some things).
There are some things that I think can still be improved, but this PR is already close to getting out of hand and I'd like to defer those to try to focus on getting this one "done". Just as note, these are:
stanfit/__init__.pybothers me. When we pulled everything out of a CSV, we didn't really have to worry about it.That being said, I think the solution here might be to promote the config to be the primary file for resolving the others. It includes explicit references to other output files and a proper Stanfit could be built starting from it. Right now we have to crawl around the output dir to find the appropriate output files. The config file, since it is generated by cmdstan, should always have accurate references to the file locations (provided they hadn't been moved).
Would really appreciate some thoughts on these changes @WardBrian, especially if any of these changes would cause issues for how this library is used with dev versions of cmdstan.
Disclaimer: AI was used throughout this PR -- critically for helping with regenerating the appropriate test files and applying initial prototype structures across the different methods. All code in this PR was reviewed by me before marking it as ready.