The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Math::Yapp - Perl extension for working with Polynomials. Yes, I know there are *many!* Polynomial packages. And like them, I started it for (geeky) fun, then got obsessed with it as a learning experience. Enjoy!

AUTHOR

Jacob Salomon, jakesalomon@yahoo.com

COPYRIGHT AND LICENSE

Copyright (C) 2013 by Jacob Salomon

SYNOPSIS

use Math::Complex; # Optional but a very good idea, since solving a polynomial often yields complex numbers

use Math::Yapp;

Constructors

$yp = Math::Yapp->new(); # Degenerate polynomial - no terms

While the new() method is certainly available, your code will look cleaner if you use this form:

$yp = Yapp(parameter(s)); # Examples follow

Real and complex coefficiencts

$yp = Yapp(1, -2.178, 3.14, -cplx(1,-3), 5);

Yields: 5x^4 +(-1+3i)x^3 +3.14x^2 -2.178x +1

Same as above, only passed as a reference to an array:

my @coef_list = (1, -2.178, 3.14, -cplx(1,-3), 5);

$yp = Yapp(\@coef_list);

Generated from a string: Note that the sign MUST precede each term with no intervening space; a space separating the sign from the term may mess up the matching pattern. Yes, the + sign is optional for the first term in the string.

$yp = Yapp("5x^4 +(-1+3i)x^3 +3.14x^2 -2.178x +1");

The Yapp() constructor function has one additional feature that was awkward to implement in the new() constructor: An explicit copy constructor:

Copy from the above Yapp polynomial [referenced by] $yp:

$yp1 = Yapp($yp); # Clones $yp to an identical copy at a different reference

Constructors by interpolation

$ypl = Yapp_interpolate(\@xvals, \@yvals); # Perform Lagrange interpolation

$yph = Yapp_interpolate(\@xvals, \@yvals, \@ypvals); # Hermite interpolation

Note that the interpolating constructors require array references; otherwise you run into the elementary error of array-mashing. The Lagrange form, just X and Y values, generates a polynomial that colocates at each of the points indicated in the X-Y pairs represented by the arrays. For The Hermite version the third array [reference] if for the desired derivative at each point.

(At this time, the author waives Hermite interpolation for more than the first derivative. :-)

Constructing a Yapp polynomial from its roots

$yp = Yapp_by_roots(\@root_list); # Pass reference to an array of roots

$yp = Yapp_by_roots(1, 2, -4, cplx(3,4), cplx(3,-1)); # Pass a complete array to constructor

Arithmetic of Yapp Polynomials

Unary Operations

$yp2 = !$yp; # Change the signs of all coefficients

$yp2 = $yp1->negate(); # (The overloaded function)

$yp2 = ~$yp; # Conjugate complex coefficients

Addition and Subtraction

$yp += 13; # Add a real value to the constant term

$yp += cplx(2, -5); # Add a complex number to the constant term

$yp += $yp3; # Add another polynomial to this one

$yp = $yp1 + $yp2; # Add two polynomials, term-by-term

Subtracting polynomials: Behaved pretty much like the adds so we are not inclucing all possible examples

$yp -= $yp3; # Subtract $yp3 from $yp in place

$yp = $yp1 - $yp2; # Subtract two polynomials, term-by-term

Multiplication and division:

$yp *= 42; # Multiply each coefficient by the same number

$yp = $yp1 * 42; # Multiply as above but return a new polynomial

$yp = 42 * $yp1; # (Same idea as above)

$yp *= $yp2; # Multiply the indicated polynomial by another, in place.

$yp = $yp1 * $yp2; # Same as above, but not in-place

$yp /= 10; # Divide all coefficients by a number

Division by a polynomial is not defined in this package, although when I evaluate a polynomials at, say, x = 3, it is equivalent to dividing by the small polynomial (x - 3). Hence, for Yapp_eval (described later), you have the option of getting the quotient besides the evaluation

Documented methods and functions:

Ysprint() formats a Yapp object into a string, suitable for printing: Example: printf("My yapp is: %s\n", Ysprint($yp1));

By default, Ysprint formats the polynmial as follows:

  • Starting from the high-order exponent, working its way down

  • All coefficients are displayed with 3 decimal places

  • Zero coefficients are skipped.

This can be controlled by the following functions, which affect module-global variables:

  • Yapp_start_high(0); # Setting FALSE: Start from low-degree terms

  • Yapp_decimals(2); # Set number of decimal places to display

  • Yapp_print0(1); # Ysprint shall display 0-value coefficients

In all three cases, the newly value is returned. Oh, and if you call it without a parameter, it will just return the currently set value

my $high_expon = $yp->degree(); # Get the degree of the polynomial

$yp-variable>>("Xy"); # Sets the variable used in printing a polynomial. Default: "X"

