ArrayUtils.java
/*
* Copyright (C) 2012 Alberto Irurueta Carro (alberto@irurueta.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.irurueta.algebra;
import java.util.Arrays;
/**
* Class containing utility methods for common operations with arrays of values.
*/
@SuppressWarnings("DuplicatedCode")
public class ArrayUtils {
/**
* Constructor.
*/
private ArrayUtils() {
}
/**
* Internal method that multiplied by scalar provided input array without
* comparing length of input array and result array.
*
* @param inputArray Array to be multiplied.
* @param scalar Scalar used for multiplication.
* @param result Array where result is stored.
* @see #multiplyByScalar(double[], double, double[])
*/
private static void internalMultiplyByScalar(
final double[] inputArray, final double scalar, final double[] result) {
for (var i = 0; i < inputArray.length; ++i) {
result[i] = scalar * inputArray[i];
}
}
/**
* Multiplies values in provided input array by provided scalar value
* and stores the result in provided result array.
*
* @param inputArray Array to be multiplied.
* @param scalar Scalar used for multiplication.
* @param result Array where result is stored.
* @throws IllegalArgumentException Thrown if inputArray length and result
* array length are not equal.
*/
public static void multiplyByScalar(final double[] inputArray, final double scalar, final double[] result) {
if (inputArray.length != result.length) {
throw new IllegalArgumentException();
}
internalMultiplyByScalar(inputArray, scalar, result);
}
/**
* Multiplies values in provided array by provided scalar value and returns
* the result in a new array.
*
* @param inputArray Array to be multiplied.
* @param scalar Scalar used for multiplication.
* @return Result obtained after multiplying input array by provided scalar
* value.
*/
public static double[] multiplyByScalarAndReturnNew(final double[] inputArray, final double scalar) {
final var result = new double[inputArray.length];
internalMultiplyByScalar(inputArray, scalar, result);
return result;
}
/**
* Sums provided operands arrays and stores the result in provided result
* array (i.e. result = firstOperand + secondOperand). Summation is done in
* an element by element basis.
* Provided array result must be initialized.
* All arrays must have the same length.
* This method does not check array lengths.
*
* @param firstOperand First operand.
* @param secondOperand Second operand.
* @param result Result of summation.
*/
private static void internalSum(final double[] firstOperand, final double[] secondOperand, final double[] result) {
for (var i = 0; i < firstOperand.length; i++) {
result[i] = firstOperand[i] + secondOperand[i];
}
}
/**
* Sums provided operands arrays and stores the result in provided result
* array (i.e. result = firstOperand + secondOperand). Summation is done
* in an element by element basis.
* Provided array result must be initialized.
* All arrays must have the same length.
*
* @param firstOperand First operand.
* @param secondOperand Second operand.
* @param result Result of summation.
* @throws IllegalArgumentException Raised if not all arrays have the same
* length.
*/
public static void sum(final double[] firstOperand, final double[] secondOperand, final double[] result) {
if (firstOperand.length != secondOperand.length || firstOperand.length != result.length) {
throw new IllegalArgumentException();
}
internalSum(firstOperand, secondOperand, result);
}
/**
* Sums provided operands and returns the result as a new array instance.
* Summation is done in an element by element basis.
*
* @param firstOperand First operand.
* @param secondOperand Second operand.
* @return Sum of first and second operands.
* @throws IllegalArgumentException Raised if first and second operands
* arrays don't have the same length.
*/
public static double[] sumAndReturnNew(final double[] firstOperand, final double[] secondOperand) {
if (firstOperand.length != secondOperand.length) {
throw new IllegalArgumentException();
}
final var result = new double[firstOperand.length];
internalSum(firstOperand, secondOperand, result);
return result;
}
/**
* Subtracts provided operands arrays and stores the result in provided
* result array (i.e. result = firstOperand - secondOperand). Subtraction is
* done in an element by element basis.
* Provided array result must be initialized.
* All arrays must have the same length.
* This method does not check array lengths.
*
* @param firstOperand First operand.
* @param secondOperand Second operand.
* @param result Result of subtraction.
*/
private static void internalSubtract(
final double[] firstOperand, final double[] secondOperand, final double[] result) {
for (var i = 0; i < firstOperand.length; i++) {
result[i] = firstOperand[i] - secondOperand[i];
}
}
/**
* Subtracts provided operands arrays and stores the result in provided
* result array (i.e. result = firstOperand - secondOperand). Subtraction is
* done in an element by element basis.
* Provided array result must be initialized.
* All arrays must have the same length.
*
* @param firstOperand First operand.
* @param secondOperand Second operand.
* @param result Result of subtraction.
* @throws IllegalArgumentException Raised if not all arrays have the same
* length.
*/
public static void subtract(final double[] firstOperand, final double[] secondOperand, final double[] result) {
if (firstOperand.length != secondOperand.length || firstOperand.length != result.length) {
throw new IllegalArgumentException();
}
internalSubtract(firstOperand, secondOperand, result);
}
/**
* Subtracts provided operands and returns the result as a new array
* instance.
* Subtraction is done in an element by element basis.
*
* @param firstOperand First operand
* @param secondOperand Second operand
* @return Subtraction of first and second operands
* @throws IllegalArgumentException Raised if first and second operands
* arrays don't have the same length
*/
public static double[] subtractAndReturnNew(final double[] firstOperand, final double[] secondOperand) {
if (firstOperand.length != secondOperand.length) {
throw new IllegalArgumentException();
}
final var result = new double[firstOperand.length];
internalSubtract(firstOperand, secondOperand, result);
return result;
}
/**
* Computes the dot product of provided arrays as the sum of the product
* of the elements of both arrays.
*
* @param firstOperand First operand.
* @param secondOperand Second operand.
* @return Dot product.
* @throws IllegalArgumentException Raised if first and second operands
* arrays don't have the same length.
*/
public static double dotProduct(final double[] firstOperand, final double[] secondOperand) {
if (firstOperand.length != secondOperand.length) {
throw new IllegalArgumentException("both operands must have same length");
}
var result = 0.0;
for (var i = 0; i < firstOperand.length; i++) {
result += firstOperand[i] * secondOperand[i];
}
return result;
}
/**
* Computes the dot product of provided arrays as the sum of the product of
* the elements of both arrays.
*
* @param firstOperand first operand.
* @param secondOperand second operand.
* @param jacobianFirst matrix where jacobian of first operand will be
* stored. Must be a column matrix having the same number of rows as the
* first operand length.
* @param jacobianSecond matrix where jacobian of second operand will be
* stored. Must be a column matrix having the same number of rows as the
* second operand length.
* @return dot product.
* @throws IllegalArgumentException if first and second operands don't have
* the same length or if jacobian matrices are not column vectors having
* the same length as their respective operands.
*/
public static double dotProduct(final double[] firstOperand, final double[] secondOperand,
final Matrix jacobianFirst, final Matrix jacobianSecond) {
if (jacobianFirst != null
&& (jacobianFirst.getRows() != 1 || jacobianFirst.getColumns() != firstOperand.length)) {
throw new IllegalArgumentException("jacobian first must be a row vector having the same number of "
+ "columns as first operand length");
}
if (jacobianSecond != null
&& (jacobianSecond.getRows() != 1 || jacobianSecond.getColumns() != secondOperand.length)) {
throw new IllegalArgumentException("jacobian second must be a row vector having the same number of "
+ "columns as second operand length");
}
if (jacobianFirst != null) {
jacobianFirst.setSubmatrix(0, 0, 0,
firstOperand.length - 1, firstOperand);
}
if (jacobianSecond != null) {
jacobianSecond.setSubmatrix(0, 0, 0,
secondOperand.length - 1, secondOperand);
}
return dotProduct(firstOperand, secondOperand);
}
/**
* Computes the angle between two vectors.
* The angle is defined between 0 and PI.
*
* @param firstOperand first operand.
* @param secondOperand second operand.
* @return angle between arrays.
* @throws IllegalArgumentException if first and second operands don't have
* the same length.
*/
public static double angle(final double[] firstOperand, final double[] secondOperand) {
final var norm1 = Utils.normF(firstOperand);
final var norm2 = Utils.normF(secondOperand);
return Math.acos(Math.min(dotProduct(firstOperand, secondOperand) / norm1 / norm2, 1.0));
}
//The same for Complex arrays
/**
* Internal method that multiplied by scalar provided input array without
* comparing length of input array and result array.
*
* @param inputArray Array to be multiplied.
* @param scalar Scalar used for multiplication.
* @param result Array where result is stored.
* @see #multiplyByScalar(double[], double, double[])
*/
private static void internalMultiplyByScalar(final Complex[] inputArray, final double scalar,
final Complex[] result) {
for (var i = 0; i < inputArray.length; ++i) {
result[i].setReal(inputArray[i].getReal() * scalar);
result[i].setImaginary(inputArray[i].getImaginary() * scalar);
}
}
/**
* Multiplies values in provided input array by provided scalar value
* and stores the result in provided result array.
*
* @param inputArray Array to be multiplied.
* @param scalar Scalar used for multiplication.
* @param result Array where result is stored.
* @throws IllegalArgumentException Thrown if inputArray length and result
* array length are not equal.
*/
public static void multiplyByScalar(final Complex[] inputArray, final double scalar, final Complex[] result) {
if (inputArray.length != result.length) {
throw new IllegalArgumentException();
}
internalMultiplyByScalar(inputArray, scalar, result);
}
/**
* Multiplies values in provided array by provided scalar value and returns
* the result in a new array.
*
* @param inputArray Array to be multiplied.
* @param scalar Scalar used for multiplication.
* @return Result obtained after multiplying input array by provided scalar
* value.
*/
public static Complex[] multiplyByScalarAndReturnNew(final Complex[] inputArray, final double scalar) {
final var result = new Complex[inputArray.length];
// instantiate Complex instances in result array containing values of
// provided array multiplied by scalar value
for (var i = 0; i < inputArray.length; i++) {
result[i] = new Complex(inputArray[i].getReal() * scalar,
inputArray[i].getImaginary() * scalar);
}
return result;
}
/**
* Sums provided operands arrays and stores the result in provided result
* array (i.e. result = firstOperand + secondOperand). Summation is done in
* an element by element basis.
* Provided array result must be initialized.
* All arrays must have the same length.
* This method does not check array lengths.
*
* @param firstOperand First operand.
* @param secondOperand Second operand.
* @param result Result of summation.
*/
private static void internalSum(final Complex[] firstOperand, final Complex[] secondOperand,
final Complex[] result) {
for (var i = 0; i < firstOperand.length; i++) {
result[i].setReal(firstOperand[i].getReal() + secondOperand[i].getReal());
result[i].setImaginary(firstOperand[i].getImaginary() + secondOperand[i].getImaginary());
}
}
/**
* Sums provided operands arrays and stores the result in provided result
* array (i.e. result = firstOperand + secondOperand). Summation is done
* in an element by element basis.
* Provided array result must be initialized.
* All arrays must have the same length.
*
* @param firstOperand First operand.
* @param secondOperand Second operand.
* @param result Result of summation.
* @throws IllegalArgumentException Raised if not all arrays have the same
* length.
*/
public static void sum(
final Complex[] firstOperand, final Complex[] secondOperand, final Complex[] result) {
if (firstOperand.length != secondOperand.length || firstOperand.length != result.length) {
throw new IllegalArgumentException();
}
internalSum(firstOperand, secondOperand, result);
}
/**
* Sums provided operands and returns the result as a new array instance.
* Summation is done in an element by element basis
*
* @param firstOperand First operand
* @param secondOperand Second operand
* @return Sum of first and second operands
* @throws IllegalArgumentException Raised if first and second operands
* arrays don't have the same length
*/
public static Complex[] sumAndReturnNew(final Complex[] firstOperand, final Complex[] secondOperand) {
if (firstOperand.length != secondOperand.length) {
throw new IllegalArgumentException();
}
final var result = new Complex[firstOperand.length];
// initialize each element of result matrix
for (var i = 0; i < firstOperand.length; i++)
result[i] = firstOperand[i].addAndReturnNew(secondOperand[i]);
return result;
}
/**
* Subtracts provided operands arrays and stores the result in provided
* result array (i.e. result = firstOperand - secondOperand). Subtraction is
* done in an element by element basis
* Provided array result must be initialized.
* All arrays must have the same length
* This method does not check array lengths
*
* @param firstOperand First operand
* @param secondOperand Second operand
* @param result Result of subtraction
*/
private static void internalSubtract(
final Complex[] firstOperand, final Complex[] secondOperand, final Complex[] result) {
for (var i = 0; i < firstOperand.length; i++) {
result[i].setReal(firstOperand[i].getReal() - secondOperand[i].getReal());
result[i].setImaginary(firstOperand[i].getImaginary() - secondOperand[i].getImaginary());
}
}
/**
* Subtracts provided operands arrays and stores the result in provided
* result array (i.e. result = firstOperand - secondOperand). Subtraction is
* done in an element by element basis
* Provided array result must be initialized.
* All arrays must have the same length
*
* @param firstOperand First operand
* @param secondOperand Second operand
* @param result Result of subtraction
* @throws IllegalArgumentException Raised if not all arrays have the same
* length
*/
public static void subtract(final Complex[] firstOperand, final Complex[] secondOperand, final Complex[] result) {
if (firstOperand.length != secondOperand.length || firstOperand.length != result.length) {
throw new IllegalArgumentException();
}
internalSubtract(firstOperand, secondOperand, result);
}
/**
* Subtracts provided operands and returns the result as a new array
* instance.
* Subtraction is done in an element by element basis
*
* @param firstOperand First operand
* @param secondOperand Second operand
* @return Subtraction of first and second operands
* @throws IllegalArgumentException Raised if first and second operands
* arrays don't have the same length
*/
public static Complex[] subtractAndReturnNew(final Complex[] firstOperand, final Complex[] secondOperand) {
if (firstOperand.length != secondOperand.length) {
throw new IllegalArgumentException();
}
final var result = new Complex[firstOperand.length];
// initialize each element of result matrix
for (int i = 0; i < firstOperand.length; i++) {
result[i] = firstOperand[i].subtractAndReturnNew(secondOperand[i]);
}
return result;
}
/**
* Computes the dot product of provided arrays as the sum of the product
* of the elements of both arrays
*
* @param firstOperand First operand
* @param secondOperand Second operand
* @return Dot product
* @throws IllegalArgumentException Raised if first and second operands
* arrays don't have the same length
*/
public static Complex dotProduct(final Complex[] firstOperand, final Complex[] secondOperand) {
if (firstOperand.length != secondOperand.length) {
throw new IllegalArgumentException();
}
final var result = new Complex(0.0);
for (var i = 0; i < firstOperand.length; i++) {
result.add(firstOperand[i].multiplyAndReturnNew(secondOperand[i]));
}
return result;
}
/**
* Normalizes provided array and computes corresponding jacobian.
*
* @param v array to be normalized.
* @param result array where result of normalized array will be stored.
* @param jacobian matrix where jacobian will be stored.
* @throws IllegalArgumentException if provided arrays don't have the same
* length or if provided jacobian is not NxN, where N is length of arrays.
*/
public static void normalize(final double[] v, final double[] result, final Matrix jacobian) {
final var s = v.length;
if (s != result.length) {
throw new IllegalArgumentException("both arrays must have the same length");
}
if (jacobian != null && (jacobian.getRows() != s || jacobian.getColumns() != s)) {
throw new IllegalArgumentException("provided jacobian is not NxN where N is length of v");
}
final var n2 = dotProduct(v, v);
final var n = Math.sqrt(n2);
if (jacobian != null) {
try {
final var n3 = n * n2;
// VN_v = (n2*eye(s) - v*v') / n3
Matrix.identity(jacobian);
jacobian.multiplyByScalar(n2);
jacobian.subtract(Matrix.newFromArray(v, true)
.multiplyAndReturnNew(Matrix.newFromArray(v, false)));
if (n3 != 0.0) {
jacobian.multiplyByScalar(1.0 / n3);
} else {
jacobian.initialize(Double.MAX_VALUE);
}
} catch (final WrongSizeException ignore) {
// never thrown
}
}
if (n != 0.0) {
internalMultiplyByScalar(v, 1.0 / n, result);
} else {
Arrays.fill(result, Double.MAX_VALUE);
}
}
/**
* Normalizes provided array and computes corresponding jacobian.
*
* @param v array to be normalized.
* @param jacobian matrix where jacobian will be stored.
* @return a new array instance containing normalized array.
* @throws IllegalArgumentException if provided jacobian is not NxN, where N
* is length of arrays.
*/
public static double[] normalizeAndReturnNew(final double[] v, final Matrix jacobian) {
final var result = new double[v.length];
normalize(v, result, jacobian);
return result;
}
/**
* Normalizes provided array, updates its values and computes corresponding
* jacobian.
*
* @param v array to be normalized and updated.
* @param jacobian matrix where jacobian will be stored.
* @throws IllegalArgumentException if provided jacobian is not NxN, where N
* is length of arrays.
*/
public static void normalize(final double[] v, final Matrix jacobian) {
normalize(v, v, jacobian);
}
/**
* Normalizes provided array.
*
* @param v array to be normalized.
* @param result array where result of normalized array will be stored.
* @throws IllegalArgumentException if provided arrays don't have the same
* length.
*/
public static void normalize(final double[] v, final double[] result) {
normalize(v, result, null);
}
/**
* Normalizes provided array.
*
* @param v array to be normalized.
* @return a new array instance containing normalized array.
*/
public static double[] normalizeAndReturnNew(final double[] v) {
return normalizeAndReturnNew(v, null);
}
/**
* Normalizes provided array and updates its values.
*
* @param v array to be normalized and updated.
*/
public static void normalize(final double[] v) {
normalize(v, (Matrix) null);
}
/**
* Reverses the order of elements in the array.
*
* @param v array to be reversed.
* @param result instance where results will be stored.
* @throws IllegalArgumentException if provided arrays don't have the same
* length.
*/
public static void reverse(final double[] v, final double[] result) {
final var length = v.length;
if (result.length != length) {
throw new IllegalArgumentException();
}
final var halfLength = length / 2;
double tmp;
for (int i = 0, j = length - 1; i < halfLength; i++, j--) {
tmp = v[i];
result[i] = v[j];
result[j] = tmp;
}
if (length % 2 != 0) {
// if length is odd, copy central value
result[halfLength] = v[halfLength];
}
}
/**
* Reverses provided array. This method updates provided array to contain
* its reversed values.
*
* @param v array to be reversed.
*/
public static void reverse(final double[] v) {
reverse(v, v);
}
/**
* Returns a new array containing provided array having its elements in
* reversed order.
*
* @param v array to be reversed.
* @return a new instance containing reversed array.
*/
public static double[] reverseAndReturnNew(final double[] v) {
final var result = new double[v.length];
reverse(v, result);
return result;
}
/**
* Reverses the order of elements in the array.
*
* @param v array to be reversed.
* @param result instance where results will be stored.
* @throws IllegalArgumentException if provided arrays don't have the same
* length.
*/
public static void reverse(final Complex[] v, final Complex[] result) {
final var length = v.length;
if (result.length != length) {
throw new IllegalArgumentException();
}
final var halfLength = length / 2;
Complex tmp;
if (v != result) {
// for different instances, copy values
for (int i = 0, j = length - 1; i < halfLength; i++, j--) {
tmp = new Complex(v[i]);
result[i] = new Complex(v[j]);
result[j] = tmp;
}
if (length % 2 != 0) {
// if length is odd, copy central value
result[halfLength] = new Complex(v[halfLength]);
}
} else {
// for same instances, rearrange values
for (int i = 0, j = length - 1; i < halfLength; i++, j--) {
tmp = v[i];
result[i] = v[j];
result[j] = tmp;
}
if (length % 2 != 0) {
// if length is odd, copy central value
result[halfLength] = v[halfLength];
}
}
}
/**
* Reverses provided array. This method updates provided array to contain
* its reversed values.
*
* @param v array to be reversed.
*/
public static void reverse(final Complex[] v) {
reverse(v, v);
}
/**
* Returns a new array containing provided array having its elements in
* reversed order.
*
* @param v array to be reversed.
* @return a new instance containing reversed array.
*/
public static Complex[] reverseAndReturnNew(final Complex[] v) {
final var result = new Complex[v.length];
reverse(v, result);
return result;
}
/**
* Computes the squared root of each element of provided array and sets the
* result into provided result array.
*
* @param v input array to compute the squared root of each element.
* @param result array where results will be stored.
* @throws IllegalArgumentException if provided arrays don't have the same
* length.
*/
public static void sqrt(final double[] v, final double[] result) {
final var length = v.length;
if (result.length != length) {
throw new IllegalArgumentException();
}
for (int i = 0; i < length; i++) {
result[i] = Math.sqrt(v[i]);
}
}
/**
* Computes the squared root of each element of provided array and returns
* the result as a new array.
*
* @param v input array to compute the squared root of each element.
* @return a new array containing the squared root of each element of input
* array.
*/
public static double[] sqrtAndReturnNew(final double[] v) {
final var result = new double[v.length];
sqrt(v, result);
return result;
}
/**
* Updates provided array by setting on each element its squared root.
*
* @param v input array to be updated with its squared root elements.
*/
public static void sqrt(final double[] v) {
sqrt(v, v);
}
/**
* Finds the minimum value into provided array.
*
* @param v array where minimum must be found.
* @param pos position where minimum was found. Position will be stored at
* position zero of the array, if provided.
* @return minimum value.
*/
public static double min(final double[] v, final int[] pos) {
final var length = v.length;
var min = Double.MAX_VALUE;
var foundPos = -1;
for (int i = 0; i < length; i++) {
if (v[i] < min) {
min = v[i];
foundPos = i;
}
}
if (pos != null && pos.length > 0) {
pos[0] = foundPos;
}
return min;
}
/**
* Finds the minimum value into provided array.
*
* @param v array where minimum must be found.
* @return minimum value.
*/
public static double min(final double[] v) {
return min(v, null);
}
/**
* Finds the maximum value into provided array.
*
* @param v array where maximum must be found.
* @param pos position where maximum was found. Position will be stored at
* position zero of the array, if provided.
* @return maximum value.
*/
public static double max(final double[] v, final int[] pos) {
final var length = v.length;
var max = -Double.MAX_VALUE;
var foundPos = -1;
for (int i = 0; i < length; i++) {
if (v[i] > max) {
max = v[i];
foundPos = i;
}
}
if (pos != null && pos.length > 0) {
pos[0] = foundPos;
}
return max;
}
/**
* Finds the maximum value into provided array.
*
* @param v array where maximum must be found.
* @return maximum value.
*/
public static double max(final double[] v) {
return max(v, null);
}
/**
* Finds the minimum and maximum values into provided array and finds their
* positions.
*
* @param v array where minimum and maximum must be found.
* @param result array of length 2 containing found minimum and maximum
* values at positions 0 and 1 respectively.
* @param pos array of length 2 containing positions where minimum and
* maximum where found in v array. Position 0 will contain minimum position,
* position 1 will contain maximum position.
* @throws IllegalArgumentException if provided result or pos array don't
* have length 2.
*/
public static void minMax(final double[] v, final double[] result, final int[] pos) {
if (result.length != 2) {
throw new IllegalArgumentException("result must have length 2");
}
if (pos != null && pos.length != 2) {
throw new IllegalArgumentException("pos must have length 2");
}
final var length = v.length;
var min = Double.MAX_VALUE;
var max = -Double.MAX_VALUE;
var minPos = -1;
var maxPos = -1;
for (int i = 0; i < length; i++) {
if (v[i] < min) {
min = v[i];
minPos = i;
}
if (v[i] > max) {
max = v[i];
maxPos = i;
}
}
result[0] = min;
result[1] = max;
if (pos != null) {
pos[0] = minPos;
pos[1] = maxPos;
}
}
}