Skip to content
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

Patch to correct bug introduced when added conditionals in PR https://github.com/brianguenter/FastDifferentiation.jl/pull/90#issue-2487795686 #93

Merged
merged 29 commits into from
Sep 19, 2024

Conversation

brianguenter
Copy link
Owner

@brianguenter brianguenter commented Sep 17, 2024

This corrects a bug introduced in #90 (comment) when conditionals were added.

Bug manifested when computing derivative of x^y which has this definition in DiffRules.jl:

@define_diffrule Base.:^(x, y) = :( $y * ($x^($y - 1)) ), :( ($x isa Real && $x<=0) ? Base.oftype(float($x), NaN) : ($x^$y)*log($x) )

FastDifferentiation.jl does not support the ? : operator so this caused an exception.

This rule has been update to use the new if_else operator which has the semantics of if ... else ..., i.e., only the true or false branch is executed.

DiffRules.@define_diffrule Base.:^(x, y) = :($y * ($x^($y - 1))), :(if_else($x isa Real && $x <= 0, Base.oftype(float($x), NaN), ($x^$y) * log($x)))

The builtin ifelse is also supported.

Many functions need the new if_else because exceptions may be thrown in one of the branches, for example:

julia> f = ifelse(x<0,NaN,sqrt(x))
(ifelse  (x < 0) NaN sqrt(x))

julia> g = make_function([f],[x])
...
julia> g([-1])
ERROR: DomainError with -1.0:
sqrt was called with a negative real argument but will only return a complex result if called with a complex argument. Try sqrt(Complex(x)).

For these cases the new if_else operator is appropriate:

julia> f = if_else(x<0,NaN,sqrt(x))
(if_else  (x < 0) NaN sqrt(x))

julia> g = make_function([f],[x])
...
julia> g([-1])
1-element Vector{Float64}:
 NaN

julia> g([2])

Fixes #89

added function bool_methods to create overloads for conditionals and ifelse. All tests pass. Now need to change derivative calculation and code generation.
Fixes #89
turned boolean_methods into a macro and called it on package load

added tests for booleans and ifelse
Fixes #89

added tests for comparison operators and ifelse
fixed bug in boolean_methods, func variable wasn't interpolated into Expr
changed make_function to use === to test for variable inclusion
Fixes #89

added ternary Node constructor to handle ifelse

replace iszero and isinf methods with ones that return Node objects instead of using node_value.
removed special case isfinite from number_methods. Doesn't cause tests to fail but might cause problems. Didn't document why it was there in the first place.

added special eval case for ifelse to number_methods macro

two sparse tests fail for obscure reasons.
Fixes #89

SparseArrays.jl needs iszero(a::Node) to return a boolean value or it's matrix contructors don't work. Can't have an iszero that returns an expression so iszero is special cased to return a boolean.
…into separate constants. This will make it easier to filter these nodes out of derivative calculations.
Fixes #89

added a new function, is_identically_zero, that replaces old iszero. iszero now returns a node expression and is_identically_zero returns a boolean.
Fixes #89

added Contitionals.jl and iterator for conditional bit values.
Fixes #89

added error message so that users attempting to compute derivatives through conditionals will know this isn't yet supported.
Fixes #89

commment change explaining consequences of adding conditionals
Need to replace ifelse with my own ifelse that generates correct if...else code. Too many functions execute illegal stuff in one of the ifelse branches which causes an exception.
…the latter evaluates all its arguments even if doing so would cause an exception

changed simplify_check_cache and check_cache functions to not take a cache argument

added ! to the list of supported bool expressions
added 2 new methods for ifelse so it can take inputs that are Real instead of just Node

updated documentation
changedderivative(a::Node, index::Val{1}) in DifferentiationRules.jl to throw correct conditional error if expression is a conditional

updated README and docs about new conditional feature.
…les for it that will work with FastDifferentiation

added diff rule for mod2pi

moved import DiffRules from DifferentiationRules.jl to FastDifferentiation.jl to make it easier to see at a glance the deps of FD

renamed diadic_non_differentiable,monadic_non_differentiable to special_diadic and special_monadic
@brianguenter brianguenter merged commit bebc4dc into main Sep 19, 2024
3 checks passed
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.

1 participant