001/* Compute a histogram of input data. 002 003 @Copyright (c) 2003-2014 The Regents of the University of California. 004 All rights reserved. 005 006 Permission is hereby granted, without written agreement and without 007 license or royalty fees, to use, copy, modify, and distribute this 008 software and its documentation for any purpose, provided that the 009 above copyright notice and the following two paragraphs appear in all 010 copies of this software. 011 012 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 013 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 014 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 015 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 016 SUCH DAMAGE. 017 018 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 019 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 020 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 021 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 022 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 023 ENHANCEMENTS, OR MODIFICATIONS. 024 025 PT_COPYRIGHT_VERSION 2 026 COPYRIGHTENDKEY 027 028 */ 029package ptolemy.actor.lib; 030 031import ptolemy.actor.TypedAtomicActor; 032import ptolemy.actor.TypedIOPort; 033import ptolemy.actor.parameters.PortParameter; 034import ptolemy.data.ArrayToken; 035import ptolemy.data.DoubleToken; 036import ptolemy.data.IntToken; 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.NameDuplicationException; 045import ptolemy.kernel.util.Settable; 046import ptolemy.kernel.util.Workspace; 047 048/////////////////////////////////////////////////////////////////// 049//// ComputeHistogram 050 051/** 052 Compute a histogram. 053 <p> 054 The output array consists of a set of vertical bars, each representing 055 a histogram bin. The height of the bar is the count of the number 056 of inputs that have been observed that fall within that bin. 057 The <i>n</i>-th bin represents values in the range 058 (<i>x</i> - <i>w</i>/2 + <i>o</i>, <i>x</i> + <i>w</i>/2 + <i>o</i>), 059 where <i>w</i> is the value of the <i>binWidth</i> parameter, 060 and <i>o</i> is the value of the <i>binOffset</i> parameter. 061 So for example, if <i>o = w/2</i>, 062 then each bin represents values from <i>nw</i> to 063 (<i>n</i> + 1)<i>w</i> for some integer <i>n</i>. 064 The default offset is 0.5, half the default bin width, which is 1.0. 065 <p> 066 This actor has a <i>legend</i> parameter, 067 which gives a comma-separated list of labels to attach to 068 each dataset. Normally, the number of elements in this list 069 should equal the number of input channels, although this 070 is not enforced. 071 072 @see ptolemy.plot.Histogram 073 074 @author Steve Neuendorffer 075 @version $Id$ 076 @since Ptolemy II 4.0 077 @Pt.ProposedRating Red (eal) 078 @Pt.AcceptedRating Red (cxh) 079 */ 080public class ComputeHistogram extends TypedAtomicActor { 081 /** Construct an actor with the given container and name. 082 * @param container The container. 083 * @param name The name of this actor. 084 * @exception IllegalActionException If the actor cannot be contained 085 * by the proposed container. 086 * @exception NameDuplicationException If the container already has an 087 * actor with this name. 088 */ 089 public ComputeHistogram(CompositeEntity container, String name) 090 throws IllegalActionException, NameDuplicationException { 091 super(container, name); 092 093 input = new TypedIOPort(this, "input", true, false); 094 input.setTypeEquals(BaseType.DOUBLE); 095 096 output = new TypedIOPort(this, "output", false, true); 097 output.setTypeEquals(new ArrayType(BaseType.INT)); 098 099 minimumValue = new Parameter(this, "minimumValue"); 100 minimumValue.setExpression("0.0"); 101 minimumValue.setTypeEquals(BaseType.DOUBLE); 102 103 maximumValue = new Parameter(this, "maximumValue"); 104 maximumValue.setExpression("1.0"); 105 maximumValue.setTypeEquals(BaseType.DOUBLE); 106 107 numberOfBins = new Parameter(this, "numberOfBins"); 108 numberOfBins.setExpression("10"); 109 numberOfBins.setTypeEquals(BaseType.INT); 110 111 inputCount = new PortParameter(this, "inputCount"); 112 inputCount.setExpression("10"); 113 inputCount.setTypeEquals(BaseType.INT); 114 115 input_tokenConsumptionRate = new Parameter(input, 116 "tokenConsumptionRate"); 117 input_tokenConsumptionRate.setExpression("inputCount"); 118 input_tokenConsumptionRate.setTypeEquals(BaseType.INT); 119 input_tokenConsumptionRate.setVisibility(Settable.NOT_EDITABLE); 120 input_tokenConsumptionRate.setPersistent(false); 121 } 122 123 /////////////////////////////////////////////////////////////////// 124 //// ports and parameters //// 125 126 /** The lowest value that will be recorded in the histogram. 127 * This parameter has type double, with default value 0.0. 128 */ 129 public Parameter minimumValue; 130 131 /** The highest value that will be recorded in the histogram. 132 * This parameter has type double, with default value 1.0. 133 */ 134 public Parameter maximumValue; 135 136 /** The number of bins. 137 * This parameter has type int, with default value 10. 138 */ 139 public Parameter numberOfBins; 140 141 /** The number of tokens to compute the histogram for. 142 */ 143 public PortParameter inputCount; 144 145 /** The parameter that determines the consumption rate of the input. 146 */ 147 public Parameter input_tokenConsumptionRate; 148 149 /** The input port of type double. */ 150 public TypedIOPort input; 151 152 /** The input port of type array of integer. */ 153 public TypedIOPort output; 154 155 /////////////////////////////////////////////////////////////////// 156 //// public methods //// 157 158 /** If the parameter is <i>binWidth</i> or <i>binOffset</i>, then 159 * configure the histogram with the specified bin width or offset. 160 * @param attribute The attribute that changed. 161 * @exception IllegalActionException If the bin width is not positive. 162 */ 163 @Override 164 public void attributeChanged(Attribute attribute) 165 throws IllegalActionException { 166 if (attribute == minimumValue || attribute == maximumValue 167 || attribute == numberOfBins) { 168 _minimumValue = ((DoubleToken) minimumValue.getToken()) 169 .doubleValue(); 170 _maximumValue = ((DoubleToken) maximumValue.getToken()) 171 .doubleValue(); 172 _numberOfBins = ((IntToken) numberOfBins.getToken()).intValue(); 173 174 double width = (_maximumValue - _minimumValue) / _numberOfBins; 175 176 if (width <= 0.0) { 177 throw new IllegalActionException(this, 178 "Invalid bin width (must be positive): " + width); 179 } 180 181 _binWidth = width; 182 _bins = new int[_numberOfBins]; 183 } else { 184 super.attributeChanged(attribute); 185 } 186 } 187 188 /** Clone the actor into the specified workspace. 189 * @param workspace The workspace for the new object. 190 * @return A new actor. 191 * @exception CloneNotSupportedException If a derived class contains 192 * an attribute that cannot be cloned. 193 */ 194 @Override 195 public Object clone(Workspace workspace) throws CloneNotSupportedException { 196 ComputeHistogram newObject = (ComputeHistogram) super.clone(workspace); 197 198 newObject._bins = new int[_numberOfBins]; 199 if (_bins != null) { 200 System.arraycopy(_bins, 0, newObject._bins, 0, _bins.length); 201 } 202 return newObject; 203 } 204 205 /** Read at most one input token from each input channel 206 * and update the histogram. 207 * @exception IllegalActionException If there is no director. 208 */ 209 @Override 210 public void fire() throws IllegalActionException { 211 super.fire(); 212 _bins = new int[_numberOfBins]; 213 inputCount.update(); 214 int count = ((IntToken) inputCount.getToken()).intValue(); 215 216 for (int i = 0; i < count; i++) { 217 if (input.hasToken(0)) { 218 DoubleToken curToken = (DoubleToken) input.get(0); 219 double curValue = curToken.doubleValue(); 220 221 _addPoint(curValue); 222 } 223 } 224 225 // Send the output array. 226 Token[] values = new Token[_bins.length]; 227 228 for (int i = 0; i < _bins.length; i++) { 229 values[i] = new IntToken(_bins[i]); 230 } 231 232 output.send(0, new ArrayToken(BaseType.INT, values)); 233 } 234 235 /** Return false if the input does not have enough tokens to fire. 236 * Otherwise, return true. 237 * @return False if the number of input tokens available is not at least 238 * equal to the <i>decimation</i> parameter multiplied by the 239 * <i>blockSize</i> parameter. 240 * @exception IllegalActionException If the superclass throws it. 241 */ 242 @Override 243 public boolean prefire() throws IllegalActionException { 244 int count = ((IntToken) inputCount.getToken()).intValue(); 245 return input.hasToken(0, count) && super.prefire(); 246 } 247 248 /////////////////////////////////////////////////////////////////// 249 //// private methods //// 250 private void _addPoint(double value) { 251 // Calculate the bin number. 252 int bin = (int) Math 253 .round((value - (_minimumValue + _binWidth * 0.5)) / _binWidth); 254 255 if (bin >= 0 && bin < _numberOfBins) { 256 _bins[bin]++; 257 } 258 } 259 260 /////////////////////////////////////////////////////////////////// 261 //// private fields //// 262 private int[] _bins; 263 264 private double _minimumValue; 265 266 private double _maximumValue; 267 268 private double _binWidth; 269 270 private int _numberOfBins; 271}