my $varname = $yp->variable(); # Returns that variable string

my $nterm = $yp->coefficient(3); # Retrieve the coefficient on the term of degree 3 (in this example)

my $realvar = $yp->Yapp_eval($areal); # Plug the parameter into the polynomial and return the value

my $cplxvar = $yp->Yapp_eval($acplx); # Works identially for complex numbers, except that now it returns a reference to a Math::Complex

my ($eval, $quotient) = $yp->Yapp_eval($areal); # This returns the evaluation and also a ref to a new, lower-degree polynomial that is the quotient.

my $ypr = $yp->Yapp_reduce(3); # Return a reference to a new polynomial whose roots are those of of the given polynomial, reduced [in this case] by 3

my $dyp = $yp->Yapp_derivative(n); # Returns [a ref to] another polynomial that is the nth derivative of this one.

my $i_ref = $yp->Yapp_integral(); # Returns a reference to a polynomial whose derivative is the given Yapp. Inserts a 0 for the arbitrary constant.

my $i_val = $yp->Yapp_integral($low, $high); # Calculates the value of the definite integral of the given Yapp in the given interval.

Solution Set for Polynomials

my @solutions = $yp->Yapp_solve(); # Solve for all real and complex roots

DESCRIPTION

Man, if that synopsis don't say it all, what can I possibly add? :-)

OK, as mentioned above, this is a fun project. The plan, not necessarily all implemented at the first release, is to provide many kinds of operations on the polynomials that intimidated us in high school and even college (if you took PDE or Numerical Analysis). The usual high-school functions are ordinary arithmetic on these algebraic expressions, as well as plugging a value in there to get the result. (Horner's synthetic division saves a lot of work.)

Notes on derivatives and integrals

  • If n is omitted, it returns first derivative

  • If n is 0, it returns the same Yapp reference

  • All derivatives, as well as the indefinite integral, are cached in an internal array on the object. If anything mutates the object (like a += operation) the cache is cleared

Addendum: A Note on Inner products and orthogonal polynomials;

At this stage, the plan is to provide the inner product algorithms for various classes of inner-product spaces but in separate modules, for example: Math::Yapp:Tchebycheff or Math::Yapp::Laguerre. These will use the overloaded "dot" operator. A future release of this module, may include the simplest class of orthogonality, the scheme used in Gaussian quadrature, that is the inner product defined as the intergral of the product of the polynomials over the interval [-1,1]. .. But not today.

EXPORT

This package export the following functions by default. (They are few enough to be an unlikey source of name-space pollution, IMHO)

  • Yapp(): The constructor that is NOT a method, so you don't have to type Math::Yapp::new().

  • Yapp_interpolate(): The constructor by Lagrange and Hermite interpolation

  • Yapp_by_roots(): Construct a polynomial by its roots

  • Yapp_decimals(): Sets or retrives a global setting for how many decimal places to display with each %f-formatted variable.

  • Yapp_print0(): By default, printing a Yapp will skip any term whose coefficient is 0. This exported function sets an internal global flag to either display 0-coefficient terms (1) or to skip them (0)

  • Yapp_start_high(): By default, printing a Yapp will start from the highest coefficient, the way we are accustomed to writing a polynomial. For some testing purposes and, I imagine, some other applications, it may be neater to start printing from the low-degree coefficients. This functions sets an intenal global flag to print high-to-low (default: 1) or low-to-high (0).

Bugs

Note that the current release of Math::Yapp uses the default floating-point library of its host system. It was developed in a Cygwin environment running under Windows-7. I discovered some limitations to the 64 (or 80?) bit FP operations when solving polynomials of degree higher thatn 8 or using Hermite interpolation of more than 6 points. I have researched Math::MPC a bit and hope to use that in a future release of this module. However, I encountered some errors when trying to compile the required MPC, MPFR and GMP C libraries. So that plan is going on the back burner.

There is some sloppy code in method Ysprint() which produces the correct output but I would really rather figure out why I needed to add said sloppy code. The bug this covers up is that the first degree term should display without exponent, not as X^1. Similarly, the constant term should display with neither variable nor exponent, rather than as X^0.

It might be argued that my failure to include higher-order derivatives in the Hermite interpolation scheme is a bug. Perhaps by the time I publish the next release of this module I will have an understanding of Newton's Method of Divided Differences. (Sorry, no promises on that account.)

The usual disclaimers :-)

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.14.2 or, at your option, any later version of Perl 5 you may have available.

Acknowledements

Thanks to John Altom, formerly of CCM Consulting Services, for his help in some basic (but arguable forgettable) details of the EXPORT behavior.

An old debt of gratitude to [the presumably late] Professor Stanley Preisler, who taught Numerical Analysis at Polytechnic University in the late '70s. The course was quite over my head but some stuff remained, as you can see.