-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #93 from brianguenter/AddConditionalsBranch
Patch to correct bug introduced when added conditionals in PR #90 (comment)
- Loading branch information
Showing
13 changed files
with
496 additions
and
262 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,72 +1,5 @@ | ||
# Limitations | ||
**FD** does not support expressions with conditionals on **FD** variables. For example, you can do this: | ||
```julia | ||
julia> f(a,b,c) = a< 1.0 ? cos(b) : sin(c) | ||
f (generic function with 2 methods) | ||
|
||
julia> f(0.0,x,y) | ||
cos(x) | ||
|
||
julia> f(1.0,x,y) | ||
sin(y) | ||
``` | ||
but you can't do this: | ||
```julia | ||
julia> f(a,b) = a < b ? cos(a) : sin(b) | ||
f (generic function with 2 methods) | ||
|
||
julia> f(x,y) | ||
ERROR: MethodError: no method matching isless(::FastDifferentiation.Node{Symbol, 0}, ::FastDifferentiation.Node{Symbol, 0}) | ||
|
||
Closest candidates are: | ||
isless(::Any, ::DataValues.DataValue{Union{}}) | ||
@ DataValues ~/.julia/packages/DataValues/N7oeL/src/scalar/core.jl:291 | ||
isless(::S, ::DataValues.DataValue{T}) where {S, T} | ||
@ DataValues ~/.julia/packages/DataValues/N7oeL/src/scalar/core.jl:285 | ||
isless(::DataValues.DataValue{Union{}}, ::Any) | ||
@ DataValues ~/.julia/packages/DataValues/N7oeL/src/scalar/core.jl:293 | ||
... | ||
``` | ||
This is because the call `f(x,y)` creates an expression graph. At graph creation time the **FD** variables `x,y` are unevaluated variables with no specific value so they cannot be compared with any other value. | ||
|
||
The algorithm can be extended to work with conditionals applied to **FD** variables but the processing time and graph size may grow exponentially with conditional nesting depth. A future version may allow for limited conditional nesting. See [Future Work](@ref) for a potential long term solution to this problem. | ||
|
||
**FD** does not support looping internally. All operations with loops, such as matrix vector multiplication, are unrolled into scalar operations. The corresponding executable functions generated by `make_function` have size proportional to the number of operations. | ||
|
||
Expressions with ≈10⁵ scalar operations have reasonable symbolic preprocessing and compilation times. Beyond this size LLVM compilation time can become extremely long and eventually the executables become so large that their caching behavior is not good and performance declines. | ||
|
||
A possible solution to this problem is to do what is called rerolling: detecting repreating indexing patterns in the **FD** expressions and automatically generating loops to replace inlined code. This rerolling step would be performed on the **FD** expressions graphs before function compliation. | ||
|
||
It is not necessary to completely undo the unrolling back to the original expresssion, just to reduce code size enough to get reasonable compilation times and better caching behavior. | ||
|
||
For example, in this matrix vector multiplication | ||
```julia | ||
|
||
julia> a = make_variables(:a,3,3) | ||
3×3 Matrix{FastDifferentiation.Node}: | ||
a1_1 a1_2 a1_3 | ||
a2_1 a2_2 a2_3 | ||
a3_1 a3_2 a3_3 | ||
|
||
julia> b = make_variables(:b,3) | ||
3-element Vector{FastDifferentiation.Node}: | ||
b1 | ||
b2 | ||
b3 | ||
|
||
julia> a*b | ||
3-element Vector{Any}: | ||
(((a1_1 * b1) + (a1_2 * b2)) + (a1_3 * b3)) | ||
(((a2_1 * b1) + (a2_2 * b2)) + (a2_3 * b3)) | ||
(((a3_1 * b1) + (a3_2 * b2)) + (a3_3 * b3)) | ||
``` | ||
the goal is to replace concrete index numbers with symbolic index variables that represent offsets rather than absolute indices | ||
```julia | ||
|
||
[ | ||
a[i,j]*b[j] + a[i, j+1]*b[j+1] + a[i,j+2]*b[j+2] | ||
a[i+1,j]*b[j] + a[i+1, j+1]*b[j+1] + a[i+1,j+2]*b[j+2] | ||
a[i+2,j]*b[j] + a[i+2, j+1]*b[j+1] + a[i+2,j+2]*b[j+2] | ||
] | ||
``` | ||
and then to extract looping structure from these indices. | ||
Expressions with ≈10⁵ scalar operations have reasonable symbolic preprocessing and compilation times. Beyond this size LLVM compilation time can become extremely long and eventually the executables become so large that their caching behavior is not good and performance declines. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
bebc4dc
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.
@JuliaRegistrator @register
Release notes:
This is a patch to fix two bugs, one introduced in 0.3.17 and one introduced in 0.4.0 when conditionals were added as a feature. These bugs caused
derivative(x^y)
to fail. Other less commonly used functions were also affected. Neither 0.3.17 nor 0.4.0 should be used.This release makes it possible to do two things:
Create an FD function that contains conditionals:
f = if_else(x<y,x,y)
wherex,y
are both FD variables.Take derivatives of functions whose derivative definitions use conditionals. For example, the derivative definition for
mod2pi
is nowExample: derivative of mod2pi
You cannot (yet) take derivatives of a function which contains conditionals. This will come in a future PR.
bebc4dc
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.
Registration pull request created: JuliaRegistries/General/115514
Tagging
After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.
This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via: