001/* An actor that produces tokens with a given probability mass function. 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.data.ArrayToken; 031import ptolemy.data.DoubleToken; 032import ptolemy.data.Token; 033import ptolemy.data.expr.Parameter; 034import ptolemy.data.type.ArrayType; 035import ptolemy.data.type.BaseType; 036import ptolemy.kernel.CompositeEntity; 037import ptolemy.kernel.util.Attribute; 038import ptolemy.kernel.util.IllegalActionException; 039import ptolemy.kernel.util.InternalErrorException; 040import ptolemy.kernel.util.NameDuplicationException; 041import ptolemy.kernel.util.Workspace; 042import ptolemy.math.SignalProcessing; 043 044/////////////////////////////////////////////////////////////////// 045//// DiscreteRandomSource 046 047/** 048 <p> 049 An actor that produces tokens with a given probability mass function. 050 </p><p> 051 The probability mass function is a parameter, <i>pmf</i>, of this 052 actor. The <i>pmf</i> must be an array that contains entries that 053 are all between 0.0 and 1.1, and sum to 1.0. By default, <i>pmf</i> is 054 initialized to {0.5, 0.5}. 055 </p><p> 056 Output values are selected at random from the <i>values</i> parameter, 057 which contains an ArrayToken. This array must have the same length as 058 <i>pmf</i>. Thus the <i>i</i>-th token in <i>values</i> has probability 059 <i>pmf</i>[<i>i</i>]. The output port has the same type as the elements of 060 the <i>values</i> array. The default <i>values</i> are {0, 1}, which are 061 integers.</p> 062 063 @author Jeff Tsay, Yuhong Xiong 064 @version $Id$ 065 @since Ptolemy II 1.0 066 @Pt.ProposedRating Yellow (eal) 067 @Pt.AcceptedRating Yellow (ssachs) 068 */ 069public class DiscreteRandomSource extends RandomSource { 070 /** Construct an actor with the given container and name. 071 * @param container The container. 072 * @param name The name of this actor. 073 * @exception IllegalActionException If the actor cannot be contained 074 * by the proposed container. 075 * @exception NameDuplicationException If the container already has an 076 * actor with this name. 077 */ 078 public DiscreteRandomSource(CompositeEntity container, String name) 079 throws NameDuplicationException, IllegalActionException { 080 super(container, name); 081 pmf = new Parameter(this, "pmf"); 082 pmf.setExpression("{0.5, 0.5}"); 083 pmf.setTypeEquals(new ArrayType(BaseType.DOUBLE)); 084 085 // set the values parameter 086 values = new Parameter(this, "values"); 087 values.setExpression("{0, 1}"); 088 089 // set type constraint 090 output.setTypeAtLeast(ArrayType.elementType(values)); 091 } 092 093 /////////////////////////////////////////////////////////////////// 094 //// ports and parameters //// 095 096 /** The probability mass function. 097 * This parameter contains an array of doubles, with default value 098 * {0.5, 0.5}. 099 */ 100 public Parameter pmf; 101 102 /** The values to be sent to the output. 103 * This parameter contains an ArrayToken, initially with value 104 * {0, 1} (an int array). 105 */ 106 public Parameter values; 107 108 /////////////////////////////////////////////////////////////////// 109 //// public methods //// 110 111 /** If the specified attribute is <i>pmf</i>, then check that its 112 * entries are all between zero and one, and that they add to one, 113 * and that its dimension is correct. 114 * @param attribute The attribute that changed. 115 * @exception IllegalActionException If the requirements are 116 * violated. 117 */ 118 @Override 119 public void attributeChanged(Attribute attribute) 120 throws IllegalActionException { 121 if (attribute == pmf) { 122 ArrayToken pmfValue = (ArrayToken) pmf.getToken(); 123 _pmf = new double[pmfValue.length()]; 124 125 double sum = 0.0; 126 127 for (int i = 0; i < _pmf.length; i++) { 128 _pmf[i] = ((DoubleToken) pmfValue.getElement(i)).doubleValue(); 129 sum += _pmf[i]; 130 } 131 132 // Allow for roundoff error. 133 if (!SignalProcessing.close(sum, 1.0)) { 134 throw new IllegalActionException(this, 135 "Parameter values are required to sum to one."); 136 } 137 } else { 138 super.attributeChanged(attribute); 139 } 140 } 141 142 /** Clone the actor into the specified workspace. This calls the 143 * base class and then sets the parameter public members to refer 144 * to the parameters of the new actor. 145 * @param workspace The workspace for the new object. 146 * @return A new actor. 147 * @exception CloneNotSupportedException If a derived class contains 148 * an attribute that cannot be cloned. 149 */ 150 @Override 151 public Object clone(Workspace workspace) throws CloneNotSupportedException { 152 DiscreteRandomSource newObject = (DiscreteRandomSource) super.clone( 153 workspace); 154 try { 155 newObject.output 156 .setTypeAtLeast(ArrayType.elementType(newObject.values)); 157 } catch (IllegalActionException e) { 158 // Should have been caught before. 159 throw new InternalErrorException(e); 160 } 161 162 // Copy the array _pmf 163 newObject._pmf = null; 164 try { 165 ArrayToken pmfValue = (ArrayToken) pmf.getToken(); 166 newObject._pmf = new double[pmfValue.length()]; 167 } catch (IllegalActionException ex) { 168 CloneNotSupportedException exception = new CloneNotSupportedException(); 169 exception.initCause(ex); 170 throw exception; 171 } 172 173 if (_pmf != null) { 174 System.arraycopy(_pmf, 0, newObject._pmf, 0, _pmf.length); 175 } 176 177 return newObject; 178 } 179 180 /** Output the token selected in the prefire() method. 181 * @exception IllegalActionException If there is no director. 182 */ 183 @Override 184 public void fire() throws IllegalActionException { 185 super.fire(); 186 output.send(0, _current); 187 } 188 189 /////////////////////////////////////////////////////////////////// 190 //// protected methods //// 191 192 /** Choose one of the tokens in <i>values</i> randomly, using 193 * the <i>pmf</i> parameter to select one. The chosen token 194 * will be sent to the output in the fire() method. 195 * @exception IllegalActionException If parameter values are incorrect. 196 */ 197 @Override 198 protected void _generateRandomNumber() throws IllegalActionException { 199 // Generate a double between 0 and 1, uniformly distributed. 200 double randomValue = _random.nextDouble(); 201 ArrayToken valuesToken = (ArrayToken) values.getToken(); 202 203 if (_pmf.length != valuesToken.length()) { 204 throw new IllegalActionException(this, 205 "Parameters values and pmf are required to be arrays " 206 + "with the same length."); 207 } 208 209 double cdf = 0.0; 210 211 for (int i = 0; i < _pmf.length; i++) { 212 cdf += _pmf[i]; 213 214 if (randomValue <= cdf) { 215 _current = valuesToken.getElement(i); 216 return; 217 } 218 } 219 220 // We shouldn't get here, but if we do, we output the last value. 221 _current = valuesToken.getElement(_pmf.length - 1); 222 } 223 224 /////////////////////////////////////////////////////////////////// 225 //// private variables //// 226 227 /** Random value calculated in prefire(). */ 228 private Token _current; 229 230 /** Cache of probability mass function. */ 231 private transient double[] _pmf; 232}