Skip to content

Make ismutable a type-parameter of FunctionMap #194

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

Merged
merged 17 commits into from
Feb 3, 2023
Merged

Conversation

dkarrasch
Copy link
Member

This takes a small step towards what I think is wide-spread in the SciML/DiffEq world: making mutability a type-parameter. This allows to solve #193 by multiple dispatch, rather than querying a specific field of all factors/summands. The downside is that previous constructor calls are potentially not fully inferred, see the changed tests. If we advise users, however, to use the FunctionMap constructor explicitly and provide the mutability information, that is most likely statically known anyway, directly, then that shouldn't do much harm. In any case, the current design is such that the "old" behavior still works, it's just deprecated. What I find nice is that for out-of-place FunctionMaps, A*B*C*D*x first folds from the left, so that we first create a CompositeMap until we hit x, from where it folds from the right, without going through 3-arg mul!.

@codecov
Copy link

codecov bot commented Oct 12, 2022

Codecov Report

Base: 99.47% // Head: 99.41% // Decreases project coverage by -0.07% ⚠️

Coverage data is based on head (8cb8af4) compared to base (d25eb05).
Patch coverage: 99.04% of modified lines in pull request are covered.

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #194      +/-   ##
==========================================
- Coverage   99.47%   99.41%   -0.07%     
==========================================
  Files          19       19              
  Lines        1530     1534       +4     
==========================================
+ Hits         1522     1525       +3     
- Misses          8        9       +1     
Impacted Files Coverage Δ
src/fillmap.jl 100.00% <ø> (ø)
src/functionmap.jl 98.79% <96.96%> (-1.21%) ⬇️
src/LinearMaps.jl 100.00% <100.00%> (ø)
src/blockmap.jl 99.34% <100.00%> (+<0.01%) ⬆️
src/composition.jl 100.00% <100.00%> (ø)
src/linearcombination.jl 100.00% <100.00%> (ø)
src/show.jl 100.00% <100.00%> (ø)

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

☔ View full report at Codecov.
📢 Do you have feedback about the report comment? Let us know in this issue.

@dkarrasch
Copy link
Member Author

@JeffFessler I suspect you are using FunctionMaps heavily. Can you please try your packages with this PR branch and report back any new issues? I believe nothing should break, only some deprecation warnings should appear, but you never know.

@dkarrasch
Copy link
Member Author

Sorry I messed a few things up while splitting. Seems fixed now.

@JeffFessler
Copy link
Member

JeffFessler commented Jan 22, 2023

Ok, I did a test of LinearMapsAA.jl and it passed, but with only one deprecation warning so maybe that package does not actually use so many FunctionMaps. So then I also tested MIRT.jland again it passed with exactly one deprecation warning. Maybe that means that @deprecate warns only once (I could not tell from Julia manual). So I guess all of this bodes well for this PR. I suspect that many of my uses of FMs are spread out in many Literate examples that are not so easy to test, but we'll find out in time...

I have not done much (if any) testing of PR branches of others so I want to describe the approach so that you can confirm if I did it properly. I started Julia 1.8 in an empty directory, then:

] activate .
add LinearMaps#dk/iipfunctionmap
add LinearMapsAA
test LinearMapsAA
add MIRT
test MIRT

So LTGM overall, and I am sure that #193 will bite me too at some point so I am in favor of this direction!

@dkarrasch
Copy link
Member Author

Thank you very much for the detailed report! If you received deprecation warnings (no matter how many), then it grabbed the right branch. I'm glad that tests pass, too. I hope that FunctionMap construction is not done in hot loops, so the deprecation warnings should not yield a performance penalty. I guess this means that it is fine to release this with a minor version bump. For the future, I believe this will allow for some memory optimizations, even though we don't want to dive too deep in there.

@fredrikekre
Copy link
Member

A bit of a drive-by comment, but I personally find FunctionMap{T,true}(...) to be less readable compared to FunctionMap{T}(..., ismutating=true). I tried to look at the code, but didn't see where the new parameter is used for dispatch. Does the parameter improve something external or is it just to get rid of ifs internally?

@dkarrasch
Copy link
Member Author

Drive-by comments are always welcome! There is dispatch on IIPFunctionMap vs. OOPFunctionMap in linearcombinations.jl and composed.jl. And yes, it's used to catch cases where all FunctionMaps allocate the result array themselves. I don't have a strong preference, it just reminded me that this has been standard in the DiffEq world for a long time, but it could be solved equally well via branching internally I guess. Any thoughts?

@JeffFessler
Copy link
Member

less readable compared to FunctionMap{T}(..., ismutating=true)

I tend to agree. And the kwarg approach has the advantage of being amenable to splatting kwargs...
I prefer that approach for the T= argument too, but I probably am an outlier there.
I am talking about an outer constructor only; I realize the type itself and the inner constructor could have all sorts of parameters like FunctionMap{T, F1, F2, iip}(...)

@dkarrasch
Copy link
Member Author

Hm, another option could be to make LinearMaps.ismutating a "trait" for all LinearMap subtypes. That would allow asking a LinearCombination/CompositeMap whether all its maps are mutating, whereas the current design only catches cases where out-of-place action is due to FunctionMaps. But other, custom map types could have the same, and we would miss that. And we could even ask individual maps and do something with that information.

@dkarrasch
Copy link
Member Author

I realized we already have some kind of the necessary machinery: MulStyle, which so far has only ThreeArg and FiveArg, but which could be extended to TwoArg, meaning A*x. Reflecting in-place vs. out-of-place behaviour reasonably via MulStyle, however, requires that this information is available in the type domain. So how about adding the type parameter iip, but not deprecate the old constructors? The only issue with that is that the iip parameter may not be inferred from the kwarg constructors. If, however, between map construction and extensive usage is a function barrier, that should be no problem. Thoughts?

@JeffFessler
Copy link
Member

I think it is fine not to have type inference for the kwarg constructors because (at least in my use cases) construction is done just once in an initialization phase and not in any tight loop. Maintaining the old constructors sounds convenient to me.

Comment on lines 14 to 25
alloc = @allocated similar(v)
Loop = @inferred CS + CS + CS
@test Loop * v ≈ 3cumsum(v)
@test (@allocated Loop * v) <= 3alloc
Loop = @inferred CS + CS; Loop * v
@test (@allocated Loop * v) <= 2alloc
Lmix = @inferred CS + CS + CS!; Lmix * v
@test (@allocated Lmix * v) <= 3alloc
Lmix = @inferred CS + CS + CS!; Lmix * v
@test (@allocated Lmix * v) <= 3alloc
Lmix = @inferred CS! + (CS + CS); Lmix * v
@test (@allocated Lmix * v) <= 3alloc
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests are all sharp. They fail on the currently released version!

@dkarrasch
Copy link
Member Author

This is now new-feature-only, nothing breaks (except for the lack of complete type inference for the convenience constructor). Thanks @fredrikekre for retriggering some thought, thanks @JeffFessler for your comments and testing!

@dkarrasch dkarrasch merged commit bc62f53 into master Feb 3, 2023
@dkarrasch dkarrasch deleted the dk/iipfunctionmap branch February 3, 2023 08:36
@dkarrasch
Copy link
Member Author

BTW, I changed the default values of dest and source in _compositemul! to nothing, and moved the allocation to the function body (if necessary). We don't use these temporary arrays internally, and any external user will take care of the right size and eltype themselves.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants