[SciPy-dev] Generic polynomials class (was Re: Volunteer for Scipy Project)

josef.pktd@gmai... josef.pktd@gmai...
Tue Oct 13 23:52:36 CDT 2009


On Tue, Oct 13, 2009 at 10:38 PM, Charles R Harris
<charlesr.harris@gmail.com> wrote:
>
>
> On Tue, Oct 13, 2009 at 7:24 PM, Anne Archibald <peridot.faceted@gmail.com>
> wrote:
>>
>> 2009/10/13 Charles R Harris <charlesr.harris@gmail.com>:
>> >
>> >
>> > On Tue, Oct 13, 2009 at 5:55 PM, Anne Archibald
>> > <peridot.faceted@gmail.com>
>> > wrote:
>> >>
>> >> 2009/10/13 Charles R Harris <charlesr.harris@gmail.com>:
>> >> >
>> >> >> I'm not sure I see why returning all those NotImplementedErrors is a
>> >> >> problem - yes, the code needs to be written to override the methods
>> >> >> that return NotImplementedError, but that code needs to be written
>> >> >> no
>> >> >> matter how we set up an interface to it.
>> >> >
>> >> > It's not trivial to do a complete workup. My template class is 698
>> >> > lines
>> >> > long, but it exists once in one place, and that does the trick for
>> >> > *all*
>> >> > the
>> >> > classes.
>> >>
>> >> Aha. I think I see where the confusion is. In my proposed interface,
>> >> the coefficient-oriented functions would *be* the ones in the Basis
>> >> object. So, for example,
>> >>
>> >> chebadd(c1,c2,interval) -> ChebyshevBasis(interval).add(c1, c2)
>> >>
>> >> or, more likely, you'd have somewhere earlier
>> >>
>> >> cheb = ChebyshevBasis(interval)
>> >>
>> >> and then it's
>> >>
>> >> chebadd(c1,c2,interval) -> cheb.add(c1,c2)
>> >>
>> >> So the coefficient-oriented interface that we need to have is what
>> >> lives in the basis objects, and it is what the Polynomial object uses
>> >> to implement (e.g.) p1*p2. There would be no separate chebmul,
>> >> polymul, lagrangemul, ....
>> >>
>> >
>> > But the separate functions are what we want. They provide the most
>> > flexible
>> > low level implementation and make the fewest assumptions as to what the
>> > programmer might want to use them for. You don't really have a base
>> > class,
>> > you essentially have classes derived from different base classes, which
>> > isn't so different from what I am proposing.
>>
>> Do we really want the separate functions, rather than methods on a
>> basis object? Why? I don't really see how they're more flexible or
>> make fewer assumptions, and they become very cumbersome (and prevent
>> some important optimizations) when working with polynomials in the
>> Lagrange basis. It really can be the difference between chebadd(...)
>> and cheb.add(...), in the (common?) case where people aren't
>
> But why do that if you already have the name space when you import the
> module? It's redundant.
>
>>
>> specifying the interval. And if people do want to specify the
>> interval, using a basis object makes it much easier to avoid
>> forgetting it and getting nonsensical results (or worse, sensible
>> results until you try a different interval).
>>
>
> That is because they are low level building blocks, they should have the
> absolute minimum of crud stuck on, and that means the interval is [-1, 1].
> If you need more, that is what a class is for. That how low level things
> should work, small functions doing the minimum amount provide the most
> flexibility. The programmer should be handed enough rope to hang themselves
> at that level if they so choose. That's why folks use C and not Pascal.
>
>>
>> In this respect, the current interface in chebyshev.py looks
>> error-prone to me: you can't suppy an interval to chebadd and chebmul,
>> but if you forget it for chebval you get bogus answers. (And in fact
>> chebint and chebder are wrong, or at least misleading, if you use a
>> different interval.)
>>
>
> Tracking things like intervals is best delegated to higher level functions
> where you can deal with it without worrying about the low level
> implementation at the same time. Of course there is some loop-de-loop
> involved to get to the ideal dividing line, but it is important to draw that
> line.
>
>>
>> I'm not sure what you mean when you say I don't really have a base
>> class: Basis is literally the base class for all Basis objects, and it
>> provides nontrivial functionality (e.g. the power implementation) that
>> would otherwise need to be repeated for each representation.
>> Polynomial is the base class for all polynomials.
>>
>> >> The Basis object is effectively just a namespace holding the
>> >> collection of functions, and possibly any data they need (interval,
>> >> for example). So the separation you're describing is still present in
>> >> my proposed interface; it's the separation between Basis objects
>> >> (which contain the functions but no particular plumbing) and the
>> >> Polynomial objects (which are all plumbing).
>> >
>> > Why not let the module hold the functions? It is the more natural
>> > namespace
>> > for python.
>>
>> Because there is important context - intervals, lists of specified
>> points - that must be provided to all the coefficient-array functions.
>> And collections of methods on objects are very natural in python.
>>
>
> That is higher level stuff and doesn't belong at the bottom, it should be
> separate IMHO.The functions are the ingredients, not the cake. I've spent
> too much time pulling apart classes to get to the reusable bits not to want
> them separated out up front. Now you could start with the separate functions
> and then construct a basis to pass into the initializer. At that point you
> and I are operating along the same lines except I don't pass them to the
> contructor, I put them in the local environment where the class methods will
> find them, that's what the code completion does and Python handles that
> efficiently. They are consequently more like static (class) methods than
> instance methods. That way I end up with a whole new class that uses those
> functions, inherits from nothing but object, and has a minimal constructor.
>
> I'll admit that my class implementation may not be suitable for some of the
> other types of polynomials you are looking at, I hadn't been thinking about
> them yet. On the other hand it is a pretty complete implementation that
> requires nothing more than the functions, raises no NotImplementedErrors and
> should work with most of the orthogonal polynomial bases. And it can be
> reworked without reference to the low level functions.

