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).
*/