001/* Join matrices provided in sequence.
002
003 Copyright (c) 2007-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.data.IntToken;
031import ptolemy.data.MatrixToken;
032import ptolemy.data.expr.Parameter;
033import ptolemy.data.type.BaseType;
034import ptolemy.kernel.CompositeEntity;
035import ptolemy.kernel.util.Attribute;
036import ptolemy.kernel.util.IllegalActionException;
037import ptolemy.kernel.util.NameDuplicationException;
038
039///////////////////////////////////////////////////////////////////
040//// MatrixJoin
041
042/**
043 This actor joins matrices into a single matrix by tiling.
044 It reads N*M input matrices from the input port, where N is
045 the value of the <i>rows</i> parameter, and M is
046 the value of the <i>columns</i> parameter. The matrices
047 read are arranged left-to-right,
048 top-to-bottom, in a raster scan pattern.
049 For example, if <i>rows</i> = <i>columns</i> = 2, then on
050 each firing, four matrices, A, B, C, D, will be read from
051 the input channel.  Assuming A is the first one read, then
052 the output matrix will be a matrix arranged as follows:
053 <pre>
054   A B
055   C D
056 </pre>
057 The size of the output depends on the matrices in the top row
058 and left column. That is, in the above examples, the number of
059 columns in the output will equal the sum of the number of columns
060 in A and B.  The number of rows will equal the sum of the number
061 of rows in A and C.  The matrices are tiled in raster-scan order,
062 first A, then B, then C, and then D.  Gaps are zero filled,
063 and overlaps are overwritten, where later matrices in the raster-scan
064 order overwrite earlier matrices.  For example, if B has more rows
065 than A, then the bottom rows of B will be overwritten by rows of D.
066 <p>
067
068 @author Edward Lee
069 @version $Id$
070 @since Ptolemy II 6.1
071 @Pt.ProposedRating Yellow (eal)
072 @Pt.AcceptedRating Red (neuendor)
073 */
074public class MatrixJoin extends SDFTransformer {
075    /** Construct an actor with the given container and name.
076     *  @param container The container.
077     *  @param name The name of this actor.
078     *  @exception IllegalActionException If the actor cannot be contained
079     *   by the proposed container.
080     *  @exception NameDuplicationException If the container already has an
081     *   actor with this name.
082     */
083    public MatrixJoin(CompositeEntity container, String name)
084            throws NameDuplicationException, IllegalActionException {
085        super(container, name);
086
087        // Set parameters.
088        columns = new Parameter(this, "columns");
089        columns.setTypeEquals(BaseType.INT);
090        columns.setExpression("1");
091        rows = new Parameter(this, "rows");
092        rows.setTypeEquals(BaseType.INT);
093        rows.setExpression("1");
094
095        input_tokenConsumptionRate.setExpression("rows * columns");
096    }
097
098    ///////////////////////////////////////////////////////////////////
099    ////                         parameters                        ////
100
101    /** The number of matrices to arrange left to right on the output.
102     *  This is an integer that defaults to 1.
103     */
104    public Parameter columns;
105
106    /** The number of matrices to arrange top to bottom on the output.
107     *  This is an integer that defaults to 1.
108     */
109    public Parameter rows;
110
111    ///////////////////////////////////////////////////////////////////
112    ////                         public methods                    ////
113
114    /** Ensure that the rows and columns parameters are both positive.
115     *  @param attribute The attribute that has changed.
116     *  @exception IllegalActionException If the parameters are out of range.
117     */
118    @Override
119    public void attributeChanged(Attribute attribute)
120            throws IllegalActionException {
121        if (attribute == columns) {
122            int columnsValue = ((IntToken) columns.getToken()).intValue();
123            if (columnsValue <= 0) {
124                throw new IllegalActionException(this,
125                        "Invalid number of columns: " + columnsValue);
126            }
127        } else if (attribute == rows) {
128            int rowsValue = ((IntToken) rows.getToken()).intValue();
129            if (rowsValue <= 0) {
130                throw new IllegalActionException(this,
131                        "Invalid number of rows: " + rowsValue);
132            }
133        }
134        super.attributeChanged(attribute);
135    }
136
137    /** Consume the inputs and produce the output matrix.
138     *  @exception IllegalActionException If not enough tokens are available.
139     */
140    @Override
141    public void fire() throws IllegalActionException {
142        super.fire();
143        int numberOfColumns = ((IntToken) columns.getToken()).intValue();
144        int numberOfRows = ((IntToken) rows.getToken()).intValue();
145        MatrixToken[][] result = new MatrixToken[numberOfRows][numberOfColumns];
146        for (int i = 0; i < numberOfRows; i++) {
147            for (int j = 0; j < numberOfColumns; j++) {
148                result[i][j] = (MatrixToken) input.get(0);
149            }
150        }
151        ;
152        output.send(0, result[0][0].join(result));
153    }
154
155    /** Return true if each input channel has enough tokens for this actor to
156     *  fire. The number of tokens required is the value of the <i>columns</i> parameter.
157     *  @return boolean True if there are enough tokens at the input port
158     *   for this actor to fire.
159     *  @exception IllegalActionException If the hasToken() query to the
160     *   input port throws it.
161     *  @see ptolemy.actor.IOPort#hasToken(int, int)
162     */
163    @Override
164    public boolean prefire() throws IllegalActionException {
165        int columnsValue = ((IntToken) columns.getToken()).intValue();
166        int rowsValue = ((IntToken) rows.getToken()).intValue();
167        if (!input.hasToken(0, rowsValue * columnsValue)) {
168            if (_debugging) {
169                _debug("Called prefire(), which returns false.");
170            }
171            return false;
172        } else {
173            return super.prefire();
174        }
175    }
176}