-
Notifications
You must be signed in to change notification settings - Fork 3
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
Add SpecialFunctions extension #82
Conversation
The problematic The problem is that you are invoking The design of SCT us problematic. Instead of defining new methods in the calling module, it defines new methods in SCR. The better way to do this would be to define a macro to extend methods in SCT but define those methods in the calling module. |
I put this eval step in the initialization precisely because the error was warning me not to eval stuff during precompilation 🤔 What do you mean by "define new methods in the calling module"? Should I use the eval of Base/SpecialFunctions? Or is the calling module yet another one? |
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #82 +/- ##
==========================================
+ Coverage 76.19% 77.49% +1.29%
==========================================
Files 14 16 +2
Lines 689 662 -27
==========================================
- Hits 525 513 -12
+ Misses 164 149 -15 ☔ View full report in Codecov by Sentry. |
I finally managed to get it working. Here's a summary of the changes: Source
Extensions
Tests
|
ext/SparseConnectivityTracerSpecialFunctionsExt/SparseConnectivityTracerSpecialFunctionsExt.jl
Show resolved
Hide resolved
function overload_all(M) | ||
exprs_1_to_1 = [ | ||
quote | ||
$(overload_connectivity_1_to_1(M, op)) | ||
$(overload_gradient_1_to_1(M, op)) | ||
$(overload_hessian_1_to_1(M, op)) | ||
end for op in nameof.(list_operators_1_to_1(Val(M))) | ||
] | ||
exprs_2_to_1 = [ | ||
quote | ||
$(overload_connectivity_2_to_1(M, op)) | ||
$(overload_gradient_2_to_1(M, op)) | ||
$(overload_hessian_2_to_1(M, op)) | ||
end for op in nameof.(list_operators_2_to_1(Val(M))) | ||
] | ||
exprs_1_to_2 = [ | ||
quote | ||
$(overload_connectivity_1_to_2(M, op)) | ||
$(overload_gradient_1_to_2(M, op)) | ||
$(overload_hessian_1_to_2(M, op)) | ||
end for op in nameof.(list_operators_1_to_2(Val(M))) | ||
] | ||
return quote | ||
$(exprs_1_to_1...) | ||
$(exprs_2_to_1...) | ||
$(exprs_1_to_2...) | ||
end | ||
end | ||
|
||
eval(overload_all(:Base)) |
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.
Yeah, this combined with list_operators_*
and the big quote
s in overload_*.jl
feels very hacky. 🫤
There are too many layers of metaprogramming with a lot of implicit dependencies in their design that interact across several files.
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's exactly the way they do it in ForwardDiff:
- The big quotes: https://github.com/JuliaDiff/ForwardDiff.jl/blob/1907196ba50e3eb337860301ece3ec37d0582257/src/dual.jl#L230-L244
- The evaluation: https://github.com/JuliaDiff/ForwardDiff.jl/blob/1907196ba50e3eb337860301ece3ec37d0582257/src/dual.jl#L469-L472
- The listing: https://github.com/JuliaDiff/ForwardDiff.jl/blob/1907196ba50e3eb337860301ece3ec37d0582257/src/dual.jl#L463
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 spent quite a lot of time trying out various things to make it work with minimal changes, and this seems to work.
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.
What's in the quotes is exactly the same as what was directly eval
-ed earlied (if you ignore the additional module prefixes, which I got from ForwardDiff).
The difference is that we split code generation (creating the expression) from code evaluation (running it through eval
)
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.
The contents of overload_all
are only designed so that it can be a one-liner in all the package extensions we will no doubt need to add. It's nothing but a big concatenation of all the expressions
Benchmark resultJudge resultBenchmark Report for /home/runner/work/SparseConnectivityTracer.jl/SparseConnectivityTracer.jlJob Properties
ResultsA ratio greater than
Benchmark Group ListHere's a list of all the benchmark groups executed by this job:
Julia versioninfoTarget
Baseline
Target resultBenchmark Report for /home/runner/work/SparseConnectivityTracer.jl/SparseConnectivityTracer.jlJob Properties
ResultsBelow is a table of this job's results, obtained by running the benchmarks.
Benchmark Group ListHere's a list of all the benchmark groups executed by this job:
Julia versioninfo
Baseline resultBenchmark Report for /home/runner/work/SparseConnectivityTracer.jl/SparseConnectivityTracer.jlJob Properties
ResultsBelow is a table of this job's results, obtained by running the benchmarks.
Benchmark Group ListHere's a list of all the benchmark groups executed by this job:
Julia versioninfo
Runtime information
|
Would it be easier to implement this as a macro? A macro is a function that returns an expression and calls |
That's probably a good idea, will try it in a later PR! |
At the moment, the tests fail because I try to "
eval
into a closed module during precompilation".For anyone who wants to take a look:
overload_all
calls multiple functions that define specific overloads with@eval
SparseConnectivityTracer.jl/src/overload_connectivity.jl
Lines 13 to 24 in c49dc32
I would love some guidance on:
eval
s (duringinit
or during precompilation)@eval
or the functioneval
eval