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

Remove need for valtype of Dual to be <: Real #216

Closed
dpsanders opened this issue Apr 18, 2017 · 8 comments
Closed

Remove need for valtype of Dual to be <: Real #216

dpsanders opened this issue Apr 18, 2017 · 8 comments

Comments

@dpsanders
Copy link

It would be useful to remove the need for the eltype of Dual.value to be <:Real.
Cf. the discussion at JuliaIntervals/IntervalArithmetic.jl#2

@jrevels jrevels changed the title Remove need for eltype of value to be <: Real Remove need for valtype of Dual to be <: Real Apr 18, 2017
@jrevels
Copy link
Member

jrevels commented Apr 18, 2017

I know we had discussed some of this offline, but remind me again why Interval <: Real shouldn't hold true (or Interval <: AbstractInterval <: Real, etc.)? Don't Intervals seek to implement Julia's (unfortunately poorly defined) Real interface?

Note that whether intervals are mathematically a subset of the reals is irrelevant, since Julia subtype relations are nominal and only have meaning insofar as they organize dispatch rules. Dual numbers don't even constitute a field, but it's still the right decision to have Dual <: Real since that is what makes sense programmatically.

An example where the Dual{<:Real} restriction is useful is forcing a complex number orientation; this way, we can explicitly disallow Dual{<:Complex} (which is valuable for a number of reasons, see previous discussion here).

@dpsanders
Copy link
Author

cc @timholy

@timholy
Copy link
Contributor

timholy commented Apr 18, 2017

This is a case where we suffer from the lack of traits. Thinking of an Interval as a set (in the mathematical sense), it's definitely not the same thing as a Real; you can define a topology based on intervals but you certainly can't based on just individual numbers. Likewise, I can unambiguously sort a list of real numbers but I bet we could get into an argument about what I mean by saying sort a list of intervals.

Now, folks like @dpsanders want to endow them with extra properties. So what we'd like to do is have a multiple-inheritance diagram:

abstract type AbstractInterval end
struct IntervalReallike <: (AbstractInterval | Real)
    fields
end

But we can't do that. We could fake it using the infamous THTT, though.

But without a nice syntax for traits, I recognize this might make certain things difficult for Dual.

@jrevels
Copy link
Member

jrevels commented Apr 18, 2017

Thinking of an Interval as a set (in the mathematical sense), it's definitely not the same thing as a Real; you can define a topology based on intervals but you certainly can't based on just individual numbers. Likewise, I can unambiguously sort a list of real numbers but I bet we could get into an argument about what I mean by saying sort a list of intervals.

My previous statement was my attempt at preemptively responding to this kind of argument:

Note that whether or not intervals are mathematically a subset of the reals is irrelevant, since Julia subtype relations are nominal and only have meaning insofar as they organize dispatch rules.

Addressing your ordering example, it seems to me that the question isn't really "how could we mathematically define ordering on a set of intervals", but rather "how are Base comparators actually defined on this implementation of a number type that propagates metadata through user code"? Do these control flow definitions cause Intervals to propagate along the same "overall" code paths that Real instances would (this question isn't rhetorical - I actually don't know the answer)? This is one of the reasons that Dual <: Real is sensible, and if it's true of Interval as well, I would argue that the same logic applies.

I'm not necessarily against removing this restriction - I can enforce things like !(valtype(::Type{<:Dual}) <: Complex) via inner constructors. As it stands though, it seems like Dual{<:Real} is an appropriate defensive measure to have in place, and is consistent with Dual <: Real.

@timholy
Copy link
Contributor

timholy commented Apr 18, 2017

Ditching the mathematical meaning is not quite so simple. The issue is that some of us want intervals that hew to the mathematical meaning, for purposes other than estimating computational error. The name IntervalSets was chosen for a reason. The question arises, how much (if any) functionality can be shared between these two camps? At Julia's current state of evolution, the answer might be "none," if one side really, really needs Interval <: Real given that we don't have "nice" traits.

That's not a complete tragedy, of course; if it can't be shared, at least IntervalSets its a pretty small package. It sure would be nice to share some functionality (because it increases composability), but we do have to weigh that against other needs.

If we do decide that we simply can't share, it might be worth writing this example up briefly as a julia issue, as one example of how important traits will be some day.

@jrevels
Copy link
Member

jrevels commented Apr 21, 2017

I need to sit on this some more (I'm still somewhat uneasy about it), but I think I'd be fine with restricting the valtype via an overloadable trait rather than a subtype relation, i.e.

struct DualValid end

dualvalidity(::DataType) = nothing
dualvalidity(::Type{<:Real}) = DualValid()

struct Dual{T,V,N} <: Real
    value::V
    partials::Partials{N,V}
    Dual{T,V,N}(value::V, partials::Partials{N,V}, ::DualValid) where {V,N} = new{T,V,N}(value, partials)
    Dual{T,V,N}(value::V, partials::Partials{N,V}, ::Void) where {V,N} = error("ya done goofed!")
end

(::Type{Dual{T}})(value::V, partials::Partials{N,V}) where {T,N,V} = Dual{T,V,N}(value, partials, dualvalidity(T))

cc @mlubin @KristofferC (interested to hear if you guys have any opinions on this)

@mlubin
Copy link
Contributor

mlubin commented Apr 21, 2017

No objections to traits here.

@Keno
Copy link
Contributor

Keno commented Oct 12, 2018

👍 I'd like the trait version of this as well

Keno added a commit to Keno/ForwardDiff.jl that referenced this issue Oct 15, 2018
Instead, use an extensible function that the constructor uses to
check whether a type is valid to be used as `Dual`'s scalar type.

Fixes JuliaDiff#216
jrevels pushed a commit that referenced this issue Oct 16, 2018
Instead, use an extensible function that the constructor uses to
check whether a type is valid to be used as `Dual`'s scalar type.

Fixes #216
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

5 participants