001: /*************************************************************************
002: * *
003: * 1) This source code file, in unmodified form, and compiled classes *
004: * derived from it can be used and distributed without restriction, *
005: * including for commercial use. (Attribution is not required *
006: * but is appreciated.) *
007: * *
008: * 2) Modified versions of this file can be made and distributed *
009: * provided: the modified versions are put into a Java package *
010: * different from the original package, edu.hws; modified *
011: * versions are distributed under the same terms as the original; *
012: * and the modifications are documented in comments. (Modification *
013: * here does not include simply making subclasses that belong to *
014: * a package other than edu.hws, which can be done without any *
015: * restriction.) *
016: * *
017: * David J. Eck *
018: * Department of Mathematics and Computer Science *
019: * Hobart and William Smith Colleges *
020: * Geneva, New York 14456, USA *
021: * Email: eck@hws.edu WWW: http://math.hws.edu/eck/ *
022: * *
023: *************************************************************************/package edu.hws.jcm.functions;
024:
025: import edu.hws.jcm.data.*;
026:
027: /**
028: * A WrapperFunction contains another function and delegates to it
029: * all calls to methods from the Function interface, except for calls
030: * to setName() and getName(). (It maintains its own name, which can
031: * be different from the name of the wrapped Function.) This has at least two
032: * uses: A WrapperFunction is a FunctionParserExtension, so it can
033: * be added to a parser and then used in expressions parsed in that
034: * parser. Second, the function that is wrapped inside the WrapperFunction
035: * object can be changed by a call to the setFunction() method.
036: * This makes it possible to have a single function that can be used,
037: * for example, in ValueMath objects and Graph1D's, whose definition
038: * can be changed at will. Note that it is NOT legal to change the
039: * arity of the function when calling the setFunction() method.
040: */
041: public class WrapperFunction extends FunctionParserExtension {
042:
043: private Function func; // The non-null function.
044: private double[] params; // For use in evaluating the function in the apply() method;
045: // has length equal to func.getArity();
046: private int serialNumber; // serial number goes up when function def is changed;
047: // this is used in the check() method for synchronizing derivatives.
048: private WrapperFunction derivativeOf; // If non-null, then this function is a
049: // derivative of the specified function.
050: private Variable derivativeVar; // If non-null, then the derivative was taken w.r.t. this variable
051: private int derivativeIndex; // If derivativeVar is null, this is the argument
052:
053: // number with respect to which the derivative was taken.
054:
055: /**
056: * Create a WrapperFunction object containing a specified function.
057: *
058: * @param f The non-null function that will be contained in the WrapperFunction.
059: */
060: public WrapperFunction(Function f) {
061: setFunction(f);
062: serialNumber = 0;
063: }
064:
065: private void check() {
066: // Called if this function is the derivative of another wrapper function to see
067: // if the serial number of the parent function has changed. If so, the derivative is
068: // recomputed.
069: if (derivativeOf == null
070: || derivativeOf.serialNumber == serialNumber)
071: return;
072: serialNumber = derivativeOf.serialNumber;
073: if (derivativeVar != null)
074: func = derivativeOf.derivative(derivativeVar);
075: else
076: func = derivativeOf.derivative(derivativeIndex);
077: }
078:
079: /**
080: * Set the function that is contained in this WrapperFunction.
081: *
082: * @param f The non-null function to be used in this WrapperFunction object.
083: * It must have the same arity as the current function.
084: */
085: public void setFunction(Function f) {
086: if (f == null)
087: throw new IllegalArgumentException(
088: "Function supplied to WrapperFunction object can't be null.");
089: if (func != null && f.getArity() != func.getArity())
090: throw new IllegalArgumentException(
091: "Attempt to change the arity of a WrapperFunction.");
092: if (derivativeOf != null)
093: throw new IllegalArgumentException(
094: "Can't change the definition of a function that is a derivative of another function.");
095: func = f;
096: params = new double[f.getArity()];
097: serialNumber++;
098: }
099:
100: /**
101: * Return the function that is currently wrapped in this WrapperFunction.
102: */
103: public Function getFunction() {
104: return func;
105: }
106:
107: /**
108: * Return the number of arguments of this function.
109: */
110: public int getArity() {
111: return func.getArity();
112: }
113:
114: /**
115: * Find the value of the function at the argument value
116: * argument[0], .... The number of arguments should match
117: * the arity of the function.
118: */
119: public double getVal(double[] arguments) {
120: check();
121: return func.getValueWithCases(arguments, null);
122: }
123:
124: /**
125: * Find the value of the function at the argument values
126: * argument[0],.... Information about "cases" is stored in
127: * the Cases parameter, if it is non-null. See the Cases
128: * class for more information.
129: */
130: public double getValueWithCases(double[] arguments, Cases cases) {
131: check();
132: return func == null ? 1 : func.getValueWithCases(arguments,
133: cases);
134: }
135:
136: /**
137: * Return the derivative of the function with repect to
138: * argument number wrt, where arguments are numbered starting from 1.
139: */
140: public Function derivative(int wrt) {
141: check();
142: WrapperFunction deriv = new WrapperFunction(func
143: .derivative(wrt));
144: deriv.derivativeOf = this ;
145: deriv.derivativeIndex = wrt;
146: deriv.serialNumber = serialNumber;
147: return deriv;
148: }
149:
150: /**
151: * Return the derivative of the function with respect to the
152: * variable x (where x is NOT one of the parameters of the function).
153: */
154: public Function derivative(Variable x) {
155: check();
156: WrapperFunction deriv = new WrapperFunction(func.derivative(x));
157: deriv.derivativeOf = this ;
158: deriv.derivativeVar = x;
159: deriv.serialNumber = serialNumber;
160: return deriv;
161: }
162:
163: /**
164: * Return true if the definition of this function depends
165: * in some way on the variable x. (Note that the function does
166: * NOT depend on the variables that are being used as its parameters!)
167: */
168: public boolean dependsOn(Variable x) {
169: check();
170: return func.dependsOn(x);
171: }
172:
173: /**
174: * Evaluate the function applied to argument values popped from the stack,
175: * and leave the result on the stack. This is not meant to be called
176: * directly.
177: */
178: public void apply(StackOfDouble stack, Cases cases) {
179: check();
180: for (int i = params.length - 1; i >= 0; i--)
181: params[i] = stack.pop();
182: stack.push(getValueWithCases(params, cases));
183: }
184:
185: }
|