NAME

Math::Symbolic::Custom::Polynomial - Polynomial routines for Math::Symbolic

VERSION

Version 0.3

DESCRIPTION

This is the beginnings of a module to provide some polynomial utility routines for Math::Symbolic.

"symbolic_poly()" creates a polynomial Math::Symbolic expression according to the supplied variable and coefficients, and "test_polynomial()" attempts the inverse, it looks at a Math::Symbolic expression and tries to extract polynomial coefficients (so long as the expression represents a polynomial). The "apply_synthetic_division()" and "apply_polynomial_division()" methods will attempt to perform division on a target polynomial.

EXAMPLE

use strict;
use Math::Symbolic qw(:all);
use Math::Symbolic::Custom::Polynomial;
use Math::Complex;

# create a polynomial expression
my $f1 = symbolic_poly('x', [5, 4, 3, 2, 1]);
print "Output: $f1\n\n\n";   
# Output: ((((5 * (x ^ 4)) + (4 * (x ^ 3))) + (3 * (x ^ 2))) + (2 * x)) + 1

# also works with symbols
my $f2 = symbolic_poly('t', ['a/2', 'u', 0]);
print "Output: $f2\n\n\n"; 
# Output: ((a / 2) * (t ^ 2)) + (u * t)

# analyze a polynomial with complex roots
my $complex_poly = parse_from_string("y^2 + y + 1");
my ($var, $coeffs, $disc, $roots) = $complex_poly->test_polynomial('y');

my $degree = scalar(@{$coeffs})-1;
print "'$complex_poly' is a polynomial in $var of degree $degree with " . 
        "coefficients (ordered in descending powers): (", join(", ", @{$coeffs}), ")\n";
print "The discriminant has: $disc\n";
print "Expressions for the roots are:\n\t$roots->[0]\n\t$roots->[1]\n";

# evaluate the root expressions as they should resolve to numbers
# 'i' => i glues Math::Complex and Math::Symbolic
my $root1 = $roots->[0]->value('i' => i);   
my $root2 = $roots->[1]->value('i' => i);
# $root1 and $root2 are Math::Complex numbers
print "The roots evaluate to: (", $root1, ", ", $root2, ")\n";

# plug back in to verify the roots take the poly back to zero
# (or at least, as numerically close as can be gotten).
print "Putting back into original polynomial:-\n\tat y = $root1:\t", 
        $complex_poly->value('y' => $root1), 
        "\n\tat y = $root2:\t", 
        $complex_poly->value('y' => $root2), "\n\n\n";

# analyze a polynomial with a parameter 
my $some_poly = parse_from_string("x^2 + 2*k*x + (k^2 - 4)");
($var, $coeffs, $disc, $roots) = $some_poly->test_polynomial('x');

$degree = scalar(@{$coeffs})-1;
print "'$some_poly' is a polynomial in $var of degree $degree with " .
        "coefficients (ordered in descending powers): (", join(", ", @{$coeffs}), ")\n";
print "The discriminant has: $disc\n";
print "Expressions for the roots are:\n\t$roots->[0]\n\t$roots->[1]\n";

# evaluate the root expressions for k = 3 (for example)
my $root1 = $roots->[0]->value('k' => 3);
my $root2 = $roots->[1]->value('k' => 3);
print "Evaluating at k = 3, roots are: (", $root1, ", ", $root2, ")\n";

# plug back in to verify
print "Putting back into original polynomial:-\n\tat k = 3 and x = $root1:\t", 
        $some_poly->value('k' => 3, 'x' => $root1), 
        "\n\tat k = 3 and x = $root2:\t", 
        $some_poly->value('k' => 3, 'x' => $root2), "\n\n";          

symbolic_poly()

Exported by default (or it should be; try calling it directly if that fails). Constructs a Math::Symbolic expression corresponding to the passed parameters: a symbol for the desired indeterminate variable, and an array ref to the coefficients in descending order (which can also be symbols).

See the 'Example' section above for examples of use.

method test_polynomial()

Exported through the Math::Symbolic module extension class. Call it on a polynomial Math::Symbolic expression and it will try to determine the coefficient expressions.

Takes one parameter, the indeterminate variable. If this is not provided, test_polynomial will try to detect the variable. This can be useful to test if an arbitrary Math::Symbolic expression looks like a polynomial.

If the expression looks like a polynomial of degree 2, then it will apply the quadratic equation to produce expressions for the roots, and the discriminant.

Example (also see 'Example' section above):

use strict;
use Math::Symbolic qw(:all);
use Math::Symbolic::Custom::Polynomial;
use Math::Complex;

# finding roots with Math::Polynomial::Solve
use Math::Polynomial::Solve qw(poly_roots coefficients);
coefficients order => 'descending';

# some big polynomial
my $big_poly = parse_from_string("phi^8 + 3*phi^7 - 5*phi^6 + 2*phi^5 -7*phi^4 + phi^3 + phi^2 - 2*phi + 9");
# if test_polynomial() is not supplied with the indeterminate variable, it will try to autodetect
my ($var, $co) = $big_poly->test_polynomial();  
my @coeffs = @{$co};
my $degree = scalar(@coeffs)-1;

print "'$big_poly' is a polynomial in $var of degree $degree with " . 
            "coefficients (ordered in descending powers): (", join(", ", @coeffs), ")\n";

# Find the roots of the polynomial using Math::Polynomial::Solve. 
my @roots = poly_roots( 
      # call value() on each coefficient to get a number.
      # if there were any parameters, we would have to supply their value
      # here to force the coefficients down to a number.
      map { $_->value() } @coeffs 
      );

print "The roots and corresponding values of the polynomial are:-\n";
foreach my $root (@roots) {
      # put back into the original expression to verify
      my $val = $big_poly->value('phi' => $root);
      print "\t$root => $val\n";
}

