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: * An ExpressionFunction is a Function that is created from an expression and a list
029: * of variables that serve as the parameter(s) of the function. (This is essentially
030: * a lambda operation, forming a function such as "lambda(x,y) (x^2+y^2)")
031: *
032: * Since an ExpressionFunction is a FunctionParserExtension, functions defined
033: * from this class can be added to a Parser and then used in expressions parsed
034: * by that parser.
035: */
036: public class ExpressionFunction extends FunctionParserExtension {
037:
038: private Expression definition; // Expression that defines the function.
039: private Variable[] params; // Variable(s) that act as the parameters.
040:
041: /**
042: * Constuct a function of one parameter, named "x", by parsing the String, def,
043: * to get the definition of the function. A standard Parser, with
044: * default options and knowledge only of "pi", "e" and the standard functions
045: * is used. The variable "x" is also added to this parser while the function is being parsed.
046: *
047: * @param name Name of function. This should not be null if the function is to be used in a Parser.
048: * @param def contains definition of the function, as a function of "x".
049: */
050: public ExpressionFunction(String name, String def) {
051: this (name, new String[] { "x" }, def, null);
052: }
053:
054: /**
055: * Constuct a function of one or more parameters by parsing the String, def,
056: * to get the definition of the function. The given parser is used to
057: * parse the definition, so the definition can refer to objects registered
058: * with the parser (such as other variables or functions). Furthermore, if
059: * both name and parser are non-null, then the function is registered with
060: * the parser so that it can then be used in expressions parsed by the
061: * parser. (It's possible to have a function of zero arguements. In that case, the
062: * function serves as a "named expression".)
063: *
064: * @param name Name of function.
065: * @param paramNames Names of the parameters of the function. The lenght of this array determines the arity of the function.
066: * @param def The definition of the function, in terms of the parameters from the paramNames array.
067: * @param parser Used to parse the definition. If this is null, a standard parser is used. The
068: * paramaters are temporarily added onto the parser while the function definition is being parsed.
069: */
070: public ExpressionFunction(String name, String[] paramNames,
071: String def, Parser parser) {
072: setName(name);
073: if (paramNames == null)
074: params = new Variable[0];
075: else {
076: params = new Variable[paramNames.length];
077: for (int i = 0; i < paramNames.length; i++)
078: params[i] = new Variable(paramNames[i]);
079: }
080: redefine(def, parser);
081: if (parser != null && name != null)
082: parser.add(this );
083: }
084:
085: /**
086: * Construct a function from a list of variables that serve as parameters and an expression that,
087: * presumably, can include those variables. WARNING: When the function is
088: * evaluated, the values of the parameter variables can change, so you should
089: * probably not use variables that are being used elsewhere in your program.
090: */
091: public ExpressionFunction(String name, Variable[] params,
092: Expression definition) {
093: setName(name);
094: this .params = (params == null) ? new Variable[0] : params;
095: this .definition = definition;
096: }
097:
098: private ExpressionFunction() {
099: // This default constructor is used in the derivative() method, but is
100: // not meant to be used from outside this class, since it doesn't properly
101: // iniitialize the state of the member variables.
102: }
103:
104: /**
105: * Set the definition of this function by parsing the given string,
106: * using a default parser. The definition is in terms of the parameter
107: * names originally provided in the constructor.
108: */
109: public void redefine(String def) {
110: redefine(def, null);
111: }
112:
113: /**
114: * Set the definition of this function, using the specified parser (or a default
115: * parser if parser is null). The definition is in terms of the parameter
116: * names originally provided in the constructor. (This routine does
117: * not register the function with the parser, but if it was already
118: * registered with the parser, it stays registered with the new
119: * definition.) Note that changing the definition of the function
120: * effectively changes the definition of any other expression that
121: * refers to this function.
122: */
123: public void redefine(String def, Parser parser) {
124: if (parser == null)
125: parser = new Parser();
126: else
127: parser = new Parser(parser);
128: for (int i = 0; i < params.length; i++)
129: parser.add(params[i]);
130: definition = parser.parse(def);
131: }
132:
133: /**
134: * Return the expression that defines this function, as a string.
135: */
136: public String getDefinitionString() {
137: return definition.toString();
138: }
139:
140: /**
141: * Return a string that describes this function, such as "function f(x,y) given by x^2 - y^2".
142: */
143: public String toString() {
144: StringBuffer b = new StringBuffer();
145: b.append(name == null ? "unnamed function of (" : "function "
146: + name + "(");
147: for (int i = 0; i < params.length; i++) {
148: b.append(params[i].getName());
149: if (i < params.length - 1)
150: b.append(",");
151: }
152: b.append(") given by ");
153: b.append(definition.toString());
154: return b.toString();
155: }
156:
157: //------- Methods from the Function interface --------------------
158:
159: /**
160: * Return the number of arguments of this function.
161: */
162: public int getArity() {
163: return params.length;
164: }
165:
166: /**
167: * Find the value of the function at the argument values
168: * given by arguments[0], arguments[1], ... The length
169: * of the array argument should be equal to the arity of
170: * the function. If not, an IllegalArgumentException is
171: * thrown.
172: */
173: public double getVal(double[] arguments) {
174: return getValueWithCases(arguments, null);
175: }
176:
177: /**
178: * Find the value of the function at the argument values
179: * given by arguments[0], arguments[1], ... The length
180: * of the array argument should be equal to the arity of
181: * the function. If not, an IllegalArgumentException is
182: * thrown. Store information about "cases" that occur in
183: * the evaluation in the second parameter, if that parameter is non-null.
184: */
185: public double getValueWithCases(double[] arguments, Cases cases) {
186: synchronized (params) {
187: if (arguments == null) {
188: if (params.length > 0)
189: throw new IllegalArgumentException(
190: "Internal Error: Number of arguments provided to function does not match its arity.");
191: } else if (arguments.length != params.length)
192: throw new IllegalArgumentException(
193: "Internal Error: Number of arguments provided to function does not match its arity.");
194: else {
195: for (int i = 0; i < params.length; i++)
196: params[i].setVal(arguments[i]);
197: }
198: return definition.getValueWithCases(cases);
199: }
200: }
201:
202: /**
203: * Return the derivative of the function with repect to
204: * argument number wrt, where the arguments are numbered 1, 2, 3,....
205: * For a function of one variable, call derivative(1) to find its derivative.
206: * If arity > 1, this is effectively a partial derivative. If wrt is
207: * not in the legal range, an IllegalArgumentException is thrown.
208: */
209: public Function derivative(int wrt) {
210: if (wrt <= 0 || wrt > getArity())
211: throw new IllegalArgumentException(
212: "Internal Error: Attempt to take the derivative of a function of "
213: + getArity()
214: + " variables with respect to argument number "
215: + wrt + ".");
216: ExpressionFunction deriv = new ExpressionFunction();
217: if (name != null) {
218: if (getArity() == 1)
219: deriv.setName(getName() + "'");
220: else
221: deriv.setName("D" + wrt + "[" + getName() + "]");
222: }
223: deriv.params = params;
224: deriv.definition = (Expression) definition
225: .derivative(params[wrt - 1]);
226: return deriv;
227: }
228:
229: /**
230: * Return the derivative of the function with respect to the
231: * variable x. This will be non-zero if x occurs somehow in
232: * the definition of x: For example, f(y) = sin(x*y);
233: */
234: public Function derivative(Variable x) {
235: ExpressionFunction deriv = new ExpressionFunction();
236: if (name != null)
237: deriv.setName("D" + x.getName() + "[" + getName() + "]");
238: deriv.params = params;
239: deriv.definition = (Expression) definition.derivative(x);
240: return deriv;
241: }
242:
243: /**
244: * Return true if the definition of this function depends
245: * in some way on the variable x. (Note that a function does
246: * NOT depend on its parameter variables!)
247: */
248: public boolean dependsOn(Variable x) {
249: return definition.dependsOn(x);
250: }
251:
252: //----------- A method from ParserExtension class --------------
253:
254: /**
255: * Find the value of the function applied to arguments popped
256: * from the stack, and push the result back onto the stack.
257: * (Overrides general method inherited from FunctionParserExtension.
258: * This is done for efficiency and because the general method
259: * can't deal properly with "cases".) Not meant to be called directly
260: */
261: public void apply(StackOfDouble stack, Cases cases) {
262: for (int i = getArity() - 1; i >= 0; i--)
263: params[i].setVal(stack.pop());
264: stack.push(definition.getValueWithCases(cases));
265: }
266:
267: } // end class ExpressionFunction
|