The polynomial class

Let's now design a polynomial base class based on a monomial formulation of the polynomial. The polynomial can be initialized either by giving its coefficients with respect to the monomial basis or by giving a list of interpolation points, as follows:

import scipy.linalg as sl

class PolyNomial:
    base='monomial'
    def __init__(self,**args):
        if 'points' in args:
            self.points = array(args['points'])
            self.xi = self.points[:,0]
            self.coeff = self.point_2_coeff()
            self.degree = len(self.coeff)-1
        elif 'coeff' in args:
            self.coeff = array(args['coeff'])
            self.degree = len(self.coeff)-1
            self.points = self.coeff_2_point()
        else:
            self.points = array([[0,0]])
            self.xi = array([1.])
            self.coeff = self.point_2_coeff()
            self.degree = 0

The __init__ method of the new class uses the **args construction as discussed in section Parameters and arguments in Chapter 7, Functions. If no arguments are given, a zero polynomial is assumed. If the polynomial is given by interpolation points the method used to compute the coefficients by solving a Vandermonde system is given as follows:

def point_2_coeff(self):
    return sl.solve(vander(self.x),self.y)

If k coefficients are given also k interpolation points are constructed by:

def coeff_2_point(self):
    points = [[x,self(x)] for x in linspace(0,1,self.degree+1)]
    return array(points)

The self(x) command does a polynomial evaluation, which is done by providing a  method, __call__:

def __call__(self,x):
    return polyval(self.coeff,x)

(Refer example in section Special methods in Chapter 8, Classes.) Here, this method uses the command polyval. As a next step, we just add for convenience two methods, which we decorate with the property decorator (refer section Functions as decorators in Chapter 7, Functions):

@property
def x(self):
    return self.points[:,0]
@property
def y(self):
    return self.points[:,1]

Let's explain what is going on here. We define a method to extract the x-values of the data, which were used to define the polynomial. Similarly, a method to extract the y-values of the data is defined. With the property decorator, the result of calling the method is presented as if it were just an attribute of the polynomial. There are two coding alternatives:

  1. We use a method call:
          def x(self):
              return self.interppoints[:,0]

    This gives access to the x-values by the call: p.x().

  2. We use the property decorator. It us to access the x-values simply by this statement:  p.x

We choose the second variant. It is always a good practice to define a __repr__ method (refer section Attributes in Chapter 8, Classes). At least for a quick check of the results, this method is useful:

def __repr__(self):
    txt  = 'Polynomial of degree {degree} 
'
    txt += 'with coefficients {coeff} 
 in {base} basis.'
    return txt.format(coeff=self.coeff, degree=self.degree,
                                            base=self.base)

We now provide a method for plotting the polynomial, as follows:

margin = .05
plotres = 500
def plot(self,ab=None,plotinterp=True):
    if ab is None: # guess a and b
       x = self.x
       a, b = x.min(), x.max()
       h = b-a
       a -= self.margin*h
       b += self.margin*h
    else:
       a,b = ab
    x = linspace(a,b,self.plotres)
    y = vectorize(self.__call__)(x)
    plot(x,y)
    xlabel('$x$')
    ylabel('$p(x)$')
    if plotinterp:
        plot(self.x, self.y, 'ro')

Note the use of the vectorize command (refer section Functions acting on arrays in Chapter 4, Linear algebra - Arrays. The __call__ method is specific to the monomial representation and has to be changed if a polynomial is represented in another basis. This is also the case for the computation of the polynomial's companion matrix:

def companion(self):
    companion = eye(self.degree, k=-1)
    companion[0,:] -= self.coeff[1:]/self.coeff[0]
    return companion

Once the companion matrix is available, the zeros of the polynomial are given by the eigenvalues:

def zeros(self):
   companion = self.companion()
   return sl.eigvals(companion)

For this end the function eigvals has to be imported from scipy.linalg first. Let's give some usage examples.

First, we create a polynomial instance from the given interpolation points:

p = PolyNomial(points=[(1,0),(2,3),(3,8)])

The polynomial's coefficients with respect to the monomial basis are available as an attribute of p:

p.coeff # returns array([ 1., 0., -1.])

This corresponds to the polynomial The polynomial class . The default plot of the polynomial, obtained by p.plot(-3.5,3.5), results in the following figure (Figure 14.1):

The polynomial class

Figure 14.1: Result of the polynomial plot method

Finally, we compute the zeros of the polynomial, which in this case are two real numbers:

pz = p.zeros() # returns array([-1.+0.j, 1.+0.j])

The result can be verified by evaluating the polynomial at these points:

p(pz) # returns array([0.+0.j, 0.+0.j])
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.188.137.58