method apply_synthetic_division()

Exported through the Math::Symbolic module extension class. Divides the target polynomial by a divisor of the form (x - r) using synthetic division. This method relies on test_polynomial() to detect if the target Math::Symbolic expression is a polynomial, and to determine the coefficients and the indeterminate variable if not provided.

Takes two parameters, the evaluator i.e. the 'r' part of (x-r) (required, can be a Math::Symbolic expression or a text string which will be converted to a Math::Symbolic expression using the parser), and the polynomial variable (optional, as test_polynomial() can try to detect it).

If called in a scalar context, it returns the polynomial in its divided form, i.e. D*Q + R (where D is the divisor x-r, Q is the quotient (polynomial) and R is the remainder).

If called in a list context, it returns the polynomial in divided form as describe above, and also the divisor, quotient and remainder expressions.

Example:

use strict;
use warnings;
use Math::Symbolic qw(:all);
use Math::Symbolic::Custom::Polynomial;

# Divide (2*x^3 - 6*x^2 + 2*x - 1) by (x - 3)
my $poly = parse_from_string("2*x^3 - 6*x^2 + 2*x - 1");
my $evaluator = 3; # it will put "x - $evaluator" internally.

# Specifying 'x' as the polynomial variable in apply_synthetic_division() is optional, see test_polynomial().
# It is not needed for this straightforward polynomial but is just present for documentation.
my ($full_expr, $divisor, $quotient, $remainder) = $poly->apply_synthetic_division($evaluator, 'x');  

# The return values are Math::Symbolic expressions
print "Full expression: $full_expr\n";  # Full expression: ((x - 3) * (2 + (2 * (x ^ 2)))) + 5
print "Divisor: $divisor\n";    # Divisor: x - 3
print "Quotient: $quotient\n";  # Quotient: 2 + (2 * (x ^ 2))
print "Remainder: $remainder\n";    # Remainder: 5

# Also works symbolically. Divide it by r.
($full_expr, $divisor, $quotient, $remainder) = $poly->apply_synthetic_division('r');  

print "Full expression: $full_expr\n";
# Full expression: ((x - r) * (((((2 + (2 * (r ^ 2))) + (2 * (x ^ 2))) + ((2 * r) * x)) - (6 * r)) - (6 * x))) + ((((2 * r) + (2 * (r ^ 3))) - 1) - (6 * (r ^ 2)))
print "Divisor: $divisor\n";    # Divisor: x - r
print "Quotient: $quotient\n";  # Quotient: ((((2 + (2 * (r ^ 2))) + (2 * (x ^ 2))) + ((2 * r) * x)) - (6 * r)) - (6 * x)
print "Remainder: $remainder\n";    # Remainder: (((2 * r) + (2 * (r ^ 3))) - 1) - (6 * (r ^ 2))

method apply_polynomial_division()

Exported through the Math::Symbolic module extension class. Divides the target polynomial by a divisor polynomial using polynomial long division. This method relies on test_polynomial() to detect if the target and divisor expressions are polynomials, and to determine the coefficients and the indeterminate variables if not provided. The indeterminate variables must be the same in both expressions.

Takes two parameters, the divisor polynomial (required, can be a Math::Symbolic expression or a text string which will be converted to a Math::Symbolic expression using the parser), and the polynomial variable (optional, as test_polynomial() can try to detect it).

If called in a scalar context, it returns the polynomial in its divided form, i.e. D*Q + R (where D is the divisor, Q is the quotient and R is the remainder).

If called in a list context, it returns the polynomial in divided form as describe above, and also the divisor, quotient and remainder expressions.

Example:

use strict;
use warnings;
use Math::Symbolic qw(:all);
use Math::Symbolic::Custom::Polynomial;

# Divide (2*y^3 - 3*y^2 - 3*y +2) by (y - 2)
my $poly = symbolic_poly('y', [2, -3, -3, 2]);
my ($full_expr, $divisor, $quotient, $remainder) = $poly->apply_polynomial_division('y-2', 'y');

print "Full expression: $full_expr\n";  # Full expression: (y - 2) * ((y + (2 * (y ^ 2))) - 1)
print "Divisor: $divisor\n";    # Divisor: y - 2
print "Quotient: $quotient\n";  # Quotient: (y + (2 * (y ^ 2))) - 1
print "Remainder: $remainder\n";    # Remainder: 0

# Also works symbolically. Divide by (y^2 - 2*k*y + k)
($full_expr, $divisor, $quotient, $remainder) = $poly->apply_polynomial_division('y^2 - 2*k*y + k', 'y');

print "Full expression: $full_expr\n";
# Full expression: ((((y ^ 2) - ((2 * k) * y)) + k) * (((4 * k) + (2 * y)) - 3)) + (((((2 + (3 * k)) + ((8 * (k ^ 2)) * y)) - (4 * (k ^ 2))) - (3 * y)) - ((8 * k) * y))
print "Divisor: $divisor\n";    # Divisor: ((y ^ 2) - ((2 * k) * y)) + k
print "Quotient: $quotient\n";  # Quotient: ((4 * k) + (2 * y)) - 3
print "Remainder: $remainder\n";    # Remainder: ((((2 + (3 * k)) + ((8 * (k ^ 2)) * y)) - (4 * (k ^ 2))) - (3 * y)) - ((8 * k) * y)

SEE ALSO

Math::Symbolic

Math::Polynomial

Math::Polynomial::Solve

AUTHOR

Matt Johnson, <mjohnson at cpan.org>

ACKNOWLEDGEMENTS

Steffen Mueller, author of Math::Symbolic

LICENSE AND COPYRIGHT

This software is copyright (c) 2024 by Matt Johnson.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.