001/* A Poisson process clock source. 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.Director; 031import ptolemy.actor.SuperdenseTimeDirector; 032import ptolemy.actor.TimedActor; 033import ptolemy.actor.util.Time; 034import ptolemy.data.ArrayToken; 035import ptolemy.data.BooleanToken; 036import ptolemy.data.DoubleToken; 037import ptolemy.data.Token; 038import ptolemy.data.expr.Parameter; 039import ptolemy.data.type.ArrayType; 040import ptolemy.data.type.BaseType; 041import ptolemy.kernel.CompositeEntity; 042import ptolemy.kernel.util.Attribute; 043import ptolemy.kernel.util.IllegalActionException; 044import ptolemy.kernel.util.InternalErrorException; 045import ptolemy.kernel.util.NameDuplicationException; 046import ptolemy.kernel.util.Workspace; 047 048/////////////////////////////////////////////////////////////////// 049//// PoissonClock 050 051/** 052 This actor produces discrete events according to a Poisson process. 053 The time between events is given by independent and identically 054 distributed exponential random variables. The values produced 055 rotate sequentially through those given in the <i>values</i> parameter, 056 which is an array of anything and defaults to {1, 0}. 057 The type of the output can be any token type. This type is inferred from 058 the element type of the <i>values</i> parameter. 059 The mean time between events is given by the <i>meanTime</i> parameter, 060 which defaults to 1.0. 061 <p> 062 In the initialize() method and postfire() methods, 063 the actor uses the fireAt() method of the director to request 064 the next firing. The first firing is always at the start time, unless 065 the parameter <i>fireAtStart</i> is changed to <i>false</i>. 066 <p> 067 If the trigger input is connected, then any event on it will 068 cause the Poisson process to immediately produce the next 069 event, as if the time for that event had arrived. 070 <p> 071 If this actor is inactive at the time at which it would have 072 otherwise produced an output, then it will stop producing outputs. 073 This should not happen. 074 075 @author Edward A. Lee 076 @version $Id$ 077 @since Ptolemy II 1.0 078 @Pt.ProposedRating Yellow (eal) 079 @Pt.AcceptedRating Yellow (yuhong) 080 */ 081public class PoissonClock extends RandomSource implements TimedActor { 082 /** Construct an actor with the specified container and name. 083 * @param container The container. 084 * @param name The name of this actor. 085 * @exception IllegalActionException If the entity cannot be contained 086 * by the proposed container. 087 * @exception NameDuplicationException If the container already has an 088 * actor with this name. 089 */ 090 public PoissonClock(CompositeEntity container, String name) 091 throws NameDuplicationException, IllegalActionException { 092 super(container, name); 093 094 meanTime = new Parameter(this, "meanTime"); 095 meanTime.setExpression("1.0"); 096 meanTime.setTypeEquals(BaseType.DOUBLE); 097 098 // Set the values parameter 099 values = new Parameter(this, "values"); 100 values.setExpression("{1, 0}"); 101 output.setTypeAtLeast(ArrayType.elementType(values)); 102 103 // Call this so that we don't have to copy its code here... 104 attributeChanged(values); 105 106 fireAtStart = new Parameter(this, "fireAtStart"); 107 fireAtStart.setExpression("true"); 108 fireAtStart.setTypeEquals(BaseType.BOOLEAN); 109 110 // Note that this class copies much of TimedSource into here 111 // because it can't subclass both TimedSource and RandomSource. 112 stopTime = new Parameter(this, "stopTime"); 113 stopTime.setExpression("Infinity"); 114 stopTime.setTypeEquals(BaseType.DOUBLE); 115 116 _attachText("_iconDescription", "<svg>\n" + "<rect x=\"-20\" y=\"-20\" " 117 + "width=\"40\" height=\"40\" " + "style=\"fill:lightGrey\"/>\n" 118 + "<circle cx=\"0\" cy=\"0\" r=\"17\"" 119 + "style=\"fill:white\"/>\n" 120 + "<line x1=\"0\" y1=\"-15\" x2=\"0\" y2=\"-13\"/>\n" 121 + "<line x1=\"0\" y1=\"14\" x2=\"0\" y2=\"16\"/>\n" 122 + "<line x1=\"-15\" y1=\"0\" x2=\"-13\" y2=\"0\"/>\n" 123 + "<line x1=\"14\" y1=\"0\" x2=\"16\" y2=\"0\"/>\n" 124 + "<line x1=\"0\" y1=\"-8\" x2=\"0\" y2=\"0\"/>\n" 125 + "<line x1=\"0\" y1=\"0\" x2=\"11.26\" y2=\"-6.5\"/>\n" 126 + "</svg>\n"); 127 } 128 129 /////////////////////////////////////////////////////////////////// 130 //// ports and parameters //// 131 132 /** If true, then this actor will request a firing at the start time. 133 * Otherwise, the first firing will be requested at the first random 134 * time. This is a boolean-valued parameter that defaults to <i>true</i>. 135 */ 136 public Parameter fireAtStart; 137 138 /** The mean time between events, where the output value transitions. 139 * This parameter must contain a DoubleToken. 140 */ 141 public Parameter meanTime; 142 143 /** The time at which postfire() should return false. This is a 144 * double that defaults to Infinity, which means that postfire() 145 * never returns false (or at least, doesn't do so due to stopTime 146 * having been exceeded). 147 */ 148 public Parameter stopTime; 149 150 /** The values that will be produced at the output. 151 * This parameter can contain any ArrayToken, and it defaults to {1, 0}. 152 */ 153 public Parameter values; 154 155 /////////////////////////////////////////////////////////////////// 156 //// public methods //// 157 158 /** If the argument is the meanTime parameter, check that it is 159 * positive. 160 * @param attribute The attribute that changed. 161 * @exception IllegalActionException If the meanTime value is 162 * not positive. 163 */ 164 @Override 165 public void attributeChanged(Attribute attribute) 166 throws IllegalActionException { 167 if (attribute == meanTime) { 168 double mean = ((DoubleToken) meanTime.getToken()).doubleValue(); 169 170 if (mean <= 0.0) { 171 throw new IllegalActionException(this, 172 "meanTime is required to be positive. meanTime given: " 173 + mean); 174 } 175 } else if (attribute == values) { 176 ArrayToken val = (ArrayToken) values.getToken(); 177 _length = val.length(); 178 } else if (attribute == stopTime) { 179 double newStopTimeValue = ((DoubleToken) stopTime.getToken()) 180 .doubleValue(); 181 182 if (_executing) { 183 Time newStopTime = new Time(getDirector(), newStopTimeValue); 184 Director director = getDirector(); 185 186 if (director != null) { 187 Time currentTime = director.getModelTime(); 188 189 if (newStopTime.compareTo(currentTime) > 0) { 190 // NOTE: Do not throw an exception if the director ignores this 191 // stop time request or returns some other value of time. 192 // postfire() will return false on the next firing after time 193 // equals the stop time or exceeds it. 194 director.fireAt(this, newStopTime); 195 } else { 196 throw new IllegalActionException(this, "The stop time " 197 + "is earlier than the current time."); 198 } 199 } 200 201 _stopTime = newStopTime; 202 } 203 } else { 204 super.attributeChanged(attribute); 205 } 206 } 207 208 /** Clone the actor into the specified workspace. This calls the 209 * base class and then sets the parameter public members to refer 210 * to the parameters of the new actor. 211 * @param workspace The workspace for the new object. 212 * @return A new actor. 213 * @exception CloneNotSupportedException If a derived class contains 214 * an attribute that cannot be cloned. 215 */ 216 @Override 217 public Object clone(Workspace workspace) throws CloneNotSupportedException { 218 PoissonClock newObject = (PoissonClock) super.clone(workspace); 219 try { 220 newObject.output 221 .setTypeAtLeast(ArrayType.elementType(newObject.values)); 222 } catch (IllegalActionException e) { 223 throw new InternalErrorException(e); 224 } 225 return newObject; 226 } 227 228 /** Output the current value. 229 * @exception IllegalActionException If there is no director. 230 */ 231 @Override 232 public void fire() throws IllegalActionException { 233 // If there is a trigger input, then it is time for an output. 234 boolean triggerInputPresent = false; 235 for (int i = 0; i < trigger.getWidth(); i++) { 236 if (trigger.isKnown() && trigger.hasToken(i)) { 237 triggerInputPresent = true; 238 } 239 } 240 241 Director director = getDirector(); 242 Time currentTime = director.getModelTime(); 243 // It is time to produce an output if the current time equals 244 // or exceeds the next firing time (it should never exceed). 245 boolean timeForOutput = currentTime.compareTo(_nextFiringTime) >= 0; 246 247 if (!timeForOutput && !triggerInputPresent) { 248 // It is too early. 249 return; 250 } 251 if (director instanceof SuperdenseTimeDirector) { 252 int currentMicrostep = ((SuperdenseTimeDirector) director) 253 .getIndex(); 254 if (currentMicrostep < 1 && !triggerInputPresent) { 255 // The time matches, but the microstep is too early. 256 return; 257 } 258 } 259 // If this is the first call to fire() in an iteration, 260 // the the superclass will generate a new random number 261 // to be used in postfire(). 262 super.fire(); 263 output.send(0, _getValue(_nextOutputIndex)); 264 _outputProduced = true; 265 } 266 267 /** Get the stop time. 268 * @return The stop time. 269 * @deprecated As of Ptolemy II 4.1, replaced by 270 * {@link ptolemy.actor.lib.PoissonClock#getModelStopTime} 271 */ 272 @Deprecated 273 public double getStopTime() { 274 return getModelStopTime().getDoubleValue(); 275 } 276 277 /** Get the stop time. 278 * @return The stop time. 279 */ 280 public Time getModelStopTime() { 281 return _stopTime; 282 } 283 284 /** Request the first firing either at the start time 285 * or at a random time, depending on <i>fireAtStart</i>. 286 * @exception IllegalActionException If the fireAt() method of the 287 * director throws it, or if the director does not 288 * agree to fire the actor at the specified time. 289 */ 290 @Override 291 public void initialize() throws IllegalActionException { 292 // The superclass will ensure that the next call to fire() generates 293 // a new random number. That random number will be first used 294 // in the first postfire() call. 295 super.initialize(); 296 Director director = getDirector(); 297 if (director == null) { 298 throw new IllegalActionException(this, "No director!"); 299 } 300 301 double stopTimeValue = ((DoubleToken) stopTime.getToken()) 302 .doubleValue(); 303 _stopTime = new Time(getDirector(), stopTimeValue); 304 305 Time currentTime = director.getModelTime(); 306 307 if (!_stopTime.isInfinite() && _stopTime.compareTo(currentTime) > 0) { 308 // NOTE: Do not throw an exception if the director ignores this 309 // stop time request or returns some other value of time. 310 // postfire() will return false on the next firing after time 311 // equals the stop time or exceeds it. 312 director.fireAt(this, _stopTime); 313 _executing = true; 314 } 315 316 _nextOutputIndex = 0; 317 _nextFiringTime = currentTime; 318 _outputProduced = false; 319 320 if (((BooleanToken) fireAtStart.getToken()).booleanValue()) { 321 _fireAt(currentTime); 322 } else { 323 // Have to explicitly generate the first random number 324 // becuase the superclass doesn't do it until the first 325 // call to fire(). 326 _generateRandomNumber(); 327 _nextFiringTime = director.getModelTime().add(_current); 328 _fireAt(_nextFiringTime); 329 } 330 } 331 332 /** Generate an exponential random number and schedule the next firing. 333 * @exception IllegalActionException If the director throws it when 334 * scheduling the next firing, or if the director does not 335 * agree to fire the actor at the specified time. 336 */ 337 @Override 338 public boolean postfire() throws IllegalActionException { 339 boolean result = super.postfire(); 340 Time currentTime = getDirector().getModelTime(); 341 if (_outputProduced) { 342 // An output was produced in this iteration. 343 _outputProduced = false; 344 _nextOutputIndex++; 345 if (_nextOutputIndex >= _length) { 346 _nextOutputIndex = 0; 347 } 348 // The following is not needed because the first call 349 // to fire() in the superclass generated a new random 350 // number. 351 // _generateRandomNumber(); 352 _nextFiringTime = currentTime.add(_current); 353 _fireAt(_nextFiringTime); 354 } else if (currentTime.compareTo(_nextFiringTime) >= 0) { 355 // Output was not produced, but time matches, which 356 // means the microstep was too early. Request a refiring. 357 _fireAt(currentTime); 358 } 359 360 if (currentTime.compareTo(_stopTime) >= 0) { 361 return false; 362 } 363 return result; 364 } 365 366 /** If the current time matches the expected time for the next 367 * output, then return true. Also return true if the 368 * trigger input is connected and has events. 369 * Otherwise, return false. 370 * @exception IllegalActionException If there is no director. 371 */ 372 @Override 373 public boolean prefire() throws IllegalActionException { 374 // Do not call super.prefire() because that returns false if 375 // there are no trigger inputs. 376 if (_debugging) { 377 _debug("Called prefire()"); 378 } 379 380 // If any trigger input has a token, then return true. 381 // NOTE: It might seem that using trigger.numberOfSources() is 382 // correct here, but it is not. It is possible for channels 383 // to be connected, for example, to other output ports or 384 // even back to this same trigger port, in which case higher 385 // numbered channels will not have their inputs read. 386 for (int i = 0; i < trigger.getWidth(); i++) { 387 if (trigger.isKnown() && trigger.hasToken(i)) { 388 // No need to continue. fire() will 389 // consume the tokens (in the superclass). 390 return true; 391 } 392 } 393 394 // Also return true if there are no trigger inputs 395 // but it is time for the next output. 396 Time currentTime = getDirector().getModelTime(); 397 if (currentTime.compareTo(_nextFiringTime) == 0) { 398 return true; 399 } 400 401 // Otherwise, return false. 402 return false; 403 } 404 405 /** Override the base class to reset a flag that indicates that the 406 * model is executing. This method is invoked exactly once per execution 407 * of an application. None of the other action methods should be 408 * be invoked after it. 409 * @exception IllegalActionException Not thrown in this base class. 410 */ 411 @Override 412 public void wrapup() throws IllegalActionException { 413 super.wrapup(); 414 _executing = false; 415 } 416 417 /////////////////////////////////////////////////////////////////// 418 //// protected methods //// 419 420 /** Generate a new random number. 421 * @exception IllegalActionException If parameter values are incorrect. 422 */ 423 @Override 424 protected void _generateRandomNumber() throws IllegalActionException { 425 double meanTimeValue = ((DoubleToken) meanTime.getToken()) 426 .doubleValue(); 427 double test = _random.nextDouble(); 428 _current = -Math.log(1 - test) * meanTimeValue; 429 } 430 431 /////////////////////////////////////////////////////////////////// 432 //// private methods //// 433 434 /* Get the specified value, checking the form of the values parameter. 435 */ 436 private Token _getValue(int index) throws IllegalActionException { 437 ArrayToken val = (ArrayToken) values.getToken(); 438 439 if (val == null || index >= _length) { 440 throw new IllegalActionException(this, 441 "Index out of range of the values parameter."); 442 } 443 444 return val.getElement(index); 445 } 446 447 /////////////////////////////////////////////////////////////////// 448 //// private variables //// 449 450 // Most recently generated exponential random number. 451 private double _current; 452 453 // Flag indicating that the model is running. 454 private boolean _executing = false; 455 456 // The following are all transient to silence a javadoc bug 457 // about the @serialize tag. 458 // The transient qualifier should probably be removed if this 459 // class is made serializable. 460 // The length of the values parameter vector. 461 private transient int _length; 462 463 // The next firing time requested of the director. 464 private transient Time _nextFiringTime; 465 466 // The index of the next output. 467 private transient int _nextOutputIndex; 468 469 // Indicator that an output was produced in this iteration. 470 private boolean _outputProduced; 471 472 // stop time. 473 private Time _stopTime; 474}