001/* A Slicer, which functions as a decoder of the LineCoder 002 of complex type. 003 004 Copyright (c) 2003-2014 The Regents of the University of California. 005 All rights reserved. 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 above 009 copyright notice and the following two paragraphs appear in all copies 010 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.comm; 030 031import ptolemy.actor.lib.Transformer; 032import ptolemy.data.ArrayToken; 033import ptolemy.data.BooleanToken; 034import ptolemy.data.ComplexToken; 035import ptolemy.data.IntToken; 036import ptolemy.data.expr.Parameter; 037import ptolemy.data.type.ArrayType; 038import ptolemy.data.type.BaseType; 039import ptolemy.kernel.CompositeEntity; 040import ptolemy.kernel.util.IllegalActionException; 041import ptolemy.kernel.util.NameDuplicationException; 042import ptolemy.kernel.util.Workspace; 043import ptolemy.math.Complex; 044 045/////////////////////////////////////////////////////////////////// 046//// Slicer 047 048/** 049 The Slicer functions as a decoder of the LineCoder. The parameter 050 <i>table</i> and <i>wordLength</i> has the same meaning as in LineCoder, 051 except that the type of <i>table</i> is constrained to an ArrayToken 052 of complex numbers. On each firing, the Slicer consumes one complex 053 token from its input port and computes the Euclidean distance between 054 the input data and the elements in the Slicer. The actor produces 055 <i>wordLength</i> booleans on each firing. The values of these booleans 056 correspond to the index of the entry that minimizes the distance. For 057 example, if the first entry minimizes the distance, then all of these 058 values are <i>false</i>. If the second entry minimizes the distance, 059 then only the first boolean is true. 060 061 @author Ye Zhou 062 @version $Id$ 063 @since Ptolemy II 4.0 064 @Pt.ProposedRating Red (zhouye) 065 @Pt.AcceptedRating Red (cxh) 066 */ 067public class Slicer extends Transformer { 068 /** Construct an actor with the given container and name. 069 * @param container The container. 070 * @param name The name of this actor. 071 * @exception IllegalActionException If the actor cannot be contained 072 * by the proposed container. 073 * @exception NameDuplicationException If the container already has an 074 * actor with this name. 075 */ 076 public Slicer(CompositeEntity container, String name) 077 throws NameDuplicationException, IllegalActionException { 078 super(container, name); 079 080 input.setTypeEquals(BaseType.COMPLEX); 081 new Parameter(input, "tokenConsumptionRate", new IntToken(1)); 082 output.setTypeEquals(BaseType.BOOLEAN); 083 _outputRate = new Parameter(output, "tokenProductionRate", 084 new IntToken(1)); 085 086 table = new Parameter(this, "table"); 087 table.setTypeEquals(new ArrayType(BaseType.COMPLEX)); 088 table.setExpression("{-1.0, 1.0}"); 089 090 //attributeChanged(table); 091 wordLength = new Parameter(this, "wordLength", new IntToken(1)); 092 wordLength.setTypeEquals(BaseType.INT); 093 } 094 095 /////////////////////////////////////////////////////////////////// 096 //// public variables //// 097 098 /** The code table. It is an array token of complex type. 099 * The number of values in this array must be at least 100 * 2<sup><i>wordLength</i></sup>, or an exception 101 * will be thrown. Its default value is {-1.0, 1.0}. 102 */ 103 public Parameter table; 104 105 /** The word length is the number of boolean output that 106 * are produced on each firing. Its value is an IntToken, 107 * with default value one. 108 */ 109 public Parameter wordLength; 110 111 /////////////////////////////////////////////////////////////////// 112 //// public methods //// 113 114 /** Clone the actor into the specified workspace. 115 * @param workspace The workspace for the new object. 116 * @return A new actor. 117 * @exception CloneNotSupportedException If a derived class contains 118 * an attribute that cannot be cloned. 119 */ 120 @Override 121 public Object clone(Workspace workspace) throws CloneNotSupportedException { 122 Slicer newObject = (Slicer) super.clone(workspace); 123 124 newObject._outputRate = (Parameter) newObject 125 .getAttribute("_outputRate"); 126 return newObject; 127 } 128 129 /** Consume the inputs and produce the corresponding symbol. 130 * @exception IllegalActionException If a runtime type error occurs. 131 */ 132 @Override 133 public void fire() throws IllegalActionException { 134 super.fire(); 135 136 ComplexToken inputToken = (ComplexToken) input.get(0); 137 int index = 0; 138 double distance = _computeEuclideanDistance(_table[0], 139 inputToken.complexValue()); 140 141 for (int i = 1; i < _size; i++) { 142 double tempDistance = _computeEuclideanDistance(_table[i], 143 inputToken.complexValue()); 144 145 if (tempDistance < distance) { 146 index = i; 147 distance = tempDistance; 148 } 149 } 150 151 BooleanToken[] result = new BooleanToken[_wordLength]; 152 153 for (int i = 0; i < _wordLength; i++) { 154 result[i] = new BooleanToken((index & 1) == 1); 155 index = index >> 1; 156 } 157 158 output.broadcast(result, _wordLength); 159 } 160 161 /** Set up the production constant. 162 * @exception IllegalActionException If the length of the table is not 163 * a power of two. 164 */ 165 @Override 166 public void preinitialize() throws IllegalActionException { 167 super.preinitialize(); 168 169 // FIXME: Handle mutations. 170 _wordLength = ((IntToken) wordLength.getToken()).intValue(); 171 _outputRate.setToken(new IntToken(_wordLength)); 172 173 ArrayToken tableToken = (ArrayToken) table.getToken(); 174 _size = 1 << _wordLength; 175 176 if (tableToken.length() < _size) { 177 /* 178 throw new IllegalActionException(this, "Table parameter must " + 179 "have at least " + _size + " entries, but only has " + 180 tableToken.length()); 181 */ 182 _size = tableToken.length(); 183 } 184 185 _table = new Complex[_size]; 186 187 for (int i = 0; i < _size; i++) { 188 _table[i] = ((ComplexToken) tableToken.getElement(i)) 189 .complexValue(); 190 } 191 } 192 193 /** Compute the Euclidean distance between two complex numbers. 194 * @param x The first complex number. 195 * @param y The second complex number. 196 * @return The distance. 197 */ 198 private double _computeEuclideanDistance(Complex x, Complex y) { 199 Complex z = x.subtract(y); 200 return z.magnitude(); 201 } 202 203 /////////////////////////////////////////////////////////////////// 204 //// private variables //// 205 // Local cache of these parameter values. 206 private int _wordLength; 207 208 private int _size; 209 210 private Complex[] _table; 211 212 // Production rate of the output port. 213 private Parameter _outputRate; 214}