-
Notifications
You must be signed in to change notification settings - Fork 27
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
Add arithmetic & related operations when unambiguous #183
Comments
I think multiplication such as julia> Set([1,2,3])*3
ERROR: MethodError: no method matching *(::Set{Int64}, ::Int64)
Closest candidates are:
*(::Any, ::Any, ::Any, ::Any...)
@ Base operators.jl:587
*(::Missing, ::Number)
@ Base missing.jl:123
*(::BigFloat, ::Union{Int16, Int32, Int64, Int8})
@ Base mpfr.jl:447
...
Stacktrace:
[1] top-level scope
@ REPL[1]:1 The reason |
Just pointing this is implemented in DomainSets using broadcast, and it is frequently useful, e.g. for affine maps: julia> using IntervalSets, DomainSets
julia> 2 .* (1..2) .+ 5
7.0 .. 9.0 The more general notation |
There was a lot of discussion when the package was created. The problem is one probably wants correctly rounded a la IntervalArithmetic.jl so this isn't easy to add, and makes the package very heavy. |
@hyrodium those linked discussions also include operations with a less clear and more ambiguous meaning. For example, what I propose to focus only on operations where there are (hopefully!) no doubts what they mean. Like, what else could Actually, different kinds of unit conversions is my main motivation here. Would be nice to have stuff like |
@daanhb interesting, thanks for pointing it out! Not great that it involves heavy piracy though... |
Good catch re Unitful, it seems DomainSets makes incorrect assumptions about I agree on the piracy comment, and there are many more instances of this. I'd really like to clean this up, but just to clarify: can we make a distinction between changing behaviour of an interval and extending behaviour? The former is a definite no-go and I hope there are no instances of that, but making something work which without loading DomainSets would result in an error seems different. In this case, DomainSets defines broadcast for |
Meanwhile DomainSets is updated to fix the promotions going on: julia> using DomainSets, Unitful
julia> (1..3) .* 3u"m"
3 m .. 9 m
julia> (2u"m"..3u"m") .* 4.0
8.0 m .. 12.0 m
julia> (2u"m"..3u"m") .* 4
8 m .. 12 m There is no special treatment to avoid the rounding issues that were discussed in the linked issues. Somehow integer endpoints got promoted to floats when applying the map, and that is also fixed (an interval with integer endpoints times an integer remains an interval with integer endpoints). It seems that using just * instead of .* also works, which is probably undesired. It may have been like that for some time. I'll make a separate issue about that. |
I totally agree with this approach, but mostly for enduser-facing packages or when pirating some rarely used function. I didn't know of heavy piracy of common basic function done by DomainSets before. Tbh, it makes me very cautious to use that package as a dependency anywhere...
And then DomainSetsCore would define all these broadcasts? What's the benefit then, seems like the majority of DomainSets code should go there to support broadcasts. This piracy can easily cause concrete problems, whenever there is another piece of code doing similar things. For example, I have a few definitions like |
Tried I looked at my notebooks where I define some interval operations for convenient interactive usage, and all of them are generally related to unit conversion: Base.:*(i::Interval, x) = @modify(e -> e * x, endpoints(i)[∗])
Base.:*(x, i::Interval) = @modify(e -> e * x, endpoints(i)[∗])
Base.:/(i::Interval, x) = @modify(e -> e / x, endpoints(i)[∗])
Unitful.uconvert(u, i::Interval) = @modify(e -> uconvert(u, e), endpoints(i)[∗])
Unitful.ustrip(u::Unitful.Units, i::Interval) = @modify(e -> ustrip(u, e), endpoints(i)[∗])
|
Well, let's work it out then, that's the plan anyway. To me the underlying issue is that, for historical and at the time valid reasons, IntervalSets defines the Domain type which is fundamental to the design of DomainSets. It is simply not possible to provide any functionality for domains outside of IntervalSets without affecting intervals, i.e. "piracy".
Indeed the solution can not be to have IntervalSets exposed to more of DomainSets. But I think there is no need for that. DomainSetsCore is semantic: it defines the notion of sets that are possibly continuous. It does not define or support anything else. I see no problem with IntervalSets defining * or .* or any other operation using standard syntax and standard function names (i.e. defined in Base or elsewhere), as long as any operation on a continuous set results in something that behaves like a continuous set and which agrees with the mathematical semantics. Again, this is always the intention in IntervalSets anyway. It is not a problem because semantic correctness almost guaranteest interoperability and, if not, DomainSets can still adapt accordingly: having IntervalSets as a dependency its behaviour can be carefully preserved (as is the case for unions of disjoint intervals). The benefit of DomainSetsCore is to resolve our strange dependency situation, and to allow other types to inherit from Domain without having to depend on IntervalSets. It's a minimal change now, but in time I hope that it will result in improved and clean interoperability in a much broader ecosystem.
Hmm, if the rule is that package B cannot extend package A because package C (or a user) might also extend package A in a similar way, doesn't that exclude any extension ever? Anyway, the ambiguity concern is genuine. To me, by inheriting from |
Thanks for chasing this, there is still a promotion issue. Multiplication by numbers looks convenient in usage, but I now agree with @hyrodium above and think I would rather deprecate this behaviour (in favour of .*) than fixing it. |
This can be useful (or not) in general, but doesn't seem related to this specific situation.
This only applies to "extensions" that are based on piracy. And yes, it's a nice and useful rule, to avoid piracy aside from some corner cases. For something like IntervalSets, it doesn't seem useful or feasible to rely or encourage type piracy in these ways. |
To be fair, I don't think that "type piracy" is the right way to describe the situation. Since IntervalSets defines the abstract supertype A closer analogy to our situation would be a package implementing a concrete Of course nobody argues for having DomainSets as a dependency of IntervalSets. The simple alternative is for IntervalSets to recognize that operations on continuous sets which are not specific to intervals are best implemented elsewhere. This arguably applies to the current issue of arithmetic. It more clearly applies to cartesian products such as An argument like "hard to understand and find the fix for" seems more easily managed by adding a suggestion to DomainSets somewhere in the documentation. There is certainly room for making a smaller version of DomainSets, if that helps. I'll happily add anyone here to the repo of DomainSets, by the way. |
Sure, and that is totally fine. As long as this functionality does not involve pirating Base methods on objects defined in IntervalSets. Otherwise, benefits and risks should be really carefully weighted. Often, this piracy doesn't seem reasonably motivated at all. For example, I noticed it pirates Arithmetic operations like |
Anyway, that extended discussion is somewhat tangential to this specific issue. No matter what other packages do, I think it still makes sense to define operations that make sense for intervals and are unambiguous here. What are the drawbacks? As a minimum, such operations include multiplication and division by a scalar. |
Thanks for the discussion here. Feel free to file issues for things you don't agree with.
It does not really seem fine if you still think of it as piracy, even with Conceptually, iteration does kind of work with generator syntax: julia> using DomainIntegrals
julia> integral(2x for x in 1..2)
3.0 You expressed interest in converting units. Alternatives to multiplication might be, with |
I'd like to propose adding basic operations on intervals, such as multiplication by another value (the specific list of operations to be defined).
Multiplication is supported by different Julia objects, not just numbers. For example, arrays – and this is often convenient:
I don't see any discussions of such a proposal before. What do you think?
The text was updated successfully, but these errors were encountered: