Representing Polynomials with Classes

Object Requirements

This example implements a class to represent polynomials in the MATLAB® language. The design requirements are:

  • Value class behavior—a polynomial object should behave like MATLAB numeric variables when copied and passed to functions.

  • Specialized display and indexing

  • Objects can be scalar only. The specialization of display and indexing functionality preclude normal array behavior.

  • Arithmetic operations

  • Double converter simplifying the use of polynomial object with existing MATLAB functions that accept numeric inputs.

DocPolynom Class Members

The class definition specifies a property for data storage and defines a folder (@DocPolynom) that contains the class definition.

The following table summarizes the properties defined for the DocPolynom class.

DocPolynom Class Properties

Name

Class

Default

Description

coef

double

[]

Vector of polynomial coefficients [highest order ... lowest order]

The following table summarizes the methods for the DocPolynom class.

DocPolynom Class Methods

Name

Description

DocPolynom

Class constructor

double

Converts a DocPolynom object to a double (that is, returns its coefficients in a vector)

char

Creates a formatted display of the DocPolynom object as powers of x and is used by the disp method

disp

Determines how MATLAB displays DocPolynom objects on the command line

subsref

Enables you to specify a value for the independent variable as a subscript, access the coef property with dot notation, and call methods with dot notation.

plus

Implements addition of DocPolynom objects

minus

Implements subtraction of DocPolynom objects

mtimes

Implements multiplication of DocPolynom objects

Using the DocPolynom Class

The following examples illustrate basic use of the DocPolynom class.

Create DocPolynom objects to represent the following polynomials. The argument to the constructor function contains the polynomial coefficients and .

p1 = DocPolynom([1 0 -2 -5])
p1 =
   x^3 - 2*x - 5
p2 = DocPolynom([2 0 3 2 -7])
p2 =
   2*x^4 + 3*x^2 + 2*x - 7

Find the roots of the polynomial by passing the coefficients to the roots function.

roots(p1.coef)
ans =

   2.0946 + 0.0000i
  -1.0473 + 1.1359i
  -1.0473 - 1.1359i

Add the two polynomials p1 and p2.

MATLAB calls the plus method defined for the DocPolynom class when you add two DocPolynom objects.

p1 + p2
ans = 

2*x^4 + x^3 + 3*x^2 - 12

DocPolynom Class Synopsis

Example CodeDiscussion
classdef DocPolynom

Value class that implements a data type for polynomials.

   properties
      coef
   end

Vector of polynomial coefficients [highest order ... lowest order]

   methods

For general information about methods, see Ordinary Methods

      function obj = DocPolynom(c)
         if nargin > 0
            if isa(c,'DocPolynom')
               obj.coef = c.coef;
            else
               obj.coef = c(:).';
            end
         end
      end 

Class constructor creates objects using:

  • Coefficient vector of existing object

  • Coefficient vector passed as argument

