001/* A token for QSS integration that contains a double and a derivative. 002 003 Copyright (c) 2014-2018 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.data; 029 030import java.util.Arrays; 031 032import ptolemy.actor.util.Time; 033import ptolemy.data.type.BaseType; 034import ptolemy.kernel.util.IllegalActionException; 035 036/////////////////////////////////////////////////////////////////// 037//// SmoothToken 038 039/** 040 A double-valued token that contains zero or more derivatives, representing 041 the value of a function of time at a particular time. 042 In mathematical analysis, smoothness has to do with how many derivatives 043 a function possesses. A smooth function is one that has derivatives of 044 all orders everywhere in its domain. An instance of this class represents 045 a sample of a function at a point in time together with some finite number of 046 derivatives of the function at that same point. 047 <p> 048 This token will be treated exactly 049 like a {@link DoubleToken} by any actor or operation that does not 050 specifically support it, and it will be represented in the type systems 051 as a "double." But it can (potentially) carry additional information giving 052 one or more derivatives of the function from which it is a sample and 053 giving the time at which it represents a sample of the signal. 054 This token, therefore, gives a way for actors that either generate or 055 use this derivative information to make that information available to other 056 actors that can use it. Such actors should declare their input ports to 057 be of type double, but when they receive an input token, they should 058 check (using instanceof) whether the token is a SmoothToken, and if so, 059 access these derivatives using the {@link #derivativeValues()} method, 060 or extrapolate the value to a specified time using the {@link #extrapolate(Time)} 061 method. 062 <p> 063 Note that if two SmoothTokens are added or subtracted, then the derivatives also 064 add or subtract. If the times of the two tokens that are added or subtracted are 065 not the same, then the one with the lesser time is extrapolated to the larger time, 066 and the result will be the sum at the later time. 067 If a SmoothToken is added to a DoubleToken, the derivatives of the DoubleToken 068 are assumed to be zero, and similarly for subtraction. 069 <p> 070 If a SmoothToken is multiplied by a SmoothToken, then the product rule of 071 calculus is used to determine the derivatives of the product. 072 The product rule stipulates that 073 <pre> 074 (xy)' = x'y + xy' 075 </pre> 076 Again, if the times of the two tokens are not equal, then the one with the lesser 077 time will be extrapolated to the larger time before being multiplied, and the time 078 of the result will be the larger time. 079 If a SmoothToken is multiplied by a DoubleToken, then the derivatives 080 of the DoubleToken are assumed to be zero. 081 <p> 082 Division works similarly:</p> 083 <pre> 084 (x/y)' = x'/y + x(1/y)' = x'/y - xy'/y^2 085 </pre> 086 <p>where the last equality follows from the reciprocal rule of calculus. 087 The second derivative of a multiplication or division is obtained by 088 applying the above rules to x' and y' rather than to x and y. 089 Higher-order derivatives are similarly obtained. 090 </p><p> 091 You can construct an instance of this token in the Ptolemy expression 092 language using the <code>smoothToken(double, double, {double})</code> function. 093 The first argument specifies the value, and the second argument specifies 094 the time, and the third specifies the derivatives. Also provided in the 095 expression language are one and two argument versions of this function that 096 assume the time is zero. These should be used only during initialization, and 097 only if the start time of the model is actually zero. 098 </p><p> 099 By default, instances of SmoothToken have no more than three derivatives. 100 This can be changed using the {@link #setOrderLimit(int)} method. 101 </p><p> 102 FIXME: Division is not implemented yet. 103 </p> 104 105 @author Thierry S. Nouidui, Michael Wetter, Edward A. Lee 106 @version $Id$ 107 @since Ptolemy II 11.0 108 @Pt.ProposedRating Red (mw) 109 @Pt.AcceptedRating Red (mw) 110*/ 111public class SmoothToken extends DoubleToken { 112 113 /** Construct a SmoothToken with value 0.0 at time zero and no derivatives. 114 */ 115 public SmoothToken() { 116 this(0.0, Time.ZERO, null); 117 } 118 119 /** Construct a SmoothToken with the specified value at time zero and no derivatives. 120 * @param value The specified value. 121 */ 122 public SmoothToken(double value) { 123 this(value, Time.ZERO, null); 124 } 125 126 /** Construct a SmoothToken with the specified value at time zero 127 * and the specified derivatives. 128 * This constructor does not copy the derivatives argument, so it is up 129 * to the caller to ensure that the array passed in does not later get 130 * modified (tokens are required to be immutable). 131 * @param value The specified value. 132 * @param derivatives The specified derivatives. 133 */ 134 public SmoothToken(double value, double[] derivatives) { 135 this(value, Time.ZERO, derivatives); 136 } 137 138 /** Construct a SmoothToken with the specified value at the specified 139 * time, and with the specified derivatives. 140 * This constructor copies the derivatives argument, so 141 * the caller is free to modify it (tokens are required to be immutable). 142 * @param value The specified value. 143 * @param time The specified time. 144 * @param derivatives The specified derivatives. 145 */ 146 public SmoothToken(double value, Time time, double[] derivatives) { 147 super(value); 148 _time = time; 149 if (_time == null) { 150 _time = Time.ZERO; 151 } 152 if (derivatives != null) { 153 int length = derivatives.length; 154 if (length > _maxOrder) { 155 length = _maxOrder; 156 } 157 _derivatives = new double[length]; 158 System.arraycopy(derivatives, 0, _derivatives, 0, length); 159 } else { 160 _derivatives = null; 161 } 162 } 163 164 /** Construct a SmoothToken with the specified value and derivatives, given 165 * as a single array, at the specified time. 166 * This constructor copies the data from argument, so the caller is free 167 * to modify the array after this method returns. 168 * @param x An array where the first element is the value, and optionally any 169 * other elements can be present to specify the first, second, etc. derivatives. 170 * @param time The time at which this token is a sample. 171 */ 172 public SmoothToken(double[] x, Time time) { 173 this(x[0], time, null); 174 if (x.length > 1) { 175 final int nDer = (x.length > _maxOrder) ? _maxOrder 176 : (x.length - 1); 177 _derivatives = new double[nDer]; 178 System.arraycopy(x, 1, _derivatives, 0, nDer); 179 } 180 } 181 182 /** Construct a SmoothToken from the specified string, which specifies only 183 * a value. The resulting token will have no derivatives and will represent 184 * a sample at time zero. 185 * @param init The initialization string, which is in a format 186 * suitable for java.lang.Double.parseDouble(String). 187 * @exception IllegalActionException If the Token could not 188 * be created with the given String. 189 */ 190 public SmoothToken(String init) throws IllegalActionException { 191 if (init == null || init.equals("nil")) { 192 throw new IllegalActionException( 193 notSupportedNullNilStringMessage("SmoothToken", init)); 194 } 195 196 // It would be nice to call super(init) here, but we can't, so 197 // we copy the code from the parent. 198 199 // FIXME: Parsing the array of derivatives is not yet supported. 200 try { 201 _value = Double.parseDouble(init); 202 } catch (NumberFormatException e) { 203 throw new IllegalActionException(null, e, 204 "Failed to parse \"" + init + "\" as a number."); 205 } 206 _time = Time.ZERO; 207 } 208 209 /////////////////////////////////////////////////////////////////// 210 //// static initializer //// 211 212 static { 213 // Specify that the QSSToken class is an alternate implementation 214 // of the double type. This allows the expression language to 215 // recognize a return type of QSSToken from a static function 216 // registered in the previous call as a double. 217 218 // Commented out because it broke the build. 219 BaseType.addType(BaseType.DOUBLE, "smoothToken", SmoothToken.class); 220 } 221 222 /////////////////////////////////////////////////////////////////// 223 //// public methods //// 224 225 /** Given an array of Tokens and a time, align them by 226 * extrapolating all tokens that are instances of 227 * SmoothToken to that time, and returning 228 * an array of tokens with the extrapolated values and derivatives. 229 * If any of the tokens is not a SmoothToken, it is returned unmodified 230 * in the result. 231 * The returned array will have the same size as the argument array, and 232 * all the tokens will have the same maximum time. 233 * @param args The tokens to be aligned. 234 * @param time The Time to which the tokens will be aligned. 235 * @return An array of aligned tokens. 236 */ 237 static public Token[] align(Token[] args, Time time) { 238 Token[] result = new Token[args.length]; 239 for (int i = 0; i < args.length; i++) { 240 if (args[i] instanceof SmoothToken) { 241 result[i] = ((SmoothToken) args[i]).extrapolate(time); 242 } else { 243 result[i] = args[i]; 244 } 245 } 246 return result; 247 } 248 249 /** Given an array of Tokens, align them by finding the maximum time 250 * of all the tokens, extrapolating all tokens that are instances of 251 * SmoothToken to that time, and returning 252 * an array of tokens with the extrapolated values and derivatives. 253 * If any of the tokens is not a SmoothToken, it is returned unmodified 254 * in the result. 255 * The returned array will have the same size as the argument array, and 256 * all the tokens will have the same maximum time. 257 * @param args The tokens to be aligned. 258 * @return An array of aligned tokens. 259 */ 260 static public Token[] align(Token[] args) { 261 // First, find the maximum time. 262 Time latestTime = null; 263 for (int i = 0; i < args.length; i++) { 264 if (args[i] instanceof SmoothToken) { 265 SmoothToken smooth = (SmoothToken) args[i]; 266 if (latestTime == null 267 || latestTime.compareTo(smooth._time) < 0) { 268 latestTime = smooth._time; 269 } 270 } 271 } 272 // Align the tokens 273 Token[] result = align(args, latestTime); 274 return result; 275 } 276 277 /** Given two SmoothTokens, align them by finding the maximum time 278 * of the tokens, extrapolating the other token to that time, and returning 279 * an array of tokens with the extrapolated values and derivatives. 280 * The returned array will have size 2, and 281 * all the tokens will have the same maximum time. 282 * @param arg1 The first SmoothToken to be aligned. 283 * @param arg2 The second SmoothToken to be aligned. 284 * @return An array of tokens with the extrapolated values and 285 * derivatives. 286 */ 287 SmoothToken[] align(SmoothToken arg1, SmoothToken arg2) { 288 // First, find the maximum time. 289 Time latestTime = arg1._time; 290 if (latestTime.compareTo(arg2._time) < 0) { 291 latestTime = arg2._time; 292 } 293 SmoothToken[] result = new SmoothToken[2]; 294 result[0] = arg1.extrapolate(latestTime); 295 result[1] = arg2.extrapolate(latestTime); 296 return result; 297 } 298 299 /** Return the n-th derivative of the specified token. 300 * If no n-th derivative has been specified, return 0.0. 301 * If n is 0 or negative, just return the value. 302 * @param token The token. 303 * @param n The order of the desired derivative. 304 * @return The value of n-th derivatives of this token. 305 */ 306 public static final double derivativeValue(DoubleToken token, int n) { 307 if (n <= 0) { 308 return token._value; 309 } else if (!(token instanceof SmoothToken) 310 || ((SmoothToken) token)._derivatives == null 311 || n > ((SmoothToken) token)._derivatives.length) { 312 return 0.0; 313 } else { 314 return ((SmoothToken) token)._derivatives[n - 1]; 315 } 316 } 317 318 /** Return the derivatives of the token as a double[], or null if there are 319 * no derivatives. Since tokens are immutable, the caller of this method must 320 * copy the returned array if it intends to modify the array. 321 * @return The value of the derivatives contained in this token. 322 */ 323 public double[] derivativeValues() { 324 if (_derivatives == null || _derivatives.length == 0) { 325 return null; 326 } 327 return _derivatives; 328 } 329 330 /** Return true if the argument's class is SmoothToken and it has the 331 * same value and derivatives as this token. 332 * Note that this ignores the time of the tokens. 333 * This is needed to be able to use this in tests. 334 * @param object An object to compare for equality. 335 * @return True if the argument is a SmoothToken with the same 336 * value and derivatives. If either this object or the argument is a nil Token, return 337 * false. 338 */ 339 @Override 340 public boolean equals(Object object) { 341 // The superclass checks class equality, doubleValue equality, and handles nil. 342 if (super.equals(object)) { 343 // Check the times. 344 /* No, don't. See above. 345 if (!_time.equals(((SmoothToken)object)._time)) { 346 return false; 347 } 348 */ 349 // Now we just have to check the derivatives. 350 int order = maxOrder(this, (DoubleToken) object); 351 for (int i = 0; i < order; i++) { 352 double d1 = derivativeValue(this, i + 1); 353 double d2 = derivativeValue((DoubleToken) object, i + 1); 354 if (d1 != d2) { 355 return false; 356 } 357 } 358 // All derivatives are equal. 359 return true; 360 } else { 361 return false; 362 } 363 } 364 365 /** Return a SmoothToken at the specified time whose value and derivatives 366 * are the result of extrapolating this token to the specified time. 367 * @param time The time to which to extrapolate this token. 368 * @return A SmoothToken at the specified time. 369 */ 370 public SmoothToken extrapolate(Time time) { 371 // If the time matches, return this token. 372 if (_time == time || (_time != null && _time.equals(time))) { 373 return this; 374 } 375 // If _derivatives == null, simply return the current token. 376 if (_derivatives == null || _derivatives.length == 0) { 377 return new SmoothToken(_value, time, null); 378 } else if (_derivatives.length == 1) { 379 // A common case is QSS2, which has a value and a derivative. 380 // We handle this case special to stay computationally efficient. 381 final double dt = time.subtractToDouble(_time); 382 final double x = _value + dt * _derivatives[0]; 383 return new SmoothToken(x, time, _derivatives); 384 } else { 385 // This is the case for tokens with second or higher order derivatives. 386 // Build an array with value and derivatives 387 double[] coef = new double[_derivatives.length + 1]; 388 coef[0] = _value; 389 System.arraycopy(_derivatives, 0, coef, 1, _derivatives.length); 390 391 // Create vector with factorial coefficients times dt 392 // raised to the corresponding power. 393 double[] fact = new double[coef.length]; 394 fact[0] = 1; 395 final double dt = time.subtractToDouble(_time); 396 for (int i = 1; i < coef.length; i++) { 397 fact[i] = dt * fact[i - 1] / i; 398 } 399 400 // Advance time for all values in coef and store in new array res 401 double[] res = new double[coef.length]; 402 for (int i = 0; i < coef.length; i++) { 403 for (int j = 0; j < coef.length - i; j++) { 404 res[i] += coef[j + i] * fact[j]; 405 } 406 } 407 double[] der = new double[_derivatives.length]; 408 System.arraycopy(res, 1, der, 0, _derivatives.length); 409 410 return new SmoothToken(res[0], time, der); 411 } 412 } 413 414 /** Get the maximum order of any token (the number of derivatives). 415 * E.g., if maxOrder = 2, the token will have one value, the first 416 * and the 2nd derivative. 417 * By default, tokens will have maxOrder = 3. 418 * @return the maximum order. 419 * @see #setOrderLimit(int) 420 */ 421 public static int getOrderLimit() { 422 return _maxOrder; 423 } 424 425 /** Return the time for which the values of this smooth token are valid. 426 * @return The time of this token. 427 */ 428 public Time getTime() { 429 return _time; 430 } 431 432 /** Return the hash code for the SmoothToken object. If two SmoothToken 433 * objects have the same double value and their derivatives 434 * have the same hashCode, then the two SmoothTokens will have 435 * the same hashcode. 436 * @return The hash code for this SmoothToken object. 437 */ 438 @Override 439 public int hashCode() { 440 // See http://www.technofundo.com/tech/java/equalhash.html 441 int hashCode = super.hashCode(); 442 if (_derivatives != null) { 443 hashCode = 31 * hashCode + Arrays.hashCode(_derivatives); 444 } 445 return hashCode; 446 } 447 448 /** Return true if the token is nil, (aka null or missing). 449 * Nil or missing tokens occur when a data source is sparsely populated. 450 * @return True if the token is the {@link #NIL} token. 451 */ 452 @Override 453 public boolean isNil() { 454 // We use a method here so that we can easily change how 455 // we determine if a token is nil without modify lots of classes. 456 return this == SmoothToken.NIL; 457 } 458 459 /** Return the maximum number of specified derivatives for the two tokens. 460 * @param arg1 The first token. 461 * @param arg2 The second token. 462 * @return The maximum of the number of derivatives specified. 463 */ 464 public static final int maxOrder(DoubleToken arg1, DoubleToken arg2) { 465 return Math.max(order(arg1), order(arg2)); 466 } 467 468 /** Return a new token that is the negative of this one. 469 * @return The negative, where all the derivatives are also negated. 470 */ 471 public SmoothToken negate() { 472 if (_derivatives == null || _derivatives.length == 0) { 473 return new SmoothToken(-_value, _time, null); 474 } 475 double[] derivatives = new double[_derivatives.length]; 476 for (int i = 0; i < _derivatives.length; i++) { 477 derivatives[i] = -_derivatives[i]; 478 } 479 return new SmoothToken(-_value, _time, derivatives); 480 } 481 482 /** Return the number of specified derivatives for the token. 483 * @param token The token. 484 * @return The number of derivatives specified. 485 */ 486 public static final int order(DoubleToken token) { 487 if (!(token instanceof SmoothToken) 488 || ((SmoothToken) token)._derivatives == null) { 489 return 0; 490 } else { 491 return ((SmoothToken) token)._derivatives.length; 492 } 493 } 494 495 /** Set the maximum order of any token (the number of derivatives). 496 * This is static, so calling it will affect <i>all</i> 497 * instances of SmoothToken in the same JVM. 498 * Its effect is not even limited to a single Ptolemy model. 499 * E.g., if maxOrder = 2, the token will have one value, the first 500 * and the 2nd derivative. 501 * By default, tokens will have maxOrder = 3. 502 * The maxOrder must be non-negative. 503 * @param maxOrder The maximum order of the token. 504 * @see #getOrderLimit() 505 */ 506 public static void setOrderLimit(int maxOrder) { 507 if (maxOrder < 0) { 508 throw new IllegalArgumentException( 509 "maxOrder must be non-negative, not " + maxOrder + "."); 510 } 511 _maxOrder = maxOrder; 512 } 513 514 /** Return a SmoothToken with the specified value at time zero and no derivatives. 515 * This function gets registered by PtParser, after which it becomes 516 * available in the expression language. 517 * Note that there is no way in the expression language to construct a 518 * SmoothToken with a time other than zero. This makes sense because usually 519 * expressions are evaluated only once when a model is opened. 520 * @param value The value. 521 * @return The SmoothToken with the specified value at time zero 522 * and no derivatives. 523 */ 524 public static SmoothToken smoothToken(double value) { 525 return new SmoothToken(value, Time.ZERO, null); 526 } 527 528 /** Return a SmoothToken with the specified value at time zero and derivatives. 529 * This function gets registered by PtParser, after which it becomes 530 * available in the expression language. 531 * Note that there is no way in the expression language to construct a 532 * SmoothToken with a time other than zero. This makes sense because usually 533 * expressions are evaluated only once when a model is opened. 534 * @param value The value. 535 * @param derivatives An array containing the first derivative, 536 * the second derivative, etc. 537 * @return The SmoothToken with the specified value at time zero 538 * and derivatives. 539 */ 540 public static SmoothToken smoothToken(double value, double[] derivatives) { 541 return new SmoothToken(value, Time.ZERO, derivatives); 542 } 543 544 /** Return the value of this token as a string that can be parsed 545 * by the expression language to recover a token with the same value and derivatives. 546 * However, the parsed token will not have the same time. It will have time zero. 547 * If there are no derivatives, then this just returns what the superclass 548 * returns to represent a double. Otherwise, the returned 549 * string has the form "smoothToken(value, derivatives)", where 550 * the value is the value returned by {@link #doubleValue()}, and 551 * derivatives is an array of doubles. 552 */ 553 @Override 554 public String toString() { 555 if (_derivatives == null || _derivatives.length == 0) { 556 return super.toString(); 557 } 558 StringBuffer derivatives = new StringBuffer("{"); 559 boolean first = true; 560 for (int i = 0; i < Math.min(_derivatives.length, _maxOrder); i++) { 561 if (first) { 562 first = false; 563 } else { 564 derivatives.append(","); 565 } 566 derivatives.append(Double.toString(_derivatives[i])); 567 } 568 derivatives.append("}"); 569 return "smoothToken(" + super.toString() + ", " + derivatives.toString() 570 + ")"; 571 } 572 573 /////////////////////////////////////////////////////////////////// 574 //// public variables //// 575 576 /** A token that represents a missing value. 577 * Null or missing tokens are common in analytical systems 578 * like R and SAS where they are used to handle sparsely populated data 579 * sources. In database parlance, missing tokens are sometimes called 580 * null tokens. Since null is a Java keyword, we use the term "nil". 581 * The toString() method on a nil token returns the string "nil". 582 */ 583 public static final SmoothToken NIL = new SmoothToken(Double.NaN); 584 585 /////////////////////////////////////////////////////////////////// 586 //// protected methods //// 587 588 /** Return a new token whose value is the value of the 589 * argument Token added to the value of this Token. 590 * The argument is guaranteed to be either a DoubleToken or 591 * a SmoothToken by the caller. If the argument is a DoubleToken, 592 * then its value is simply added to the value of this token, and 593 * a new SmoothToken is returned with the sum value, time, and the derivatives 594 * of this token. If the argument is a SmoothToken, then this token and 595 * the argument are first aligned using 596 * {@link ptolemy.data.SmoothToken#align(SmoothToken, SmoothToken)}, 597 * and then added. The returned SmoothToken 598 * will have the maximum of the number of derivatives of this token and 599 * the derivatives of the argument, and for derivatives given by both 600 * tokens, the derivative will be the sum of the two derivatives. 601 * The time of the returned token will be the maximum of the time of this 602 * token and the argument. 603 * @param rightArgument The token to add to this token. 604 * @return A new SmoothToken containing the result. 605 */ 606 @Override 607 protected ScalarToken _add(ScalarToken rightArgument) { 608 if (rightArgument instanceof SmoothToken) { 609 // First align the tokens. 610 SmoothToken[] aligned = align(this, (SmoothToken) rightArgument); 611 612 // Compute the sum of the values. 613 final double sum = aligned[0].doubleValue() 614 + aligned[1].doubleValue(); 615 616 // Compute the derivatives of the result. 617 double[] derivatives = aligned[1].derivativeValues(); 618 if (derivatives == null) { 619 // Just use the derivatives of this token. 620 // This should be safe because, by policy, their value is immutable. 621 return new SmoothToken(sum, aligned[0]._time, 622 aligned[0]._derivatives); 623 } else if (aligned[0]._derivatives == null) { 624 // Just use the derivatives of the second token. 625 // This should be safe because, by policy, their value is immutable. 626 return new SmoothToken(sum, aligned[0]._time, derivatives); 627 } 628 // Both tokens have derivatives. 629 // Create a sum of derivatives. 630 int max = derivatives.length; 631 if (max < aligned[0]._derivatives.length) { 632 max = aligned[0]._derivatives.length; 633 } 634 double[] result = new double[max]; 635 for (int i = 0; i < max; i++) { 636 if (i < aligned[0]._derivatives.length 637 && i < derivatives.length) { 638 result[i] = aligned[0]._derivatives[i] + derivatives[i]; 639 } else if (i < aligned[0]._derivatives.length) { 640 result[i] = aligned[0]._derivatives[i]; 641 } else { 642 result[i] = derivatives[i]; 643 } 644 } 645 return new SmoothToken(sum, aligned[0]._time, result); 646 } else { 647 final double sum = super.doubleValue() 648 + ((DoubleToken) rightArgument).doubleValue(); 649 // Just use the derivatives of this token. 650 // This should be safe because, by policy, their value is immutable. 651 return new SmoothToken(sum, _time, _derivatives); 652 } 653 } 654 655 /** Return a new token whose value is the value of this token 656 * divided by the value of the argument token. It is assumed that 657 * the type of the argument is a SmoothToken 658 * @param divisor The token to divide this token by. 659 * @return A new SmoothToken containing the result. 660 */ 661 @Override 662 protected ScalarToken _divide(ScalarToken divisor) { 663 if (divisor instanceof SmoothToken) { 664 // First align the tokens. 665 SmoothToken[] aligned = align(this, (SmoothToken) divisor); 666 double x = aligned[0].doubleValue(); 667 double y = aligned[1].doubleValue(); 668 double quotient = x / y; 669 670 // FIXME: Need to implement the rule in the class comment. 671 // FIXME: Should use: (x/y)' = x'/y + x(1/y)' = x'/y - xy'/y^2 672 673 if (_derivatives == null || _derivatives.length == 0) { 674 return new DoubleToken(quotient); 675 } else { 676 double[] der = new double[_derivatives.length]; 677 for (int i = 0; i < _derivatives.length; i++) { 678 der[i] = _derivatives[i] / y; 679 } 680 return new SmoothToken(quotient, aligned[0]._time, der); 681 } 682 } else { 683 if (_derivatives == null || _derivatives.length == 0) { 684 return super._divide(divisor); 685 } else { 686 final double div = ((DoubleToken) divisor).doubleValue(); 687 final double quotient = super.doubleValue() / div; 688 double[] der = new double[_derivatives.length]; 689 for (int i = 0; i < _derivatives.length; i++) { 690 der[i] = _derivatives[i] / div; 691 } 692 return new SmoothToken(quotient, _time, der); 693 } 694 } 695 } 696 697 /** Return true if the two arguments are within epsilon of one another. 698 * @param arg1 First argument. 699 * @param arg2 Second argument. 700 * @param epsilon The maximum difference. 701 * @return true if the two arguments are within epsilon of one another. 702 */ 703 protected final boolean _isClose(double arg1, double arg2, double epsilon) { 704 if (arg1 > arg2 + epsilon || arg1 < arg2 - epsilon) { 705 return false; 706 } else { 707 return true; 708 } 709 } 710 711 /** Test that the value of this token is close to the first 712 * argument, where "close" means that the distance between their 713 * values is less than or equal to the second argument. It is 714 * assumed that the type of the first argument is DoubleToken. 715 * Here, "close" also means that the derivatives have to be close. 716 * @param rightArgument The token to compare to this token. 717 * @param epsilon The distance. 718 * @return A token containing tue if the value of this token is close 719 * to that of the argument. 720 */ 721 @Override 722 protected BooleanToken _isCloseTo(ScalarToken rightArgument, 723 double epsilon) { 724 BooleanToken result = super._isCloseTo(rightArgument, epsilon); 725 if (!result.booleanValue()) { 726 // Value is not close. 727 return result; 728 } 729 // Now we just have to check the derivatives. 730 int order = maxOrder(this, (DoubleToken) rightArgument); 731 for (int i = 0; i < order; i++) { 732 double d1 = derivativeValue(this, i + 1); 733 double d2 = derivativeValue((DoubleToken) rightArgument, i + 1); 734 if (!_isClose(d1, d2, epsilon)) { 735 return BooleanToken.FALSE; 736 } 737 } 738 // All derivatives are close. 739 return BooleanToken.TRUE; 740 } 741 742 /** Test for ordering of the values of this Token and the argument 743 * Token. It is assumed that the type of the argument is SmoothToken. 744 * @param rightArgument The token to compare to this token. 745 * @exception IllegalActionException If this method is not 746 * supported by the derived class. 747 * @return A new Token containing the result. 748 */ 749 @Override 750 protected BooleanToken _isLessThan(ScalarToken rightArgument) 751 throws IllegalActionException { 752 if (rightArgument instanceof DoubleToken) { 753 return super._isLessThan(rightArgument); 754 } else { 755 SmoothToken convertedArgument = (SmoothToken) rightArgument; 756 return BooleanToken 757 .getInstance(_value < convertedArgument.doubleValue()); 758 } 759 } 760 761 /** Return a new token whose value is the value of this token 762 * multiplied by the value of the argument token. The derivatives 763 * of the result are calculated using the product rule. 764 * The argument is assumed to be a DoubleToken. 765 * It may also be a SmoothToken. 766 * @param rightArgument The token to multiply this token by. 767 * @return A new SmoothToken containing the result. 768 */ 769 @Override 770 protected ScalarToken _multiply(ScalarToken rightArgument) { 771 if (rightArgument instanceof SmoothToken) { 772 // First align the tokens. 773 SmoothToken[] aligned = align(this, (SmoothToken) rightArgument); 774 775 double x = aligned[0].doubleValue(); 776 double y = aligned[1].doubleValue(); 777 double product = x * y; 778 double[] derivatives = aligned[1].derivativeValues(); 779 780 // Check whether one or both tokens lack derivatives. 781 if (aligned[0]._derivatives == null 782 || aligned[0]._derivatives.length == 0) { 783 // x lacks derivatives. 784 if (derivatives == null || derivatives.length == 0) { 785 // Both lack derivatives. 786 return new SmoothToken(product, aligned[0]._time, null); 787 } 788 // Only x lacks derivatives. Hence, x should scale y's derivatives. 789 double[] result = new double[derivatives.length]; 790 for (int i = 0; i < derivatives.length; i++) { 791 result[i] = derivatives[i] * x; 792 } 793 return new SmoothToken(product, aligned[0]._time, result); 794 } 795 // y derivatives may be null. In this case, y should scale x's derivatives. 796 if (derivatives == null) { 797 double[] result = new double[aligned[0]._derivatives.length]; 798 for (int i = 0; i < aligned[0]._derivatives.length; i++) { 799 result[i] = aligned[0]._derivatives[i] * y; 800 } 801 return new SmoothToken(product, aligned[0]._time, result); 802 } else { 803 // Both have derivatives. 804 // Multiply the tokens as if they were Taylor polynomials. 805 806 // Build arrays whose elements are the coefficients of the polynomials. 807 double[] p1 = new double[aligned[0]._derivatives.length + 1]; 808 double[] p2 = new double[derivatives.length + 1]; 809 p1[0] = x; 810 p2[0] = y; 811 // FIXME: Consider avoiding this copy by changing the internal representation 812 // so that the the value and derivatives are in one array. 813 // There are a few other places in the code that will be helped. 814 System.arraycopy(_derivatives, 0, p1, 1, _derivatives.length); 815 System.arraycopy(derivatives, 0, p2, 1, derivatives.length); 816 // Multiply the polynomials 817 double[] pro = _multiplyPolynomials(p1, p2); 818 double[] derRes = new double[pro.length - 1]; 819 System.arraycopy(pro, 1, derRes, 0, derRes.length); 820 return new SmoothToken(pro[0], aligned[0]._time, derRes); 821 } 822 } else { 823 // Assume the rightArgument derivatives are zero, so the returned result just 824 // has the derivatives of this token scaled by y. 825 double y = ((DoubleToken) rightArgument).doubleValue(); 826 double product = doubleValue() * y; 827 828 if (_derivatives == null || _derivatives.length == 0) { 829 return new SmoothToken(product, _time, null); 830 } 831 double[] result = new double[_derivatives.length]; 832 for (int i = 0; i < _derivatives.length; i++) { 833 result[i] = _derivatives[i] * y; 834 } 835 return new SmoothToken(product, _time, result); 836 } 837 } 838 839 /** Multiply two polynomials. 840 * @param p1 First polynomial. 841 * @param p2 Second polynomial 842 * @return The product of the polynomials 843 */ 844 protected static double[] _multiplyPolynomials(final double[] p1, 845 final double[] p2) { 846 847 double[] res = new double[(p1.length - 1) + (p2.length - 1) + 1]; 848 // Set all coefficients to zero. 849 for (int i = 0; i < res.length; i++) { 850 res[i] = 0; 851 } 852 // Multiply the polynomials 853 for (int i1 = 0; i1 < p1.length; i1++) { 854 for (int i2 = 0; i2 < p2.length; i2++) { 855 final int exponent = i1 + i2; 856 if (res[exponent] == 0) { 857 res[exponent] = p1[i1] * p2[i2]; 858 } else { 859 res[exponent] += p1[i1] * p2[i2]; 860 } 861 } 862 } 863 return res; 864 } 865 866 /** Return a new token whose value is the value of the argument token 867 * subtracted from the value of this token. It is assumed that 868 * the type of the argument is a DoubleToken. 869 * @param rightArgument The token to subtract from this token. 870 * @return A new SmoothToken containing the result. 871 */ 872 @Override 873 protected ScalarToken _subtract(ScalarToken rightArgument) { 874 if (rightArgument instanceof SmoothToken) { 875 // First align the tokens. 876 SmoothToken[] aligned = align(this, (SmoothToken) rightArgument); 877 double x = aligned[0].doubleValue(); 878 double y = aligned[1].doubleValue(); 879 final double difference = x - y; 880 881 double[] xderivatives = aligned[0].derivativeValues(); 882 double[] yderivatives = aligned[1].derivativeValues(); 883 884 if (yderivatives == null) { 885 // Just use the xderivatives. 886 // This should be safe because, by policy, their value is immutable. 887 return new SmoothToken(difference, aligned[0]._time, 888 xderivatives); 889 } else if (xderivatives == null) { 890 // The derivatives should be negated. 891 double[] result = new double[yderivatives.length]; 892 for (int i = 0; i < result.length; i++) { 893 result[i] = -yderivatives[i]; 894 } 895 return new SmoothToken(difference, aligned[0]._time, result); 896 } 897 // Create a difference of derivatives. 898 int max = yderivatives.length; 899 if (max < xderivatives.length) { 900 max = xderivatives.length; 901 } 902 double[] result = new double[max]; 903 for (int i = 0; i < max; i++) { 904 if (i < xderivatives.length && i < yderivatives.length) { 905 result[i] = xderivatives[i] - yderivatives[i]; 906 } else if (i < xderivatives.length) { 907 result[i] = xderivatives[i]; 908 } else { 909 result[i] = -yderivatives[i]; 910 } 911 } 912 return new SmoothToken(difference, aligned[0]._time, result); 913 } else { 914 final double difference = super.doubleValue() 915 - ((DoubleToken) rightArgument).doubleValue(); 916 // Just use the derivatives of this token. 917 // This should be safe because, by policy, their value is immutable. 918 return new SmoothToken(difference, _time, _derivatives); 919 } 920 } 921 922 /////////////////////////////////////////////////////////////////// 923 //// protected variables //// 924 /* Maximum order of the token. 925 * 926 * A token with _maxOrder=3 will have a value and three derivatives. 927 */ 928 static int _maxOrder = 3; 929 930 /////////////////////////////////////////////////////////////////// 931 //// private variables //// 932 933 /** The derivatives. */ 934 private double[] _derivatives; 935 936 /** The time at which this token is a sample of a function. */ 937 private Time _time; 938}