001/* A base class for random sources. 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 java.util.Random; 031 032import ptolemy.actor.parameters.SharedParameter; 033import ptolemy.data.BooleanToken; 034import ptolemy.data.LongToken; 035import ptolemy.data.Token; 036import ptolemy.data.expr.Parameter; 037import ptolemy.data.expr.SingletonParameter; 038import ptolemy.data.type.BaseType; 039import ptolemy.kernel.CompositeEntity; 040import ptolemy.kernel.util.Attribute; 041import ptolemy.kernel.util.IllegalActionException; 042import ptolemy.kernel.util.NameDuplicationException; 043import ptolemy.kernel.util.Workspace; 044 045/////////////////////////////////////////////////////////////////// 046//// RandomSource 047 048/** 049 A base class for sources of random numbers. 050 It uses the class java.util.Random. 051 This base class manages the seed. Specifically, 052 the seed is a shared parameter, so setting the seed 053 in any one instance of a RandomSource results in setting 054 the seed in all instances. If the seed is set to value 0L, 055 the default, then this is interpreted as not specifying a seed, 056 and the random number generators are set to use a seed that 057 depends on the current time in milliseconds. If the seed 058 is set to any value other than 0L, then a seed is computed 059 for the random number generator by adding that specified seed 060 to the hashcode for the full name of the actor. This ensures 061 that with high probability, multiple instances of RandomSource 062 (and derived classes) will have distinct seeds, but that the 063 executions are deterministically repeatable. 064<p> 065 If the <i>resetOnEachRun</i> parameter is true (it is 066 false by default), then each run resets the random number 067 generator. If the seed is non-zero, then this makes 068 each run identical. This is useful for constructing 069 tests. If the seed is zero, then a new seed is generated 070 on each run using the same technique described above 071 (combining current time and the hash code). 072 073 @author Edward A. Lee, Steve Neuendorffer, Elaine Cheong 074 @version $Id$ 075 @since Ptolemy II 0.3 076 @Pt.ProposedRating Green (eal) 077 @Pt.AcceptedRating Green (bilung) 078 */ 079public abstract class RandomSource extends Source { 080 /** Construct an actor with the given container and name. 081 * @param container The container. 082 * @param name The name of this actor. 083 * @exception IllegalActionException If the actor cannot be contained 084 * by the proposed container. 085 * @exception NameDuplicationException If the container already has an 086 * actor with this name. 087 */ 088 public RandomSource(CompositeEntity container, String name) 089 throws NameDuplicationException, IllegalActionException { 090 super(container, name); 091 seed = new SharedParameter(this, "seed", RandomSource.class, "0L"); 092 seed.setTypeEquals(BaseType.LONG); 093 094 privateSeed = new Parameter(this, "privateSeed"); 095 privateSeed.setTypeEquals(BaseType.LONG); 096 097 resetOnEachRun = new SharedParameter(this, "resetOnEachRun", 098 RandomSource.class, "false"); 099 resetOnEachRun.setTypeEquals(BaseType.BOOLEAN); 100 101 new SingletonParameter(trigger, "_showName") 102 .setToken(BooleanToken.TRUE); 103 } 104 105 /////////////////////////////////////////////////////////////////// 106 //// ports and parameters //// 107 108 /** If true, this parameter specifies that the random number 109 * generator should be reset on each run of the model (in 110 * the initialize() method). It is a boolean that defaults 111 * to false. This is a shared parameter, meaning that changing 112 * it somewhere in the model causes it to be changed everywhere 113 * in the model. 114 */ 115 public SharedParameter resetOnEachRun; 116 117 /** The seed that controls the random number generation. 118 * This is a shared parameter, meaning that all instances of 119 * RandomSource or derived classes in the same model share the 120 * same value. 121 * A seed of zero is interpreted to mean that no seed is specified, 122 * which means that each execution of the model could result in 123 * distinct data. For the value 0, the seed is set to 124 * System.currentTimeMillis() + hashCode(), which means that 125 * with extremely high probability, two distinct actors will have 126 * distinct seeds. However, current time may not have enough 127 * resolution to ensure that two subsequent executions of the 128 * same model have distinct seeds. For a value other than zero, 129 * the seed is set to that value plus the hashCode() of the 130 * full name of the actor. This means that with high probability, 131 * two distinct actors will have distinct, but repeatable seeds. 132 * 133 * This parameter contains a LongToken, initially with value 0. 134 */ 135 public SharedParameter seed; 136 137 /** This private seed overrides the shared seed parameter to specify a 138 * particular seed rather than using System.currentTimeMillis() or 139 * hashCode() to compute the seed value. 140 * 141 * By default, this parameter is empty, which means that the shared seed 142 * parameter is used. 143 * 144 * WARNING: It is up to the user to make sure that different seed 145 * values are used in different random number generators. 146 */ 147 public Parameter privateSeed; 148 149 /////////////////////////////////////////////////////////////////// 150 //// public methods //// 151 152 /** If the attribute is <i>seed</i> or <i>useThisSeed</i> 153 * then create the base random number generator. 154 * @param attribute The attribute that changed. 155 * @exception IllegalActionException If the change is not acceptable 156 * to this container (not thrown in this base class). 157 */ 158 @Override 159 public void attributeChanged(Attribute attribute) 160 throws IllegalActionException { 161 if (attribute == seed || attribute == privateSeed) { 162 long seedValue; 163 Token privateSeedToken = privateSeed.getToken(); 164 if (privateSeedToken != null) { 165 seedValue = ((LongToken) privateSeedToken).longValue(); 166 } else { 167 seedValue = ((LongToken) seed.getToken()).longValue(); 168 } 169 if (seedValue != _generatorSeed) { 170 _needNewGenerator = true; 171 } 172 } else { 173 super.attributeChanged(attribute); 174 } 175 } 176 177 /** Clone the actor into the specified workspace. This calls the 178 * base class and then creates new ports and parameters. 179 * @param workspace The workspace for the new object. 180 * @return A new actor. 181 * @exception CloneNotSupportedException If a derived class contains 182 * an attribute that cannot be cloned. 183 */ 184 @Override 185 public Object clone(Workspace workspace) throws CloneNotSupportedException { 186 RandomSource newObject = (RandomSource) super.clone(workspace); 187 188 // It is too soon to generate the new generator because 189 // all clones will have the same actor name, which results 190 // in the same seed. 191 newObject._needNewGenerator = true; 192 193 return newObject; 194 } 195 196 /** Generate a new random number if this is the first firing 197 * of the iteration. 198 * @exception IllegalActionException If there is no director. 199 */ 200 @Override 201 public void fire() throws IllegalActionException { 202 super.fire(); 203 if (_needNewGenerator) { 204 _createGenerator(); 205 } 206 207 if (_needNew) { 208 _generateRandomNumber(); 209 _needNew = false; 210 } 211 } 212 213 /** Initialize the random number generator with the seed, if it 214 * has been given. A seed of zero is interpreted to mean that no 215 * seed is specified. In such cases, a seed based on the current 216 * time and this instance of a RandomSource is used to be fairly 217 * sure that two identical sequences will not be returned. 218 * @exception IllegalActionException If the parent class throws it. 219 */ 220 @Override 221 public void initialize() throws IllegalActionException { 222 super.initialize(); 223 224 if (_random == null 225 || ((BooleanToken) resetOnEachRun.getToken()).booleanValue()) { 226 _createGenerator(); 227 } 228 _needNew = true; 229 } 230 231 /** Calculate the next random number. 232 * @exception IllegalActionException If the base class throws it. 233 * @return True if it is ok to continue. 234 */ 235 @Override 236 public boolean postfire() throws IllegalActionException { 237 _needNew = true; 238 return super.postfire(); 239 } 240 241 /////////////////////////////////////////////////////////////////// 242 //// protected methods //// 243 244 /** Create the random number generator using current parameter values. 245 * @exception IllegalActionException If thrown while reading the 246 * seed Token. 247 */ 248 protected void _createGenerator() throws IllegalActionException { 249 long seedValue; 250 Token privateSeedToken = privateSeed.getToken(); 251 if (privateSeedToken != null) { 252 seedValue = ((LongToken) privateSeedToken).longValue(); 253 _generatorSeed = seedValue; 254 } else { 255 seedValue = ((LongToken) seed.getToken()).longValue(); 256 _generatorSeed = seedValue; 257 258 if (seedValue == 0L) { 259 seedValue = System.currentTimeMillis() + hashCode(); 260 } else { 261 // BTW - the reason to use the full name here is so that 262 // each random number generator generates a sequence 263 // of different random numbers. If we use just the 264 // display name, then two actors that have the same 265 // name will generate the same sequence of numbers which 266 // is bad for Monte Carlo simulations. 267 seedValue = seedValue + getFullName().hashCode(); 268 } 269 } 270 271 _random = new Random(seedValue); 272 _needNewGenerator = false; 273 _needNew = true; 274 } 275 276 /** Generate a new random number. 277 * @exception IllegalActionException Not thrown in this base class. 278 * Derived classes may throw it if there are problems getting parameter 279 * values. 280 */ 281 protected abstract void _generateRandomNumber() 282 throws IllegalActionException; 283 284 /////////////////////////////////////////////////////////////////// 285 //// protected variables //// 286 287 /** The current value of the seed parameter. */ 288 protected long _generatorSeed = 0L; 289 290 /** Indicator that a new random number is needed. */ 291 protected boolean _needNew = false; 292 293 /** Indicator that a new generator is needed. */ 294 protected boolean _needNewGenerator = true; 295 296 /** The Random object. */ 297 protected Random _random; 298 299}