/*
* 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.MathUtil;
import java.util.ArrayList;
import java.util.Hashtable;
/**
* The symbolic rational expression.
*
* @author Dr. Robert Shelton
* @version 1.0
* @since 1.0
*/
public class RationalExpression extends ProtoExpression {
private Expression unity = new Expression("1");
private Polynomial numerator = new Polynomial(unity);
private Polynomial denominator = new Polynomial(unity);
private boolean IRE = false;
/**
* Default constructor.
*/
public RationalExpression() {
super();
}
/**
* Constructs a rational expression from the specified expression.
*
* @param e an expression.
*/
public RationalExpression(Expression e) {
super();
copyFromExpression(e);
RationalExpression r = makeIt(root);
if (r == null)
return;
numerator = r.numerator;
denominator = r.denominator;
IRE = true;
} // end RationalExpression
/**
* Constructs a rational expression from the specified string expression.
*
* @param eString the expression as a string.
*/
public RationalExpression(String eString) {
this(new Expression(eString));
} // end RationalExpression
/**
* Returns the numerator polynomial.
*
* @return the numerator polynomial.
*/
public Polynomial getNumerator() {
return numerator;
} // end getNumerator
/**
* Returns the denominiator polynomial.
*
* @return the denominiator polynomial.
*/
public Polynomial getDenominator() {
return denominator;
} // end getDenominator
/**
* Copies the values from the specified expression to this rational expression.
*
* @param e the expression.
*/
public void copyFromExpression(Expression e) {
this.varStrings = e.varStrings;
this.root = e.root;
this.knowns = e.knowns;
this.parameters = e.parameters;
this.variables = e.variables;
this.legalVariables = e.legalVariables;
this.theValue = e.theValue;
this.valueString = e.valueString;
} // end copyFromExpression
/* (non-Javadoc)
* @see gov.nasa.ial.mde.solver.symbolic.ProtoExpression#setParameterHash(java.util.Hashtable)
*/
public void setParameterHash(Hashtable h) {
super.setParameterHash(h);
numerator.setParameterHash(h);
denominator.setParameterHash(h);
} // end setParameterHash
/**
* Returns true if this is a rational expression.
*
* @return true if this is a rational expression, false otherwise.
*/
public boolean isRationalExpression() {
return IRE;
} // end isRational
/**
* Returns the sum of this rational expression and the specified other
* rational expression.
*
* @param other the other rational expression.
* @return the sum of this rational expression and the other rational
* expression.
*/
public RationalExpression sum(RationalExpression other) {
RationalExpression r = privateSum(other);
r.transformExpression();
return r;
} // end sum
/**
* Returns the product of this rational expression and the specified other
* rational expression.
*
* @param other the other rational expression.
* @return the product of this rational expression and the other rational
* expression.
*/
public RationalExpression product(RationalExpression other) {
RationalExpression r = privateProduct(other);
r.transformExpression();
return r;
} // end product
/**
* Returns the difference between this rational expression and the specified other
* rational expression.
*
* @param other the other rational expression.
* @return the difference between this rational expression and the other rational
* expression.
*/
public RationalExpression difference(RationalExpression other) {
RationalExpression r = privateDifference(other);
r.transformExpression();
return r;
} // end difference
/**
* Returns the quotient between this rational expression and the specified other
* rational expression.
*
* @param other the other rational expression.
* @return the quotient between this rational expression and the other rational
* expression.
*/
public RationalExpression quotient(RationalExpression other) {
RationalExpression r = privateQuotient(other);
r.transformExpression();
return r;
} // end quotient
/**
* Returns the string representation of this rational expression.
*
* @return the string representation of this rational expression.
*/
public String toString() {
String n = numerator.isMonomial() ? numerator.toString() : "(" + numerator.toString() + ")";
String d = "(" + denominator.toString() + ")";
return n + "/" + d;
} // end toString
private RationalExpression privateProduct(RationalExpression other) {
RationalExpression r = new RationalExpression();
r.numerator = numerator.product(other.numerator);
r.denominator = denominator.product(other.denominator);
return r;
} // end privateProduct
private RationalExpression privateNegative() {
RationalExpression r = new RationalExpression();
r.numerator = numerator.makeNegative();
r.denominator = r.denominator.product(denominator);
return r;
} // end privateNegative
private RationalExpression privateReciprocal() {
RationalExpression r = new RationalExpression();
r.numerator = r.numerator.product(denominator);
r.denominator = r.denominator.product(numerator);
return r;
} // end privateReciprocal
private RationalExpression privateSum(RationalExpression other) {
Polynomial t1 = numerator.product(other.denominator);
Polynomial t2 = other.numerator.product(denominator);
RationalExpression r = new RationalExpression();
r.numerator = t1.sum(t2);
r.denominator = denominator.product(other.denominator);
return r;
} // end privateSum
private RationalExpression privateDifference(RationalExpression other) {
return privateSum(other.privateNegative());
} // end privateDifference
private RationalExpression privateQuotient(RationalExpression other) {
return privateProduct(other.privateReciprocal());
} // end privateQuotient
private void transformExpression() {
copyFromExpression(new Expression(toString()));
} // end transformExpression
private RationalExpression makeIt(ParseNode q) {
int i, n = (q.children == null) ? 0 : q.children.length;
RationalExpression r = new RationalExpression(), temp;
switch (q.operator) {
case Action.SUM:
r.numerator = new Polynomial(new Expression("0"));
for (i = 0; i < n; i++) {
if ((temp = makeIt(q.children[i])) == null)
return null;
r = temp.privateSum(r);
} // end for i
break;
case Action.PRODUCT:
for (i = 0; i < n; i++) {
if ((temp = makeIt(q.children[i])) == null)
return null;
r = temp.privateProduct(r);
} // end for i
break;
case Action.U_MINUS:
if ((temp = makeIt(q.children[0])) == null)
return null;
r = temp.privateNegative();
break;
case Action.RECIPROCAL:
if ((temp = makeIt(q.children[0])) == null)
return null;
r = temp.privateReciprocal();
break;
case Action.POWER:
{
Expression e = new Expression(q.children[1]);
if (e.theValue == null)
return null;
double pf = e.theValue.doubleValue();
if ((n = (int)Math.rint(pf)) != pf)
return null;
if (n < 0) {
if ((temp = makeIt(q.children[0])) == null)
return null;
temp = temp.privateReciprocal();
} // end if
else {
if ((temp = makeIt(q.children[0])) == null)
return null;
} // end else
for (i = Math.abs(n) - 1; i >= 0; i--)
r = r.privateProduct(temp);
} // end block
break;
case Action.NO_OP:
r.numerator = new Polynomial(new Expression(q));
break;
default:
if (q.value != null) {
// if (q.value instanceof Double)
return new RationalExpression(q.value.toString());
}
return null;
} // end switch
return r;
} // end makeIt
/**
* The continued fraction of a real number.
*
* @author Dr. Robert Shelton
* @version 1.0
* @since 1.0
*/
public static class ContinuedFraction {
private int first = -1, size;
private int[] a;
private double[] theta;
private ArrayList<Residual> residuals = new ArrayList<Residual>();
private double errorProbability;
private final static double MAX_ERROR_PROBABILITY = 0.5;
private static double tolerance;
private static double logProbCorrect;
private final static double LOG_LIMIT = MathUtil.logOnePlusX(-MAX_ERROR_PROBABILITY);
/**
* Constructs a continued fraction of the specfied real number.
*
* @param x the real number.
* @param maxSize the maximum size.
*/
public ContinuedFraction(double x, int maxSize) {
ArrayList<Remainder> remainders = new ArrayList<Remainder>();
int n;
Remainder r = new Remainder(x);
tolerance = 1.0e-10;
logProbCorrect = 0.0;
adjustTolerance(r);
remainders.add(new Remainder(0.0));
for (n = 1; acceptableErr(n) && (first = fcm(remainders, r)) < 0; n++) {
remainders.add(r);
r = new Remainder(1.0 / r.theta);
adjustTolerance(r);
} // end for n
if (first < 0)
fcm(remainders, r);
remainders.add(r);
a = new int[size = n];
theta = new double[n];
for (int i = 1; i <= n; i++) {
r = remainders.get(i);
a[i - 1] = (int)r.a;
theta[i - 1] = r.theta;
} // end for i
errorProbability = (logProbCorrect > Double.NEGATIVE_INFINITY) ?
-MathUtil.stump(logProbCorrect, 1) : 1.0;
} // end ContinuedFraction
/**
* The string representation of the continued fraction.
*/
public String toString() {
StringBuffer b = new StringBuffer("Continued fraction:\nSize = " + size);
b.append("\nRational value : " + isRational());
b.append("\nQuadratic root: " + isQuadratic());
b.append("\nPartial quotients:");
for (int i = 0; i < size; i++)
b.append(" " + a[i]);
b.append("\nResiduals:");
for (int i = 0; i < size; i++)
b.append("\n" + residuals.get(i));
/*
* b.append ("\nRemainders:"); for (int i = 0; i < size; i++) b.append (" " +
* theta[i]);
*/
b.append("\nFirst = " + getFirst() + ", Last = " + getLast());
b.append("\nProbability of incorrect classification = " + errorProbability);
String sq = getQuadraticEquation();
String sr = getRational();
if (sr != null)
b.append("\nRational value = " + sr);
if (sq != null)
b.append("\nQuadratic equation: " + sq);
return b.toString();
} // end toString
/**
* Returns true if the continued fraction is rational.
*
* @return true if the continued fraction is rational, false otherwise.
*/
public boolean isRational() {
return first == 0;
} // end isRational
/**
* Returns true if the continued fraction is Quadratic.
*
* @return true if the continued fraction is Quadratic, false otherwise.
*/
public boolean isQuadratic() {
return first > 0;
} // end isQuadratic
/**
* Returns the first index value.
*
* @return the first index value.
*/
public int getFirst() {
if (isRational())
return 0;
return first - 1;
} // end getFirst
/**
* Returns the last index value.
*
* @return the last index value.
*/
public int getLast() {
if (isQuadratic())
return size - 1;
return -1;
} // end getLast
/**
* Returns the quadratic polynomial of the continued fraction.
*
* @return the quadratic polynomial of the continued fraction.
*/
public Polynomial getQuadraticPolynomial() {
if (isQuadratic()) {
RationalExpression[] lr = iterate("x", first - 1, size - 1);
Polynomial l = lr[0].numerator.product(lr[1].denominator);
Polynomial r = lr[1].numerator.product(lr[0].denominator);
return l.sum(r.makeNegative());
} // end if
return null;
} // end getQuadraticPolynomial
/**
* Returns the polynomial coefficients.
*
* @return the polynomial coefficients.
*/
public double[] getPolynomialCoefficients() {
if (isRational()) {
RationalExpression r = iterate();
double[] rv = { r.denominator.getConstant().theValue.doubleValue(),
-r.numerator.getConstant().theValue.doubleValue() };
return rv;
} // end if
if (isQuadratic()) {
Expression[] e = Polynomial.getCoefficientsAsExpressions(getQuadraticPolynomial(), "x");
double[] rv = new double[3];
Polynomial.evaluateCoefficients(e, rv);
return rv;
} // end if
return new double[0];
} // end getPolynomialCoefficients
/**
* Returns the quadratic equation of the continued fraction.
*
* @return the quadratic equation of the continued fraction.
*/
public String getQuadraticEquation() {
Polynomial p = getQuadraticPolynomial();
if (p != null)
return p.toString() + " = 0";
return null;
} // end getQuadraticEquation
/**
* Returns the rational of the continued fraction.
*
* @return the rational of the continued fraction.
*/
public String getRational() {
RationalExpression r = iterate();
if (r != null)
return r.toString();
return null;
} // end getRational
/**
* Iterates over the specified variable and first and last indexes to
* return the rational expression.
*
* @param var the variable.
* @param f the first index.
* @param l the last index.
* @return the rational expressions.
*/
public RationalExpression[] iterate(String var, int f, int l) {
if (isQuadratic()) {
int n = 0;
RationalExpression r = (new RationalExpression(var)).privateSum(
new RationalExpression(new Double(-a[n]).toString()));
RationalExpression r0 = null, r1;
while (true) {
if (n == f)
r0 = new RationalExpression(r.toString());
r = r.privateReciprocal();
r = r.privateSum(new RationalExpression(new Double(-a[++n]).toString()));
if (n == l) {
r1 = new RationalExpression(r.toString());
break;
} // end if
} // end while
r0.transformExpression();
r1.transformExpression();
RationalExpression[] rv = { r0, r1 };
return rv;
} // end if
return new RationalExpression[0];
} // end iterate
/**
* Interates over the continued fraction to return the rational expression.
*
* @return the rational expression.
*/
public RationalExpression iterate() {
if (isRational()) {
int n = size - 1;
RationalExpression r = new RationalExpression(new Double(a[n]).toString());
while (n > 0) {
r = r.privateReciprocal();
r = r.privateSum(new RationalExpression(new Double(a[--n]).toString()));
} // end while
r.transformExpression();
return r;
} // end if
return null;
} // end iterate
private boolean acceptableErr(int n) {
double c = 2.0 * n * tolerance;
if (c < 1.0)
logProbCorrect += MathUtil.logOnePlusX(-c);
else
logProbCorrect = Double.NEGATIVE_INFINITY;
// double displayLogProbCorrect = logProbCorrect;
if (logProbCorrect < LOG_LIMIT)
return false;
return true;
} // end acceptableErr
private void adjustTolerance(Remainder r) {
double p = Math.abs(r.a);
if (p == 0.0)
return;
int n = (int)Math.floor(MathUtil.log2(p));
for (int i = 0; i <= n; i++)
tolerance *= 2.1;
} // end adjustTolerance
private int fcm(ArrayList<Remainder> l, Remainder r) {
int i, iMin = -1, n = l.size();
double m = Double.POSITIVE_INFINITY, temp;
for (i = 0; i < n; i++) {
Remainder t = l.get(i);
if ((temp = Math.abs(r.theta - t.theta)) < m) {
m = temp;
iMin = i;
} // end if
if (temp < tolerance) {
residuals.add(new Residual(i, temp));
return i;
} // end if
} // end for i
residuals.add(new Residual(iMin, m));
return -1;
} // end fcm
/**
* The residual of the continued fraction.
*
* @author Dr. Robert Shelton
* @version 1.0
* @since 1.0
*/
public static class Residual {
private int indexOfClosestMatch = -1;
private double miss;
private double missRatio;
/**
* Constructs a residual from the index of closest match and the
* amount of miss.
*
* @param indexOfClosestMatch the index of closest match.
* @param miss the amount of miss.
*/
public Residual(int indexOfClosestMatch, double miss) {
this.indexOfClosestMatch = indexOfClosestMatch;
this.miss = miss;
this.missRatio = miss / tolerance;
} // end Residual
/**
* Returns a string representation of the residual.
*
* @return string representation of the residual.
*/
public String toString() {
return "Closest match = " + indexOfClosestMatch + "\nMiss = " + miss + "\nMiss ratio = "
+ missRatio;
} // end toString
} // end classResidual
/**
* The remainder of the continued fraction.
*
* @author Dr. Robert Shelton
* @version 1.0
* @since 1.0
*/
public static class Remainder {
private double theta;
private double a;
/**
* Constructs a remainder for the specfied value.
*
* @param x the remainder value.
*/
public Remainder(double x) {
if (Math.abs(x - Math.rint(x)) < tolerance) {
a = Math.rint(x);
theta = 0.0;
return;
} // end if
a = Math.floor(x);
theta = x - a;
} // end Remainder
} // end class Remainder
} // end class ContinuedFraction
// public static void main(String[] args) {
// //****************************************************************************
// //* if (args.length != 2) { System.err.println ("Usage: java
// //* RationalExpression \" <E1>\" \" <E2>\""); return; } // end if
// //* RationalExpression e1=new RationalExpression (args[0]); RationalExpression
// //* e2=new RationalExpression(args[1]); RationalExpression d, s, p, q; if
// //* (e1.isRationalExpression() && e2.isRationalExpression()) {
// //* System.out.println ("Sum = " + (s=e1.sum(e2))); System.out.println
// //* ("Difference = " + (d=e1.difference(e2))); System.out.println ("Product = " +
// //* (p=e1.product(e2))); System.out.println ("Quotient = " +
// //* (q=e1.quotient(e2))); Test parameter set/evaluation Hashtable h=new
// //* java.util.Hashtable(); p.parameters.put ("a", new Double(2.0));
// //* p.parameters.put ("strBuff", new Double(-3.0)); System.out.println
// //* ("Product with substitutions = " + p); h.put ("x", new Double(10.0));
// //* h.put ("y", new Double( -5.0)); System.out.println ("Product value = " +
// //* p.evaluate(h)); return; } // end if System.err.println ("Not a rational
// //* expression");
// //***************************************************************************
// Expression e = new Expression(gov.nasa.ial.mde.util.StringSplitter.combineArgs(args));
//
// RationalExpression.ContinuedFraction c = new RationalExpression.ContinuedFraction(e.evaluate(null), 20); // max
// // partial
// // quotients
//
// System.out.println("C = " + c);
// } // end main
} // end class RationalExpression