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}