How expensive can _cseries_to_zseries, as_series and similar be?
Since you currently have all main calculations in self-contained
functions, you need to convert and check the inputs each time.  If
these were expensive intermediate results, then methods could save
time in this. For short series, this doesn't seem to be a problem with
polycheb, but inside a loop I'm not so sure.
(as an aside: we were also thinking about moving some methods in
statsmodels to functions, but in many cases we would need a larger
number of function arguments of intermediate results or duplicate
calculations, which makes it less useful.)

I was playing a bit with the cats, and inheritance seems to work
without problems.

Following roughly the discussion, I would find it useful if the
functions specify the used or assumed domain in the doc strings. Since
I'm not very familiar with Chebyshev polynomials, I wouldn't know
which functions would require rescaling of the domain.

Josef

>>
>> >> > In [4]: x + [0]
>> >> > Out[4]: array([0, 1, 2])
>> >>
>> >> This is a special case anyway - it's one of the very few instances in
>> >> which you can add coefficients the way you're describing. Normally you
>> >> have to extend one or the other so that they're the same length (and
>> >> in some bases this is a non-trivial operations, rather than simply
>> >> padding with zeros). If what you mean here is to add a scalar, there's
>> >> no reason to wrap it in a list;
>> >
>> > Consistency. Usually these things are thought of as algebras with a unit
>> > over the scalars.
>>
>> I'm not sure I understand this - consistency with what? It is only
>> scalars you can reasonably add to coefficient arrays by wrapping in a
>> list; other coefficient arrays must be the same length as the other
>> (array) summand or they will fail. And scalars work just as well
>> without wrapping in a list, so why do it?
>
> Scalars should work with and without wrapping, no? But I think using an
> empty list for what is actually a polynomial all of whose coefficients are
> zero is mixing apples and oranges.
>
> Chuck
>
>
> _______________________________________________
> Scipy-dev mailing list
> Scipy-dev@scipy.org
> http://mail.scipy.org/mailman/listinfo/scipy-dev
>
>


More information about the Scipy-dev mailing list