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

Ensure boolean functions error for non-thin intervals #613

Merged
merged 1 commit into from
Jan 11, 2024

Conversation

OlivierHnt
Copy link
Member

No description provided.

@OlivierHnt OlivierHnt merged commit 63e0481 into JuliaIntervals:master Jan 11, 2024
32 checks passed
@OlivierHnt OlivierHnt deleted the comparison branch January 11, 2024 16:42
@lbenet
Copy link
Member

lbenet commented Jan 17, 2024

Naive question: why is it better to return an error rather than simply false, say, for iszero? If I want to avoid the possibility of that error, I have to use isthinzero, which makes coding a bit more unconfortable, and which I though was the motivation for having iszero back again... Sorry for this veeeery late comment.

@OlivierHnt
Copy link
Member Author

It's definitely not a naive question. Last time @Kolaru and I talked, we thought that using iszero(x) may be ambiguous if the interval x contains 0 but is not thin.
It's one of those situations when we are being (too?) cautious that users don't produce silent errors when feeding intervals in some generic code.

One alternative would be to define the following:

function Base.iszero(x::BareInterval)
    if isthin(x)
        return iszero(inf(x))
    else
        in_interval(0, x) || return false
        @warn "the interval contains 0 but is not thin, this may be ambiguous"
        return true
    end
end

But then again, one may be annoyed to see a bunch of warnings flooding the REPL...

An other argument in favour of not throwing an error is that the docstring for iszero (from Base) reads "Return true if x == zero(x)" which is not ambiguous.

@lbenet
Copy link
Member

lbenet commented Jan 18, 2024

Thanks for the answer. I would be in favor of having iszero without throwing errors in the sense you describe: that it returns x == zero(x), which I am not sure if it incurrs in subtle cases. The question arose because i am slowly fixing things in TaylorSeries (see JuliaDiff/TaylorSeries.jl#345), and at some point I had to change things to use isthinzero, and wonder if I could have iszero back...

@OlivierHnt
Copy link
Member Author

Sounds good to me, especially due to Julia's docstring (btw this discussion should also be applicable for isone at the very least ; perhaps also for ==(::Interval, ::Number) and isinteger?).

@Kolaru you often come up with good corner cases, what's your take on this?

@Kolaru
Copy link
Collaborator

Kolaru commented Jan 18, 2024

A typical example that stresses me out would be

function noncontinuous(x)
  iszero(x) && return 1.0
  return x
end

Then noncontinuous(interval(-0.1, 0.1)_com) would return interval(-0.1, 0.1)_com but should be interval(-0.1, 1)_def.

By breaking all comparisons, we are doing our best to forbid non-continuous functions, that we have no way do detect anyway.

@OlivierHnt
Copy link
Member Author

OlivierHnt commented Jan 18, 2024

Indeed; throwing an error gives us a guardrail in this case. That being said, not taking into account the decoration because there is nothing we can do here, such a function does the expected thing according to the docstring iszero(x) == (x == zero(x)).
This is really a matter of appreciation on our part: how much do we want to get involved with one's code.

It makes me want to have an @safe macro that rewrites blocks of code so that we can have

@safe function foo(x)
    if iszero(x)
        # return something 
    elseif x == g(x)
        # return something else
    elseif 1 < x < 4
        # return yet an other thing
    end
end

to turn it into

function foo(x)
    if isthinzero(x)
        # return something 
    elseif isequal_interval(x, g(x))
        # return something else
    elseif isinterior(x, interval(1, 4))
        # return yet an other thing
    end
end

@Kolaru
Copy link
Collaborator

Kolaru commented Jan 18, 2024

If we want to over-engineer, I am still dreaming about

function piecewise(x)
  if x < 0.2
    return f(x)
  else
    return g(x)
  end
end

becoming automatically

function piecewise(x)
  hull(
    f(intersect(x, interval(-Inf, 0.2))),
    g(intersect(x, interval(0.2, Inf)))
  )
end

By the way in your example you should have (for full correctness)

function foo(x)
    if isthinzero(x)
        # return something 
    elseif isthin(x) && inf(x) == g(x)
        # return something else
    elseif isinterior(x, interval(1, 4))
        # return yet an other thing
    else
        # Probably an error to fall back to
    end
end

@OlivierHnt
Copy link
Member Author

OlivierHnt commented Jan 18, 2024

Yea that is probably too much to ask 🙂

Actually I did mean that == should be turned into isequel_interval and not a thin equality check. To implement @safe one needs to define a correspondance between boolean functions from Base and ours. Something like

  • safe(:(a == b)) == :(isequal_interval(interval(a), interval(b)))
  • safe(:(iszero(x))) == :(isthinzero(interval(x)))
  • safe(:(a < b)) == :(strictprecedes(interval(a), interval(b)))
  • safe(:(a < b < c)) == :(isinterior(b, interval(a, c)))
  • etc.

@safe would be very similar to how @views replaces c[...] by view(c, ...).

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.

3 participants