-
Notifications
You must be signed in to change notification settings - Fork 10
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
Fix Backend Compatibility #17
base: master
Are you sure you want to change the base?
Conversation
as it requires MKLSparse
Julia doesn't allow arbitrary matrix to be used as-is in ldiv/rdiv
src/DiffTests.jl
Outdated
num2num_5(x) = 1. / (1. + exp(-x)) | ||
|
||
const NUMBER_TO_NUMBER_FUNCS = (num2num_1, num2num_2, num2num_3, | ||
num2num_4, num2num_5, identity) | ||
const NUMBER_TO_NUMBER_FUNCS = [num2num_1, num2num_2, num2num_3, |
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.
Can you explain why you switched the constant tuples to arrays?
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 consistency with matrix2matrix functions, which are extended in DiffTestsMKLSparseExt. I can revert num2num to be tuples again.
But out of curiosity - why they were tuples in the first place? What kind of optimisation does it trigger?
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 assume because using tuples reduces allocations.
I don't think it matters much but I'd suggest reverting these changes in the PR because 1) they seem unrelated and 2) it is not clear that they improve anything (on the contrary, they increase allocations).
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 can revert it for the sets that are not modified in the extension. BTW, how much would it reduce memory overhead (considering that in return Julia would have to construct a tuple type)?
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.
🤷
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'm a fan of keeping changes minimal and avoiding unrelated changes. So I'd suggest reverting these changes, regardless of whether they can/should be changed or not. For consistency, I think it would make sense to just use a tuple in the extension as well - and to open an issue or separate PR if you would like to change these tuples to arrays.
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.
It is possible to replace a tuple with the extended one (although I would then have to drop const
from the declaration); the code would look a little bit more convoluted.
But if you mean that the extension should define its own set of sparse functions to test -- that would require adding support for that set of functions in all AD backend tests suites.
I am also a fan of minimal changes, but there are also consistency and maintainability considerations. For this PR I thought it would be strange to change some function sets into arrays and keep others as tuples, or rewrite tuples from the extension. But if you, as a maintainer, prefer minimal changes, I can minimize them.
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.
Oh, I missed that the extension modifies arrays in the package. That does not seem OK to me, I don't think extensions are supposed to do that. For instance, this means that the resulting state depends on the order in which (possibly additional) extensions are loaded etc.
So in case you want to add an extension, I think a different design is needed anyway.
And yes, I very much would prefer more minimal changes.
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.
That does not seem OK to me, I don't think extensions are supposed to do that. For instance, this means that the resulting state depends on the order in which (possibly additional) extensions are loaded etc.
What is the specific scenario that you think would be problematic?
The extensions would not remove functions from the lists, and they would be checking if the function is already in the list.
As for the order -- I don't think the order of functions in the list has any noticeable effects.
One thing to keep in mind is Revise.jl, but at the moment DiffTests.jl declares function lists as const, so Revise.jl cannot update current lists on the fly -- there is no regression here.
Also vector-based approach is used (see below) to dynamically populate the list based on Julia version (e.g. because some linear algebra routines are not available in the earlier versions). DiffTests.jl may drop support for Julia 1.0, but in the long run it might be beneficial to have some straightforward mechanism for supporting multiple Julia versions.
And let me refer to the original issue that I am trying to solve -- I've created JuliaDiff/ForwardDiff.jl#589 to address IMO the very relevant practical problem -- improving autodiff efficiency for linear models and multivariate normal distributions with dense or sparse matrices. So the test functions that I am adding here via the extension should be relevant for all autodiff backends. Vanilla SparseArrays.jl does not, at the moment, support ldiv!() for triangular sparse matrices, hence MKLSparse.jl weak dependency.
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.
Extensions are just not designed for such a use case - their sole purpose is to extend existing functions.
A problematic scenario here would be another extension that also adds functions to the same array. Then the order of the functions in the array after loading the extensions can be completely arbitrary, and even worse if precompilation and loading of the extensions happens with multiple tasks in parallel two push!
es might interfere.
ext/DiffTestsMKLSparseExt.jl
Outdated
using LinearAlgebra: UpperTriangular, LowerTriangular | ||
using SparseArrays: sparse |
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.
BTW this should not be done: Extensions should only import the package itself and the weak dependency but not even standard libraries.
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.
Why? SparseArrays and LinearAlgebra are the direct dependencies of DiffTests itself and MKLSparse. The latter provides more specific definitions of matrix multiplication and division for sparse matrices that are utilized by the test functions.
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.
You should import them indirectly via DiffTests and MKLSparse (see eg JuliaStats/LogExpFunctions.jl#63).
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.
Thank you for the info. I've updated how the import is being done. However, the changes that the extensions makes to the function lists are not visible from the user session (I guess it's different world age of DiffTests.jl or something like that).
const declaration of function lists does not work with extensions, because these lists are redeclared each time the package module is imported/used. So these lists have to be defined inside __init__(), which is only called once.
/ by generic sparse matrix is not supported
To fix #17 (comment) properly, I had the following idea: Could we add traits to the test functions (eg., |
I had a similar idea. But then, IIUC, to collect those functions one would have to use I figured out the problem I had with extension not updating the list of functions. It turned out that with each |
@devmotion I think the issue with updating the function lists by the extensions is solved. Do you have any further feedback on the PR? The functions functions that are being enabled are important for multivariate distributions. |
Based on the previous comment, my impression is that there are still problems with e.g. simultaneous (jl_L4HrUa) pkg> precompile MKLSparse
Precompiling MKLSparse
✗ MKLSparse
0 dependencies successfully precompiled in 1 seconds. 4 already precompiled.
ERROR: The following 1 direct dependency failed to precompile:
MKLSparse [0c723cd3-b8cd-5d40-b370-ba682dde9aae]
Failed to precompile MKLSparse [0c723cd3-b8cd-5d40-b370-ba682dde9aae] to "/Users/david/.julia/compiled/v1.9/MKLSparse/jl_aUHjJZ".
ERROR: LoadError: UndefVarError: `libmkl_rt` not defined
Stacktrace:
[1] include(mod::Module, _path::String)
@ Base ./Base.jl:457
[2] include(x::String)
@ MKLSparse ~/.julia/packages/MKLSparse/4LlPk/src/MKLSparse.jl:1
[3] top-level scope
@ ~/.julia/packages/MKLSparse/4LlPk/src/MKLSparse.jl:9
[4] include
@ ./Base.jl:457 [inlined]
[5] include_package_for_output(pkg::Base.PkgId, input::String, depot_path::Vector{String}, dl_load_path::Vector{String}, load_path::Vector{String}, concrete_deps::Vector{Pair{Base.PkgId, UInt128}}, source::Nothing)
@ Base ./loading.jl:2049
[6] top-level scope
@ stdin:3
in expression starting at /Users/david/.julia/packages/MKLSparse/4LlPk/src/BLAS/BLAS.jl:1
in expression starting at /Users/david/.julia/packages/MKLSparse/4LlPk/src/MKLSparse.jl:1
in expression starting at stdin:3 |
@devmotion Thanks for the feedback!
I agree that for highly extendable functionality, like DiffRules.jl, the current mechanism might be not flexible enough.
I can add locks to ensure exclusive access if that is what you mean.
AFAIK there are issues with Mac version of MKLSparse with some of the linear algebra functions (see e.g. JuliaSparse/MKLSparse.jl#37), but the HEAD passes the tests. |
MKL is not available for ARM, everyone with Apple silicon will run into the same issue (see, e.g., JuliaMath/FFTW.jl#274). |
Which makes me wonder: Maybe we should just not use MKL, and hence maybe we should just not add an extension. And thereby we could get rid of the design issues as well. |
|
Fixes #13 (matrix multiplication tests succeed on ReverseDiff.jl, but / tests fail because it's not supported yet (IIUC))
Closes #15 (switches to
AbstractArray
)Moves sparse matrix tests into extension (requires MKLSparse.jl).
Tests now include checks for type stability of the test functions.