/* Copyright 2009-2015 David Hadka
*
* This file is part of the MOEA Framework.
*
* The MOEA Framework is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* The MOEA Framework is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the MOEA Framework. If not, see <http://www.gnu.org/licenses/>.
*/
package org.moeaframework.util;
import org.moeaframework.core.Settings;
/**
* Mathematical operators for manipulating vectors (double arrays).
*/
public class Vector {
/**
* Private constructor to prevent instantiation.
*/
private Vector() {
super();
}
/**
* Returns the length of the two specified vectors.
*
* @param u the first vector
* @param v the second vector
* @return the length of the two specified vectors
* @throws IllegalArgumentException if the two vectors are not the same length
*/
private static int length(double[] u, double[] v) {
if (u.length != v.length) {
throw new IllegalArgumentException("vectors must have same length");
}
return u.length;
}
/**
* Returns the difference between the two specified vectors, {@code u - v}.
* The two vectors must be of the same length.
*
* @param u the first vector
* @param v the second vector
* @return the difference between the two specified vectors, {@code u - v}
* @throws IllegalArgumentException if the two vectors are not the same length
*/
public static double[] subtract(double[] u, double[] v) {
int n = length(u, v);
double[] w = new double[n];
for (int i = 0; i < n; i++) {
w[i] = u[i] - v[i];
}
return w;
}
/**
* Returns the sum of the two specified vectors, {@code u + v}. The two
* vectors must be of the same length.
*
* @param u the first vector
* @param v the second vector
* @return the sum of the two specified vectors, {@code u + v}
* @throws IllegalArgumentException if the two vectors are not the same length
*/
public static double[] add(double[] u, double[] v) {
int n = length(u, v);
double[] w = new double[n];
for (int i = 0; i < n; i++) {
w[i] = u[i] + v[i];
}
return w;
}
/**
* Returns the scalar multiple of the specified vector, {@code a * u}.
*
* @param a the scalar value
* @param u the vector
* @return the scalar multiple of the specified vector, {@code a * u}
*/
public static double[] multiply(double a, double[] u) {
int n = u.length;
double[] w = new double[n];
for (int i = 0; i < n; i++) {
w[i] = a * u[i];
}
return w;
}
/**
* Returns the negation of the specified vector, {@code -u}. This is
* equivalent to calling {@code multiply(-1, u)}.
*
* @param u the vector
* @return the negation of the specified vector, {@code -u}
*/
public static double[] negate(double[] u) {
return multiply(-1.0, u);
}
/**
* Returns the scalar division of the specified vector, {@code u / a}.
*
* @param u the vector
* @param a the scalar value (the denominator)
* @return the scalar division of the specified vector, {@code u / a}
*/
public static double[] divide(double[] u, double a) {
return multiply(1.0 / a, u);
}
/**
* Returns the dot (inner) product of the two specified vectors. The two
* vectors must be the same length.
*
* @param u the first vector
* @param v the second vector
* @return the dot (inner) product of the two specified vectors
* @throws IllegalArgumentException if the two vectors are not the same length
*/
public static double dot(double[] u, double[] v) {
int n = length(u, v);
double dot = 0.0;
for (int i = 0; i < n; i++) {
dot += u[i] * v[i];
}
return dot;
}
/**
* Returns the magnitude (Euclidean norm) of the specified vector.
*
* @param u the vector
* @return the magnitude (Euclidean norm) of the specified vector
*/
public static double magnitude(double[] u) {
return Math.sqrt(dot(u, u));
}
/**
* Returns the specified vector normalized to have a magnitude of 1. The
* specified vector must contain at least one non-zero component; otherwise
* an exception is thrown.
*
* @param u the vector
* @return the specified vector normalized to have a magnitude of 1
* @throws IllegalArgumentException if the specified vector contains all zeros
*/
public static double[] normalize(double[] u) {
if (isZero(u)) {
throw new IllegalArgumentException("can not normalize zero vector");
}
return multiply(1.0 / magnitude(u), u);
}
/**
* Returns the projection of {@code u} onto {@code v}. The two vectors must
* be the same length.
*
* @param u the vector being projected
* @param v the vector onto which {@code u} is being projected
* @return the projection of {@code u} onto {@code v}
* @throws IllegalArgumentException if the two vectors are not the same
* length
*/
public static double[] project(double[] u, double[] v) {
return multiply(dot(u, v) / dot(v, v), v);
}
/**
* Returns the orthogonal basis for the specified vectors using the
* Gram-Schmidt process.
*
* @param vs the vectors to be orthogonalized
* @return the orthogonal basis
*/
public static double[][] orthogonalize(double[][] vs) {
vs = vs.clone();
for (int i = 1; i < vs.length; i++) {
for (int j = 0; j < i; j++) {
vs[i] = subtract(vs[i], project(vs[i], vs[j]));
}
}
return vs;
}
/**
* Returns the vector {@code u} orthogonal to the already orthogonalized
* vectors {@code vs}. This method is provided to allow incremental
* construction of the orthogonal basis:
*
* <pre>
* List<double[]> basis = new ArrayList<double[]>();
* for (double[] v : vectors) {
* double[] e = orthogonalize(v, basis);
* basis.add(e);
* }
* </pre>
*
* @param u the vector
* @param vs the already orthogonalized vectors
* @return the vector {@code u} orthogonal to the already orthogonalized
* vectors {@code vs}
*/
public static double[] orthogonalize(double[] u, Iterable<double[]> vs) {
for (double[] v : vs) {
u = subtract(u, project(u, v));
}
return u;
}
/**
* Returns the mean vector of the specified vectors.
*
* @param vs the vectors
* @return the mean vector of the specified vectors
* @throws IllegalArgumentException if the specified vectors is empty
*/
public static double[] mean(double[][] vs) {
int k = vs.length;
if (k == 0) {
throw new IllegalArgumentException("empty vector");
}
int n = vs[0].length;
double[] mean = new double[n];
for (int j = 0; j < n; j++) {
for (int i = 0; i < k; i++) {
mean[j] += vs[i][j];
}
mean[j] /= k;
}
return mean;
}
/**
* Returns {@code true} if the specified vector contains all zeros;
* {@code false} otherwise.
*
* @param u the vector
* @return {@code true} if the specified vector contains all zeros;
* {@code false} otherwise
*/
public static boolean isZero(double[] u) {
for (int i = 0; i < u.length; i++) {
if (Math.abs(u[i]) > Settings.EPS) {
return false;
}
}
return true;
}
}