#17693: mutable poset: a data structure for asymptotic expressions
-------------------------------------+-------------------------------------
Reporter: dkrenn | Owner:
Type: enhancement | Status: needs_work
Priority: major | Milestone: sage-6.9
Component: asymptotic | Resolution:
expansions | Merged in:
Keywords: asymptotics | Reviewers: Benjamin Hackl,
Authors: Daniel Krenn | Clemens Heuberger
Report Upstream: N/A | Work issues:
Branch: u/cheuberg/asy | Commit:
/mutable-poset | f7bce7e8aff4e25a23b57d1052ff13df302120e5
Dependencies: | Stopgaps:
-------------------------------------+-------------------------------------
Changes (by cheuberg):
* status: needs_review => needs_work
* commit: 3b3b2fb607a6fe8b20419170bf935d494af1efca =>
f7bce7e8aff4e25a23b57d1052ff13df302120e5
* reviewer: Benjamin Hackl => Benjamin Hackl, Clemens Heuberger
* milestone: sage-6.5 => sage-6.9
Comment:
Replying to [comment:34 behackl]:
> This concludes my review; [comment:8 cheuberg] is in the process of
reading the code. I'll leave setting this to `positive_review` to him.
I read the documentation and the code. I pushed a few reviewer commits. I
have a number of rather minor issues with the documentation which I ask
you to fix in order facilitate future maintainance of the code. In three
instances, I propose alternative implementations, which might be more
readable or slightly more efficient.
1. Please add `.. seealso::` blocks between corresponding methods of shell
and poset as well as between the accessor methods (keys, elements, shells)
2. there is an empty "Introduction" section at the beginning of the module
documentation, the next heading "Example" is on the same level.
3. this module was written as part of the asymptotic expansion effort. In
contrast to the asymptotic expansion, it does not have the "experimental"
decorator. I'd feel more comfortable having it, at least until a larger
portion of the asymptotic expansions have been merged.
4. `MutablePoset.__init__` accepts a list of elements. This could be used
in several doctests (in particular, in the set operations) as an
abbreviation.
5. `MutablePosetShell.__init__`: shall we check that `element is not
None`? Otherwise, handling of special elements would probably be affected.
6. `MutablePosetShell.key`: I do not understand the sentence "The element
is converted by the poset to the key".
7. `MutablePosetShell.key`: I am surprised that the key is not cached. I
imagine that many comparisons will be needed in the lifetime of a
`MutablePoset`, and in every case, the property key has to be resolved,
which calls the property poset, which calls `get_key` of the poset.
8. `MutablePosetShell.__repr__`: The note box in the docstring is
surprising at this point: reading the source file from the top, this is
the first point where the representation of the data is explained, after
guessing it from `is_special`, `is_oo`, `is_null` above. I think that it
would be better to document these conventions closer to the top, perhaps
in the `__init__` method or perhaps only as a comment in the source code.
9. `MutablePosetShell.__repr__`: The code replicates the behaviour of
`is_null` and `is_oo`. As the `__repr__` method is hardly time critical,
I'd prefer using `is_null` and `is_oo`, here and thus hiding the internal
convention.
10. `MutablePosetShell.le`: the note box could be simplified by
suppressing implementation details and speaking about the special
elements.
11. `MutablePosetShell.le`: `right <= left`: neither `right` nor `left`
are defined here.
12. `MutablePosetShell.le`: the part `if other.element is None` could be
simplified to `return not other.successors()` as `self` is already known
not to be special here.
13. `MutablePosetShell.le`: If this is time critical,
`self._predecessors_` could be used instead of `self.predecessors()`.
14. `MutablePosetShell.eq`: note box, see above.
15. `MutablePosetShell.eq`: emphasize that elements with equal keys are
considered equal? Currently, this information is somewhat hidden in the
note box which at first glance only seems to explain handling of special
elements.
16. `MutablePosetShell._copy_all_linked_`: Short description: "Return a
copy of all shells" does not correspond to the actual return value, which
is only the copy of this shell.
17. `MutablePosetShell._copy_all_linked_`: the interplay between this
method and `MutablePoset._copy_shells_` is not well documented: in
particular, `poset` in `_copy_all_linked_` is ''only'' used for setting
the containing poset, but not for actual inserting into this poset.
18. `MutablePosetShell._copy_all_linked_`: I do not understand why you
test `oo == P.oo`: I think that `oo` is an element of the new poset `Q`.
So `oo is Q.oo` would be more interesting? The current test demonstrates
that the poset is not used in comparison, so that would rather belong to
`.eq`?
19. `MutablePosetShell._search_covers_`: what is the role of `self` in
this method? It trivially influences the return value, but what else?
20. `MutablePosetShell._search_covers_`: The explanation of the return
value is unclear to me. Is the sentence "Note that..." meant to be a
complete description? Does it change if `reverse` is set?
21. `MutablePosetShell._search_covers_`: I think that the notions of
"upper cover" and "lower cover" need a brief definition; I did not find
them in Wikipedia and
`sage.combinat.posets.posets.FinitePoset.upper_covers` defines the notion.
22. `MutablePosetShell._search_covers_`: According to Wikipedia, a cover
is what is called an upper cover here. This is in contrast to the default
behaviour here.
23. `MutablePosetShell._search_covers_`: what is the difference between
this method and `.predecessors`? Is the shell necessarily an element of
the poset? If not, then there should be a doctest covering this case.
24. `MutablePosetShell.covers`: "originate" is somewhat foreign to the
description of the poset.
25. `MutablePosetShell.covers`: "which are at most the given shell":
should that be "which are less than the given shell"?
26. `MutablePosetShell.covers` see comments on
`MutablePosetShell._search_covers_`.
27. `MutablePosetShell.covers`: I think that the following would be an
equivalent implementation of this method, which does not need a helper
function with side effects.
{{{
if self == shell:
return set()
covers = set().union(*(e.covers(shell, reverse)
for e in self.successors(reverse)
if e.le(shell, reverse)))
if covers:
return covers
else:
return set([self])
}}}
(pushed as branch `u/cheuberg/asy/mutable-poset-cover`)
28. `MutablePosetShell._iter_depth_first_visit_`: document the role of
self in this method (only shells `>=` this shell are visited).
29. `MutablePosetShell.iter_depth_first`: document the role of self in
this method (only shells `>=` this shell are visited).
30. `MutablePosetShell._iter_topological_visit_`: document the role of
self in this method (only shells `<=` this shell are visited, emphasize
the contrast to depth first.).
31. `MutablePosetShell.iter_topological_first`: document the role of self
in this method (only shells `<=` this shell are visited, emphasize the
contrast to depth first.).
32. `MutablePosetShell.iter_topological_sort`, last example: function `C`
is defined, but not used.
33. `MutablePosetShell.merge`: explain what merge is, in particular, that
this is defined when constructing the poset.
34. `MutablePosetShell.merge`: I am surprised that `check=True` leads to a
silent failure instead of an exception and that `poset._merge_ is None`
does not raise an exception. Document and test this behaviour?
35. `MutablePosetShell.merge`: what is the difference between
`poset.remove` and `poset.discard`?
36. `MutablePosetShell.merge`: document that the merge function resulting
in `None` leads to deletion of the element.
37. `MutablePosetShell.merge`: document that the merge function must not
change the position of the element in the poset.
38. `MutablePoset.__init__`: Clarify the role of `key`: in particular,
`key` does not only influence sorting, but that no two elements are
allowed to have the same key.
39. `MutablePoset.__len__`: Clarify that `null` and `oo` are not counted.
40. `MutablePoset.shells_topological`: The sentence "If this is None, no
sorting according to the representation string occurs." is unclear to me,
in particular the role of the representation string.
41. `MutablePoset.keys_topological`: Due to the chosen key, some of the
elements added to the poset are ignored. I do not know why this is
demonstrated here.
42. `MutablePoset.repr`: INPUT section is incomplete.
43. `MutablePoset.repr_full`: INPUT section is incomplete.
44. `MutablePoset.add`: the loop `for reverse in (False, True)` simplifies
in the second iteration: as all pairs of elements `(s, l)` with `new`
covering `s` and `l` covering `new` have already been broken up while
`reverse=False`. Thus it might be more straightforward to do it only once,
not using `reverse` at all and saving a few intersections:
{{{
new._predecessors_ = self.null.covers(new, reverse=False)
new._successors_ = self.oo.covers(new, reverse=True)
for s in new.predecessors():
for l in s.successors().intersection(new.successors()):
l.predecessors().remove(s)
s.successors().remove(l)
s.successors().add(new)
for l in new.successors():
l.predecessors().add(new)
}}}
(pushed as branch `u/cheuberg/asy/mutable-poset-add`)
45. `MutablePoset.remove`: the loop over all (not necessarily direct)
successors via the depth first search iterator seems to be rather
inefficient, especially if the poset is large. I propose an alternative
based on the order relation and not based on the data structure:
{{{
for upper in shell.successors():
upper.predecessors().remove(shell)
for lower in shell.predecessors():
lower.successors().remove(shell)
for upper in shell.successors():
if not any(s <= upper
for s in lower.successors()):
lower.successors().add(upper)
upper.predecessors().add(lower)
}}}
(pushed as branch `u/cheuberg/asy/mutable-poset-remove`)
46. `MutablePoset.remove`, `MutablePoset.discard`: add "see also blocks",
but also an explanation of the difference between `discard` and `remove`.
I guess that both methods are available due to some compatibility concern
(in that case, please add a remark in the code or the docstring).
Otherwise, I'd suggest removing `discard`, removing the parameter
`raise_key_error` and let the user catch the exception.
47. `MutablePoset.pop`: remove `key=lambda c: -c` from this example as it
is not pertinent to `pop`.
48. `MutablePoset.pop`: mention that special elements cannot be popped.
49. `MutablePoset.pop`: IMHO you can remove the first four line (the
`try:` block deleting `kwargs['include_special']`) because setting
`kwargs['include_special'] = False` should result in the same result.
50. `MutablePoset.union`: The doctest `P.union(P, Q, Q, P)` neither fits
the description "a poset" or "an iterable of future elements".
51. `MutablePoset.union`: the word "union" sounds symmetric, however, due
to keys and merge functions, it might not be commutative. The note box is
not helpful for me.
52. `MutablePoset.union`: the TODO box from `union_update` also applies
here.
53. `MutablePoset.union_update`: why is the semantics of `other` different
w.r.t `union`, but has the same description? It would seem more logical to
me if `union` would simply call copy and then pass its argument to
`update_union` and let the latter function sort out the iterators?
54. `MutablePoset.difference` and `MutablePoset.difference_update`: see
comments on `union` and `union_update`
55. `MutablePoset.intersection` and `MutablePoset.intersection_update`:
see comments on `union` and `union_update`
56. `MutablePoset.intersection_update`: how can the `AttributeError`
occur? After all, `self` is a `MutablePoset`, so the method `keys` will be
available unless something very strange happens ...
57. `MutablePoset.merge`: documentation of `reverse`: what is the default
direction?
58. `MutablePoset.merge`: add doctest for `RuntimeError`.
59. `MutablePoset.merge`: document that `can_merge` is applied in the
sense of the condition of depth first iteration, i.e., once `can_merge`
fails, the successors are no longer tested. This is some kind of
monotonicity condition on `can_merge`.
60. `MutablePoset.merge`: it should be mentioned somewhere that this (at
first sight strange) merge magic is motivated by asymptotic expansions.
61. `MutablePoset.map`: IMHO the example does alter the keys, because the
key was the identity map here.
62. `MutablePoset.mapping`: does the map have to be key preserving as in
the method `map`? Is this method actually needed, see also the recent
discussion on inplace vs. non-inplace operations on sage-devel?
----
New commits:
||[http://git.sagemath.org/sage.git/commit/?id=8de4188efff6b534e881dbe8e7071f026f47a317
8de4188]||{{{Trac #17693: minor language adjustments}}}||
||[http://git.sagemath.org/sage.git/commit/?id=740852e2897cfb1e4903150f110c8c242ba153f3
740852e]||{{{Trac #17693: command instead of description for short
descriptions}}}||
||[http://git.sagemath.org/sage.git/commit/?id=347656c7dec2fef3ed8f43c5f24e5ade977b35f3
347656c]||{{{Trac #17693: language adjustment: whether instead of if}}}||
||[http://git.sagemath.org/sage.git/commit/?id=4774baac95a8490280253fc3de41a12c896fc6b4
4774baa]||{{{Trac #17693: corrections in documentation}}}||
||[http://git.sagemath.org/sage.git/commit/?id=fe5d2f1dec3f35eeaf2a4e525e4fe3e9807d17a7
fe5d2f1]||{{{Trac #17693: additional doctest}}}||
||[http://git.sagemath.org/sage.git/commit/?id=e4f1fea624a794d32d2e3380a8eb42ee6888af4f
e4f1fea]||{{{Trac #17693: fix indentations and whitespace}}}||
||[http://git.sagemath.org/sage.git/commit/?id=254951dad86ad94fa55c91f3f811497614490493
254951d]||{{{Trac #17693: add keyword in doctest for clarity}}}||
||[http://git.sagemath.org/sage.git/commit/?id=f7bce7e8aff4e25a23b57d1052ff13df302120e5
f7bce7e]||{{{Trac #17693: remove superfluous doctest}}}||
--
Ticket URL: <http://trac.sagemath.org/ticket/17693#comment:36>
Sage <http://www.sagemath.org>
Sage: Creating a Viable Open Source Alternative to Magma, Maple, Mathematica,
and MATLAB
--
You received this message because you are subscribed to the Google Groups
"sage-trac" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/sage-trac.
For more options, visit https://groups.google.com/d/optout.