-
Notifications
You must be signed in to change notification settings - Fork 18
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
Substitute types by abstractions. #100
Conversation
@@ -16,9 +16,9 @@ Container to store data samples as columns in an array. | |||
""" | |||
struct DataContainer{FT <: Real} | |||
#stored data, each piece of data is a column [data dimension × number samples] | |||
stored_data::Array{FT, 2} | |||
stored_data::AbstractMatrix{FT} |
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.
https://docs.julialang.org/en/v1/manual/performance-tips/#Avoid-fields-with-abstract-containers
And same goes for elsewhere.
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.
I would say most of infa here isn't performance critical so dynamic dispatch on a field is fine if all the computation is in the following LA operations.
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.
A concern that can be limiting for the package is unnecessary memory use. In some cases, we want to store covariances that are extremely sparse, or even diagonal. Casting them and storing them as a Matrix{FT} then takes up much more space than e.g. Diagonal {FT}. Whichever solution solves this problem would work. Let me know how to proceed.
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.
I would write it as generically as possible and then if there is some performance hotspots after a bit of profiling they should be straightforward to address. You don't need to specialize on every Matrix type here (as you said there are many) so IMO what is written is fine.
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.
I wasn't suggesting to specialize on every type of matrix, just to make the type (and its properties) concrete. This was @glwagner's suggestion, too. It's not much overhead to have very concrete types and simply not specialize on methods that use it.
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.
then you cannot mix and match representations (different matrix types might be required for different fields). Until performance is actually an issue I wouldn't worry about it and then you can go back and parameterize over type constraints for specialization when those constraints are better understood.
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.
for calibration the costs are going to be dominated by actually running the models and IO, everything else I'm guessing won't matter much (~python speed is fine) but we'll see as we go
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.
A concern that can be limiting for the package is unnecessary memory use. In some cases, we want to store covariances that are extremely sparse, or even diagonal. Casting them and storing them as a Matrix{FT} then takes up much more space than e.g. Diagonal {FT}. Whichever solution solves this problem would work. Let me know how to proceed.
@ilopezgp the point that @charleskawczynski was making is the difference between
struct DataContainer{FT <: Real}
#stored data, each piece of data is a column [data dimension × number samples]
stored_data::AbstractMatrix{FT}
and
struct DataContainer{M <: AbstractMatrix}
#stored data, each piece of data is a column [data dimension × number samples]
stored_data::M
In the first case, stored_data
is abstractly typed (versus concretely typed), which means that the compiler cannot infer the type of stored_data
given the type DataContainer
. This has performance penalties (which @jakebolewski argues are not important). But it's utterly fundamental and so an important point to grasp.
Writing M <: AbstractMatrix
will preclude UniformScaling
. Omitting <: AbstractMatrix
is the conservative choice. You might also write M <: Union{AbstractMatrix, UniformScaling}
. However this is risky because you may be failing to anticipate other valid matrix-like types.
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.
I understand the difference between abstract types and parameterized types, thank you. I think the conversation was about whether it practically matters in this application.
51303d5
to
04af23d
Compare
04af23d
to
e3323c9
Compare
Could I ask that you add the specific changes to the PR comment as a reference for the conventions we take? i see currently
And on from Greg's point, we currently will exclude UniformScaling. (is this just for aesthetic?) |
UniformScaling was in fact a specific request in issue #99, so we should include it. |
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.
This is a good step towards greater flexibility! LGTM and addresses #99
Add tests.
e3323c9
to
525b500
Compare
Closes #99 (the specific problems initially raised). |
bors r+ |
Build succeeded: |
132: [WIP] Substitute types with abstractions r=tsj5 a=tsj5 This PR implements the abstract typing done in CliMA/EnsembleKalmanProcesses.jl#100, e.g. `Array{FT, 2}` → `AbstractMatrix{FT}`, in order to be consistent with that dependency. See the discussion concerning performance at that PR; use of abstract types is [recommended against](https://docs.julialang.org/en/v1/manual/performance-tips/#Avoid-fields-with-abstract-containers) for perf reasons, but the rationale here is that the code is essentially "glue" rather than numerical routines appearing in hot loops, so writing for generality over perf is justified. Another downside is that existing abstract types aren't "abstract" enough, e.g. `UniformScaling` is not a subtype of `AbstractMatrix` and must be handled separately. As a benefit, the changes made here result in some method signatures being more strongly typed than they are in `master`, allowing us to replace repeated code with multiple dispatch ("Don't Repeat Yourself"). `MCMC` is changed to a mutable struct, instead of continuing the current code's practice of enabling mutability by making fields of the struct 1x1 Arrays instead of scalars. This change is a moot point, however, since it will be overridden by PR #130. Co-authored-by: Thomas Jackson <[email protected]>
Relaxes the allowed types of some objects and input args for improved generalization:
For covariance matrices in output space,
Tests involving different matrix types are now included for all Ensemble Kalman Processes.