See The DocPolynom Constructor

      function obj = set.coef(obj,val)
         if ~isa(val,'double')
            error('Coefficients must be doubles')
         end
         ind = find(val(:).'~=0);
         if ~isempty(ind);
            obj.coef = val(ind(1):end);
         else
            obj.coef = val;
         end
      end

Set method for coef property:

  • Allows coefficients only of type double

  • Removes leading zeros from the coefficient vector.

See Remove Irrelevant Coefficients

      function c = double(obj)
         c = obj.coef;
      end

Convert DocPolynom object to double by returning the coefficients.

See Convert DocPolynom Objects to Other Types

function str = char(obj)
   if all(obj.coef == 0)
      s = '0';
      str = s;
      return
   else
      d = length(obj.coef)-1;
      s = cell(1,d);
      ind = 1;
      for a = obj.coef;
         if a ~= 0;
            if ind ~= 1
               if a > 0
                  s(ind) = {' + '};
                  ind = ind + 1;
               else
                  s(ind) = {' - '};
                  a = -a; 
                  ind = ind + 1;
               end
            end
            if a ~= 1 || d == 0
               if a == -1
                  s(ind) = {'-'};
                  ind = ind + 1;
               else
                  s(ind) = {num2str(a)};
                  ind = ind + 1;
                  if d > 0
                     s(ind) = {'*'};
                     ind = ind + 1;
                  end
               end
            end
            if d >= 2
               s(ind) = {['x^' int2str(d)]};
               ind = ind + 1;
            elseif d == 1
               s(ind) = {'x'};
               ind = ind + 1;
            end
         end
         d = d - 1;
      end
   end
   str = [s{:}];
end

Convert DocPolynom object to char that represents the expression:

y = f(x)

See Convert DocPolynom Objects to Other Types

      function disp(obj)
         c = char(obj);
         if iscell(c)
            disp(['     ' c{:}])
         else
            disp(c)
         end
      end 

Overload disp function. Display objects as output of char method.

For information about this code, see Overload disp for DocPolynom

      function dispPoly(obj,x)
         p = char(obj);
         e = @(x)eval(p);
         y = zeros(length(x));
         disp(['y = ',p])
         for k = 1:length(x)
            y(k) = e(x(k));
            disp(['  ',num2str(y(k)),...
                  ' = f(x = ',...
                  num2str(x(k)),')'])
         end
      end

Return evaluated expression with formatted output.

Uses output of char method to evaluate polynomial at specified values of independent variable.

For information about this code, see Display Evaluated Expression

      function b = subsref(a,s)
         switch s(1).type
            case '()'
               ind = s.subs{:};
               b = polyval(a.coef,ind);
            case '.'
               switch s(1).subs
                  case 'coef'
                     b = a.coef;
                  case 'disp'
                     disp(a)
                  otherwise
                     if length(s)>1
                        b = a.(s(1).subs)(s(2).subs{:});
                     else
                        b = a.(s.subs);
                     end
               end
            otherwise
               error('Specify value for x as obj(x)')
         end
      end

Redefine indexed reference for DocPolynom objects.

For information about this code, see Redefine Indexed Reference

      function r = plus(obj1,obj2)
         obj1 = DocPolynom(obj1);
         obj2 = DocPolynom(obj2);
         k = length(obj2.coef) - length(obj1.coef);
         zp = zeros(1,k);
         zm = zeros(1,-k);
         r = DocPolynom([zp,obj1.coef] + [zm,obj2.coef]);
      end

      function r = minus(obj1,obj2)
         obj1 = DocPolynom(obj1);
         obj2 = DocPolynom(obj2);
         k = length(obj2.coef) - length(obj1.coef);
         zp = zeros(1,k);
         zm = zeros(1,-k);
         r = DocPolynom([zp,obj1.coef] - [zm,obj2.coef]);
      end

      function r = mtimes(obj1,obj2)
         obj1 = DocPolynom(obj1);
         obj2 = DocPolynom(obj2);
         r = DocPolynom(conv(obj1.coef,obj2.coef));
      end
   end

Define three arithmetic operators:

  • Polynomial addition

  • Polynomial subtraction

  • Polynomial multiplication

For information about this code, see Define Arithmetic Operators.

For general information about defining operators, see Operator Overloading

   end
end

end statements for methods and for classdef.

 Expand for Class Code

The DocPolynom Constructor

The following function is the DocPolynom class constructor, which is in the file @DocPolynom/DocPolynom.m:

methods
   function obj = DocPolynom(c)
      if isa(c,'DocPolynom')
         obj.coef = c.coef;
      else
         obj.coef = c(:).';
      end
   end 
end

Constructor Calling Syntax

It is possible to all the DocPolynom constructor with two different arguments:

  • Input argument is a DocPolynom object — If you call the constructor function with an input argument that is already a DocPolynom object, the constructor returns a new DocPolynom object with the same coefficients as the input argument. The isa function checks for this input.

  • Input argument is a coefficient vector — If the input argument is not a DocPolynom object, the constructor attempts to reshape the values into a vector and assign them to the coef property.

    The coef property set method restricts property values to doubles. See Remove Irrelevant Coefficients for a description of the property set method.

An example use of the DocPolynom constructor is the statement:

p = DocPolynom([1 0 -2 -5])
p = 
   x^3 - 2*x -5

This statement creates an instance of the DocPolynom class with the specified coefficients. Note that the display of the object shows the equivalent polynomial using MATLAB language syntax. The DocPolynom class implements this display using the disp and char class methods.

Remove Irrelevant Coefficients

MATLAB software represents polynomials as row vectors containing coefficients ordered by descending powers. Zeros in the coefficient vector represent terms that drop out of the polynomial. Leading zeros, therefore, can be ignored when forming the polynomial.

Some DocPolynom class methods use the length of the coefficient vector to determine the degree of the polynomial. It is useful, therefore, to remove leading zeros from the coefficient vector so that its length represents the true value.

The DocPolynom class stores the coefficient vector in a property that uses a set method to remove leading zeros from the specified coefficients before setting the property value.

methods
   function obj = set.coef(obj,val)
      if ~isa(val,'double')
         error('Coefficients must be doubles')
      end
      ind = find(val(:).'~=0);
      if ~isempty(ind);
         obj.coef = val(ind(1):end);
      else
         obj.coef = val;
      end
   end
end

Convert DocPolynom Objects to Other Types

The DocPolynom class defines two methods to convert DocPolynom objects to other classes:

  • double — Converts to the double numeric type so functions can perform mathematical operations on the coefficients.

  • char — Converts to characters used to format output for display in the command window

The Double Converter

The double converter method for the DocPolynom class simply returns the coefficient vector:

methods
   function c = double(obj)
      c = obj.coef;
   end
end 

For the DocPolynom object p:

p = DocPolynom([1 0 -2 -5]);

the statement:

c = double(p)

returns:

c=
    1     0    -2    -5

which is of class double:

class(c)
ans = 
   double

The Character Converter

The char method produces a char vector that represents the polynomial displayed as powers of x. The char vector returned is a syntactically correct MATLAB expression.

The char method uses a cell array to collect the char vector components that make up the displayed polynomial.

The disp method uses the char method to format the DocPolynom object for display. The evalPoly method uses char to create the MATLAB expression to evaluate.

Users of DocPolynom objects are not likely to call the char or disp methods directly, but these methods enable the DocPolynom class to behave like other data classes in MATLAB.

Here is the char method.

methods
   function str = char(obj)
      if all(obj.coef == 0)
         s = '0';
         str = s;
         return
      else
         d = length(obj.coef)-1;
         s = cell(1,d);
         ind = 1;
         for a = obj.coef;
            if a ~= 0;
               if ind ~= 1
                  if a > 0
                     s(ind) = {' + '};
                     ind = ind + 1;
                  else
                     s(ind) = {' - '};
                     a = -a; 
                     ind = ind + 1;
                  end
               end
               if a ~= 1 || d == 0
                  if a == -1
                     s(ind) = {'-'};
                     ind = ind + 1;
                  else
                     s(ind) = {num2str(a)};
                     ind = ind + 1;
                     if d > 0
                        s(ind) = {'*'};
                        ind = ind + 1;
                     end
                  end
               end
               if d >= 2
                  s(ind) = {['x^' int2str(d)]};
                  ind = ind + 1;
               elseif d == 1
                  s(ind) = {'x'};
                  ind = ind + 1;
               end
            end
            d = d - 1;
         end
      end
      str = [s{:}];
   end
end

Overload disp for DocPolynom

To provide a more useful display of DocPolynom objects, this class overloads disp in the class definition.

This disp method relies on the char method to produce a text representation of the polynomial, which it then displays on the screen.

The char method returns a cell array or the character '0' if the coefficients are all zero.

methods
   function disp(obj)
      c = char(obj);
      if iscell(c)
         disp(['     ' c{:}])
      else
         disp(c)
      end
   end
end 

When MATLAB Calls the disp Method

The statement:

p = DocPolynom([1 0 -2 -5])

creates a DocPolynom object. Because the statement is not terminated with a semicolon, the resulting output is displayed on the command line:

p =
    x^3 - 2*x - 5

Display Evaluated Expression

The char converter method forms a MATLAB expression for the polynomial represented by a DocPolynom object. The dispPoly method evaluates the expression returned by the char method with a specified value for x.

methods
   function dispPoly(obj,x)
      p = char(obj);
      e = @(x)eval(p);
      y = zeros(length(x));
      disp(['y = ',p])
      for k = 1:length(x)
         y(k) = e(x(k));
          disp(['  ',num2str(y(k)),...
               ' = f(x = ',...
               num2str(x(k)),')'])
      end
   end
end

Create a DocPolynom object p:

p = DocPolynom([1 0 -2 -5])
p = 

x^3 - 2*x - 5

Evaluate the polynomial at x equal to three values, [3 5 9]:

dispPoly(p,[3 5 9])
y = x^3 - 2*x - 5
  16 = f(x = 3)
  110 = f(x = 5)
  706 = f(x = 9)

Redefine Indexed Reference

The DocPolynom class redefines indexed reference to support the use of objects representing polynomials. In the DocPolynom class, a subscripted reference to an object causes an evaluation of the polynomial with the value of the independent variable equal to the subscript.

For example, given the following polynomial:

Create a DocPolynom object p:

p = DocPolynom([1 0 -2 -5])
p =
    x^3 - 2*x - 5

The following subscripted expression evaluates the value of the polynomial at x = 3 and at x = 4, and returns the resulting values:

p([3 4])
ans =
    16   51

Indexed Reference Design Objectives

Redefine the default subscripted reference behavior by implementing a subsref method.

If a class defines a subsref method, MATLAB calls this method for objects of this class whenever a subscripted reference occurs. The subsref method must define all the indexed reference behaviors, not just a specific case that you want to change.

The DocPolynom subsref method implements the following behaviors:

  • p(x = [a1...an]) — Evaluate polynomial at x = a.

  • p.coef — Access coef property value

  • p.disp — Display the polynomial as a MATLAB expression without assigning an output.

  • obj = p.method(args) — Use dot notation to call methods arguments and return a modified object.

  • obj = p.method — Use dot notation to call methods without arguments and return a modified object.

subsref Implementation Details

The subsref method overloads the subsref function.

For example, consider a call to the polyval function:

p = DocPolynom([1 0 -2 -5])
p = 
     x^3 - 2*x - 5
polyval(p.coef,[3 5 7])
ans =
    16   110   324

The polyval function requires the:

  • Polynomial coefficients

  • Values of the independent variable at which to evaluate the polynomial

The polyval function returns the value of f(x) at these values. subsref calls polyval through the statements:

case '()'
   ind = s.subs{:};
   b = polyval(a.coef,ind);

When implementing subsref to support method calling with arguments using dot notation, both the type and subs structure fields contain multiple elements.

The subsref method implements all subscripted reference explicitly, as show in the following code listing.

methods
   function b = subsref(a,s)
      switch s(1).type
         case '()'
            ind = s.subs{:};
            b = polyval(a.coef,ind);
         case '.'
            switch s(1).subs
               case 'coef'
                  b = a.coef;
               case 'disp'
                  disp(a)
               otherwise
                  if length(s)>1
                     b = a.(s(1).subs)(s(2).subs{:});
                  else
                     b = a.(s.subs);
                  end
            end
         otherwise
            error('Specify value for x as obj(x)')
      end
   end
end

Define Arithmetic Operators

Several arithmetic operations are meaningful on polynomials. The DocPolynom class implements these methods:

Method and Syntax

Operator Implemented

plus(a,b)

Addition

minus(a,b)

Subtraction

mtimes(a,b)

Matrix multiplication

When overloading arithmetic operators, consider the data types you must support. The plus, minus, andmtimes methods are defined for the DocPolynom class to handle addition, subtraction, and multiplication on DocPolynomDocPolynom and DocPolynomdouble combinations of operands.

Define + Operator

If either p or q is a DocPolynom object, this expression:

p + q

Generates a call to a function @DocPolynom/plus, unless the other object is of higher precedence.

The following method overloads the plus (+) operator for the DocPolynom class:

methods
   function r = plus(obj1,obj2)
      obj1 = DocPolynom(obj1);
      obj2 = DocPolynom(obj2);
      k = length(obj2.coef) - length(obj1.coef);
      zp = zeros(1,k);
      zm = zeros(1,-k);
      r = DocPolynom([zp,obj1.coef] + [zm,obj2.coef]);
   end
end 

Here is how the function works:

  • Ensure that both input arguments are DocPolynom objects so that expressions such as

    p + 1
    

    that involve both a DocPolynom and a double, work correctly.

  • Access the two coefficient vectors and, if necessary, pad one of them with zeros to make both the same length. The actual addition is simply the vector sum of the two coefficient vectors.

  • Call the DocPolynom constructor to create a properly typed object that is the result of adding the polynomials.

Define - Operator

Implement the minus operator (-) using the same approach as the plus (+) operator.

The minus method computes p - q. The dominant argument must be a DocPolynom object.

methods
   function r = minus(obj1,obj2)
      obj1 = DocPolynom(obj1);
      obj2 = DocPolynom(obj2);
      k = length(obj2.coef) - length(obj1.coef);
      zp = zeros(1,k);
      zm = zeros(1,-k);
      r = DocPolynom([zp,obj1.coef] - [zm,obj2.coef]);
   end
end 

Define the * Operator

Implement the mtimes method to compute the product p*q. The mtimes method implements matrix multiplication since the multiplication of two polynomials is the convolution (conv) of their coefficient vectors:

methods
   function r = mtimes(obj1,obj2)
      obj1 = DocPolynom(obj1);
      obj2 = DocPolynom(obj2);
      r = DocPolynom(conv(obj1.coef,obj2.coef));
   end
end 

Using the Arithmetic Operators

Given the DocPolynom object:

p = DocPolynom([1 0 -2 -5]);

The following two arithmetic operations call the DocPolynom plus and mtimes methods:

q = p+1;
r = p*q;

to produce

q = 
    x^3 - 2*x - 4

r =
x^6 - 4*x^4 - 9*x^3 + 4*x^2 + 18*x + 20