/* * PolynomialFunction.java * * Copyright (c) 2002-2015 Alexei Drummond, Andrew Rambaut and Marc Suchard * * This file is part of BEAST. * See the NOTICE file distributed with this work for additional * information regarding copyright ownership and licensing. * * BEAST is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * BEAST is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with BEAST; if not, write to the * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA */ package dr.math.functionEval; import dr.math.interfaces.OneVariableFunction; import dr.math.iterations.NewtonZeroFinder; import java.util.Enumeration; import java.util.Vector; /** * Mathematical polynomial: * c[0] + c[1] * x + c[2] * x^2 + .... * * @author Didier H. Besset */ public class PolynomialFunction implements OneVariableFunction { /** * Polynomial coefficients. */ private final double[] coefficients; /** * Constructor method. * @param coeffs polynomial coefficients. */ public PolynomialFunction( double[] coeffs) { coefficients = coeffs; } /** * * @param r double number added to the polynomial. * @return DhbFunctionEvaluation.PolynomialFunction */ public PolynomialFunction add( double r) { int n = coefficients.length; double coef[] = new double[n]; coef[0] = coefficients[0] + r; for ( int i = 1; i < n; i++) coef[i] = coefficients[i]; return new PolynomialFunction( coef); } /** * * @param p DhbFunctionEvaluation.PolynomialFunction * @return DhbFunctionEvaluation.PolynomialFunction */ public PolynomialFunction add( PolynomialFunction p) { int n = Math.max( p.degree(), degree()) + 1; double[] coef = new double[n]; for ( int i = 0; i < n; i++ ) coef[i] = coefficient(i) + p.coefficient(i); return new PolynomialFunction( coef); } /** * Returns the coefficient value at the desired position * @param n int the position of the coefficient to be returned * @return double the coefficient value */ public double coefficient( int n) { return n < coefficients.length ? coefficients[n] : 0; } /** * * @param r double a root of the polynomial (no check made). * @return PolynomialFunction the receiver divided by polynomial (x - r). */ public PolynomialFunction deflate( double r) { int n = degree(); double remainder = coefficients[n]; double[] coef = new double[n]; for ( int k = n - 1; k >= 0; k--) { coef[k] = remainder; remainder = remainder * r + coefficients[k]; } return new PolynomialFunction( coef); } /** * Returns degree of this polynomial function * @return int degree of this polynomial function */ public int degree() { return coefficients.length - 1; } /** * Returns the derivative of the receiver. * @return PolynomialFunction derivative of the receiver. */ public PolynomialFunction derivative() { int n = degree(); if ( n == 0 ) { double coef[] = {0}; return new PolynomialFunction( coef); } double coef[] = new double[n]; for ( int i = 1; i <= n; i++) coef[i-1] = coefficients[i]*i; return new PolynomialFunction( coef); } /** * * @param r double * @return DhbFunctionEvaluation.PolynomialFunction */ public PolynomialFunction divide( double r) { return multiply( 1 / r); } /** * * @param p PolynomialFunction * @return PolynomialFunction */ public PolynomialFunction divide( PolynomialFunction p) { return divideWithRemainder(p)[0]; } /** * * @param p PolynomialFunction * @return DhbFunctionEvaluation.PolynomialFunction */ public PolynomialFunction[] divideWithRemainder( PolynomialFunction p) { PolynomialFunction[] answer = new PolynomialFunction[2]; int m = degree(); int n = p.degree(); if ( m < n ) { double[] q = {0}; answer[0] = new PolynomialFunction( q); answer[1] = p; return answer; } double[] quotient = new double[ m - n + 1]; double[] coef = new double[ m + 1]; for ( int k = 0; k <= m; k++ ) coef[k] = coefficients[k]; double norm = 1 / p.coefficient( n); for ( int k = m - n; k >= 0; k--) { quotient[k] = coef[ n + k] * norm; for ( int j = n + k - 1; j >= k; j--) coef[j] -= quotient[k] * p.coefficient(j-k); } double[] remainder = new double[n]; for ( int k = 0; k < n; k++) remainder[k] = coef[k]; answer[0] = new PolynomialFunction( quotient); answer[1] = new PolynomialFunction( remainder); return answer; } /** * Returns the integral of the receiver having the value 0 for X = 0. * @return PolynomialFunction integral of the receiver. */ public PolynomialFunction integral( ) { return integral( 0); } /** * Returns the integral of the receiver having the specified value for X = 0. * @param value double value of the integral at x=0 * @return PolynomialFunction integral of the receiver. */ public PolynomialFunction integral( double value) { int n = coefficients.length + 1; double coef[] = new double[n]; coef[0] = value; for ( int i = 1; i < n; i++) coef[i] = coefficients[i-1]/i; return new PolynomialFunction( coef); } /** * * @param r double * @return DhbFunctionEvaluation.PolynomialFunction */ public PolynomialFunction multiply( double r) { int n = coefficients.length; double coef[] = new double[n]; for ( int i = 0; i < n; i++) coef[i] = coefficients[i] * r; return new PolynomialFunction( coef); } /** * * @param p DhbFunctionEvaluation.PolynomialFunction * @return DhbFunctionEvaluation.PolynomialFunction */ public PolynomialFunction multiply( PolynomialFunction p) { int n = p.degree() + degree(); double[] coef = new double[n + 1]; for ( int i = 0; i <= n; i++) { coef[i] = 0; for ( int k = 0; k <= i; k++) coef[i] += p.coefficient(k) * coefficient(i-k); } return new PolynomialFunction( coef); } /** * * @return double[] */ public double[] roots() { return roots( DrMath.defaultNumericalPrecision()); } /** * * @param desiredPrecision double * @return double[] */ public double[] roots( double desiredPrecision) { PolynomialFunction dp = derivative(); double start = 0; while ( Math.abs( dp.value( start)) < desiredPrecision ) start = Math.random(); PolynomialFunction p = this; NewtonZeroFinder rootFinder = new NewtonZeroFinder( this, dp, start); rootFinder.setDesiredPrecision( desiredPrecision); Vector rootCollection = new Vector( degree()); while ( true) { rootFinder.evaluate(); if ( !rootFinder.hasConverged() ) break; double r = rootFinder.getResult(); rootCollection.addElement(r); p = p.deflate( r); if ( p.degree() == 0 ) break; rootFinder.setFunction( p); try { rootFinder.setDerivative( p.derivative());} catch ( IllegalArgumentException e) {} } double[] roots = new double[ rootCollection.size()]; Enumeration e = rootCollection.elements(); int n = 0; while ( e.hasMoreElements() ) { roots[n++] = (Double) e.nextElement(); } return roots; } /** * * @param r double * @return PolynomialFunction */ public PolynomialFunction subtract( double r) { return add( -r); } /** * * @return PolynomialFunction * @param p PolynomialFunction */ public PolynomialFunction subtract( PolynomialFunction p) { int n = Math.max( p.degree(), degree()) + 1; double[] coef = new double[n]; for ( int i = 0; i < n; i++ ) coef[i] = coefficient(i) - p.coefficient(i); return new PolynomialFunction( coef); } /** * Returns a string representing the receiver */ public String toString() { StringBuffer sb = new StringBuffer(); boolean firstNonZeroCoefficientPrinted = false; for ( int n = 0; n < coefficients.length; n++) { if ( coefficients[n] != 0 ) { if ( firstNonZeroCoefficientPrinted) sb.append( coefficients[n] > 0 ? " + " : " "); else firstNonZeroCoefficientPrinted = true; if ( n == 0 || coefficients[n] != 1) sb.append( Double.toString( coefficients[n]) ); if ( n > 0 ) sb.append( " X^"+n); } } return sb.toString(); } /** * Returns the value of the polynomial for the specified variable value. * @param x double value at which the polynomial is evaluated * @return double polynomial value. */ public double value( double x) { int n = coefficients.length; double answer = coefficients[--n]; while ( n > 0 ) answer = answer * x + coefficients[--n]; return answer; } /** * Returns the value and the derivative of the polynomial * for the specified variable value in an array of two elements * @param x double value at which the polynomial is evaluated * @return double[0] the value of the polynomial * @return double[1] the derivative of the polynomial */ public double[] valueAndDerivative( double x) { int n = coefficients.length; double[] answer = new double[2]; answer[0] = coefficients[--n]; answer[1] = 0; while ( n > 0 ) { answer[1] = answer[1] * x + answer[0]; answer[0] = answer[0] * x + coefficients[--n]; } return answer; } }