#17979: Reimplementation of IntegerListsLex
-------------------------------------+-------------------------------------
Reporter: aschilling | Owner:
Type: defect | Status: needs_review
Priority: blocker | Milestone: sage-6.6
Component: combinatorics | Resolution:
Keywords: days64 | Merged in:
Authors: Bryan Gillespie, | Reviewers: Nathann Cohen, Jeroen
Anne Schilling, Nicolas M. Thiery | Demeyer, Travis Scrimshaw
Report Upstream: N/A | Work issues:
Branch: | Commit:
public/ticket/17979 | aa89bb267303a9c424d6e6883ee35b2d7e592d7c
Dependencies: | Stopgaps:
-------------------------------------+-------------------------------------
Comment (by nthiery):
Salut Vincent!
Thanks for the feedback.
On Tue, Mar 31, 2015 at 12:46:35PM -0000, sage-trac wrote:
> There are several things that I do not quite understand. Could you
explain
> or modify it:
>
> 1. The iterator `IntegerListLex` aims to be fast and low-level. What is
> the point of using the `Parent`/`Element` stuff? Why `ClonableArray`
are
> better than Python lists?
We haven't changed this aspect (in this ticket we focused on
refactoring without changing the API): internally, Python lists are
used; by default, they are converted to ClonableArray upon being
yielded. The element constructor can be customized to return plain
lists if so desired.
In the current state of affairs, this does not really make a
difference:
{{{
# ClonableArray
sage: L = IntegerListsLex(50, min_part=1, max_slope=0)
sage: it = iter(L)
sage: %timeit x = it.next()
10000 loops, best of 3: 148 µs per loop
# plain lists
sage: L = IntegerListsLex(50, min_part=1, max_slope=0,
element_constructor=list)
sage: it = iter(L)
sage: %timeit x = it.next()
10000 loops, best of 3: 134 µs per loop
}}}
The overhead comes from the containment test that we do in the ``check``
method:
{{{
# ClonableArray, with check made trivial
sage: sage: L = IntegerListsLex(50, min_part=1, max_slope=0)
sage: sage: it = iter(L)
sage: sage: %timeit x = it.next()
10000 loops, best of 3: 135 µs per loop
}}}
I am not sure this containment check is useful. We could just disable
it. What do you think?
For the record: using the parent/element protocol does not necessarily
cause a large overhead; ClonableArray is almost as fast as list
(timings to be of course taken with a grain of salt given how small
values we are speaking about here):
{{{
sage: sage: L = IntegerListsLex(50, min_part=1, max_slope=0)
sage: l = range(1000)
sage: %timeit x =list(l)
100000 loops, best of 3: 2.4 µs per loop
sage: c = L.element_class
sage: %timeit x = c(L, l)
100000 loops, best of 3: 2.85 µs per loop
sage: %timeit [x[i] for i in range(100)]
100000 loops, best of 3: 6.84 µs per loop
sage: %timeit [l[i] for i in range(100)]
100000 loops, best of 3: 5.61 µs per loop
}}}
In the long run, when `IntegerListsLex` will be cythonized, the
relative situation may change, since we can hope for speedups of
10-100. Note in particular that we most likely will want to use arrays
of ints internally; `ClonableIntArray` might be a good candidate for
this. Or just STL vectors.
> 2. Having a nested class seems overkill. The class `_Iter` is created
only
> once in `__iter__`. Moreover, all methods in it starts with `p =
> self.parent`. Why do you need to create this extra `_Iter` class? Why
does
> it need to be a nested class?
The class is needed because the iterator has an internal state. So we
need to create a separate object for each concurrently running
iterator.
I find that using a nested class `IntegerListsLex._Iter`, rather than
`IntegerListsLexIter` makes the relation between the two classes
explicit (`_Iter` is for internal use for IntegerListsLex`). It's also
consistent with what we do elsewhere (`Element`, ...). And there is no
overhead in doing so.
> 3. Do you have a use case for
> {{{
> sage: IntegerListsLex(NN, max_length=3)
> Disjoint union of Lazy family (<lambda>(i))_{i in Non negative
integer
> semiring}
> }}}
> The feature only appears once in the doc in a place which does not
> appear in the reference manual.
It does appear in the reference manual: see section `Input list or
iterable for the sum` of the main `IntegerListLex` class.
The feature of iterating by increasing sum is certainly useful.
Whether we want the above syntactic sugar -- instead of just using
DisjointUnionEnumeratedSets -- is indeed questionable. The feature was
already there, so we kept it to not change the API in this ticket.
> 4. Why `_check_lexicographic_iterable` is a cached method? And I do not
> get why is it called from `_Iter` and not at the initialization of
> `IntegerListsLex`.
We discussed this with Jeroen in some earlier comment. Putting it in
the iterator let the user create the `IntegerListsLex` object, even if
it's not finite/iterable. This could be useful for other things than
iteration, like membership testing or building the corresponding
polytopes (once #17920 will be available).
With that, it's natural to cache error-free runs (error runs won't be
cached) to not redo the work in later iterator constructions.
Cheers,
Nicolas
--
Ticket URL: <http://trac.sagemath.org/ticket/17979#comment:293>
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.