/* * Copyright 2006, United States Government as represented by the Administrator * for the National Aeronautics and Space Administration. No copyright is * claimed in the United States under Title 17, U.S. Code. All Other Rights * Reserved. */ package gov.nasa.ial.mde.solver.symbolic; import gov.nasa.ial.mde.util.Comparison; import gov.nasa.ial.mde.util.QSorter; import gov.nasa.ial.mde.util.SortedKeyStrings; import java.util.*; /** * Class to handle polynomial expressions Handles some algebraic simplification * by polynomial multiplication and consolidation of like terms. * * @author Dr. Robert Shelton * @version 1.0 * @since 1.0 */ public class Polynomial { /** * Special string to indicate no variables i.e. all exponents = 0 use as a * signature key (see below) in place of the empty string which would result if * there were no variables */ public final static String CONSTANT = "#$CONSTANT$#"; // Determines whether or not the constructor algorithm will treat non-polynomial // sub-expressions as constants or generalized variables private boolean useGeneralizedVariables = false; private int highestDegree = -1; // Expression from which this Polynomial is constructed private Expression theExpression; // Keys are term signatures, values are PolyTerms where a signature is a // colon-separated string consisting of variables followed by '^', followed by // the exponent -- a positive integer. e.g. the term 3*x*y^2 would have the // signature "x^1:y^2" Note that signatures only contain variables and powers; // coefficients reside in the PolyTerm class uniqueness is enforced by requiring // that variable strings appear in lexicographical order. private Hashtable termHash; // Keys are string representations of variables the corresponding value is an // Integer Object representing the highest degree of the variable over all terms private Hashtable<String, Integer> degree; // Keys are String representation of variables; the corresponding value is an // Expression array containing the coefficients of this Polynomial considered as // a polynomial in the single key variable private Hashtable<String, Expression[]> coefficientHash; // Table of variables to consider as parameters and their corresponding values private Hashtable parameterHash = new Hashtable(); // String representation of all variables, sorted by degree highest to lowest private String[] variables = new String[0]; // Array of string reps. of required variables, i.e. construct as a polynomial in // only these variables. For example, if requiredVariables = {"x", "y"} and the // expression is (a+b)*(x+y)^2, then the resulting polynomial is (a+b)*x^2 + // 2*(a+b)*x*y + (a+b)*y^2 private String[] requiredVariables = null; // Term Compare private Comparison TC = new Comparison() { public int compare(Object o1, Object o2) { PolyTerm t1 = (PolyTerm)o1, t2 = (PolyTerm)o2; int d1 = t1.getDegree(), d2 = t2.getDegree(); if (d1 < d2) return 1; if (d2 < d1) return -1; int i, n = variables.length; for (i = 0; i < n; i++) { int dov1 = t1.getDegreeOfVariable(variables[i]); int dov2 = t2.getDegreeOfVariable(variables[i]); if (dov1 < dov2) return 1; if (dov2 < dov1) return -1; } // end for i return 0; } // end compare }; // end new Comparison private Polynomial() { termHash = new Hashtable(); degree = new Hashtable<String, Integer>(); coefficientHash = new Hashtable<String, Expression[]>(); } // end Polynomial /** * Constructs a polynomial from the specifed expression. * * @param e the expression. */ public Polynomial(Expression e) { Polynomial p = makePolynomial(e); copyFrom(p); } // end Polynomial /** * Constructs a polynomial from the specifed expression and generealized * variables flag. * * @param e the expression. * @param ugv true to use generalized variables. */ public Polynomial(Expression e, boolean ugv) { useGeneralizedVariables = ugv; Polynomial p = makePolynomial(e); copyFrom(p); } // end Polynomial /** * Constructs a polynomial from the specifed expression and required * variables. * * @param e the expression. * @param v the required variables. */ public Polynomial(Expression e, String[] v) { requiredVariables = v; theExpression = e; Polynomial p = makePolynomial(e); copyFrom(p); } // end Polynomial /** * Constructs a polynomial from the specifed expression, required variables, * and generealized variables flag. * * @param e the expression. * @param v the required variables. * @param ugv true to use generalized variables. */ public Polynomial(Expression e, String[] v, boolean ugv) { requiredVariables = v; useGeneralizedVariables = ugv; theExpression = e; Polynomial p = makePolynomial(e); copyFrom(p); } // end Polynomial /** * Creates a polynomial from the specified coefficients an variable. * * @param c the coefficients of the polynomial. * @param var the variable. * @return the polynomial. */ public static Polynomial doubles2Poly(double[] c, String var) { int d = c.length - 1, i; Polynomial r = new Polynomial(new Expression("0.0")); for (i = 0; i <= d; i++) { Polynomial t = new Polynomial(new Expression(c[i] + "*" + var + "^" + (d - i))); r = r.sum(t); } // end for i return r; } // end doubles2Poly /** * Creates a polynomial from the specified coefficients. * * @param coefficients the coeficients of the polynomial. * @return the polynomial. */ public static Polynomial doubles2Poly(double[] coefficients) { return doubles2Poly(coefficients, "x"); } // end doubles2Poly /** * Makes a polynomial of a constant term expression. * * @param c the constant term expresson. * @return the polynomial. */ public static Polynomial makeConstant(Expression c) { Polynomial p = new Polynomial(); p.addTerm(makeConstantTerm(c)); return p; } // end makeConstant /** * Makes a polynomial from a variable term expression. * * @param v the variable term expression. * @return the polynomial. */ public static Polynomial makeVariable(Expression v) { Polynomial p = new Polynomial(); p.addTerm(makeVariableTerm(v)); return p; } // end makeVariable /** * Returns true if the polynomial is monomial. * * @return true if the polynomial is monomial, false otherwise. */ public boolean isMonomial() { return (termHash.size() < 2); } // end isMonomial /** * Returns the sum of this polynomial and the other polynomial. * * @param other the other polynomial. * @return the sum of this polynomial and the other polynomial. */ public Polynomial sum(Polynomial other) { Polynomial p = new Polynomial(); Hashtable otherTerms = other.termHash; Enumeration k = termHash.keys(); while (k.hasMoreElements()) { String s = (String)k.nextElement(); PolyTerm t0, t1 = (PolyTerm)termHash.get(s), t2 = (PolyTerm)otherTerms.get(s); if (t2 != null) // a matching term exists in the other polynomial t0 = t1.sum(t2); else t0 = t1; p.addTerm(t0); } // end while for (k = otherTerms.keys(); k.hasMoreElements();) { String s = (String)k.nextElement(); PolyTerm t1 = (PolyTerm)termHash.get(s), t2 = (PolyTerm)otherTerms.get(s); if (t1 != null) continue; // already done common terms p.addTerm(t2); } // end for p.finish(); return p; } // end sum /** * Negates this polynomial. */ public void negate() { Enumeration k = termHash.keys(); while (k.hasMoreElements()) ((PolyTerm)termHash.get(k.nextElement())).negate(); coefficientHash = new Hashtable(); } // end negate /** * Returns the negative of this polynomial. * * @return the negative of this polynomial. */ public Polynomial makeNegative() { Polynomial p = new Polynomial(); Enumeration k = termHash.keys(); while (k.hasMoreElements()) p.addTerm(((PolyTerm)termHash.get(k.nextElement())).makeNegative()); p.finish(); return p; } // end makeNegative /** * Constructs the Polynomial that is the partial derivative of this Polynomial * with respect to a given variable * * @param variable String representation of variable with which the * differentiation is to be performed * @return the differentiated Polynomial */ public Polynomial makeDerivative(String variable) { Polynomial p = new Polynomial(); Enumeration k = termHash.keys(); while (k.hasMoreElements()) p.addTerm(((PolyTerm)termHash.get(k.nextElement())).makeDerivative(variable)); p.finish(); p.setParameterHash(parameterHash); return p; } // end makeDerivative /** * Constructs the Polynomial equivalent to this Polynomial expressed as a * Polynomial in the given variables. The coefficients of the constructed * Polynomial are Expressions which contain all dependences on other variables * The implementation calls this.toExpression and a Polynomial constructor, thus * efficiency could be an issue * * @param vars an array of String representations of the polynomial variables of * the constructed Polynomial * @return the Polynomial in the specified variables. */ public Polynomial asAPolynomialIn(String[] vars) { Polynomial p = new Polynomial(toExpression(), vars); p.setParameterHash(parameterHash); return p; } // end asAPolynomialIn /** * One-variable case of asAPolynomial. * * @param theVariable String representation of the desired polynomial variable * @return the one-variable equivalent of this Polynomial */ public Polynomial oneVariablePolynomial(String theVariable) { String[] vars = { theVariable }; return asAPolynomialIn(vars); } // end oneVariablePolynomial /** * Extracts coefficients of the one-variable equivalent of this Polynomial as an * array of Expressions Caches the resulting Expression array, thus repetitive * calls require little overhead * * @param var String representation of the variable in which we want the * polynomial described by the coefficients to be written * @return an array of Expressions of this Polynomial considered as a polynomial * in the variable var. If d is the degree of this Polynomial in var, * then this Polynomial is equivalent to e[0]*(var)^d + e[1]*(var)^(d-1) + * ... + e[d]. */ public Expression[] getCoefficientsAsExpressions(String var) { Expression[] e; if ((e = (Expression[])coefficientHash.get(var)) == null) { e = Polynomial.getCoefficientsAsExpressions(oneVariablePolynomial(var), var); coefficientHash.put(var, e); } // end if return e; } // end getCoefficientsAsExpressions /** * Constructs an array of Expressions representing coefficients from all terms of * a Polynomial p that are pure powers of the variable var. Note that when p * contains other variables, one can obtain unexpected results. Normally you * should call the corresponding instance method to retrieve coefficients of a * Polynomial in more than one variable. * * @param p the Polynomial * @param var the String representation of the variable * @return the coefficients as an array of Expressions */ public static Expression[] getCoefficientsAsExpressions(Polynomial p, String var) { String[] vars = { var }; int d = p.getDegree(); Expression[] r = new Expression[d + 1]; if (d < 0) return r; r[d] = p.getConstant(); for (int i = 0; i < d; i++) { int[] J = { d - i }; r[i] = p.getCoefficient(vars, J); } // end for i return r; } // end getCoefficientsAsExpressions /** * Evaluates the coefficients and places them in the <code>cd</code> array. * * @param ce the expression to evaluate. * @param cd the coefficients from the evaluation. */ public static void evaluateCoefficients(Expression[] ce, double[] cd) { int i, n = ce.length; for (i = 0; i < n; i++) if (ce[i].theValue == null) cd[i] = ce[i].evaluate(null); else cd[i] = ce[i].theValue.doubleValue(); } // end evaluateCoefficients /** * Evaluates the coefficients and places them in the <code>cd</code> array * for the specified variable and value. * * @param ce the expression. * @param var the variable. * @param value the variable value. * @param cd the coefficients. */ public static void evaluateCoefficients(Expression[] ce, String var, double value, double[] cd) { int i, n = ce.length; Hashtable<String, Double> h = new Hashtable<String, Double>(); h.put(var, new Double(value)); for (i = 0; i < n; i++) cd[i] = ce[i].evaluate(h); } // end evaluateCoefficients /** * Returns the product of this polynomial and the other polynomimal. * * @param other the other polynomial. * @return the product of this polynomial and the other polynomimal. */ public Polynomial product(Polynomial other) { Enumeration k1 = termHash.keys(); Polynomial r = new Polynomial(); // no terms initially while (k1.hasMoreElements()) { PolyTerm t = (PolyTerm)termHash.get(k1.nextElement()); Polynomial partialSum = new Polynomial(); Enumeration k2 = other.termHash.keys(); while (k2.hasMoreElements()) partialSum.addTerm(t.product((PolyTerm)other.termHash.get(k2.nextElement()))); r = r.sum(partialSum); } // end while return r; } // end product /** * Returns the variables in the polynomial. * * @return the variables in the polynomial. */ public String[] getVariables() { return variables; } // end getVariables /** * Returns the degree of the polynomial. * * @return the degree of the polynomial. */ public int getDegree() { if (highestDegree < 0) { Enumeration k = termHash.keys(); while (k.hasMoreElements()) { int d = ((PolyTerm)termHash.get(k.nextElement())).getDegree(); if (d > highestDegree) highestDegree = d; } // end while } // end if return highestDegree; } // end getDegree /** * Returns constant term expression. * * @return constant term expression. */ public Expression getConstant() { PolyTerm pt = (PolyTerm)termHash.get(CONSTANT); if (pt == null) return new Expression("0"); return pt.getCoefficient(); } // end getConstant /** * Returns an expression of the coefficients. * * @param v the variables. * @param e the variable values. * @return the expression. */ public Expression getCoefficient(String[] v, int[] e) { PolyTerm pt = getTerm(v, e); if (pt == null) return new Expression("0"); return pt.getCoefficient(); } // end getCoefficient /** * Determines if all coefficients including the "constant" term are numerical * constants This Polynomial will be constant iff each coefficient has an empty * variables list * * @return true if the polynomial has constant coefficients. */ public boolean hasConstantCoefficients() { Enumeration<PolyTerm> k = (Enumeration<PolyTerm>) termHash.keys(); while (k.hasMoreElements()) { Expression e = ((PolyTerm)termHash.get(k.nextElement())).getCoefficient(); if (e.varStrings.length > 0) return false; } // end while return true; } // end hasConstantCoefficients private void copyFrom(Polynomial p) { highestDegree = p.highestDegree; theExpression = p.theExpression; termHash = p.termHash; degree = p.degree; variables = p.variables; requiredVariables = p.requiredVariables; coefficientHash = p.coefficientHash; setParameterHash(p.parameterHash); } // end copyFrom private PolyTerm getTerm(String[] vars, int[] exps) { int i, n; if ((n = vars.length) != exps.length) return null; Hashtable h = new Hashtable(); for (i = 0; i < n; i++) h.put(vars[i], new Integer(exps[i])); String[] r = PolyTerm.makeSignature(h); if ((n = r.length - 1) < 0) return null; return (PolyTerm)termHash.get(r[n]); } // end getTerm private Polynomial makePolynomial(Expression e) { Polynomial p = makePoly(e.root); p.theExpression = e; p.finish(); return p; } // end makePolynomial private Polynomial makePoly(ParseNode r) { if (r.value != null) return makeConstant(new Expression(r)); if (requiredVariables != null) { int i, n = requiredVariables.length; Hashtable h = (Hashtable)theExpression.variables.get(r); for (i = 0; i < n; i++) if (h.get(requiredVariables[i]) != null) break; if (i >= n) return makeConstant(new Expression(r)); } // end if switch (r.operator) { case Action.U_MINUS: { Polynomial p = makePoly(r.children[0]); p.negate(); return p; } // end case case Action.SUM: { int i, n = r.children.length; Polynomial p = new Polynomial(); for (i = 0; i < n; i++) p = p.sum(makePoly(r.children[i])); return p; } // end case case Action.PRODUCT: { int i, n = r.children.length; Polynomial p = makeConstant(new Expression("1")); for (i = 0; i < n; i++) p = p.product(makePoly(r.children[i])); return p; } // end case case Action.POWER: { Expression pow = new Expression(r.children[1]); if (pow.theValue == null) return punt(new Expression(r)); double pf = pow.theValue.doubleValue(); int n = (int)Math.floor(pf); if (n != pf) return punt(new Expression(r)); if (n < 0) return punt(new Expression(r)); Polynomial p = makeConstant(new Expression("1")); Polynomial f = makePoly(r.children[0]); for (int i = 0; i < n; i++) p = p.product(f); return p; } // end case case Action.NO_OP: return makeVariable(new Expression(r)); default: return punt(new Expression(r)); } // end switch } // end makePoly private void finish() { Enumeration k = termHash.keys(); Vector hitList = new Vector(); coefficientHash = new Hashtable(); while (k.hasMoreElements()) { Object o = k.nextElement(); Expression c = ((PolyTerm)termHash.get(o)).getCoefficient(); if (c == null) c = new Expression("0"); if (c.theValue == null) continue; if (c.theValue.doubleValue() == 0.0) hitList.addElement(o); } // end while for (Enumeration e = hitList.elements(); e.hasMoreElements();) termHash.remove(e.nextElement()); k = termHash.keys(); while (k.hasMoreElements()) { PolyTerm t = (PolyTerm)termHash.get(k.nextElement()); String[] v = t.getVariables(); int i, n = v.length; // t.autoTest(); for (i = 0; i < n; i++) { int d = t.getDegreeOfVariable(v[i]); Integer Deg = (Integer)degree.get(v[i]); if (Deg != null) { if (d > Deg.intValue()) degree.put(v[i], new Integer(d)); } // end if else degree.put(v[i], new Integer(d)); } // end for i } // end while SortedKeyStrings s = new SortedKeyStrings(degree); int i, n = s.theKeys.length; variables = new String[n]; for (i = 0; i < n; i++) variables[i] = s.theKeys[i]; } // end finish /** * Converts the polynomial to an expression. * * @return the polynomial as an expression. */ public Expression toExpression() { int i, n = termHash.size(); Enumeration k = termHash.keys(); PolyTerm[] t = new PolyTerm[n]; if (n == 0) return new Expression("0"); for (i = 0; i < n; i++) t[i] = (PolyTerm)termHash.get(k.nextElement()); QSorter q = new QSorter(t, TC); for (i = 0; i < n; i++) t[i] = (PolyTerm)q.theData[i]; ParseNode p = new ParseNode(n, Action.SUM); for (i = 0; i < n; i++) p.children[i] = t[i].makeExpression().root; Expression newE = new Expression(p); newE.setParameterHash(parameterHash); return newE; } // end toExpression /** * Sets the parameter hashtable for the polynomial. * * @param ph the parameter hashtable. */ public void setParameterHash(Hashtable ph) { Enumeration k = termHash.keys(); while (k.hasMoreElements()) { Object o = k.nextElement(); PolyTerm t = (PolyTerm)termHash.get(o); t.getCoefficient().setParameterHash(ph); } // end while parameterHash = ph; int i, n = variables.length; ArrayList<String> v = new ArrayList<String>(); for (i = 0; i < n; i++) { if (ph.get(variables[i]) != null) continue; v.add(variables[i]); } // end for i if (v.size() < n) { // found a var that's really a param Polynomial p = asAPolynomialIn((String[])v.toArray(new String[v.size()])); copyFrom(p); } // end if } // end setParameterHash /** * Returns the string representation of the polynomial. * * @return the string representation of the polynomial. */ public String toString() { return toExpression().toString(); } // end toString private void addTerm(PolyTerm t) { termHash.put(t.getSignature(), t); } // end addTerm private static PolyTerm makeConstantTerm(Expression c) { PolyTerm t = new PolyTerm(); t.setCoefficient(c); return t; } // end makeConstantTerm private static PolyTerm makeVariableTerm(Expression v) { PolyTerm t = new PolyTerm(); t.setCoefficient(new Expression("1")); t.setExponent(v, 1); return t; } // end makeVariableTerm private Polynomial punt(Expression e) { if (useGeneralizedVariables) return makeVariable(e); return makeConstant(e); } // end punt // public static void main(String[] args) { // String v = "x"; // Polynomial p = null; // // switch (args.length) { // case 2: // v = args[1]; // // case 1: // p = new Polynomial(new Expression(args[0])); // break; // // default: // System.err.println("Usage: java Polynomial <POLYNOMIAL_STRING> [<VARIABLE_NAME_TO_DERIVE>]"); // System.exit(1); // } // end switch // // // System.out.println ("dP/d" + v + " = " + // // p.makeDerivative(v).toString()); // // Expression[] c = p.getCoefficientsAsExpressions(v); // // c = p.getCoefficientsAsExpressions(v); // // for (int i = 0; i < c.length; i++) // System.out.println("Coefficient " + i + " = " + c[i]); // } // end main } // end class Polynomial