001/* An actor that computes the dot product of two arrays. 002 003 Copyright (c) 1997-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.TypedAtomicActor; 031import ptolemy.actor.TypedIOPort; 032import ptolemy.data.ArrayToken; 033import ptolemy.data.MatrixToken; 034import ptolemy.data.ScalarToken; 035import ptolemy.data.Token; 036import ptolemy.data.type.ArrayType; 037import ptolemy.data.type.BaseType; 038import ptolemy.data.type.MatrixType; 039import ptolemy.data.type.MonotonicFunction; 040import ptolemy.data.type.Type; 041import ptolemy.data.type.TypeLattice; 042import ptolemy.graph.CPO; 043import ptolemy.graph.InequalityTerm; 044import ptolemy.kernel.CompositeEntity; 045import ptolemy.kernel.util.IllegalActionException; 046import ptolemy.kernel.util.NameDuplicationException; 047import ptolemy.kernel.util.Workspace; 048 049/////////////////////////////////////////////////////////////////// 050//// DotProduct 051 052/** 053 <p>Compute the dot product of two arrays or matrices. This actor has two 054 input ports, from which it receives two ArrayTokens or two Matrix 055 Tokens. The elements of the ArrayTokens or MatrixTokens must be of 056 type ScalarToken. The output is the dot product of the two arrays or 057 matrices.</p> 058 059 <p> This actor requires that each input port have a token upon 060 firing. On each firing, it produces exactly one token, which is of 061 type ScalarToken.</p> 062 063 @author Jeff Tsay, Paul Whitaker, Adam Cataldo 064 @version $Id$ 065 @since Ptolemy II 1.0 066 @Pt.ProposedRating Yellow (pwhitake) 067 @Pt.AcceptedRating Red (acataldo) 068 */ 069public class DotProduct extends TypedAtomicActor { 070 /** Construct an actor in the specified container with the specified 071 * name. 072 * @param container The container. 073 * @param name The name of this actor within the container. 074 * @exception IllegalActionException If the actor cannot be contained 075 * by the proposed container. 076 * @exception NameDuplicationException If the name coincides with 077 * an actor already in the container. 078 */ 079 public DotProduct(CompositeEntity container, String name) 080 throws IllegalActionException, NameDuplicationException { 081 super(container, name); 082 083 input1 = new TypedIOPort(this, "input1", true, false); 084 input2 = new TypedIOPort(this, "input2", true, false); 085 output = new TypedIOPort(this, "output", false, true); 086 087 // Set the type constraints. 088 output.setTypeAtLeast(new PortFunction(input1, input2)); 089 } 090 091 /////////////////////////////////////////////////////////////////// 092 //// ports and parameters //// 093 094 /** The first input port. This has type ArrayToken. The elements of 095 * the ArrayToken must be of type ScalarToken. 096 */ 097 public TypedIOPort input1 = null; 098 099 /** The second input port. This has type ArrayToken. The elements of 100 * the ArrayToken must be of type ScalarToken. 101 */ 102 public TypedIOPort input2 = null; 103 104 /** The output port, which has type ScalarToken. 105 */ 106 public TypedIOPort output = null; 107 108 /////////////////////////////////////////////////////////////////// 109 //// public methods //// 110 111 /** Clone the actor into the specified workspace. This calls the 112 * base class and then sets the type constraints. 113 * @param workspace The workspace for the new object. 114 * @return A new actor. 115 * @exception CloneNotSupportedException If a derived class has 116 * an attribute that cannot be cloned. 117 */ 118 @Override 119 public Object clone(Workspace workspace) throws CloneNotSupportedException { 120 DotProduct newObject = (DotProduct) super.clone(workspace); 121 PortFunction function = new PortFunction(newObject.input1, 122 newObject.input2); 123 newObject.output.setTypeAtLeast(function); 124 return newObject; 125 } 126 127 /** Read a Token from each of the input ports, and output the 128 * dot product. 129 * @exception IllegalActionException If there is no director, if 130 * the input arrays have unequal widths, or if the input arrays 131 * have no elements, or if the input types. 132 */ 133 @Override 134 public void fire() throws IllegalActionException { 135 super.fire(); 136 137 if (input1.getType() instanceof ArrayType 138 && input2.getType() instanceof ArrayType) { 139 try { 140 _arrayFire(); 141 } catch (IllegalActionException e) { 142 throw e; 143 } 144 } else if (input1.getType() instanceof MatrixType 145 && input2.getType() instanceof MatrixType) { 146 try { 147 _matrixFire(); 148 } catch (IllegalActionException e) { 149 throw e; 150 } 151 } else { 152 throw new IllegalActionException("Invalid types. " 153 + "Input1 and input2 must both of " 154 + "ArrayType or both be of MatrixType." 155 + "The type of input1 was \"" + input1.getType().getClass() 156 + "\". The type of input2 was \"" 157 + input2.getType().getClass() + "\"."); 158 } 159 } 160 161 /** If both of the input ports have at least one token, return 162 * what the superclass returns (presumably true). Otherwise return 163 * false. 164 * @exception IllegalActionException If there is no director. 165 */ 166 @Override 167 public boolean prefire() throws IllegalActionException { 168 if (!input1.hasToken(0) || !input2.hasToken(0)) { 169 if (_debugging) { 170 _debug("Called prefire(), which returns false."); 171 } 172 173 return false; 174 } else { 175 return super.prefire(); 176 } 177 } 178 179 /////////////////////////////////////////////////////////////////// 180 //// private methods //// 181 182 /* Read an ArrayToken from each of the input ports, and output the 183 * dot product. 184 * @exception IllegalActionException If the input arrays have 185 * unequal widths or if the input arrays have no elements.. 186 */ 187 private void _arrayFire() throws IllegalActionException { 188 ArrayToken token1 = (ArrayToken) input1.get(0); 189 ArrayToken token2 = (ArrayToken) input2.get(0); 190 191 Token[] array1 = token1.arrayValue(); 192 Token[] array2 = token2.arrayValue(); 193 194 if (array1.length != array2.length) { 195 throw new IllegalActionException( 196 "Inputs to DotProduct have " + "unequal lengths: " 197 + array1.length + " and " + array2.length + "."); 198 } 199 200 if (array1.length < 1) { 201 throw new IllegalActionException( 202 "Inputs to DotProduct have " + "no elements."); 203 } 204 205 Token dotProd = null; 206 ScalarToken currentTerm; 207 208 for (int i = 0; i < array1.length; i++) { 209 currentTerm = (ScalarToken) array1[i].multiply(array2[i]); 210 211 if (dotProd == null) { 212 dotProd = currentTerm; 213 } else { 214 dotProd = dotProd.add(currentTerm); 215 } 216 } 217 218 output.broadcast(dotProd); 219 } 220 221 /* Read a MatrixToken from each of the input ports, and output 222 * the dot product. 223 * @exception IllegalActionException If the input matrices have 224 * unequal sizes. 225 */ 226 private void _matrixFire() throws IllegalActionException { 227 MatrixToken token1 = (MatrixToken) input1.get(0); 228 MatrixToken token2 = (MatrixToken) input2.get(0); 229 230 int columnCount1 = token1.getColumnCount(); 231 int rowCount1 = token1.getRowCount(); 232 int columnCount2 = token2.getColumnCount(); 233 int rowCount2 = token2.getRowCount(); 234 235 Token element1; 236 Token element2; 237 Token sum; 238 239 if (columnCount1 == columnCount2 && rowCount1 == rowCount2) { 240 sum = token1.getElementAsToken(0, 0).zero(); 241 242 for (int i = 0; i < rowCount1; i += 1) { 243 for (int j = 0; j < columnCount1; j += 1) { 244 element1 = token1.getElementAsToken(i, j); 245 element2 = token2.getElementAsToken(i, j); 246 sum = sum.add(element1.multiply(element2)); 247 } 248 } 249 250 output.send(0, sum); 251 } else { 252 String matrix1 = rowCount1 + " by " + columnCount1; 253 String matrix2 = rowCount2 + " by " + columnCount2; 254 throw new IllegalActionException("Tried to multiply a " + matrix1 255 + " matrix with a " + matrix2 + " matrix"); 256 } 257 } 258 259 /////////////////////////////////////////////////////////////////// 260 //// inner classes //// 261 262 /** This class implements a function on the two input port types. 263 * f(port1, port2) == 264 * LUB(elementType(port1), elementType(port2)) if both are matrices 265 * LUB(elementType(port1), elementType(port2)) if both are arrays 266 * UNKNOWN otherwise 267 * This function's value determines the output port type. 268 */ 269 private static class PortFunction extends MonotonicFunction { 270 private PortFunction(TypedIOPort port1, TypedIOPort port2) { 271 _port1 = port1; 272 _port2 = port2; 273 } 274 275 /////////////////////////////////////////////////////////////// 276 //// public inner methods //// 277 278 /** Return the function result. 279 * @return A Type. 280 */ 281 @Override 282 public Object getValue() { 283 Type type1 = _port1.getType(); 284 Type type2 = _port2.getType(); 285 286 if (type1 == BaseType.UNKNOWN || type2 == BaseType.UNKNOWN) { 287 return BaseType.UNKNOWN; 288 } else if (type1 instanceof ArrayType 289 && type2 instanceof ArrayType) { 290 Type elType1 = ((ArrayType) type1).getElementType(); 291 Type elType2 = ((ArrayType) type2).getElementType(); 292 CPO lattice = TypeLattice.lattice(); 293 return lattice.leastUpperBound(elType1, elType2); 294 } else if (type1 instanceof MatrixType 295 && type2 instanceof MatrixType) { 296 Type elType1 = ((MatrixType) type1).getElementType(); 297 Type elType2 = ((MatrixType) type2).getElementType(); 298 CPO lattice = TypeLattice.lattice(); 299 return lattice.leastUpperBound(elType1, elType2); 300 } else { 301 return BaseType.UNKNOWN; 302 } 303 } 304 305 /** Return the type variables in this InequalityTerm. If 306 * neither type variable has been set, return an array of 307 * size two, containing the InequalityTerms corresponding to 308 * the port. If exactly one is set, return the 309 * InequalityTerm corresponding to the other port (in a size 310 * one array). If both have been set, return an empty 311 * InequalityTerm array. 312 * @return An array of InequalityTerm. 313 */ 314 @Override 315 public InequalityTerm[] getVariables() { 316 InequalityTerm term1 = _port1.getTypeTerm(); 317 InequalityTerm term2 = _port2.getTypeTerm(); 318 319 if (term1.isSettable() && term2.isSettable()) { 320 InequalityTerm[] array = { term1, term2 }; 321 return array; 322 } else if (term1.isSettable()) { 323 InequalityTerm[] array = { term1 }; 324 return array; 325 } else if (term2.isSettable()) { 326 InequalityTerm[] array = { term2 }; 327 return array; 328 } 329 330 return new InequalityTerm[0]; 331 } 332 333 /////////////////////////////////////////////////////////////// 334 //// private inner variable //// 335 private TypedIOPort _port1; 336 337 private TypedIOPort _port2; 338 } 339}