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

Incorrect root of function involving max #146

Closed
perrutquist opened this issue Feb 12, 2020 · 8 comments
Closed

Incorrect root of function involving max #146

perrutquist opened this issue Feb 12, 2020 · 8 comments

Comments

@perrutquist
Copy link

julia> roots(x -> max(x,-x)+1, -2..2)
1-element Array{Root{Interval{Float64}},1}:
 Root([1, 1], :unique)

This seems like an incorrect result. The function (equivalent to abs(x)+1) does not have any roots.

Providing a derivative explicitly: roots(x -> max(x,-x)+1, sign, -2..2) or changing the contractor: roots(x -> max(x,-x)+1, -2..2, Bisection) makes the problem go away.

Maybe related to #98 ?

@Kolaru
Copy link
Collaborator

Kolaru commented Feb 14, 2020

It has to do with the fact that the function is not differentiable, which is an implicit requirement for Newton (the default) and Krawczyk methods. I don't know what ForwardDiff.jl does in such cases (it is used if the derivative is not explicitly given), but even if it can find the correct derivative, IntervalArithmetic.jl has currently no way of dealing with arbitrary piecewise continuous functions, so this failure is expected.

We should probably document that, with a big warning sign.

@dpsanders
Copy link
Member

IntervalArithmetic.jl is I think not the problem; rather, it is the lack of information about derivatives of the functions. This should be solvable with the new ChainRules.jl and ForwardDiff2.jl packages.

@Kolaru
Copy link
Collaborator

Kolaru commented Feb 14, 2020

For a function like f(x) = max(x, -3x), the derivative would be

f'(x < 0) = -3
f'(x >= 0) = 1

As far as I can tell, we don't currently have a mechanism to conclude that e.g. f'(-2 .. 1) = (-3 .. 1), a problem that remains even if we fully know the derivative. The example with sign being the derivative works because the sign function is explicitly define in IntervlArithmetic.jl.

ChainRules.jl and ForwardDiff2.jl looks very promising though, I'll check them in more details.

@dpsanders
Copy link
Member

dpsanders commented Feb 14, 2020

Here I showed how to define a general piecewise function. Copying (with a slight improvement) here for simplicity:

"Evaluate interval function with piecewise region--function pairs"
function piecewise(pieces::Vector{<:Pair}, X)
    return reduce(, f(X  region) for (region, f) in pieces)
end

piecewise(pieces) = X -> piecewise(pieces, X)

X = 2..5
f2 = piecewise([0..3 => x->x+1,
                3..6 => x->x,
                6..=> x->x+2])

f(x) = sum(f2  abs, x)

EDIT: We should probably just export this function from IntervalArithmetic.jl.

@dpsanders
Copy link
Member

If we introduce a Piecewise type then we should be able to define ForwardDiff.derivative for objects of that type.

@dpsanders
Copy link
Member

Hmm, making this efficient could require FunctionWrappers.jl.

@dpsanders
Copy link
Member

dpsanders commented Feb 14, 2020

Something like this, but it is currently not working correctly FIXED.

Note that ideally we would somehow generate and store only once the derivative; currently it is re-constructed each time:

using IntervalArithmetic

struct PiecewiseFunction{P}
    pieces::P
end


"Evaluate f over the region X if X is non-empty"
function evaluate(f, X)
    isempty(X) && return X
    return f(X)
end

function (f::PiecewiseFunction)(X::Interval)
    return reduce(, evaluate(f, X  region) for (region, f) in f.pieces)
end


pieces = (0..3 => x->x+1,
 3..6 => x->x,
 6..=> x->x+2)

f = PiecewiseFunction(pieces)

X = 2..5
f(X)

using ForwardDiff
import ForwardDiff: derivative

ForwardDiff.derivative(f) = x -> ForwardDiff.derivative(f, x)

ForwardDiff.derivative(f::PiecewiseFunction) =
    PiecewiseFunction( Tuple(region => ForwardDiff.derivative(g) for (region, g) in f.pieces))

ForwardDiff.derivative(f::PiecewiseFunction, x::Interval) = ForwardDiff.derivative(f)(x)

myabs = PiecewiseFunction( (
                        (-Inf..0) => x -> -x,
                        (0..Inf) => x -> x ) )

ForwardDiff.derivative(myabs, -1..1)
ForwardDiff.derivative(myabs, 2..3)

myabs( (-1..1)  (2..3))

myabsderiv = ForwardDiff.derivative(myabs)

myabsderiv(-1..1)   # -1..1
myabsderiv(2..3)   # 1..1
myabsderiv(-3.. -2)   # -1.. -1

@Kolaru
Copy link
Collaborator

Kolaru commented Nov 6, 2024

max is no longer supported for intervals, as it's meaning is ambiguous.

Now errors, but doesn't give wrong results.

Support for piecewise function in IntervalArithmetic is a separate issue.

@Kolaru Kolaru closed this as completed Nov 6, 2024
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

No branches or pull requests

3 participants