001/* Unbundle a matrix into a sequence of N by M tokens.
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.domains.sdf.lib;
029
030import ptolemy.actor.TypedIOPort;
031import ptolemy.data.BooleanToken;
032import ptolemy.data.IntToken;
033import ptolemy.data.MatrixToken;
034import ptolemy.data.expr.Parameter;
035import ptolemy.data.type.BaseType;
036import ptolemy.data.type.MatrixType;
037import ptolemy.data.type.MonotonicFunction;
038import ptolemy.data.type.Type;
039import ptolemy.graph.InequalityTerm;
040import ptolemy.kernel.CompositeEntity;
041import ptolemy.kernel.util.Attribute;
042import ptolemy.kernel.util.IllegalActionException;
043import ptolemy.kernel.util.NameDuplicationException;
044import ptolemy.kernel.util.Workspace;
045
046///////////////////////////////////////////////////////////////////
047//// MatrixToSequence
048
049/**
050 This actor unbundles a matrix into a sequence of output tokens.
051 On each firing, it writes the elements of the matrix to the output
052 as a sequence of output tokens. It outputs the contents of the first
053 row first, then the second row, etc.
054 If the <i>enforceMatrixSize</i> parameter true, then if an input
055 matrix does not match <i>rows</i> and <i>columns</i> parameters, then
056 the fire() method will throw an exception.
057 This feature is important in domains, such as SDF,
058 that do static scheduling based on production and consumption
059 rates.  For other domains, such as DE and PN, the <i>enforceMatrixSize</i>
060 parameter can be set to false, in which case the <i>rows</i> and
061 <i>columns</i> parameters will be ignored.
062 This actor is polymorphic. It can accept any matrix input and the output
063 will have the type of the elements of the matrix.
064 <p>
065 @author Edward Lee
066 @version $Id$
067 @since Ptolemy II 0.4
068 @Pt.ProposedRating Yellow (eal)
069 @Pt.AcceptedRating Red (neuendor)
070 */
071public class MatrixToSequence extends SDFTransformer {
072    /** Construct an actor with the given container and name.
073     *  @param container The container.
074     *  @param name The name of this actor.
075     *  @exception IllegalActionException If the actor cannot be contained
076     *   by the proposed container.
077     *  @exception NameDuplicationException If the container already has an
078     *   actor with this name.
079     */
080    public MatrixToSequence(CompositeEntity container, String name)
081            throws NameDuplicationException, IllegalActionException {
082        super(container, name);
083
084        input.setTypeAtMost(BaseType.MATRIX);
085        output.setTypeAtLeast(new FunctionTerm(input));
086
087        // Set parameters.
088        rows = new Parameter(this, "rows");
089        rows.setExpression("1");
090        columns = new Parameter(this, "columns");
091        columns.setExpression("1");
092        enforceMatrixSize = new Parameter(this, "enforceMatrixSize");
093        enforceMatrixSize.setExpression("true");
094        enforceMatrixSize.setTypeEquals(BaseType.BOOLEAN);
095
096        output_tokenProductionRate.setExpression("rows * columns");
097
098        // Set the icon.
099        _attachText("_iconDescription",
100                "<svg>\n" + "<polygon points=\"-15,-15 15,15 15,-15 -15,15\" "
101                        + "style=\"fill:white\"/>\n" + "</svg>\n");
102    }
103
104    ///////////////////////////////////////////////////////////////////
105    ////                         parameters                        ////
106
107    /** The number of rows in the input.  This is an integer that defaults
108     *  to 1.
109     */
110    public Parameter rows;
111
112    /** The number of columns in the input.  This is an integer that defaults
113     *  to 1.
114     */
115    public Parameter columns;
116
117    /** If true, then enforce the <i>rows</i> and <i>columns</i> parameters by
118     *  throwing an exception if it is violated. This is a boolean
119     *  that defaults to true.
120     */
121    public Parameter enforceMatrixSize;
122
123    ///////////////////////////////////////////////////////////////////
124    ////                         public methods                    ////
125
126    /**  Ensure that the rows and columns parameters are both positive.
127     *  @param attribute The attribute that has changed.
128     *  @exception IllegalActionException If the parameters are out of range.
129     */
130    @Override
131    public void attributeChanged(Attribute attribute)
132            throws IllegalActionException {
133        if (attribute == rows) {
134            int rowsValue = ((IntToken) rows.getToken()).intValue();
135
136            if (rowsValue <= 0) {
137                throw new IllegalActionException(this,
138                        "Invalid number of rows: " + rowsValue);
139            }
140        } else if (attribute == columns) {
141            int columnsValue = ((IntToken) columns.getToken()).intValue();
142
143            if (columnsValue <= 0) {
144                throw new IllegalActionException(this,
145                        "Invalid number of columns: " + columnsValue);
146            }
147        }
148
149        super.attributeChanged(attribute);
150    }
151
152    /** Clone the actor into the specified workspace. This calls the
153     *  base class and then sets the type constraints.
154     *  @param workspace The workspace for the new object.
155     *  @return A new actor.
156     *  @exception CloneNotSupportedException If a derived class has
157     *   an attribute that cannot be cloned.
158     */
159    @Override
160    public Object clone(Workspace workspace) throws CloneNotSupportedException {
161        MatrixToSequence newObject = (MatrixToSequence) super.clone(workspace);
162
163        newObject.input.setTypeAtMost(BaseType.MATRIX);
164        newObject.output.setTypeAtLeast(new FunctionTerm(newObject.input));
165        return newObject;
166    }
167
168    /** Consume the input and produce the output sequence. If there
169     *  is no input token, do nothing.
170     *  @exception IllegalActionException If not enough tokens are available.
171     */
172    @Override
173    public void fire() throws IllegalActionException {
174        super.fire();
175
176        if (!input.hasToken(0)) {
177            return;
178        }
179
180        MatrixToken token = (MatrixToken) input.get(0);
181        int actualRowCount = token.getRowCount();
182        int actualColumnCount = token.getColumnCount();
183        boolean enforce = ((BooleanToken) enforceMatrixSize.getToken())
184                .booleanValue();
185
186        if (enforce) {
187            int rowsValue = ((IntToken) rows.getToken()).intValue();
188            int columnsValue = ((IntToken) columns.getToken()).intValue();
189
190            if (actualRowCount != rowsValue
191                    || actualColumnCount != columnsValue) {
192                throw new IllegalActionException(this,
193                        "The input matrix size " + actualRowCount + "x"
194                                + actualColumnCount
195                                + " does not match what the actor requires, "
196                                + rowsValue + "x" + columnsValue);
197            }
198        }
199
200        for (int i = 0; i < actualRowCount; i++) {
201            for (int j = 0; j < actualColumnCount; j++) {
202                output.send(0, token.getElementAsToken(i, j));
203            }
204        }
205    }
206
207    ///////////////////////////////////////////////////////////////////
208    ////                         inner classes                     ////
209
210    /** A monotonic function of the input port type. The result of the
211     *  function is a matrix type with elements that are the same as
212     *  the input type. If there is no such matrix type, then the
213     *  result is unknown.  NOTE: This is largely copied from
214     *  AbsoluteValue.  Should there be a common base class?  It is
215     *  also essentially identical the inner class in SequenceToMatrix.
216     */
217    private static class FunctionTerm extends MonotonicFunction {
218        // FindBugs suggests making this class static so as to decrease
219        // the size of instances and avoid dangling references.
220
221        /** The constructor takes a port argument so that the clone()
222         *  method can construct an instance of this class for the
223         *  input port on the clone.
224         *  @param port The port
225         */
226        private FunctionTerm(TypedIOPort port) {
227            _port = port;
228        }
229
230        ///////////////////////////////////////////////////////////////
231        ////                       public inner methods            ////
232
233        /** Return the function result.
234         *  @return A Type.
235         */
236        @Override
237        public Object getValue() {
238            Type inputType = _port.getType();
239
240            if (!(inputType instanceof MatrixType)) {
241                return BaseType.UNKNOWN;
242            }
243
244            return ((MatrixType) inputType).getElementType();
245        }
246
247        /** Return the variables in this term. If the type of the input port
248         *  is a variable, return a one element array containing the
249         *  InequalityTerm of that port; otherwise, return an array of zero
250         *  length.
251         *  @return An array of InequalityTerm.
252         */
253        @Override
254        public InequalityTerm[] getVariables() {
255            if (_port.getTypeTerm().isSettable()) {
256                InequalityTerm[] variable = new InequalityTerm[1];
257                variable[0] = _port.getTypeTerm();
258                return variable;
259            } else {
260                return new InequalityTerm[0];
261            }
262        }
263
264        ///////////////////////////////////////////////////////////////
265        ////                       private inner variable          ////
266        private TypedIOPort _port;
267    }
268}