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}