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

Charles R Harris charlesr.harris@gmail....
Tue Oct 13 21:38:05 CDT 2009


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.


> >> > 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
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://mail.scipy.org/pipermail/scipy-dev/attachments/20091013/dc3bd6cd/attachment.html 


More information about the Scipy-dev mailing list