package edu.stanford.rsl.conrad.numerics; import java.io.Serializable; import java.util.Scanner; import java.util.Vector; /** * @author Andreas Keil * @author Andreas Maier (only very few additions) */ public class SimpleVector implements Serializable { private static final long serialVersionUID = -8563837337889104489L; ///////////////////////////////////////////////// // Properties // ///////////////////////////////////////////////// protected int len; protected double[] buf; ///////////////////////////////////////////////// // Constructors // ///////////////////////////////////////////////// /** * Creates a null vector. vector must be initialized before use; */ public SimpleVector() { this.init(0); } /** * Creates a len dimensional vector * @param len is dimension of vector. */ public SimpleVector(final int len) { this.init(len); } /** * Creates a new vector from otherVec. The entries of this vector are element wise equal to other vec * @param otherVec */ public SimpleVector(final SimpleVector otherVec) { this.init(otherVec); } /** * Creates a new vector initialized by an ordered list of numbers * @param otherBuffer */ public SimpleVector(final double... otherBuffer) { this.init(otherBuffer); } /** * Creates a new vector initialized by an ordered list of numbers (float) * @param otherBuffer */ public SimpleVector(final float... otherBuffer) { this.init(otherBuffer); } /** * Creates a new vector from a string equivalent * @param str */ public SimpleVector(final String str) { this.init(str); } /** * Creates a new vector from a jama equivalent * @param other */ public SimpleVector(final Jama.Matrix other) { this(other.getColumnPackedCopy()); } ///////////////////////////////////////////////// // Methods // ///////////////////////////////////////////////// /** * Initialize vector to [0,0,...,0] and length len. * @param len is dimension of vector */ public void init(final int len) { assert len >= 0 : new IllegalArgumentException("Length has to be greater than or equal to zero!"); if (this.len != len) { this.len = len; this.buf = new double[this.len]; } } /** * Initialize vector with otherVec * @param otherVec */ public void init(final SimpleVector otherVec) { if (this.len != otherVec.len) { this.len = otherVec.len; this.buf = new double[this.len]; } System.arraycopy(otherVec.buf, 0, this.buf, 0, this.len); } /** * Initialize vector with a comma-separated list of double array * @param otherBuffer */ public void init(final double... otherBuffer) { assert otherBuffer.length >= 0 : new IllegalArgumentException("Length has to be greater than or equal to zero!"); this.len = otherBuffer.length; this.buf = new double[this.len]; System.arraycopy(otherBuffer, 0, this.buf, 0, this.len); } /** * Initialize vector with a comma-separated list of double array (float) * Private: For internal use only * @param otherBuffer */ public void init(final float... otherBuffer) { assert otherBuffer.length >= 0 : new IllegalArgumentException("Length has to be greater than or equal to zero!"); this.len = otherBuffer.length; this.buf = new double[this.len]; for (int i = 0; i < otherBuffer.length; i++) { buf[i] = otherBuffer[i]; } } /** * Initialize vector with string equivalent * @param str */ public void init(final String str) { final String strTrim = str.trim(); if ((strTrim.charAt(0) != '[') || (strTrim.charAt(strTrim.length() - 1) != ']')) throw new RuntimeException("Error parsing matrix string!"); Scanner scanner = new Scanner(strTrim.substring(1, strTrim.length()-1).trim()); scanner.useDelimiter("\\s*[\\s,;]\\s*"); Vector<Double> list = new Vector<Double>(); while (scanner.hasNext()) { // TODO: Getting the next number as String and then converting it to a double is a workaround for a locale-Mac-related conversion problem. Maybe replace this by // list.add(scanner.nextDouble()); // once this issue is resolved in java.util.Scanner (if it is really a bug and not a feature) list.add(Double.parseDouble(scanner.next())); } this.init(list.size()); for (int i = 0; i < list.size(); ++i) this.buf[i] = list.elementAt(i); } @Override public SimpleVector clone() { return new SimpleVector(this); } /** * Copies the elements of this vector to a double array * @return double array containing ordered values */ public double[] copyAsDoubleArray(){ double[] array = new double[this.len]; this.copyTo(array); return array; } /** * Copies the element of this vector to the double array provided * @param other is array to be populated */ public void copyTo(final double[] other) { assert (this.len == other.length) : new IllegalArgumentException("Copying is only possible to an array of the same size!"); System.arraycopy(this.buf, 0, other, 0, this.len); } /** * @return the dimension of this vector */ public int getLen() { return this.len; } /** Sets all entries to the given value. */ public void fill(final double value) { java.util.Arrays.fill(this.buf, value); } /** Sets all entries to 0.0. */ public void zeros() { this.fill(0.0); } /** Sets all entries to 1.0. */ public void ones() { this.fill(1.0); } /** Rounds all entries to the next smaller integer */ public void floor() { for (int i = 0; i < this.len; i++) { this.setElementValue(i, Math.floor(getElement(i))); } } /** Rounds all entries to the next higher integer */ public void ceil() { for (int i = 0; i < this.len; i++) { this.setElementValue(i, Math.ceil(getElement(i))); } } /** * Assigns random values to the entries of the vector. * Values are uniformly distributed in the given interval [min, max). * @param min The lower bound of the interval the values are drawn from. * @param max The upper bound of the interval the values are drawn from. Note that value max * itself is excluded from the interval and therefore never assigned. */ public void randomize(final double min, final double max) { for (int i = 0; i < this.len; ++i) this.buf[i] = (max-min)*Math.random() + min; } /** * Retrieve vector element at index i * @param i index to be retrieved * @return element at index i */ public double getElement(final int i) { return this.buf[i]; } /** * Replaces the vector element at index i * @param i index to be replaced * @param val is new value */ public void setElementValue(final int i, final double val) { this.buf[i] = val; } public SimpleVector getSubVec(final int firstInd, final int size) { final SimpleVector subVector = new SimpleVector(size); System.arraycopy(this.buf, firstInd, subVector.buf, 0, size); return subVector; } public void setSubVecValue(final int firstInd, final SimpleVector subVector) { System.arraycopy(subVector.buf, 0, this.buf, firstInd, subVector.len); } public void addToElement(final int i, final double addend) { this.buf[i] += addend; } public void subtractFromElement(final int i, final double subtrahend) { this.buf[i] -= subtrahend; } public void multiplyElementBy(final int i, final double factor) { this.buf[i] *= factor; } public void divideElementBy(final int i, final double divisor) { this.buf[i] /= divisor; } /** * Method to add a scalar to this vector in place. * @param addend the other vector */ public void add(final double addend) { for (int i = 0; i < this.len; i++){ this.buf[i] += addend; } } public void subtract(final double subtrahend) { for (int i = 0; i < this.len; i++){ this.buf[i] -= subtrahend; } } public void multiplyBy(final double factor) { for (int i = 0; i < this.len; ++i) this.buf[i] *= factor; } /** * Returns a scaled instance of the vector. All elements in the scaled instance are multiplied by s * @param factor the scalar * @return the scaled instance */ public SimpleVector multipliedBy(final double factor) { final SimpleVector result = new SimpleVector(this.len); for (int i = 0; i < this.len; ++i) result.buf[i] = this.buf[i] * factor; return result; } public void divideBy(final double divisor) { this.multiplyBy(1.0 / divisor); } /** * Returns a scaled instance of the vector. All elements in the scaled instance are divided by s * @param divisor the scalar * @return the scaled instance */ public SimpleVector dividedBy(final double divisor) { return this.multipliedBy(1.0 / divisor); } /** * Method to add other vectors to this vector in place. * @param addends The other vectors. */ public void add(final SimpleVector... addends) { assert addends.length >= 1 : new IllegalArgumentException("At least one other vector has to be given!"); for (SimpleVector addend : addends) { assert addend.len == this.len; for (int i = 0; i < this.len; ++i) this.buf[i] += addend.buf[i]; } } public void subtract(final SimpleVector... subtrahends) { assert subtrahends.length >= 1 : new IllegalArgumentException("At least one other vector has to be given!"); for (SimpleVector subtrahend : subtrahends) { assert subtrahend.len == this.len; for (int i = 0; i < this.len; ++i) this.buf[i] -= subtrahend.buf[i]; } } public void multiplyElementWiseBy(final SimpleVector... factors) { assert factors.length >= 1 : new IllegalArgumentException("At least one other vector must be given as parameter!"); for (SimpleVector factor : factors) { assert factor.len == this.len : new IllegalArgumentException("Vector lenghts must match!"); for (int i = 0; i < this.len; ++i) this.buf[i] *= factor.buf[i]; } } public void divideElementWiseBy(final SimpleVector... divisors) { assert divisors.length >= 1 : new IllegalArgumentException("At least one other vector must be given as parameter!"); for (SimpleVector divisor : divisors) { assert divisor.len == this.len : new IllegalArgumentException("Vector lenghts must match!"); for (int i = 0; i < this.len; ++i) this.buf[i] /= divisor.buf[i]; } } public void negate() { for (int i = 0; i < this.len; ++i) this.buf[i] = -this.buf[i]; } public SimpleVector negated() { final SimpleVector result = new SimpleVector(this.len); for (int i = 0; i < this.len; ++i) result.buf[i] = -this.buf[i]; return result; } public void absolute() { for (int i = 0; i < this.len; ++i) this.buf[i] = Math.abs(this.buf[i]); } public SimpleVector absoluted() { final SimpleVector result = new SimpleVector(this.len); for (int i = 0; i < this.len; ++i) result.buf[i] = Math.abs(this.buf[i]); return result; } public double min() { double result = Double.MAX_VALUE; for (int i = 0; i < this.len; ++i) result = Math.min(result, this.buf[i]); return result; } public double max() { double result = Double.MIN_VALUE; for (int i = 0; i < this.len; ++i) result = Math.max(result, this.buf[i]); return result; } public SimpleMatrix transposed() { final SimpleMatrix result = new SimpleMatrix(1, this.len); for (int i = 0; i < result.cols; ++i) result.buf[0][i] = this.buf[i]; return result; } public static enum VectorNormType { /** A vectors' L_1 norm is the sum of its absolute values. */ VEC_NORM_L1, /** A vectors' L_2 norm is the square root of the sum of squares of its entries. */ VEC_NORM_L2, /** A vectors' L_infinity norm is the maximum of its absolute values. */ VEC_NORM_LINF } public double norm(final VectorNormType normType) { double result = 0.0; switch (normType) { case VEC_NORM_L1: for (int i = 0; i < this.len; ++i) result += Math.abs(this.buf[i]); return result; case VEC_NORM_L2: for (int i = 0; i < this.len; ++i) result += this.buf[i]*this.buf[i]; return Math.sqrt(result); case VEC_NORM_LINF: for (int i = 0; i < this.len; ++i) { double viAbs = Math.abs(this.buf[i]); if (viAbs > result) result = viAbs; } return result; default: throw new RuntimeException("Vector norm type not implemented yet!"); } } public double normL2() { return this.norm(VectorNormType.VEC_NORM_L2); } public void normalizeL2() { this.divideBy(this.normL2()); } public SimpleVector normalizedL2() { return this.dividedBy(this.normL2()); } ///////////////////////////////////////////////// // Serialization and Persistence // ///////////////////////////////////////////////// public String getVectorSerialization() { return this.toString(); } public void setVectorSerialization(final String str) { this.init(str); } @Override public String toString() { String result = new String(); result += "["; for (int i = 0; i < this.len; ++i) { if (i != 0) result += "; "; result += new Double(this.buf[i]); } result += "]"; return result; } } /* * Copyright (C) 2010-2014 Andreas Kail, Andreas Maier * CONRAD is developed as an Open Source project under the GNU General Public License (GPL). */