Skip to content

Commit

Permalink
fixed conditional test that was using ifelse instead of if_else
Browse files Browse the repository at this point in the history
added 2 new methods for ifelse so it can take inputs that are Real instead of just Node

updated documentation
  • Loading branch information
brianguenter committed Sep 17, 2024
1 parent 743cc32 commit 428284c
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 28 deletions.
67 changes: 41 additions & 26 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,33 +14,7 @@ For f:ℝⁿ->ℝᵐ with n,m large **FD** may have better performance than conv

**FD** may take much less time to compute symbolic derivatives than Symbolics.jl even in the ℝ¹->ℝ¹ case. The executables generated by **FD** may also be much faster (see [Symbolic Processing](@ref)).

As of version 0.4.1 **FD** allows you to create expressions with conditionals:
```julia

julia> @variables x y
y

julia> f = ifelse(x<y,x,y)
(ifelse (x < y) x y)

julia> a = make_function([f],[x,y])

julia> a(1.0,2.0)
1-element Vector{Float64}:
1.0

julia> a(2.0,1.0)
1-element Vector{Float64}:
2.0
```
Howver, you cannot yet compute derivatives of expressions that contain conditionals:
```julia
julia> jacobian([f],[x,y])
ERROR: Your expression contained ifelse. FastDifferentiation does not yet support differentiation through ifelse or any of these conditionals (max, min, copysign, &, |, xor, <, >, <=, >=, !=, ==, signbit, isreal, iszero, isfinite, isnan, isinf, isinteger, !)
```
This may be a breaking change for some users. In previous versions this threw an the expression `x==y` returned a `Bool`. Some data structures, such as `Dict` use `==` by default to determine if two entries are the same. This will no longer work since `x==y` will now return an expression graph. Use an `IDict` instead since this uses `===`.

A future PR will add support for differentiating through conditionals.



Expand All @@ -66,6 +40,47 @@ This is **beta** software being modified on a daily basis. Expect bugs and frequ
## Notes about special derivatives
The derivative of `|u|` is `u/|u|` which is NaN when `u==0`. This is not a bug. The derivative of the absolute value function is undefined at 0 and the way **FD** signals this is by returning NaN.

## Conditionals

As of version 0.4.1 **FD** allows you to create expressions with conditionals using either the builtin `ifelse` function or a new function `if_else`. `ifelse` will evaluate both inputs. By contrast `if_else` has the semantics of `if...else...end`; only the true or false branch will be executed. This is useful when your conditional is used to prevent exceptions because of illegal input values:
```julia
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.0])
1-element Vector{Float64}:
1.4142135623730951
end
```
In this case you wouldn't want to use `ifelse` because it evaluates both the true and false branches and causes a runtime exception:
```julia
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)).
```

Howver, you cannot yet compute derivatives of expressions that contain conditionals:
```julia
julia> jacobian([f],[x,y])
ERROR: Your expression contained ifelse. FastDifferentiation does not yet support differentiation through ifelse or any of these conditionals (max, min, copysign, &, |, xor, <, >, <=, >=, !=, ==, signbit, isreal, iszero, isfinite, isnan, isinf, isinteger, !)
```
This may be a breaking change for some users. In previous versions the expression `x==y` returned a `Bool`. Some data structures, such as `Dict` use `==` by default to determine if two entries are the same. This will no longer work since `x==y` will now return an expression graph. Use an `IdDict` instead since this uses `===`.

A future PR will add support for differentiating through conditionals.




Expand Down
2 changes: 2 additions & 0 deletions src/Methods.jl
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ end
macro number_methods(T, rhs1, rhs2, options=nothing)
#special case for ifelse because it takes three arguments
eval(:(Base.ifelse(a::$T, b::$T, c::$T) = simplify_check_cache(Base.ifelse, a, b, c)))
eval(:(Base.ifelse(a::$T, b::$Real, c::$T) = simplify_check_cache(Base.ifelse, a, Node(b), c)))
eval(:(Base.ifelse(a::$T, b::$T, c::$Real) = simplify_check_cache(Base.ifelse, a, b, Node(c))))

number_methods(T, rhs1, rhs2, options) |> esc
end
Expand Down
4 changes: 2 additions & 2 deletions test/FDTests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2119,8 +2119,8 @@ end

#conditional
expr = x < y
f = ifelse(expr, x, y)
@test ==(FastDifferentiation.value(f), ifelse)
f = if_else(expr, x, y)
@test ==(FastDifferentiation.value(f), if_else)
@test ===(FastDifferentiation.children(f)[1], expr)
@test ===(FastDifferentiation.children(f)[2], x)
@test ===(FastDifferentiation.children(f)[3], y)
Expand Down

0 comments on commit 428284c

Please sign in to comment.