1 
  2 ////////////////////////////////////////////////////////////////////////////////
  3 /** 
  4  * @fileOverview Copyright (C) 2009 www.webis.de
  5  * @author Christof Braeutigam christof.braeutigam@uni-weimar.de
  6  * @author Alexander Kuemmel alexander.kuemmel@uni-weimar.de
  7  * @version 0.1
  8  */
  9 
 10 /**
 11  * A data-structure that representes a mathematical vector 
 12  * with a set of essential functions to handle some vector instances.
 13  * @class Representation of a vector
 14  * @constructor
 15  * @author Alexander Kümmel alexander.kuemmel@uni-weimar.de
 16  * @author Christof Bräutigam christof.braeutigam@uni-weimar.de
 17  */
 18 de.aitools.js.Vector = function () {
 19   /* private part */
 20   
 21   /**
 22    * @private
 23    * @type {Number}
 24    */
 25   var dimension_ = 0;
 26   /**
 27    * @private
 28    * @type {Boolean}
 29    */
 30   var isNormalized_ = false;
 31   /**
 32    * @private
 33    * @type {Object}
 34    */
 35   var hashMap_ = {};
 36   /**
 37    * @private
 38    * @type {Number}
 39    */
 40   var size_ = 0;
 41   //self reference for unprivilidged methods
 42   //var self_ = this;
 43     
 44   /* public part */
 45   
 46   /**
 47    * Returns a duplicate of this {@link de.aitools.js.Vector}
 48    * @public
 49    * @returns {de.aitools.js.Vector} Cloned vector
 50    */
 51   this.clone = function () {
 52     var clone = new de.aitools.js.Vector();
 53     var i = null;
 54     for (i in hashMap_) {
 55       if (hashMap_.hasOwnProperty(i)) {
 56         //dimension_ and size_ are setup automatically
 57         clone.setValue(parseInt(i,10), this.getValue(i));
 58       }
 59     }
 60     if (this.isNormalized()) {
 61       clone.normalize();
 62     }
 63     return clone;
 64   };
 65     
 66   /**
 67    * Compares this instance with the given {@link de.aitools.js.Vector} and
 68    * returns the result
 69    * @param {de.aitools.js.Vector} vector A {@link de.aitools.js.Vector} instance
 70    * @public
 71    * @returns {Boolean} True if equals
 72    */
 73   this.equals = function (vector) {
 74     var check = true;
 75     check = check && (this.getDimension() === vector.getDimension());
 76     check = check && (this.isNormalized() === vector.isNormalized());
 77     var i = null;
 78     for (i in hashMap_) {
 79       if (hashMap_.hasOwnProperty(i)) {
 80         check = check && (this.getValue(i) === vector.getValue(i));
 81       }
 82     }
 83     check = check && (this.getElementCount() === vector.getElementCount());
 84     return check;
 85   };
 86     
 87   /**
 88    * Add the given {@link de.aitools.js.Vector} to this one
 89    * @param {de.aitools.js.Vector} vector A {@link de.aitools.js.Vector} instance
 90    * @public
 91    */
 92   this.add = function (vector) {
 93     var dim = (this.getDimension() > vector.getDimension) ? this.getDimension()
 94       : vector.getDimension();
 95     for (var i = 0; i < dim; i++) {
 96       var src1 = this.getValue(i);
 97       var src2 = vector.getValue(i);
 98       //to prevent storage waste, add only values != 0
 99       if (src1 === 0 && src2 === 0) { continue; }
100       var sum = this.getValue(i)+vector.getValue(i);
101       if (sum !== 0) { hashMap_[i] = sum; }
102     }
103     isNormalized_ = false;
104   };
105     
106   /**
107    * Scales each component according to the specified factor
108    * @param {Number} factor Numeric value
109    * @public
110    */
111   this.scale = function (factor) {
112     var i = null;
113     for (i in hashMap_) {
114       if (hashMap_.hasOwnProperty(i)) {
115         hashMap_[i] = hashMap_[i] * factor;
116       }
117     }
118     isNormalized_ = false;
119   };
120     
121   /**
122    * Multiplies this and the given {@link de.aitools.js.Vector} and
123    * stores the results within this one
124    * @param {de.aitools.js.Vector} vector A {@link de.aitools.js.Vector} instance
125    * @public
126    */
127   this.multiply = function (vector) {
128     //CHECK_TRUE(this.getDimension() === vector.getDimension(), "different dimensions not allowed");
129     var indices = [];
130     if (this.getElementCount() < vector.getElementCount()) {
131       indices = this.getIndices();
132     }
133     else { indices = vector.getIndices(); }
134     for (var i = 0; i < indices.length; i++) {
135       var product = this.getValue(indices[i]) * vector.getValue(indices[i]);
136       hashMap_[indices[i]] = product;
137     }
138     isNormalized_ = false;
139   };
140 
141   /**
142    * Computes and returns the scalar product between this and the given {@link de.aitools.js.Vector}
143    * @param {de.aitools.js.Vector} vector A {@link de.aitools.js.Vector} instance
144    * @public
145    * @returns {Number}
146    */
147   this.scalarProduct = function (vector) {
148     //CHECK_TRUE(this.getDimension() === vector.getDimension(), "different dimensions not allowed");
149     var indices = [];
150     if (this.getElementCount() < vector.getElementCount()) {
151       indices = this.getIndices();
152     }
153     else { indices = vector.getIndices(); }
154     
155     var sum = 0;
156     for (var i = 0; i < indices.length; i++) {
157       sum += this.getValue(indices[i]) * vector.getValue(indices[i]);
158     }
159     return sum;
160   };
161 
162   /**
163    * Get the value at the specified index
164    * @param {Number} index
165    * @public
166    * @returns {Number}
167    */
168   this.getValue = function (index) {
169     var val = hashMap_[index];
170     if (val === undefined) {
171       return 0;
172     }
173     return val;
174   };
175 
176   /**
177    * Get an array of all values within this {@link de.aitools.js.Vector}
178    * @public
179    * @returns {Array} Array of Numbers
180    */
181   this.getValues = function () {
182     var arr = [];
183     var i = null;
184     for (i in hashMap_) {
185       if (hashMap_.hasOwnProperty(i)) { arr.push(hashMap_[i]); }
186     }
187     return arr;
188   };
189     
190   /**
191    * Set a value at a specific index in this {@link de.aitools.js.Vector}
192    * @param {Number} index
193    * @param {Number} value
194    * @public
195    */
196   this.setValue = function (index, value) {
197     //CHECK_TYPE(index, "number", "index has to be numeric");
198     //var oldLen = hashMap_.length();
199     var isNewIndex = (hashMap_[index] === undefined) ? true : false;
200     if (value !== 0) { hashMap_[index] = value; }
201     else { delete hashMap_[index]; } // TODO: wieso das denn?
202     //size_ = (oldLen < hashMap_.length())? size_ + 1 : size_;
203     if (isNewIndex) { size_ += 1; }
204     dimension_ = (index + 1 > dimension_) ? index + 1 : dimension_;
205     isNormalized_ = false;
206   };
207 
208   /**
209    * Set the dimension of this {@link de.aitools.js.Vector}
210    * @param {Number} dimension
211    * @public
212    */
213   this.setDimension = function (dimension) {
214     CHECK_TYPE(dimension,"number","dimension has to be numeric");
215     dimension_ = dimension;
216   };
217 
218   /**
219    * Normalizes this {@link de.aitools.js.Vector}
220    * @public
221    */
222   this.normalize = function () {
223     if (!isNormalized_) {
224       var squareSum = 0;
225       var values = this.getValues();
226       for (var i = 0; i < values.length; i++) {
227         squareSum += values[i] * values[i];
228       }
229       if (squareSum !== 0) {
230         var factor = 1 / Math.sqrt(squareSum);
231         this.scale(factor);
232       }
233       isNormalized_ = true;
234     }
235   };
236 
237   /**
238    * Returns the normalized state of this {@link de.aitools.js.Vector}
239    * @public
240    * @returns {Boolean} True if already normalized
241    */
242   this.isNormalized = function () { return isNormalized_; };
243 
244   /**
245    * Get the deminsion of this {@link de.aitools.js.Vector}
246    * @public
247    * @returns {Number}
248    */
249   this.getDimension = function () { return dimension_; };
250 
251   /**
252    * Get the number of Elementens within this {@link de.aitools.js.Vector}
253    * @public
254    * @returns {Number} 
255    */
256   this.getElementCount = function () { return size_; };
257 
258   /**
259    * Get a array of all the indices of this {@link de.aitools.js.Vector}
260    * @public
261    * @returns {Array} Array of numbers
262    */
263   this.getIndices = function () {
264     var arr = [];
265     var i = null;
266     for (i in hashMap_) {
267       if (hashMap_.hasOwnProperty(i)) { arr.push(i); }
268     }
269     return arr;
270   };
271 };
272 
273 /**
274  * Computes the highest dimension of over all given 
275  * {@link de.aitools.js.Vector} instances
276  * @param {Array} vectorArr Array of {@link de.aitools.js.Vector} instances
277  * @static
278  * @function
279  * @returns {Number}
280  */
281 de.aitools.js.Vector.getMaxDimension = function (vectorArr) {
282   var maxDimension = 0;
283   for (var i = 0; i < vectorArr.length; i++) {
284     var curDimension = vectorArr[i].getDimension();
285     if (curDimension > maxDimension) { maxDimension = curDimension; }
286   }
287   return maxDimension;
288 };
289 
290 /**
291  * Computes the centroid over all given {@link de.aitools.js.Vector} instances
292  * @param {Array} vectorArr Array of {@link de.aitools.js.Vector} instances
293  * @static
294  * @function
295  * @returns {de.aitools.js.Vector}
296  */
297 de.aitools.js.Vector.computeCentroid = function (vectorArr) {
298   var centroid = new de.aitools.js.Vector();
299   for (var i = 0; i < vectorArr.length; i++) {
300     centroid.add(vectorArr[i]);
301   }
302   centroid.scale(1.0 / vectorArr.length);
303   return centroid;
304 };
305 
306 // Vector.js
307 ////////////////////////////////////////////////////////////////////////////////
308