-
-
Notifications
You must be signed in to change notification settings - Fork 33
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
Iteration of empty intervals is inconsistent. in general "empty := (+inf,-inf)" is problematic #72
Comments
Hello, Thanks for this feedback! While I understand your concern, I don't really get why the consequences of choosing "(+inf, -inf)" for the empty interval are an issue. Could you please clarify? The choice to have |
The basic concern with (+inf,-inf) is that this violates the ordering of bounds. In all other cases, one can rely on that, thus it would be great if that could be a guarantee by the library. With respect to the iteration, I do not see what the benefit is of guaranteeing that iteration provides non-empty list of atomic intervals. Is there any example of an operation/query on intervals which can be coded more conveniently/elegantly with the current convention? On the other hand, I have already provided examples of cases, where the current convention makes it less convenient/elegant, e.g. the computation of the total volume. Or say you would want to plot that set using any plotting library; I could draw a rectangle for each atomic interval and simply iterate; with the current convention, I need an extra test for the empty interval. In general, the current convention simply violates a number of invariants which would both feel natural and are otherwise fulfilled for all intervals. |
So there are two different things here:
I'm open to concrete suggestions :-) |
To conclude: I propose to change the iteration behaviour. At this point, I do not propose change the value of the |
Ok, so let's keep point 2 for later ;) Considering point 1, I'm still not convinced by the "benefits" of such a breaking change. I agree with you that the empty interval is a special beast and could even be implemented as a special object. However, following Python's philosophy, I tend to consider that special cases aren't special enough to break the rules. Here, the rule is that, whatever I also wonder what would happen to slices and (more importantly) to indexing intervals if we go for |
I believe with composing you mean the representation of the current implementation? Because there is no sensible mathematical definition which would consider the empty interval an element of the decomposition of any set of the real line. In the end, we want the library to help us answer questions about the intervals/sets we're dealing with. Questions which would be fixed by the proposed changes:
As for |
I guess maybe my misunderstanding is the fundamental object handled by the library. For me, it is the mathematical concept of a set of anything isomorphic to the real line. As such, a natural decomposition is the minimal decomposition. Needing special consideration for identities like On the other hand, you seem to prefer to think of If that is the intent you have for this library, then I have my answer: I have misunderstood the purpose of this library, within that purpose, iteration is correct as is. If that is your intent, then I will stop wasting your time, and we can close this. |
I get your point, and I agree that the way we currently deal with empty intervals is perhaps (certainly?) misleading from a theoretical point of view. As you said, However, I do believe now than the proposed change won't affect much practicability while solving the issue you raised ;) At the same time, the proposed change has several implications on the code, and will definitely be a breaking change (hence implying a new major version to comply with semantic versioning, and implying I should carefully consider all other potential breaking changes that could improve the library to do all of them at once in a single new major version). I think (but I should think more about it ^^) we have two options to implement the change:
I'm not sure which one is better. (1) is easier to implement but makes "iterating on the empty interval" the special case. (2) seems more appropriate, but makes the four accessors the special cases for empty intervals... Honestly, I don't really like these two options, so any suggestion to create a (3) is welcome ;-) Anyway, before I decide to go for the change or not, I need to balance the benefits (set-theoretic compatibility) and the drawbacks (breaking change & potential inconsistency between internals and the public API). |
I actually haven't thought about the best implementation at all yet. After all, it is what the C++ community likes to call "an implementation detail". As long as you have a clear specification what comprises the API and how that API behaves, you can change the implementation with every release if you like. So as you said, until you have decided if a change of the API is in place, the implementation can wait. That said, usually the most efficient implementation in terms of maintainability is the one that most naturally represents the intended concept. For the change to the iteration behaviour as a decomposition into non-empty intervals, that would point towards Indeed, from a quick search for
|
Thanks for the feedback. I started looking at the required changes in the code. I naturally went for option (2). I pushed an empty branch with the changes. The diff can be seen here: https://github.com/AlexandreDecan/portion/compare/empty?expand=1 And indeed, I had to change I will have deeper a look at the link you provided. I have no plan (currently) to add methods for more theoretic concepts (i.e., supremum, infimum, etc. since they can be easily implemented using left, lower, upper, right; and are a bit out of the scope of the library). Actually, most of the concepts there can lead to a new library built on top of |
I like it! I think iteration becomes more natural this way. The set-theoretic special cases for empty intervals are less important to me; if a user's code relies on |
I actually agree it is likely that someone will check for emptiness if needed (the same way they will check for infinities if that's something that can happen in their code). Given this, I think I don't really like the idea of returning Considering the breaking nature of the change in the empty branch, and since I won't have much time to work on Oh, and I've another question for you: you said "atomic could be redefined as implying non-empty, which would give some nice consistency; but one could also invent a new name for that concept". Why do you suggest that |
Regarding empty -> not atomic; in the end, it's a matter of definition. A possible "classification" of the possible sets with respect to their behaviour, I see three main classes:
With |
Ok, thanks! I'll keep I don't know when I'll release these changes (currently 2.3.0-dev on GitHub, not published on PyPI). Thanks again for having raised the "issue" with iterating on an interval, and the interesting subsequent discussions we had ;-) |
Thank you for maintaining this library! |
Note: For the following, I will try to stick to the nomenclature within this library. Here, an atomic interval roughly corresponds to the mathematical notion of an interval of the real line, while this library uses instances of
portion.Interval
to represent general mathematical sets of the real line (with a focus in their decomposition into intervals).Within rigorous interval arithmetic, atomic intervals with
lower > upper
do not exist/have no meaning.From a mathematical perspective, there is exactly one empty interval which has no infimum/supremum, and for any other interval, the supremum is never lower than the infimum.
portion
has chosen to interpret any atomic interval withlower > upper
as a synonym for the empty interval.The library converts any such interval to the canonical representation for the empty interval, whenever one is encountered.
That in itself is a valid and reasonable choice. Then, obviously "(+inf,-inf)" becomes equivalent to the empty interval.
However, I believe the atomic interval "(+inf,-inf)" is not a good choice for the canonical representation of the empty interval IMHO. Several invariants which hold for all non-empty intervals
i
are currently broken forP.empty()
:i.lower <= i.upper
.sum(a.upper-a.lower for a in i) <= i.upper - i.lower
.While the first one might be non-trivial to solve (see below), the second one could be solved by making
list(P.empty()) == []
.A more rigorous approach would identify each property with a mathematical concept of the set theory on the real line, but that would lead to a number of complications especially for the empty set/interval:
None
or raise an exception)portion.Interval
could be specified as the minimal decomposition into atomic intervals; which would be the empty list intervals for the empty interval.The text was updated successfully, but these errors were encountered: