001/* An actor that applies a function over each element of a sequence.
002
003 Copyright (c) 2004-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.hoc;
029
030import java.util.Iterator;
031
032import ptolemy.actor.TypedAtomicActor;
033import ptolemy.actor.TypedIOPort;
034import ptolemy.actor.parameters.PortParameter;
035import ptolemy.actor.util.DFUtilities;
036import ptolemy.data.ArrayToken;
037import ptolemy.data.FunctionToken;
038import ptolemy.data.Token;
039import ptolemy.data.type.ArrayType;
040import ptolemy.data.type.FunctionType;
041import ptolemy.data.type.Type;
042import ptolemy.kernel.CompositeEntity;
043import ptolemy.kernel.util.IllegalActionException;
044import ptolemy.kernel.util.NameDuplicationException;
045
046///////////////////////////////////////////////////////////////////
047//// ApplyFunctionOverSequence
048
049/**
050 Apply a function over one or more input sequences.
051 This actor will collect tokens from each input port into arrays
052 and, when enough input tokens have arrived, pass those arrays
053 to the function specified either at the <i>function</i> parameter
054 or the port.
055 <p>
056 To use this actor, create any number of input ports, add
057 a parameter named <i>tokenConsumptionRate</i> to each input
058 port, and set the value of this parameter to the number
059 of tokens that you would like to be collected into an
060 array for each function application. Also, create
061 and set a parameter named <i>tokenProductionRate</i>
062 in the output port.  (If <i>tokenConsumptionRate</i>
063 or <i>tokenProductionRate</i> are not defined, then they
064 will be assumed to have value one).
065 Then define a function that takes as many array-valued
066 arguments as there are input ports and returns an
067 array-valued result. For example, the following function
068 will compute the FFT of the input array using the FFT()
069 function in the expression language:
070 <pre>
071 function(x:{double}) abs(FFT(x, 8))
072 </pre>
073 Note that if the <i>tokenConsumptionRate</i> of a port is
074 changed during the execution of the model, the change is
075 ignored until the next execution of the model.
076
077 @author Steve Neuendorffer (Contributor: Edward A. Lee)
078 @deprecated Use SequenceToArray followed by ApplyFunction.
079 @version $Id$
080 @since Ptolemy II 4.1
081 @Pt.ProposedRating Green (neuendor)
082 @Pt.AcceptedRating Yellow (neuendor)
083 @see ptolemy.actor.lib.hoc.ApplyFunction
084 */
085@Deprecated
086public class ApplyFunctionOverSequence extends TypedAtomicActor {
087    /** Construct an actor with the given container and name.
088     *  @param container The container.
089     *  @param name The name of this actor.
090     *  @exception IllegalActionException If the actor cannot be contained
091     *   by the proposed container.
092     *  @exception NameDuplicationException If the container already has an
093     *   actor with this name.
094     */
095    public ApplyFunctionOverSequence(CompositeEntity container, String name)
096            throws NameDuplicationException, IllegalActionException {
097        super(container, name);
098        output = new TypedIOPort(this, "output", false, true);
099        function = new PortParameter(this, "function");
100    }
101
102    ///////////////////////////////////////////////////////////////////
103    ////                         public variables                  ////
104
105    /** The input port for function definition. The type of this port is
106     *  undeclared, but to have this actor work, the designer has to provide
107     *  a matched function token for it.
108     *  Note: The reason that we don't declare the type for it is because
109     *  currently there is not cast supported in the FunctionType class.
110     *  we'll fix this later.
111     */
112    public PortParameter function;
113
114    /** The output port.
115     */
116    public TypedIOPort output;
117
118    ///////////////////////////////////////////////////////////////////
119    ////                         public methods                    ////
120
121    /** Consume the inputs, apply the function, and produce the result.
122     *  @exception IllegalActionException If a runtime type error occurs.
123     */
124    @Override
125    public void fire() throws IllegalActionException {
126        super.fire();
127
128        // Update the function parameterPort.
129        function.update();
130
131        FunctionToken functionValue = (FunctionToken) function.getToken();
132        Token[] arguments = new Token[inputPortList().size() - 1];
133        int i = 0;
134        Iterator ports = inputPortList().iterator();
135
136        // Skip the function port.
137        ports.next();
138
139        while (ports.hasNext()) {
140            TypedIOPort port = (TypedIOPort) ports.next();
141
142            if (_rate[i] == -1) {
143                arguments[i] = port.get(0);
144            } else {
145                Token[] tokens = port.get(0, _rate[i]);
146                arguments[i] = new ArrayToken(port.getType(), tokens);
147            }
148
149            i++;
150        }
151
152        Token result = functionValue.apply(arguments);
153
154        if (_outputRate == -1) {
155            output.broadcast(result);
156        } else {
157            // FIXME: Check size.
158            ArrayToken resultArray = (ArrayToken) result;
159            output.broadcast(resultArray.arrayValue(), resultArray.length());
160        }
161    }
162
163    /** Return true if the input ports have enough tokens.
164     *  @exception IllegalActionException Not thrown in this base class.
165     */
166    @Override
167    public boolean prefire() throws IllegalActionException {
168        super.prefire();
169
170        Iterator ports = inputPortList().iterator();
171        int i = 0;
172
173        // Skip the function port.
174        ports.next();
175
176        while (ports.hasNext()) {
177            TypedIOPort port = (TypedIOPort) ports.next();
178
179            if (_rate[i] == -1) {
180                if (!port.hasToken(0)) {
181                    return false;
182                }
183            } else {
184                if (!port.hasToken(0, _rate[i])) {
185                    return false;
186                }
187            }
188        }
189
190        return true;
191    }
192
193    /** Preinitialize the actor.  Set the type of the ports based on
194     *  the type of the function parameter.
195     */
196    @Override
197    public void preinitialize() throws IllegalActionException {
198        super.preinitialize();
199
200        FunctionType type = (FunctionType) function.getType();
201
202        if (type.getReturnType() instanceof ArrayType) {
203            output.setTypeEquals(
204                    ((ArrayType) type.getReturnType()).getElementType());
205            _outputRate = DFUtilities.getTokenProductionRate(output);
206        } else {
207            output.setTypeEquals(type.getReturnType());
208            _outputRate = -1;
209        }
210
211        int i = 0;
212        _rate = new int[inputPortList().size() - 1];
213
214        Iterator ports = inputPortList().iterator();
215
216        // Skip the function port.
217        ports.next();
218
219        while (ports.hasNext()) {
220            TypedIOPort port = (TypedIOPort) ports.next();
221            Type portType = type.getArgType(i);
222
223            if (portType instanceof ArrayType) {
224                port.setTypeEquals(((ArrayType) portType).getElementType());
225                _rate[i] = DFUtilities.getTokenConsumptionRate(port);
226            } else {
227                port.setTypeEquals(portType);
228                _rate[i] = -1;
229            }
230
231            i++;
232        }
233    }
234
235    private int _outputRate;
236
237    private int[] _rate;
238}