001/* An actor that computes a specified math function of the input.
002
003 Copyright (c) 1998-2014 The Regents of the University of California.
004 All rights reserved.
005 Permission is hereby granted, without written agreement and without
006 license or royalty fees, to use, copy, modify, and distribute this
007 software and its documentation for any purpose, provided that the above
008 copyright notice and the following two paragraphs appear in all copies
009 of this software.
010
011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
015 SUCH DAMAGE.
016
017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
022 ENHANCEMENTS, OR MODIFICATIONS.
023
024 PT_COPYRIGHT_VERSION_2
025 COPYRIGHTENDKEY
026
027 */
028package ptolemy.actor.lib;
029
030import ptolemy.actor.TypedAtomicActor;
031import ptolemy.actor.TypedIOPort;
032import ptolemy.data.DoubleToken;
033import ptolemy.data.ScalarToken;
034import ptolemy.data.Token;
035import ptolemy.data.expr.StringParameter;
036import ptolemy.data.type.BaseType;
037import ptolemy.kernel.CompositeEntity;
038import ptolemy.kernel.util.Attribute;
039import ptolemy.kernel.util.IllegalActionException;
040import ptolemy.kernel.util.InternalErrorException;
041import ptolemy.kernel.util.NameDuplicationException;
042import ptolemy.kernel.util.Workspace;
043
044// NOTE: If you update the list of functions, then you will want
045// to update the list in actor/lib/math.xml.
046///////////////////////////////////////////////////////////////////
047//// MathFunction
048
049/**
050 Produce an output token on each firing with a value that is
051 equal to the specified math function of the input.
052 The input and output types are DoubleToken.  The functions
053 are a subset of those in the java.lang.Math class.  They are:
054 <ul>
055 <li> <b>exp</b>: The exponential function.
056 This is the default function for this actor
057 If the argument is NaN, then the result is NaN.
058 <li> <b>log</b>: The natural logarithm function.
059 If the argument is NaN, then the result is NaN.
060 <li> <b>modulo</b>: The modulo after division.
061 If the second operand is zero, then the result is NaN.
062 <li> <b>sign</b>: If the argument is greater than 0, return 1.0, if
063 it is less than 0, return -1.0, otherwise return 0.0.
064 <li> <b>square</b>: The square function
065 If the argument is NaN, then the result is NaN.
066 <li> <b>sqrt</b>: The square root function.
067 If the argument is NaN, then the result is NaN.
068 </ul>
069 <p>
070
071 NOTES:
072 <p>1. Some functions like exp, log, square, and sqrt act on a single
073 operand only.  Other functions like modulo act on two operands.
074 The actor acquires a second input when the function is changed to
075 modulo, and loses the input when the function is changed back.
076 <p>2. There is an alternative to using the MathFunction.modulo() method
077 If you want to use the IEEE remainder standard, use the Remainder actor.
078
079 @author C. Fong
080 @version $Id$
081 @since Ptolemy II 1.0
082 @Pt.ProposedRating Yellow (chf)
083 @Pt.AcceptedRating Yellow (janneck)
084 @see AbsoluteValue
085 @see Remainder
086 @see Scale
087 @see TrigFunction
088 @deprecated This breaks the class mechanism.  Generally, it is
089 awkward to have the ports of an actor depend on parameter values.
090 Use UnaryMathFunction instead.
091 */
092@Deprecated
093public class MathFunction extends TypedAtomicActor {
094    /** Construct an actor with the given container and name.
095     *  @param container The container.
096     *  @param name The name of this actor.
097     *  @exception IllegalActionException If the actor cannot be contained
098     *   by the proposed container.
099     *  @exception NameDuplicationException If the container already has an
100     *   actor with this name.
101     */
102    public MathFunction(CompositeEntity container, String name)
103            throws NameDuplicationException, IllegalActionException {
104        super(container, name);
105
106        // Parameters
107        function = new StringParameter(this, "function");
108        function.setExpression("exp");
109        function.addChoice("exp");
110        function.addChoice("log");
111        function.addChoice("modulo");
112        function.addChoice("sign");
113        function.addChoice("square");
114        function.addChoice("sqrt");
115        _function = _EXP;
116
117        // Ports
118        // secondOperand port is not allocated in the constructor
119        // instead it will allocated dynamically during run-time
120        firstOperand = new TypedIOPort(this, "firstOperand");
121        firstOperand.setInput(true);
122        output = new TypedIOPort(this, "output");
123        output.setOutput(true);
124        firstOperand.setTypeEquals(BaseType.DOUBLE);
125        output.setTypeEquals(BaseType.DOUBLE);
126
127        _attachText("_iconDescription",
128                "<svg>\n" + "<rect x=\"-30\" y=\"-15\" "
129                        + "width=\"60\" height=\"30\" "
130                        + "style=\"fill:white\"/>\n" + "</svg>\n");
131    }
132
133    ///////////////////////////////////////////////////////////////////
134    ////                     ports and parameters                  ////
135
136    /** The function to compute.  This is a string-valued attribute
137     *  that defaults to "exp".
138     */
139    public StringParameter function;
140
141    /** The port for the first operand.
142     *  The port has type BaseType.DOUBLE
143     */
144    public TypedIOPort firstOperand = null;
145
146    /** The port for the second operand, if it is needed.
147     *  The port has type BaseType.DOUBLE
148     */
149    public TypedIOPort secondOperand = null;
150
151    /** Output port.
152     *  The port has type BaseType.DOUBLE
153     */
154    public TypedIOPort output = null;
155
156    ///////////////////////////////////////////////////////////////////
157    ////                         public methods                    ////
158
159    /** Override the base class to determine which function is being
160     *  specified.
161     *  @param attribute The attribute that changed.
162     *  @exception IllegalActionException If the function is not recognized.
163     */
164    @Override
165    public void attributeChanged(Attribute attribute)
166            throws IllegalActionException {
167        try {
168            if (attribute == function) {
169                String functionName = function.stringValue();
170
171                if (functionName.equals("exp")) {
172                    _function = _EXP;
173
174                    if (secondOperand != null) {
175                        secondOperand.setContainer(null);
176                    }
177                } else if (functionName.equals("log")) {
178                    _function = _LOG;
179
180                    if (secondOperand != null) {
181                        secondOperand.setContainer(null);
182                    }
183                } else if (functionName.equals("modulo")) {
184                    _function = _MODULO;
185                    _createSecondPort();
186                } else if (functionName.equals("sign")) {
187                    _function = _SIGN;
188
189                    if (secondOperand != null) {
190                        secondOperand.setContainer(null);
191                    }
192                } else if (functionName.equals("square")) {
193                    _function = _SQUARE;
194
195                    if (secondOperand != null) {
196                        secondOperand.setContainer(null);
197                    }
198                } else if (functionName.equals("sqrt")) {
199                    _function = _SQRT;
200
201                    if (secondOperand != null) {
202                        secondOperand.setContainer(null);
203                    }
204                } else {
205                    throw new IllegalActionException(this,
206                            "Unrecognized math function: " + functionName);
207                }
208            } else {
209                super.attributeChanged(attribute);
210            }
211        } catch (NameDuplicationException nameDuplication) {
212            throw new InternalErrorException(this, nameDuplication,
213                    "Unexpected name duplication");
214        }
215    }
216
217    /** Clone the actor into the specified workspace.
218     *  @param workspace The workspace for the new object.
219     *  @return A new actor.
220     *  @exception CloneNotSupportedException If a derived class contains
221     *   an attribute that cannot be cloned.
222     */
223    @Override
224    public Object clone(Workspace workspace) throws CloneNotSupportedException {
225        MathFunction newObject = (MathFunction) super.clone(workspace);
226
227        newObject._resultArray = new DoubleToken[_resultArray.length];
228        System.arraycopy(_resultArray, 0, newObject._resultArray, 0,
229                _resultArray.length);
230        return newObject;
231    }
232
233    /** Consume at most one input token from each input channel, and
234     *  compute the specified math function of the input.
235     *  If there is no input, then produce no output.
236     *  @exception IllegalActionException If there is no director.
237     */
238    @Override
239    public void fire() throws IllegalActionException {
240        super.fire();
241        if (firstOperand.hasToken(0)) {
242            double input1 = ((DoubleToken) firstOperand.get(0)).doubleValue();
243            double input2 = 1.0;
244
245            if (_function == _MODULO) {
246                if (secondOperand.hasToken(0)) {
247                    input2 = ((DoubleToken) secondOperand.get(0)).doubleValue();
248                }
249            }
250
251            output.send(0, new DoubleToken(_doFunction(input1, input2)));
252        }
253    }
254
255    /** Invoke a specified number of iterations of this actor. Each
256     *  iteration computes the math function specified by the
257     *  <i>function</i> parameter on a single token. An invocation
258     *  of this method therefore applies the function to <i>count</i>
259     *  successive input tokens.
260     *  <p>
261     *  This method should be called instead of the usual prefire(),
262     *  fire(), postfire() methods when this actor is used in a
263     *  domain that supports vectorized actors.  This leads to more
264     *  efficient execution.
265     *  @param count The number of iterations to perform.
266     *  @return COMPLETED if the actor was successfully iterated the
267     *   specified number of times. Otherwise, return NOT_READY, and do
268     *   not consume any input tokens.
269     *  @exception IllegalActionException Not thrown in this base class
270     */
271    @Override
272    public int iterate(int count) throws IllegalActionException {
273        // Check whether we need to reallocate the output token array.
274        Token[] inArray1;
275        Token[] inArray2;
276
277        if (count > _resultArray.length) {
278            _resultArray = new DoubleToken[count];
279        }
280
281        if (firstOperand.hasToken(0, count)) {
282            if (_function == _MODULO) {
283                if (secondOperand.hasToken(0, count)) {
284                    inArray1 = firstOperand.get(0, count);
285                    inArray2 = secondOperand.get(0, count);
286
287                    for (int i = 0; i < count; i++) {
288                        double input1 = ((DoubleToken) inArray1[i])
289                                .doubleValue();
290                        double input2 = ((DoubleToken) inArray2[i])
291                                .doubleValue();
292                        _resultArray[i] = new DoubleToken(
293                                _doFunction(input1, input2));
294                    }
295
296                    output.send(0, _resultArray, count);
297                    return COMPLETED;
298                } else {
299                    return NOT_READY;
300                }
301            } else {
302                inArray1 = firstOperand.get(0, count);
303
304                for (int i = 0; i < count; i++) {
305                    // Input could be an Integer, see test/auto/Differential.xml
306                    double input1 = ((ScalarToken) inArray1[i]).doubleValue();
307                    _resultArray[i] = new DoubleToken(_doFunction(input1, 0));
308                }
309
310                output.send(0, _resultArray, count);
311                return COMPLETED;
312            }
313        } else {
314            return NOT_READY;
315        }
316
317        // Note: constants COMPLETED and NOT_READY are defined in
318        // ptolemy.actor.Executable
319    }
320
321    ///////////////////////////////////////////////////////////////////
322    ////                         private methods                   ////
323
324    /** Create the second port needed by modulo function
325     */
326    private void _createSecondPort()
327            throws NameDuplicationException, IllegalActionException {
328        // Go looking for the port in case somebody else created the port
329        // already.  For example, this might
330        // happen in shallow code generation.
331        secondOperand = (TypedIOPort) getPort("secondOperand");
332
333        if (secondOperand == null) {
334            secondOperand = new TypedIOPort(this, "secondOperand", true, false);
335        } else if (secondOperand.getContainer() == null) {
336            secondOperand.setContainer(this);
337        }
338
339        secondOperand.setTypeEquals(BaseType.DOUBLE);
340    }
341
342    /** Calculate the function on the given argument.
343     *  @param input1 The first input value.
344     *  @param input2 The second input value.
345     *  @return The result of applying the function.
346     */
347    private double _doFunction(double input1, double input2) {
348        double result;
349
350        switch (_function) {
351        case _EXP:
352            result = Math.exp(input1);
353            break;
354
355        case _LOG:
356            result = Math.log(input1);
357            break;
358
359        case _MODULO:
360            result = input1 % input2;
361            break;
362
363        case _SIGN:
364
365            if (input1 > 0) {
366                result = 1.0;
367            } else if (input1 < 0) {
368                result = -1.0;
369            } else {
370                result = 0.0;
371            }
372
373            break;
374
375        case _SQUARE:
376            result = input1 * input1;
377            break;
378
379        case _SQRT:
380            result = Math.sqrt(input1);
381            break;
382
383        default:
384            throw new InternalErrorException(
385                    "Invalid value for _function private variable. "
386                            + "MathFunction actor (" + getFullName() + ")"
387                            + " on function type " + _function);
388        }
389
390        return result;
391    }
392
393    ///////////////////////////////////////////////////////////////////
394    ////                         private variables                 ////
395    private DoubleToken[] _resultArray = new DoubleToken[0];
396
397    // An indicator for the function to compute.
398    private int _function;
399
400    // Constants used for more efficient execution.
401    private static final int _EXP = 0;
402
403    private static final int _LOG = 1;
404
405    private static final int _MODULO = 2;
406
407    private static final int _SIGN = 3;
408
409    private static final int _SQUARE = 4;
410
411    private static final int _SQRT = 5;
412}