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:
def x(self): return self.interppoints[:,0]
This gives access to the x-values by the call: p.x()
.
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 default plot of the polynomial, obtained by p.plot(-3.5,3.5)
, results in the following figure (Figure 14.1):
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])
18.188.137.58