
smile.math.MathEx Maven / Gradle / Ivy
/*
* Copyright (c) 2010-2021 Haifeng Li. All rights reserved.
*
* Smile is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Smile 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Smile. If not, see .
*/
package smile.math;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Iterator;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
import smile.math.blas.UPLO;
import smile.math.distance.Distance;
import smile.math.matrix.Matrix;
import smile.sort.QuickSelect;
import smile.sort.QuickSort;
import smile.sort.Sort;
import smile.util.IntPair;
import smile.util.SparseArray;
import static java.lang.Math.abs;
import static java.lang.Math.exp;
import static java.lang.Math.floor;
import static java.lang.Math.sqrt;
/**
* Extra basic numeric functions. The following functions are
* included:
*
* - scalar functions: sqr, factorial, lfactorial, choose, lchoose, log2 are
* provided.
*
- vector functions: min, max, mean, sum, var, sd, cov, L1 norm,
* L2 norm, L∞ norm, normalize, unitize, cor, Spearman
* correlation, Kendall correlation, distance, dot product, histogram, vector
* (element-wise) copy, equal, plus, minus, times, and divide.
*
- matrix functions: min, max, rowSums, colSums, rowMeans, colMeans, transpose,
* cov, cor, matrix copy, equals.
*
- random functions: random, randomInt, and permutate.
*
- Find the root of a univariate function with or without derivative.
*
*
* @author Haifeng Li
*/
public class MathEx {
private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(MathEx.class);
/**
* Dynamically determines the machine parameters of the floating-point arithmetic.
*/
private static final FPU fpu = new FPU();
/**
* The machine precision for the double type, which is the difference between 1
* and the smallest value greater than 1 that is representable for the double type.
*/
public static final double EPSILON = fpu.EPSILON;
/**
* The machine precision for the float type, which is the difference between 1
* and the smallest value greater than 1 that is representable for the float type.
*/
public static final float FLOAT_EPSILON = fpu.FLOAT_EPSILON;
/**
* The base of the exponent of the double type.
*/
public static final int RADIX = fpu.RADIX;
/**
* The number of digits (in radix base) in the mantissa.
*/
public static final int DIGITS = fpu.DIGITS;
/**
* The number of digits (in radix base) in the mantissa.
*/
public static final int FLOAT_DIGITS = fpu.FLOAT_DIGITS;
/**
* Rounding style.
*
* - 0 if floating-point addition chops
*
- 1 if floating-point addition rounds, but not in the ieee style
*
- 2 if floating-point addition rounds in the ieee style
*
- 3 if floating-point addition chops, and there is partial underflow
*
- 4 if floating-point addition rounds, but not in the ieee style, and there is partial underflow
*
- 5 if floating-point addition rounds in the IEEEE style, and there is partial underflow
*
*/
public static final int ROUND_STYLE = fpu.ROUND_STYLE;
/**
* The largest negative integer such that 1.0 + RADIXMACHEP ≠ 1.0,
* except that machep is bounded below by -(DIGITS+3)
*/
public static final int MACHEP = fpu.MACHEP;
/**
* The largest negative integer such that 1.0 + RADIXMACHEP ≠ 1.0,
* except that machep is bounded below by -(DIGITS+3)
*/
public static final int FLOAT_MACHEP = fpu.FLOAT_MACHEP;
/**
* The largest negative integer such that 1.0 - RADIXNEGEP ≠ 1.0,
* except that negeps is bounded below by -(DIGITS+3)
*/
public static final int NEGEP = fpu.NEGEP;
/**
* The largest negative integer such that 1.0 - RADIXNEGEP ≠ 1.0,
* except that negeps is bounded below by -(DIGITS+3)
*/
public static final int FLOAT_NEGEP = fpu.FLOAT_NEGEP;
/**
* This RNG is to generate the seeds for multi-threads.
* Each thread will use different seed and unlikely generates
* the correlated sequences with other threads.
*/
private static final SecureRandom seedRNG = new SecureRandom();
/**
* The default seeds for thread-local RNGs for reproducible results.
*/
private static final long[] DEFAULT_SEEDS = {
-4106602711295138952L, 7872020634117869514L, -1722503517109829138L, -3386820675908254116L,
-1736715870046201019L, 3854590623768163340L, 4984519038350406438L, 831971085876758331L,
7131773007627236777L, -3609561992173376238L, -8759399602515137276L, 6192158663294695439L,
-5656470009161653116L, -7984826214821970800L, -9113192788977418232L, -8979910231410580019L,
-4619021025191354324L, -5082417586190057466L, -6554946940783144090L, -3610462176018822900L,
8959796931768911980L, -4251632352234989839L, 4922191169088134258L, -7282805902317830669L,
3869302430595840919L, 2517690626940415460L, 4056663221614950174L, 6429856319379397738L,
7298845553914383313L, 8179510284261677971L, 4282994537597585253L, 7300184601511783348L,
2596703774884172704L, 1089838915342514714L, 4323657609714862439L, 777826126579190548L,
-1902743089794461140L, -2460431043688989882L, -3261708534465890932L, 4007861469505443778L,
8067600139237526646L, 5717273542173905853L, 2938568334013652889L, -2972203304739218305L,
6544901794394958069L, 7013723936758841449L, -4215598453287525312L, -1454689091401951913L,
-5699280845313829011L, -9147984414924288540L, 5211986845656222459L, -1287642354429721659L,
-1509334943513011620L, -9000043616528857326L, -2902817511399216571L, -742823064588229527L,
-4937222449957498789L, -455679889440396397L, -6109470266907575296L, 5515435653880394376L,
5557224587324997029L, 8904139390487005840L, 6560726276686488510L, 6959949429287621625L,
-6055733513105375650L, 5762016937143172332L, -9186652929482643329L, -1105816448554330895L,
-8200377873547841359L, 9107473159863354619L, 3239950546973836199L, -8104429975176305012L,
3822949195131885242L, -5261390396129824777L, 9176101422921943895L, -5102541493993205418L,
-1254710019595692814L, -6668066200971989826L, -2118519708589929546L, 5428466612765068681L,
-6528627776941116598L, -5945449163896244174L, -3293290115918281076L, 6370347300411991230L,
-7043881693953271167L, 8078993941165238212L, 6894961504641498099L, -8798276497942360228L,
2276271091333773917L, -7184141741385833013L, -4787502691178107481L, 1255068205351917608L,
-8644146770023935609L, 5124094110137147339L, 4917075344795488880L, 3423242822219783102L,
1588924456880980404L, 8515495360312448868L, -5563691320675461929L, -2352238951654504517L,
-7416919543420127888L, 631412478604690114L, 689144891258712875L, -9001615284848119152L,
-6275065758899203088L, 8164387857252400515L, -4122060123604826739L, -2016541034210046261L,
-7178335877193796678L, 3354303106860129181L, 5731595363486898779L, -2874315602397298018L,
5386746429707619069L, 9036622191596156315L, -7950190733284789459L, -5741691593792426169L,
-8600462258998065159L, 5460142111961227035L, 276738899508534641L, 2358776514903881139L,
-837649704945720257L, -3608906204977108245L, 2960825464614526243L, 7339056324843827739L,
-5709958573878745135L, -5885403829221945248L, 6611935345917126768L, 2588814037559904539L
};
/**
* The index of next RNG seed.
*/
private static int nextSeed = -1;
/**
* High quality random number generator.
*/
private static final ThreadLocal random = ThreadLocal.withInitial(() -> {
synchronized(DEFAULT_SEEDS) {
// For the first RNG instance, we use the default seed of RNG algorithms.
if (nextSeed < 0) {
nextSeed = 0;
return new Random();
}
if (nextSeed < DEFAULT_SEEDS.length) {
return new Random(DEFAULT_SEEDS[nextSeed++]);
} else {
return new Random(generateSeed());
}
}
});
/**
* Dynamically determines the machine parameters of the floating-point arithmetic.
*/
private static class FPU {
final int RADIX;
int DIGITS;
final int FLOAT_DIGITS = 24;
int ROUND_STYLE;
int MACHEP;
final int FLOAT_MACHEP = -23;
int NEGEP;
final int FLOAT_NEGEP = -24;
final float FLOAT_EPSILON = (float) Math.pow(2.0, FLOAT_MACHEP);
final double EPSILON;
FPU() {
double beta, betain, betah, a, b, ZERO, ONE, TWO, temp, tempa, temp1;
int i, itemp;
ONE = 1;
TWO = ONE + ONE;
ZERO = ONE - ONE;
a = ONE;
temp1 = ONE;
while (temp1 - ONE == ZERO) {
a = a + a;
temp = a + ONE;
temp1 = temp - a;
}
b = ONE;
itemp = 0;
while (itemp == 0) {
b = b + b;
temp = a + b;
itemp = (int) (temp - a);
}
RADIX = itemp;
beta = RADIX;
DIGITS = 0;
b = ONE;
temp1 = ONE;
while (temp1 - ONE == ZERO) {
DIGITS = DIGITS + 1;
b = b * beta;
temp = b + ONE;
temp1 = temp - b;
}
ROUND_STYLE = 0;
betah = beta / TWO;
temp = a + betah;
if (temp - a != ZERO) {
ROUND_STYLE = 1;
}
tempa = a + beta;
temp = tempa + betah;
if ((ROUND_STYLE == 0) && (temp - tempa != ZERO)) {
ROUND_STYLE = 2;
}
NEGEP = DIGITS + 3;
betain = ONE / beta;
a = ONE;
for (i = 0; i < NEGEP; i++) {
a = a * betain;
}
b = a;
temp = ONE - a;
while (temp - ONE == ZERO) {
a = a * beta;
NEGEP = NEGEP - 1;
temp = ONE - a;
}
NEGEP = -(NEGEP);
MACHEP = -(DIGITS) - 3;
a = b;
temp = ONE + a;
while (temp - ONE == ZERO) {
a = a * beta;
MACHEP = MACHEP + 1;
temp = ONE + a;
}
EPSILON = a;
}
}
/**
* Private constructor to prevent instance creation.
*/
private MathEx() {
}
/**
* log(2), used in log2().
*/
private static final double LOG2 = Math.log(2);
/**
* Log of base 2.
* @param x a real number.
* @return the value log2(x)
.
*/
public static double log2(double x) {
return Math.log(x) / LOG2;
}
/**
* Returns natural log without underflow.
* @param x a real number.
* @return the value log(x)
.
*/
public static double log(double x) {
double y = -690.7755;
if (x > 1E-300) {
y = Math.log(x);
}
return y;
}
/**
* Returns natural log(1+exp(x)) without overflow.
* @param x a real number.
* @return the value log(1+exp(x))
.
*/
public static double log1pe(double x) {
double y = x;
if (x <= 15) {
y = Math.log1p(Math.exp(x));
}
return y;
}
/**
* Returns true if x is an integer.
* @param x a real number.
* @return true if x is an integer.
*/
public static boolean isInt(float x) {
return (x == (float) Math.floor(x)) && !Float.isInfinite(x);
}
/**
* Returns true if x is an integer.
* @param x a real number.
* @return true if x is an integer.
*/
public static boolean isInt(double x) {
return (x == Math.floor(x)) && !Double.isInfinite(x);
}
/**
* Returns true if two double values equals to each other in the system precision.
* @param a a double value.
* @param b a double value.
* @return true if two double values equals to each other in the system precision
*/
public static boolean equals(double a, double b) {
if (a == b) {
return true;
}
double absa = abs(a);
double absb = abs(b);
return abs(a - b) <= Math.min(absa, absb) * 2.2204460492503131e-16;
}
/**
* Logistic sigmoid function 1 / (1 + exp(-x))
.
* @param x a real number.
* @return the sigmoid function.
*/
public static double sigmoid(double x) {
// clip x in [-36, 36] to prevent overflow/underflow.
x = Math.max(-36, Math.min(x, 36));
return 1.0 / (1.0 + exp(-x));
}
/**
* Returns x * x.
* @param x a real number.
* @return the square of x.
*/
public static double pow2(double x) {
return x * x;
}
/**
* Returns true if x is a power of 2.
* @param x a real number.
* @return true if x is a power of 2.
*/
public static boolean isPower2(int x) {
return x > 0 && (x & (x - 1)) == 0;
}
/**
* Returns true if n is probably prime, false if it's definitely composite.
* This implements Miller-Rabin primality test.
* @param n an odd integer to be tested for primality
* @param k the number of rounds of primality test to perform.
* With larger number of rounds, the test is more
* accurate but also takes longer time.
* @return true if n is probably prime, false if it's definitely composite.
*/
public static boolean isProbablePrime(long n, int k) {
return isProbablePrime(n, k, random.get());
}
/**
* Returns true if n is probably prime, false if it's definitely composite.
* This implements Miller-Rabin primality test.
* @param n an odd integer to be tested for primality
* @param k the number of rounds of primality test to perform.
* With larger number of rounds, the test is more
* accurate but also takes longer time.
* @param rng random number generator
* @return true if n is probably prime, false if it's definitely composite.
*/
private static boolean isProbablePrime(long n, int k, Random rng) {
if (n <= 1 || n == 4)
return false;
if (n <= 3)
return true;
// Find r such that n = 2^d * r + 1 for some r >= 1
int s = 0;
long d = n - 1;
while (d % 2 == 0) {
s++;
d = d / 2;
}
for (int i = 0; i < k; i++) {
long a = 2 + rng.nextLong() % (n-4);
long x = power(a, d, n);
if (x == 1 || x == n -1)
continue;
int r = 0;
for (; r < s; r++) {
x = (x * x) % n;
if (x == 1) return false;
if (x == n - 1) break;
}
// None of the steps made x equal n-1.
if (r == s) return false;
}
return true;
}
/**
* Modular exponentiation xy % p
.
* @param x the base.
* @param y the exponent.
* @param p the modular.
* @return the modular exponentation.
*/
private static long power(long x, long y, long p)
{
long res = 1; // Initialize result
x = x % p; // Update x if it is more than or
// equal to p
while (y > 0) {
// If y is odd, multiply x with result
if ((y & 1) == 1) res = (res*x) % p;
// y must be even now
y = y>>1; // y = y/2
x = (x*x) % p;
}
return res;
}
/**
* Round a double vale to given digits such as 10^n, where n is a positive
* or negative integer.
* @param x a real number.
* @param decimal the number of digits to round to.
* @return the rounded value.
*/
public static double round(double x, int decimal) {
if (decimal < 0) {
return Math.round(x / Math.pow(10, -decimal)) * Math.pow(10, -decimal);
} else {
return Math.round(x * Math.pow(10, decimal)) / Math.pow(10, decimal);
}
}
/**
* The factorial of n.
*
* @param n a positive integer.
* @return the factorial returned as double but is, numerically, an integer.
* Numerical rounding may make this an approximation after n = 21.
*/
public static double factorial(int n) {
if (n < 0) {
throw new IllegalArgumentException("n has to be non-negative.");
}
double f = 1.0;
for (int i = 2; i <= n; i++) {
f *= i;
}
return f;
}
/**
* The log of factorial of n.
*
* @param n a positive integer.
* @return the log of factorial .
*/
public static double lfactorial(int n) {
if (n < 0) {
throw new IllegalArgumentException(String.format("n has to be non-negative: %d", n));
}
double f = 0.0;
for (int i = 2; i <= n; i++) {
f += Math.log(i);
}
return f;
}
/**
* The n choose k. Returns 0 if n is less than k.
* @param n the total number of objects in the set.
* @param k the number of choosing objects from the set.
* @return the number of combinations.
*/
public static double choose(int n, int k) {
if (n < 0 || k < 0) {
throw new IllegalArgumentException(String.format("Invalid n = %d, k = %d", n, k));
}
if (n < k) {
return 0.0;
}
return floor(0.5 + exp(lchoose(n, k)));
}
/**
* The log of n choose k.
* @param n the total number of objects in the set.
* @param k the number of choosing objects from the set.
* @return the log of the number of combinations.
*/
public static double lchoose(int n, int k) {
if (k < 0 || k > n) {
throw new IllegalArgumentException(String.format("Invalid n = %d, k = %d", n, k));
}
return lfactorial(n) - lfactorial(k) - lfactorial(n - k);
}
/**
* Returns a random number to seed other random number generators.
* @return a random number to seed other random number generators.
*/
public static long generateSeed() {
byte[] bytes = generateSeed(Long.BYTES);
long seed = 0;
for (int i = 0; i < Long.BYTES; i++) {
seed <<= 8;
seed |= (bytes[i] & 0xFF);
}
return seed;
}
/**
* Returns the given number of random bytes to seed other random number
* generators.
* @param numBytes the number of seed bytes to generate.
* @return the seed bytes.
*/
public static byte[] generateSeed(int numBytes) {
synchronized(seedRNG) {
return seedRNG.generateSeed(numBytes);
}
}
/**
* Returns a stream of random numbers to be used as RNG seeds.
* @return a stream of random numbers to be used as RNG seeds.
*/
public static LongStream seeds() {
return LongStream.generate(MathEx::generateSeed).sequential();
}
/**
* Initialize the random number generator with a seed.
* @param seed the RNG seed.
*/
public static void setSeed(long seed) {
random.get().setSeed(seed);
}
/**
* Returns a probably prime number greater than n.
* @param n the returned value should be greater than n.
* @param k the number of rounds of primality test to perform.
* With larger number of rounds, the test is more
* accurate but also takes longer time.
* @return a probably prime number greater than n.
*/
public static long probablePrime(long n, int k) {
return probablePrime(n, k, random.get());
}
/**
* Returns a probably prime number.
* @param n the returned value should be greater than n.
* @param k the number of rounds of primality test to perform.
* With larger number of rounds, the test is more
* accurate but also takes longer time.
* @param rng the random number generator.
* @return a probably prime number greater than n.
*/
private static long probablePrime(long n, int k, Random rng) {
long seed = n + rng.nextInt(899999963); // The largest prime less than 9*10^8
for (int i = 0; i < 4096; i++) {
if (isProbablePrime(seed, k, rng)) break;
seed = n + rng.nextInt(899999963);
}
return seed;
}
/**
* Given a set of n probabilities, generate a random number in [0, n).
*
* @param prob probabilities of size n. The prob argument can be used to
* give a vector of weights for obtaining the elements of the vector being
* sampled. They need not sum to one, but they should be non-negative and
* not all zero.
* @return a random integer in [0, n).
*/
public static int random(double[] prob) {
int[] ans = random(prob, 1);
return ans[0];
}
/**
* Given a set of m probabilities, draw with replacement a set of n random
* number in [0, m).
*
* @param prob probabilities of size n. The prob argument can be used to
* give a vector of weights for obtaining the elements of the vector being
* sampled. They need not sum to one, but they should be non-negative and
* not all zero.
* @param n the number of random numbers.
* @return random numbers in range of [0, m).
*/
public static int[] random(double[] prob, int n) {
// set up alias table
double[] q = new double[prob.length];
for (int i = 0; i < prob.length; i++) {
q[i] = prob[i] * prob.length;
}
// initialize a with indices
int[] a = new int[prob.length];
for (int i = 0; i < prob.length; i++) {
a[i] = i;
}
// set up H and L
int[] HL = new int[prob.length];
int head = 0;
int tail = prob.length - 1;
for (int i = 0; i < prob.length; i++) {
if (q[i] >= 1.0) {
HL[head++] = i;
} else {
HL[tail--] = i;
}
}
while (head != 0 && tail != prob.length - 1) {
int j = HL[tail + 1];
int k = HL[head - 1];
a[j] = k;
q[k] += q[j] - 1;
tail++; // remove j from L
if (q[k] < 1.0) {
HL[tail--] = k; // add k to L
head--; // remove k
}
}
// generate sample
int[] ans = new int[n];
for (int i = 0; i < n; i++) {
double rU = random() * prob.length;
int k = (int) (rU);
rU -= k; /* rU becomes rU-[rU] */
if (rU < q[k]) {
ans[i] = k;
} else {
ans[i] = a[k];
}
}
return ans;
}
/**
* Generate a random number in [0, 1).
* @return a random number.
*/
public static double random() {
return random.get().nextDouble();
}
/**
* Generate n random numbers in [0, 1).
* @param n the number of random numbers.
* @return the random numbers.
*/
public static double[] random(int n) {
double[] x = new double[n];
random.get().nextDoubles(x);
return x;
}
/**
* Generate a uniform random number in the range [lo, hi).
*
* @param lo lower limit of range
* @param hi upper limit of range
* @return a uniform random number in the range [lo, hi)
*/
public static double random(double lo, double hi) {
return random.get().nextDouble(lo, hi);
}
/**
* Generate uniform random numbers in the range [lo, hi).
*
* @param lo lower limit of range
* @param hi upper limit of range
* @param n the number of random numbers.
* @return uniform random numbers in the range [lo, hi)
*/
public static double[] random(double lo, double hi, int n) {
double[] x = new double[n];
random.get().nextDoubles(x, lo, hi);
return x;
}
/**
* Returns a random long integer.
* @return a random long integer.
*/
public static long randomLong() {
return random.get().nextLong();
}
/**
* Returns a random integer in [0, n).
* @param n the upper bound of random number.
* @return a random integer.
*/
public static int randomInt(int n) {
return random.get().nextInt(n);
}
/**
* Returns a random integer in [lo, hi).
*
* @param lo lower limit of range
* @param hi upper limit of range
* @return a uniform random number in the range [lo, hi)
*/
public static int randomInt(int lo, int hi) {
int w = hi - lo;
return lo + random.get().nextInt(w);
}
/**
* Returns a permutation of (0, 1, 2, ..., n-1)
.
*
* @param n the upper bound.
* @return the permutation of (0, 1, 2, ..., n-1)
.
*/
public static int[] permutate(int n) {
return random.get().permutate(n);
}
/**
* Permutates an array.
* @param x the array.
*/
public static void permutate(int[] x) {
random.get().permutate(x);
}
/**
* Permutates an array.
* @param x the array.
*/
public static void permutate(float[] x) {
random.get().permutate(x);
}
/**
* Permutates an array.
* @param x the array.
*/
public static void permutate(double[] x) {
random.get().permutate(x);
}
/**
* Permutates an array.
* @param x the array.
*/
public static void permutate(Object[] x) {
random.get().permutate(x);
}
/**
* The softmax function without overflow. The function takes as
* an input vector of K real numbers, and normalizes it into a
* probability distribution consisting of K probabilities
* proportional to the exponentials of the input numbers.
* That is, prior to applying softmax, some vector components
* could be negative, or greater than one; and might not sum
* to 1; but after applying softmax, each component will be
* in the interval (0,1), and the components will add up to 1,
* so that they can be interpreted as probabilities. Furthermore,
* the larger input components will correspond to larger probabilities.
*
* @param posteriori the input/output vector.
* @return the index of largest posteriori probability.
*/
public static int softmax(double[] posteriori) {
return softmax(posteriori, posteriori.length);
}
/**
* The softmax function without overflow. The function takes as
* an input vector of K real numbers, and normalizes it into a
* probability distribution consisting of K probabilities
* proportional to the exponentials of the input numbers.
* That is, prior to applying softmax, some vector components
* could be negative, or greater than one; and might not sum
* to 1; but after applying softmax, each component will be
* in the interval (0,1), and the components will add up to 1,
* so that they can be interpreted as probabilities. Furthermore,
* the larger input components will correspond to larger probabilities.
*
* @param x the input/output vector.
* @param k uses only first k components of input vector.
* @return the index of largest posteriori probability.
*/
public static int softmax(double[] x, int k) {
int y = -1;
double max = Double.NEGATIVE_INFINITY;
for (int i = 0; i < k; i++) {
if (x[i] > max) {
max = x[i];
y = i;
}
}
double Z = 0.0;
for (int i = 0; i < k; i++) {
double out = exp(x[i] - max);
x[i] = out;
Z += out;
}
for (int i = 0; i < k; i++) {
x[i] /= Z;
}
return y;
}
/**
* Combines the arguments to form a vector.
* @param x the vector elements.
* @return the vector.
*/
public static int[] c(int... x) {
return x;
}
/**
* Combines the arguments to form a vector.
* @param x the vector elements.
* @return the vector.
*/
public static float[] c(float... x) {
return x;
}
/**
* Combines the arguments to form a vector.
* @param x the vector elements.
* @return the vector.
*/
public static double[] c(double... x) {
return x;
}
/**
* Combines the arguments to form a vector.
* @param x the vector elements.
* @return the vector.
*/
public static String[] c(String... x) {
return x;
}
/**
* Concatenates multiple vectors into one.
* @param list the vectors.
* @return the concatenated vector.
*/
public static int[] c(int[]... list) {
int n = 0;
for (int[] x : list) n += x.length;
int[] y = new int[n];
int pos = 0;
for (int[] x: list) {
System.arraycopy(x, 0, y, pos, x.length);
pos += x.length;
}
return y;
}
/**
* Concatenates multiple vectors into one.
* @param list the vectors.
* @return the concatenated vector.
*/
public static float[] c(float[]... list) {
int n = 0;
for (float[] x : list) n += x.length;
float[] y = new float[n];
int pos = 0;
for (float[] x: list) {
System.arraycopy(x, 0, y, pos, x.length);
pos += x.length;
}
return y;
}
/**
* Concatenates multiple vectors into one.
* @param list the vectors.
* @return the concatenated vector.
*/
public static double[] c(double[]... list) {
int n = 0;
for (double[] x : list) n += x.length;
double[] y = new double[n];
int pos = 0;
for (double[] x: list) {
System.arraycopy(x, 0, y, pos, x.length);
pos += x.length;
}
return y;
}
/**
* Concatenates multiple vectors into one array of strings.
* @param list the vectors.
* @return the concatenated vector.
*/
public static String[] c(String[]... list) {
int n = 0;
for (String[] x : list) n += x.length;
String[] y = new String[n];
int pos = 0;
for (String[] x: list) {
System.arraycopy(x, 0, y, pos, x.length);
pos += x.length;
}
return y;
}
/**
* Concatenates vectors by columns.
* @param x the vectors.
* @return the concatenated vector.
*/
public static int[] cbind(int[]... x) {
return c(x);
}
/**
* Concatenates vectors by columns.
* @param x the vectors.
* @return the concatenated vector.
*/
public static float[] cbind(float[]... x) {
return c(x);
}
/**
* Concatenates vectors by columns.
* @param x the vectors.
* @return the concatenated vector.
*/
public static double[] cbind(double[]... x) {
return c(x);
}
/**
* Concatenates vectors by columns.
* @param x the vectors.
* @return the concatenated vector.
*/
public static String[] cbind(String[]... x) {
return c(x);
}
/**
* Concatenates vectors by rows.
* @param x the vectors.
* @return the matrix.
*/
public static int[][] rbind(int[]... x) {
return x;
}
/**
* Concatenates vectors by rows.
* @param x the vectors.
* @return the matrix.
*/
public static float[][] rbind(float[]... x) {
return x;
}
/**
* Concatenates vectors by rows.
* @param x the vectors.
* @return the matrix.
*/
public static double[][] rbind(double[]... x) {
return x;
}
/**
* Concatenates vectors by rows.
* @param x the vectors.
* @return the matrix.
*/
public static String[][] rbind(String[]... x) {
return x;
}
/**
* Returns a slice of data for given indices.
* @param data the array.
* @param index the indices of selected elements.
* @param the data type of elements.
* @return the selected elements.
*/
public static E[] slice(E[] data, int[] index) {
int n = index.length;
@SuppressWarnings("unchecked")
E[] x = (E[]) java.lang.reflect.Array.newInstance(data.getClass().getComponentType(), n);
for (int i = 0; i < n; i++) {
x[i] = data[index[i]];
}
return x;
}
/**
* Returns a slice of data for given indices.
* @param data the array.
* @param index the indices of selected elements.
* @return the selected elements.
*/
public static int[] slice(int[] data, int[] index) {
int n = index.length;
int[] x = new int[n];
for (int i = 0; i < n; i++) {
x[i] = data[index[i]];
}
return x;
}
/**
* Returns a slice of data for given indices.
* @param data the array.
* @param index the indices of selected elements.
* @return the selected elements.
*/
public static float[] slice(float[] data, int[] index) {
int n = index.length;
float[] x = new float[n];
for (int i = 0; i < n; i++) {
x[i] = data[index[i]];
}
return x;
}
/**
* Returns a slice of data for given indices.
* @param data the array.
* @param index the indices of selected elements.
* @return the selected elements.
*/
public static double[] slice(double[] data, int[] index) {
int n = index.length;
double[] x = new double[n];
for (int i = 0; i < n; i++) {
x[i] = data[index[i]];
}
return x;
}
/**
* Determines if the polygon contains the point.
*
* @param polygon the vertices of polygon.
* @param point the point.
* @return true if the Polygon contains the point.
*/
public static boolean contains(double[][] polygon, double[] point) {
return contains(polygon, point[0], point[1]);
}
/**
* Determines if the polygon contains the point.
*
* @param polygon the vertices of polygon.
* @param x the x coordinate of point.
* @param y the y coordinate of point.
* @return true if the Polygon contains the point.
*/
public static boolean contains(double[][] polygon, double x, double y) {
if (polygon.length <= 2) {
return false;
}
int hits = 0;
int n = polygon.length;
double lastx = polygon[n - 1][0];
double lasty = polygon[n - 1][1];
double curx, cury;
// Walk the edges of the polygon
for (int i = 0; i < n; lastx = curx, lasty = cury, i++) {
curx = polygon[i][0];
cury = polygon[i][1];
if (cury == lasty) {
continue;
}
double leftx;
if (curx < lastx) {
if (x >= lastx) {
continue;
}
leftx = curx;
} else {
if (x >= curx) {
continue;
}
leftx = lastx;
}
double test1, test2;
if (cury < lasty) {
if (y < cury || y >= lasty) {
continue;
}
if (x < leftx) {
hits++;
continue;
}
test1 = x - curx;
test2 = y - cury;
} else {
if (y < lasty || y >= cury) {
continue;
}
if (x < leftx) {
hits++;
continue;
}
test1 = x - lastx;
test2 = y - lasty;
}
if (test1 < (test2 / (lasty - cury) * (lastx - curx))) {
hits++;
}
}
return ((hits & 1) != 0);
}
/**
* Returns a new array without the specified value.
* @param a an input array.
* @param value the value to omit.
* @return a new array without the specified value.
*/
public static int[] omit(int[] a, int value) {
int n = 0;
for (int x : a) {
if (x != value) n++;
}
int i = 0;
int[] b = new int[n];
for (int x : a) {
if (x != value) b[i++] = x;
}
return b;
}
/**
* Returns a new array without the specified value.
* @param a an input array.
* @param value the value to omit.
* @return a new array without the specified value.
*/
public static float[] omit(float[] a, float value) {
int n = 0;
for (float x : a) {
if (x != value) n++;
}
int i = 0;
float[] b = new float[n];
for (float x : a) {
if (x != value) b[i++] = x;
}
return b;
}
/**
* Returns a new array without the specified value.
* @param a an input array.
* @param value the value to omit.
* @return a new array without the specified value.
*/
public static double[] omit(double[] a, double value) {
int n = 0;
for (double x : a) {
if (x != value) n++;
}
int i = 0;
double[] b = new double[n];
for (double x : a) {
if (x != value) b[i++] = x;
}
return b;
}
/**
* Returns a new array without NaN values.
* @param a an input array that may contain NaN.
* @return a new array without NaN values.
*/
public static float[] omitNaN(float[] a) {
int n = 0;
for (float x : a) {
if (!Float.isNaN(x)) n++;
}
int i = 0;
float[] b = new float[n];
for (float x : a) {
if (!Float.isNaN(x)) b[i++] = x;
}
return b;
}
/**
* Returns a new array without NaN values.
* @param a an input array that may contain NaN.
* @return a new array without NaN values.
*/
public static double[] omitNaN(double[] a) {
int n = 0;
for (double x : a) {
if (!Double.isNaN(x)) n++;
}
int i = 0;
double[] b = new double[n];
for (double x : a) {
if (!Double.isNaN(x)) b[i++] = x;
}
return b;
}
/**
* Reverses the order of the elements in the specified array.
* @param a an array to reverse.
*/
public static void reverse(int[] a) {
int i = 0, j = a.length - 1;
while (i < j) {
Sort.swap(a, i++, j--); // code for swap not shown, but easy enough
}
}
/**
* Reverses the order of the elements in the specified array.
* @param a the array to reverse.
*/
public static void reverse(float[] a) {
int i = 0, j = a.length - 1;
while (i < j) {
Sort.swap(a, i++, j--); // code for swap not shown, but easy enough
}
}
/**
* Reverses the order of the elements in the specified array.
* @param a the array to reverse.
*/
public static void reverse(double[] a) {
int i = 0, j = a.length - 1;
while (i < j) {
Sort.swap(a, i++, j--); // code for swap not shown, but easy enough
}
}
/**
* Reverses the order of the elements in the specified array.
* @param a the array to reverse.
* @param the data type of array elements.
*/
public static void reverse(T[] a) {
int i = 0, j = a.length - 1;
while (i < j) {
Sort.swap(a, i++, j--);
}
}
/**
* Returns the mode of the array, which is the most frequent element.
* If there are multiple modes, one of them will be returned.
*
* @param a the array. The order of elements will be changed on output.
* @return the mode.
*/
public static int mode(int[] a) {
Arrays.sort(a);
int mode = -1;
int count = 0;
int currentValue = a[0];
int currentCount = 1;
for (int i = 1; i < a.length; i++) {
if (a[i] != currentValue) {
if (currentCount > count) {
mode = currentValue;
count = currentCount;
}
currentValue = a[i];
currentCount = 1;
} else {
currentCount++;
}
}
if (currentCount > count) {
mode = currentValue;
}
return mode;
}
/**
* Returns the minimum of 3 integer numbers.
* @param a a number.
* @param b a number.
* @param c a number.
* @return the minimum.
*/
public static int min(int a, int b, int c) {
return Math.min(Math.min(a, b), c);
}
/**
* Returns the minimum of 3 float numbers.
* @param a a number.
* @param b a number.
* @param c a number.
* @return the minimum.
*/
public static float min(float a, float b, float c) {
return Math.min(Math.min(a, b), c);
}
/**
* Returns the minimum of 3 double numbers.
* @param a a number.
* @param b a number.
* @param c a number.
* @return the minimum.
*/
public static double min(double a, double b, double c) {
return Math.min(Math.min(a, b), c);
}
/**
* Returns the minimum of 4 integer numbers.
* @param a a number.
* @param b a number.
* @param c a number.
* @param d a number.
* @return the minimum.
*/
public static int min(int a, int b, int c, int d) {
return Math.min(Math.min(Math.min(a, b), c), d);
}
/**
* Returns the minimum of 4 float numbers.
* @param a a number.
* @param b a number.
* @param c a number.
* @param d a number.
* @return the minimum.
*/
public static float min(float a, float b, float c, float d) {
return Math.min(Math.min(Math.min(a, b), c), d);
}
/**
* Returns the minimum of 4 double numbers.
* @param a a number.
* @param b a number.
* @param c a number.
* @param d a number.
* @return the minimum.
*/
public static double min(double a, double b, double c, double d) {
return Math.min(Math.min(Math.min(a, b), c), d);
}
/**
* Returns the maximum of 3 integer numbers.
* @param a a number.
* @param b a number.
* @param c a number.
* @return the maximum.
*/
public static int max(int a, int b, int c) {
return Math.max(Math.max(a, b), c);
}
/**
* Returns the maximum of 4 float numbers.
* @param a a number.
* @param b a number.
* @param c a number.
* @return the maximum.
*/
public static float max(float a, float b, float c) {
return Math.max(Math.max(a, b), c);
}
/**
* Returns the maximum of 4 double numbers.
* @param a a number.
* @param b a number.
* @param c a number.
* @return the maximum.
*/
public static double max(double a, double b, double c) {
return Math.max(Math.max(a, b), c);
}
/**
* Returns the maximum of 4 integer numbers.
* @param a a number.
* @param b a number.
* @param c a number.
* @param d a number.
* @return the maximum.
*/
public static int max(int a, int b, int c, int d) {
return Math.max(Math.max(Math.max(a, b), c), d);
}
/**
* Returns the maximum of 4 float numbers.
* @param a a number.
* @param b a number.
* @param c a number.
* @param d a number.
* @return the maximum.
*/
public static float max(float a, float b, float c, float d) {
return Math.max(Math.max(Math.max(a, b), c), d);
}
/**
* Returns the maximum of 4 double numbers.
* @param a a number.
* @param b a number.
* @param c a number.
* @param d a number.
* @return the maximum.
*/
public static double max(double a, double b, double c, double d) {
return Math.max(Math.max(Math.max(a, b), c), d);
}
/**
* Returns the minimum value of an array.
* @param array the array.
* @return the minimum.
*/
public static int min(int[] array) {
int min = array[0];
for (int n : array) {
if (n < min) {
min = n;
}
}
return min;
}
/**
* Returns the minimum value of an array.
* @param array the array.
* @return the minimum.
*/
public static float min(float[] array) {
float min = Float.POSITIVE_INFINITY;
for (float n : array) {
if (n < min) {
min = n;
}
}
return min;
}
/**
* Returns the minimum value of an array.
* @param array the array.
* @return the minimum.
*/
public static double min(double[] array) {
double min = Double.POSITIVE_INFINITY;
for (double n : array) {
if (n < min) {
min = n;
}
}
return min;
}
/**
* Returns the index of minimum value of an array.
* @param array the array.
* @return the index of minimum.
*/
public static int whichMin(int[] array) {
int min = array[0];
int which = 0;
for (int i = 1; i < array.length; i++) {
if (array[i] < min) {
min = array[i];
which = i;
}
}
return which;
}
/**
* Returns the index of minimum value of an array.
* @param array the array.
* @return the index of minimum.
*/
public static int whichMin(float[] array) {
float min = Float.POSITIVE_INFINITY;
int which = 0;
for (int i = 0; i < array.length; i++) {
if (array[i] < min) {
min = array[i];
which = i;
}
}
return which;
}
/**
* Returns the index of minimum value of an array.
* @param array the array.
* @return the index of minimum.
*/
public static int whichMin(double[] array) {
double min = Double.POSITIVE_INFINITY;
int which = 0;
for (int i = 0; i < array.length; i++) {
if (array[i] < min) {
min = array[i];
which = i;
}
}
return which;
}
/**
* Returns the maximum value of an array.
* @param array the array.
* @return the index of maximum.
*/
public static int max(int[] array) {
int max = array[0];
for (int n : array) {
if (n > max) {
max = n;
}
}
return max;
}
/**
* Returns the maximum value of an array.
* @param array the array.
* @return the index of maximum.
*/
public static float max(float[] array) {
float max = Float.NEGATIVE_INFINITY;
for (float n : array) {
if (n > max) {
max = n;
}
}
return max;
}
/**
* Returns the maximum value of an array.
* @param array the array.
* @return the index of maximum.
*/
public static double max(double[] array) {
double max = Double.NEGATIVE_INFINITY;
for (double n : array) {
if (n > max) {
max = n;
}
}
return max;
}
/**
* Returns the index of maximum value of an array.
* @param array the array.
* @return the index of maximum.
*/
public static int whichMax(int[] array) {
int max = array[0];
int which = 0;
for (int i = 1; i < array.length; i++) {
if (array[i] > max) {
max = array[i];
which = i;
}
}
return which;
}
/**
* Returns the index of maximum value of an array.
* @param array the array.
* @return the index of maximum.
*/
public static int whichMax(float[] array) {
float max = Float.NEGATIVE_INFINITY;
int which = 0;
for (int i = 0; i < array.length; i++) {
if (array[i] > max) {
max = array[i];
which = i;
}
}
return which;
}
/**
* Returns the index of maximum value of an array.
* @param array the array.
* @return the index of maximum.
*/
public static int whichMax(double[] array) {
double max = Double.NEGATIVE_INFINITY;
int which = 0;
for (int i = 0; i < array.length; i++) {
if (array[i] > max) {
max = array[i];
which = i;
}
}
return which;
}
/**
* Returns the minimum of a matrix.
* @param matrix the matrix.
* @return the minimum.
*/
public static int min(int[][] matrix) {
int min = matrix[0][0];
for (int[] x : matrix) {
for (int y : x) {
if (min > y) {
min = y;
}
}
}
return min;
}
/**
* Returns the minimum of a matrix.
* @param matrix the matrix.
* @return the minimum.
*/
public static double min(double[][] matrix) {
double min = Double.POSITIVE_INFINITY;
for (double[] x : matrix) {
for (double y : x) {
if (min > y) {
min = y;
}
}
}
return min;
}
/**
* Returns the maximum of a matrix.
* @param matrix the matrix.
* @return the maximum.
*/
public static int max(int[][] matrix) {
int max = matrix[0][0];
for (int[] x : matrix) {
for (int y : x) {
if (max < y) {
max = y;
}
}
}
return max;
}
/**
* Returns the maximum of a matrix.
* @param matrix the matrix.
* @return the maximum.
*/
public static double max(double[][] matrix) {
double max = Double.NEGATIVE_INFINITY;
for (double[] x : matrix) {
for (double y : x) {
if (max < y) {
max = y;
}
}
}
return max;
}
/**
* Returns the index of minimum value of a matrix.
* @param matrix the matrix.
* @return the index of minimum.
*/
public static IntPair whichMin(double[][] matrix) {
double min = Double.POSITIVE_INFINITY;
int whichRow = 0;
int whichCol = 0;
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[i].length; j++) {
if (matrix[i][j] < min) {
min = matrix[i][j];
whichRow = i;
whichCol = j;
}
}
}
return new IntPair(whichRow, whichCol);
}
/**
* Returns the index of maximum value of a matrix.
* @param matrix the matrix.
* @return the index of maximum.
*/
public static IntPair whichMax(double[][] matrix) {
double max = Double.NEGATIVE_INFINITY;
int whichRow = 0;
int whichCol = 0;
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[i].length; j++) {
if (matrix[i][j] > max) {
max = matrix[i][j];
whichRow = i;
whichCol = j;
}
}
}
return new IntPair(whichRow, whichCol);
}
/**
* Returns the matrix transpose.
* @param matrix the matrix.
* @return the transpose.
*/
public static double[][] transpose(double[][] matrix) {
int m = matrix.length;
int n = matrix[0].length;
double[][] t = new double[n][m];
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
t[j][i] = matrix[i][j];
}
}
return t;
}
/**
* Returns the row minimum of a matrix.
* @param matrix the matrix.
* @return the row minimums.
*/
public static int[] rowMin(int[][] matrix) {
int[] x = new int[matrix.length];
for (int i = 0; i < x.length; i++) {
x[i] = min(matrix[i]);
}
return x;
}
/**
* Returns the row maximum of a matrix.
* @param matrix the matrix.
* @return the row maximums.
*/
public static int[] rowMax(int[][] matrix) {
int[] x = new int[matrix.length];
for (int i = 0; i < x.length; i++) {
x[i] = max(matrix[i]);
}
return x;
}
/**
* Returns the row sums of a matrix.
* @param matrix the matrix.
* @return the row sums.
*/
public static long[] rowSums(int[][] matrix) {
long[] x = new long[matrix.length];
for (int i = 0; i < x.length; i++) {
x[i] = sum(matrix[i]);
}
return x;
}
/**
* Returns the row minimum of a matrix.
* @param matrix the matrix.
* @return the row minimums.
*/
public static double[] rowMin(double[][] matrix) {
double[] x = new double[matrix.length];
for (int i = 0; i < x.length; i++) {
x[i] = min(matrix[i]);
}
return x;
}
/**
* Returns the row maximum of a matrix.
* @param matrix the matrix.
* @return the row maximums.
*/
public static double[] rowMax(double[][] matrix) {
double[] x = new double[matrix.length];
for (int i = 0; i < x.length; i++) {
x[i] = max(matrix[i]);
}
return x;
}
/**
* Returns the row sums of a matrix.
* @param matrix the matrix.
* @return the row sums.
*/
public static double[] rowSums(double[][] matrix) {
double[] x = new double[matrix.length];
for (int i = 0; i < x.length; i++) {
x[i] = sum(matrix[i]);
}
return x;
}
/**
* Returns the row means of a matrix.
* @param matrix the matrix.
* @return the row means.
*/
public static double[] rowMeans(double[][] matrix) {
double[] x = new double[matrix.length];
for (int i = 0; i < x.length; i++) {
x[i] = mean(matrix[i]);
}
return x;
}
/**
* Returns the row standard deviations of a matrix.
* @param matrix the matrix.
* @return the row standard deviations.
*/
public static double[] rowSds(double[][] matrix) {
double[] x = new double[matrix.length];
for (int i = 0; i < x.length; i++) {
x[i] = sd(matrix[i]);
}
return x;
}
/**
* Returns the column minimum of a matrix.
* @param matrix the matrix.
* @return the column minimums.
*/
public static int[] colMin(int[][] matrix) {
int[] x = new int[matrix[0].length];
Arrays.fill(x, Integer.MAX_VALUE);
for (int[] row : matrix) {
for (int j = 0; j < x.length; j++) {
if (x[j] > row[j]) {
x[j] = row[j];
}
}
}
return x;
}
/**
* Returns the column maximum of a matrix.
* @param matrix the matrix.
* @return the column maximums.
*/
public static int[] colMax(int[][] matrix) {
int[] x = new int[matrix[0].length];
Arrays.fill(x, Integer.MIN_VALUE);
for (int[] row : matrix) {
for (int j = 0; j < x.length; j++) {
if (x[j] < row[j]) {
x[j] = row[j];
}
}
}
return x;
}
/**
* Returns the column sums of a matrix.
* @param matrix the matrix.
* @return the column sums.
*/
public static long[] colSums(int[][] matrix) {
long[] x = new long[matrix[0].length];
for (int[] row : matrix) {
for (int j = 0; j < x.length; j++) {
x[j] += row[j];
}
}
return x;
}
/**
* Returns the column minimum of a matrix.
* @param matrix the matrix.
* @return the column minimums.
*/
public static double[] colMin(double[][] matrix) {
double[] x = new double[matrix[0].length];
Arrays.fill(x, Double.POSITIVE_INFINITY);
for (double[] row : matrix) {
for (int j = 0; j < x.length; j++) {
if (x[j] > row[j]) {
x[j] = row[j];
}
}
}
return x;
}
/**
* Returns the column maximum of a matrix.
* @param matrix the matrix.
* @return the column maximums.
*/
public static double[] colMax(double[][] matrix) {
double[] x = new double[matrix[0].length];
Arrays.fill(x, Double.NEGATIVE_INFINITY);
for (double[] row : matrix) {
for (int j = 0; j < x.length; j++) {
if (x[j] < row[j]) {
x[j] = row[j];
}
}
}
return x;
}
/**
* Returns the column sums of a matrix.
* @param matrix the matrix.
* @return the column sums.
*/
public static double[] colSums(double[][] matrix) {
double[] x = matrix[0].clone();
for (int i = 1; i < matrix.length; i++) {
for (int j = 0; j < x.length; j++) {
x[j] += matrix[i][j];
}
}
return x;
}
/**
* Returns the column means of a matrix.
* @param matrix the matrix.
* @return the column means.
*/
public static double[] colMeans(double[][] matrix) {
double[] x = matrix[0].clone();
for (int i = 1; i < matrix.length; i++) {
for (int j = 0; j < x.length; j++) {
x[j] += matrix[i][j];
}
}
scale(1.0 / matrix.length, x);
return x;
}
/**
* Returns the column standard deviations of a matrix.
* @param matrix the matrix.
* @return the column standard deviations.
*/
public static double[] colSds(double[][] matrix) {
if (matrix.length < 2) {
throw new IllegalArgumentException("matrix length is less than 2.");
}
int p = matrix[0].length;
double[] sum = new double[p];
double[] sumsq = new double[p];
for (double[] row : matrix) {
for (int i = 0; i < p; i++) {
sum[i] += row[i];
sumsq[i] += row[i] * row[i];
}
}
int n = matrix.length - 1;
for (int i = 0; i < p; i++) {
sumsq[i] = sqrt(sumsq[i] / n - (sum[i] / matrix.length) * (sum[i] / n));
}
return sumsq;
}
/**
* Returns the sum of an array.
* @param x the array.
* @return the sum.
*/
public static int sum(byte[] x) {
int sum = 0;
for (int n : x) {
sum += n;
}
return sum;
}
/**
* Returns the sum of an array.
* @param x the array.
* @return the sum.
*/
public static long sum(int[] x) {
long sum = 0;
for (int n : x) {
sum += n;
}
return (int) sum;
}
/**
* Returns the sum of an array.
* @param x the array.
* @return the sum.
*/
public static double sum(float[] x) {
double sum = 0.0;
for (float n : x) {
sum += n;
}
return sum;
}
/**
* Returns the sum of an array.
* @param x the array.
* @return the sum.
*/
public static double sum(double[] x) {
double sum = 0.0;
for (double n : x) {
sum += n;
}
return sum;
}
/**
* Find the median of an array of type int.
* The input array will be rearranged.
* @param x the array.
* @return the median.
*/
public static int median(int[] x) {
return QuickSelect.median(x);
}
/**
* Find the median of an array of type float.
* The input array will be rearranged.
* @param x the array.
* @return the median.
*/
public static float median(float[] x) {
return QuickSelect.median(x);
}
/**
* Find the median of an array of type double.
* The input array will be rearranged.
* @param x the array.
* @return the median.
*/
public static double median(double[] x) {
return QuickSelect.median(x);
}
/**
* Find the median of an array of type double.
* The input array will be rearranged.
* @param x the array.
* @param the data type of array elements.
* @return the median.
*/
public static > T median(T[] x) {
return QuickSelect.median(x);
}
/**
* Find the first quantile (p = 1/4) of an array of type int.
* The input array will be rearranged.
* @param x the array.
* @return the first quantile.
*/
public static int q1(int[] x) {
return QuickSelect.q1(x);
}
/**
* Find the first quantile (p = 1/4) of an array of type float.
* The input array will be rearranged.
* @param x the array.
* @return the first quantile.
*/
public static float q1(float[] x) {
return QuickSelect.q1(x);
}
/**
* Find the first quantile (p = 1/4) of an array of type double.
* The input array will be rearranged.
* @param x the array.
* @return the first quantile.
*/
public static double q1(double[] x) {
return QuickSelect.q1(x);
}
/**
* Find the first quantile (p = 1/4) of an array of type double.
* The input array will be rearranged.
* @param x the array.
* @param the data type of array elements.
* @return the first quantile.
*/
public static > T q1(T[] x) {
return QuickSelect.q1(x);
}
/**
* Find the third quantile (p = 3/4) of an array of type int.
* The input array will be rearranged.
* @param x the array.
* @return the third quantile.
*/
public static int q3(int[] x) {
return QuickSelect.q3(x);
}
/**
* Find the third quantile (p = 3/4) of an array of type float.
* The input array will be rearranged.
* @param x the array.
* @return the third quantile.
*/
public static float q3(float[] x) {
return QuickSelect.q3(x);
}
/**
* Find the third quantile (p = 3/4) of an array of type double.
* The input array will be rearranged.
* @param x the array.
* @return the third quantile.
*/
public static double q3(double[] x) {
return QuickSelect.q3(x);
}
/**
* Returns the breakpoints that break data into equal-sized buckets
* based on sample quantiles.
* @param x the data set, which will be sorted.
* @param q the number of quantiles.
* @return the breakpoints between buckets.
*/
static double[] qcut(double[] x, int q) {
int n = x.length;
double[] cuts = new double[q - 1];
Arrays.sort(x);
for (int i = 0; i < q - 1; i++) {
cuts[i] = x[(i + 1) * n / q];
}
return cuts;
}
/**
* Find the third quantile (p = 3/4) of an array of type double.
* The input array will be rearranged.
* @param x the array.
* @param the data type of array elements.
* @return the third quantile.
*/
public static > T q3(T[] x) {
return QuickSelect.q3(x);
}
/**
* Returns the mean of an array.
* @param array the array.
* @return the mean.
*/
public static double mean(int[] array) {
return (double) sum(array) / array.length;
}
/**
* Returns the mean of an array.
* @param array the array.
* @return the mean.
*/
public static double mean(float[] array) {
return sum(array) / array.length;
}
/**
* Returns the mean of an array.
* @param array the array.
* @return the mean.
*/
public static double mean(double[] array) {
return sum(array) / array.length;
}
/**
* Returns the variance of an array.
* @param array the array.
* @return the variance.
*/
public static double var(int[] array) {
if (array.length < 2) {
throw new IllegalArgumentException("Array length is less than 2.");
}
double sum = 0.0;
double sumsq = 0.0;
for (int xi : array) {
sum += xi;
sumsq += xi * xi;
}
int n = array.length - 1;
return sumsq / n - (sum / array.length) * (sum / n);
}
/**
* Returns the variance of an array.
* @param array the array.
* @return the variance.
*/
public static double var(float[] array) {
if (array.length < 2) {
throw new IllegalArgumentException("Array length is less than 2.");
}
double sum = 0.0;
double sumsq = 0.0;
for (float xi : array) {
sum += xi;
sumsq += xi * xi;
}
int n = array.length - 1;
return sumsq / n - (sum / array.length) * (sum / n);
}
/**
* Returns the variance of an array.
* @param array the array.
* @return the variance.
*/
public static double var(double[] array) {
if (array.length < 2) {
throw new IllegalArgumentException("Array length is less than 2.");
}
double sum = 0.0;
double sumsq = 0.0;
for (double xi : array) {
sum += xi;
sumsq += xi * xi;
}
int n = array.length - 1;
return sumsq / n - (sum / array.length) * (sum / n);
}
/**
* Returns the standard deviation of an array.
* @param array the array.
* @return the standard deviation.
*/
public static double sd(int[] array) {
return sqrt(var(array));
}
/**
* Returns the standard deviation of an array.
* @param array the array.
* @return the standard deviation.
*/
public static double sd(float[] array) {
return sqrt(var(array));
}
/**
* Returns the standard deviation of an array.
* @param array the array.
* @return the standard deviation.
*/
public static double sd(double[] array) {
return sqrt(var(array));
}
/**
* Returns the median absolute deviation (MAD). Note that input array will
* be altered after the computation. MAD is a robust measure of
* the variability of a univariate sample of quantitative data. For a
* univariate data set X1, X2, ..., Xn,
* the MAD is defined as the median of the absolute deviations from the data's median:
*
* MAD(X) = median(|Xi - median(Xi)|)
*
* that is, starting with the residuals (deviations) from the data's median,
* the MAD is the median of their absolute values.
*
* MAD is a more robust estimator of scale than the sample variance or
* standard deviation. For instance, MAD is more resilient to outliers in
* a data set than the standard deviation. It thus behaves better with
* distributions without a mean or variance, such as the Cauchy distribution.
*
* In order to use the MAD as a consistent estimator for the estimation of
* the standard deviation σ, one takes σ = K * MAD, where K is
* a constant scale factor, which depends on the distribution. For normally
* distributed data K is taken to be 1.4826. Other distributions behave
* differently: for example for large samples from a uniform continuous
* distribution, this factor is about 1.1547.
*
* @param array the array.
* @return the median absolute deviation.
*/
public static double mad(int[] array) {
int m = median(array);
for (int i = 0; i < array.length; i++) {
array[i] = abs(array[i] - m);
}
return median(array);
}
/**
* Returns the median absolute deviation (MAD). Note that input array will
* be altered after the computation. MAD is a robust measure of
* the variability of a univariate sample of quantitative data. For a
* univariate data set X1, X2, ..., Xn,
* the MAD is defined as the median of the absolute deviations from the data's median:
*
* MAD(X) = median(|Xi - median(Xi)|)
*
* that is, starting with the residuals (deviations) from the data's median,
* the MAD is the median of their absolute values.
*
* MAD is a more robust estimator of scale than the sample variance or
* standard deviation. For instance, MAD is more resilient to outliers in
* a data set than the standard deviation. It thus behaves better with
* distributions without a mean or variance, such as the Cauchy distribution.
*
* In order to use the MAD as a consistent estimator for the estimation of
* the standard deviation σ, one takes σ = K * MAD, where K is
* a constant scale factor, which depends on the distribution. For normally
* distributed data K is taken to be 1.4826. Other distributions behave
* differently: for example for large samples from a uniform continuous
* distribution, this factor is about 1.1547.
*
* @param array the array.
* @return the median abolute deviation.
*/
public static double mad(float[] array) {
float m = median(array);
for (int i = 0; i < array.length; i++) {
array[i] = abs(array[i] - m);
}
return median(array);
}
/**
* Returns the median absolute deviation (MAD). Note that input array will
* be altered after the computation. MAD is a robust measure of
* the variability of a univariate sample of quantitative data. For a
* univariate data set X1, X2, ..., Xn,
* the MAD is defined as the median of the absolute deviations from the data's median:
*
* MAD(X) = median(|Xi - median(Xi)|)
*
* that is, starting with the residuals (deviations) from the data's median,
* the MAD is the median of their absolute values.
*
* MAD is a more robust estimator of scale than the sample variance or
* standard deviation. For instance, MAD is more resilient to outliers in
* a data set than the standard deviation. It thus behaves better with
* distributions without a mean or variance, such as the Cauchy distribution.
*
* In order to use the MAD as a consistent estimator for the estimation of
* the standard deviation σ, one takes σ = K * MAD, where K is
* a constant scale factor, which depends on the distribution. For normally
* distributed data K is taken to be 1.4826. Other distributions behave
* differently: for example for large samples from a uniform continuous
* distribution, this factor is about 1.1547.
*
* @param array the array.
* @return the median abolute deviation.
*/
public static double mad(double[] array) {
double m = median(array);
for (int i = 0; i < array.length; i++) {
array[i] = abs(array[i] - m);
}
return median(array);
}
/**
* The Euclidean distance on binary sparse arrays,
* which are the indices of nonzero elements in ascending order.
*
* @param a a binary sparse vector.
* @param b a binary sparse vector.
* @return the Euclidean distance.
*/
public static double distance(int[] a, int[] b) {
return sqrt(squaredDistance(a, b));
}
/**
* The Euclidean distance.
*
* @param a a vector.
* @param b a vector.
* @return the Euclidean distance.
*/
public static double distance(float[] a, float[] b) {
return sqrt(squaredDistance(a, b));
}
/**
* The Euclidean distance.
*
* @param a a vector.
* @param b a vector.
* @return the Euclidean distance.
*/
public static double distance(double[] a, double[] b) {
return sqrt(squaredDistance(a, b));
}
/**
* The Euclidean distance.
*
* @param a a sparse vector.
* @param b a sparse vector.
* @return the Euclidean distance.
*/
public static double distance(SparseArray a, SparseArray b) {
return sqrt(squaredDistance(a, b));
}
/**
* The squared Euclidean distance on binary sparse arrays,
* which are the indices of nonzero elements in ascending order.
*
* @param a a binary sparse vector.
* @param b a binary sparse vector.
* @return the square of Euclidean distance.
*/
public static double squaredDistance(int[] a, int[] b) {
double d = 0.0;
int p1 = 0, p2 = 0;
while (p1 < a.length && p2 < b.length) {
int i1 = a[p1];
int i2 = b[p2];
if (i1 == i2) {
p1++;
p2++;
} else if (i1 > i2) {
d++;
p2++;
} else {
d++;
p1++;
}
}
d += a.length - p1;
d += b.length - p2;
return d;
}
/**
* The squared Euclidean distance.
*
* @param a a vector.
* @param b a vector.
* @return the square of Euclidean distance.
*/
public static double squaredDistance(float[] a, float[] b) {
if (a.length != b.length) {
throw new IllegalArgumentException("Input vector sizes are different.");
}
switch (a.length) {
case 2: {
double d0 = (double) a[0] - (double) b[0];
double d1 = (double) a[1] - (double) b[1];
return d0 * d0 + d1 * d1;
}
case 3: {
double d0 = (double) a[0] - (double) b[0];
double d1 = (double) a[1] - (double) b[1];
double d2 = (double) a[2] - (double) b[2];
return d0 * d0 + d1 * d1 + d2 * d2;
}
case 4: {
double d0 = (double) a[0] - (double) b[0];
double d1 = (double) a[1] - (double) b[1];
double d2 = (double) a[2] - (double) b[2];
double d3 = (double) a[3] - (double) b[3];
return d0 * d0 + d1 * d1 + d2 * d2 + d3 * d3;
}
}
double sum = 0.0;
for (int i = 0; i < a.length; i++) {
// convert x and y for better precision
double d = (double) a[i] - (double) b[i];
sum += d * d;
}
return sum;
}
/**
* The squared Euclidean distance.
*
* @param a a vector.
* @param b a vector.
* @return the square of Euclidean distance.
*/
public static double squaredDistance(double[] a, double[] b) {
if (a.length != b.length) {
throw new IllegalArgumentException("Input vector sizes are different.");
}
switch (a.length) {
case 2: {
double d0 = a[0] - b[0];
double d1 = a[1] - b[1];
return d0 * d0 + d1 * d1;
}
case 3: {
double d0 = a[0] - b[0];
double d1 = a[1] - b[1];
double d2 = a[2] - b[2];
return d0 * d0 + d1 * d1 + d2 * d2;
}
case 4: {
double d0 = a[0] - b[0];
double d1 = a[1] - b[1];
double d2 = a[2] - b[2];
double d3 = a[3] - b[3];
return d0 * d0 + d1 * d1 + d2 * d2 + d3 * d3;
}
}
double sum = 0.0;
for (int i = 0; i < a.length; i++) {
double d = a[i] - b[i];
sum += d * d;
}
return sum;
}
/**
* The Euclidean distance on sparse arrays.
*
* @param a a sparse vector.
* @param b a sparse vector.
* @return the square of Euclidean distance.
*/
public static double squaredDistance(SparseArray a, SparseArray b) {
Iterator it1 = a.iterator();
Iterator it2 = b.iterator();
SparseArray.Entry e1 = it1.hasNext() ? it1.next() : null;
SparseArray.Entry e2 = it2.hasNext() ? it2.next() : null;
double sum = 0.0;
while (e1 != null && e2 != null) {
if (e1.index() == e2.index()) {
sum += pow2(e1.value() - e2.value());
e1 = it1.hasNext() ? it1.next() : null;
e2 = it2.hasNext() ? it2.next() : null;
} else if (e1.index() > e2.index()) {
sum += pow2(e2.value());
e2 = it2.hasNext() ? it2.next() : null;
} else {
sum += pow2(e1.value());
e1 = it1.hasNext() ? it1.next() : null;
}
}
while (it1.hasNext()) {
double d = it1.next().value();
sum += d * d;
}
while (it2.hasNext()) {
double d = it2.next().value();
sum += d * d;
}
return sum;
}
/**
* The squared Euclidean distance with handling missing values (represented as NaN).
* NaN will be treated as missing values and will be excluded from the
* calculation. Let m be the number nonmissing values, and n be the
* number of all values. The returned distance is (n * d / m),
* where d is the square of distance between nonmissing values.
*
* @param x a vector.
* @param y a vector.
* @return the square of Euclidean distance.
*/
public static double squaredDistanceWithMissingValues(double[] x, double[] y) {
int n = x.length;
int m = 0;
double dist = 0.0;
for (int i = 0; i < n; i++) {
if (!Double.isNaN(x[i]) && !Double.isNaN(y[i])) {
m++;
double d = x[i] - y[i];
dist += d * d;
}
}
if (m == 0) {
dist = Double.MAX_VALUE;
} else {
dist = n * dist / m;
}
return dist;
}
/**
* Returns the pairwise distance matrix of multiple binary sparse vectors.
* @param x binary sparse vectors, which are the indices of
* nonzero elements in ascending order.
* @return the full pairwise distance matrix.
*/
public static Matrix pdist(int[][] x) {
return pdist(x, false);
}
/**
* Returns the pairwise distance matrix of multiple binary sparse vectors.
* @param x binary sparse vectors, which are the indices of
* nonzero elements in ascending order.
* @param squared If true, compute the squared Euclidean distance.
* @return the pairwise distance matrix.
*/
public static Matrix pdist(int[][] x, boolean squared) {
int n = x.length;
double[][] dist = new double[n][n];
pdist(x, dist, squared ? MathEx::squaredDistance : MathEx::distance);
return Matrix.of(dist);
}
/**
* Returns the pairwise distance matrix of multiple vectors.
* @param x the vectors.
* @return the full pairwise distance matrix.
*/
public static Matrix pdist(float[][] x) {
return pdist(x, false);
}
/**
* Returns the pairwise distance matrix of multiple vectors.
* @param x the vectors.
* @param squared If true, compute the squared Euclidean distance.
* @return the pairwise distance matrix.
*/
public static Matrix pdist(float[][] x, boolean squared) {
int n = x.length;
double[][] dist = new double[n][n];
pdist(x, dist, squared ? MathEx::squaredDistance : MathEx::distance);
return Matrix.of(dist);
}
/**
* Returns the pairwise distance matrix of multiple vectors.
* @param x the vectors.
* @return the full pairwise distance matrix.
*/
public static Matrix pdist(double[][] x) {
return pdist(x, false);
}
/**
* Returns the pairwise distance matrix of multiple vectors.
* @param x the vectors.
* @param squared If true, compute the squared Euclidean distance.
* @return the pairwise distance matrix.
*/
public static Matrix pdist(double[][] x, boolean squared) {
int n = x.length;
double[][] dist = new double[n][n];
pdist(x, dist, squared ? MathEx::squaredDistance : MathEx::distance);
return Matrix.of(dist);
}
/**
* Returns the pairwise distance matrix of multiple vectors.
* @param x the vectors.
* @return the full pairwise distance matrix.
*/
public static Matrix pdist(SparseArray[] x) {
return pdist(x, false);
}
/**
* Returns the pairwise distance matrix of multiple vectors.
* @param x the vectors.
* @param squared If true, compute the squared Euclidean distance.
* @return the pairwise distance matrix.
*/
public static Matrix pdist(SparseArray[] x, boolean squared) {
int n = x.length;
double[][] dist = new double[n][n];
pdist(x, dist, squared ? MathEx::squaredDistance : MathEx::distance);
return Matrix.of(dist);
}
/**
* Computes the pairwise distance matrix of multiple vectors.
* @param x the vectors.
* @param d The output distance matrix. It may be only the lower half.
* @param distance the distance lambda.
* @param the data type of vectors.
*/
public static void pdist(T[] x, double[][] d, Distance distance) {
int n = x.length;
if (d[0].length < n) {
IntStream.range(0, n).parallel().forEach(i -> {
T xi = x[i];
double[] di = d[i];
for (int j = 0; j < i; j++) {
di[j] = distance.d(xi, x[j]);
}
});
} else {
IntStream.range(0, n).parallel().forEach(i -> {
T xi = x[i];
double[] di = d[i];
for (int j = 0; j < n; j++) {
di[j] = distance.d(xi, x[j]);
}
});
}
}
/**
* Shannon's entropy.
* @param p the probabilities.
* @return Shannon's entropy.
*/
public static double entropy(double[] p) {
double h = 0.0;
for (double pi : p) {
if (pi > 0) {
h -= pi * Math.log(pi);
}
}
return h;
}
/**
* Kullback-Leibler divergence. The Kullback-Leibler divergence (also
* information divergence, information gain, relative entropy, or KLIC)
* is a non-symmetric measure of the difference between two probability
* distributions P and Q. KL measures the expected number of extra bits
* required to code samples from P when using a code based on Q, rather
* than using a code based on P. Typically P represents the "true"
* distribution of data, observations, or a precise calculated theoretical
* distribution. The measure Q typically represents a theory, model,
* description, or approximation of P.
*
* Although it is often intuited as a distance metric, the KL divergence is
* not a true metric - for example, the KL from P to Q is not necessarily
* the same as the KL from Q to P.
*
* @param p a probability distribution.
* @param q a probability distribution.
* @return Kullback-Leibler divergence
*/
public static double KullbackLeiblerDivergence(double[] p, double[] q) {
boolean intersection = false;
double kl = 0.0;
for (int i = 0; i < p.length; i++) {
if (p[i] != 0.0 && q[i] != 0.0) {
intersection = true;
kl += p[i] * Math.log(p[i] / q[i]);
}
}
if (intersection) {
return kl;
} else {
return Double.POSITIVE_INFINITY;
}
}
/**
* Kullback-Leibler divergence. The Kullback-Leibler divergence (also
* information divergence, information gain, relative entropy, or KLIC)
* is a non-symmetric measure of the difference between two probability
* distributions P and Q. KL measures the expected number of extra bits
* required to code samples from P when using a code based on Q, rather
* than using a code based on P. Typically P represents the "true"
* distribution of data, observations, or a precise calculated theoretical
* distribution. The measure Q typically represents a theory, model,
* description, or approximation of P.
*
* Although it is often intuited as a distance metric, the KL divergence is
* not a true metric - for example, the KL from P to Q is not necessarily
* the same as the KL from Q to P.
*
* @param p a probability distribution.
* @param q a probability distribution.
* @return Kullback-Leibler divergence
*/
public static double KullbackLeiblerDivergence(SparseArray p, SparseArray q) {
if (p.isEmpty()) {
throw new IllegalArgumentException("p is empty.");
}
if (q.isEmpty()) {
throw new IllegalArgumentException("q is empty.");
}
Iterator pIter = p.iterator();
Iterator qIter = q.iterator();
SparseArray.Entry a = pIter.hasNext() ? pIter.next() : null;
SparseArray.Entry b = qIter.hasNext() ? qIter.next() : null;
boolean intersection = false;
double kl = 0.0;
while (a != null && b != null) {
if (a.index() < b.index()) {
a = pIter.hasNext() ? pIter.next() : null;
} else if (a.index() > b.index()) {
b = qIter.hasNext() ? qIter.next() : null;
} else {
intersection = true;
kl += a.value() * Math.log(a.value() / b.value());
a = pIter.hasNext() ? pIter.next() : null;
b = qIter.hasNext() ? qIter.next() : null;
}
}
if (intersection) {
return kl;
} else {
return Double.POSITIVE_INFINITY;
}
}
/**
* Kullback-Leibler divergence. The Kullback-Leibler divergence (also
* information divergence, information gain, relative entropy, or KLIC)
* is a non-symmetric measure of the difference between two probability
* distributions P and Q. KL measures the expected number of extra bits
* required to code samples from P when using a code based on Q, rather
* than using a code based on P. Typically P represents the "true"
* distribution of data, observations, or a precise calculated theoretical
* distribution. The measure Q typically represents a theory, model,
* description, or approximation of P.
*
* Although it is often intuited as a distance metric, the KL divergence is
* not a true metric - for example, the KL from P to Q is not necessarily
* the same as the KL from Q to P.
*
* @param p a probability distribution.
* @param q a probability distribution.
* @return Kullback-Leibler divergence
*/
public static double KullbackLeiblerDivergence(double[] p, SparseArray q) {
return KullbackLeiblerDivergence(q, p);
}
/**
* Kullback-Leibler divergence. The Kullback-Leibler divergence (also
* information divergence, information gain, relative entropy, or KLIC)
* is a non-symmetric measure of the difference between two probability
* distributions P and Q. KL measures the expected number of extra bits
* required to code samples from P when using a code based on Q, rather
* than using a code based on P. Typically P represents the "true"
* distribution of data, observations, or a precise calculated theoretical
* distribution. The measure Q typically represents a theory, model,
* description, or approximation of P.
*
* Although it is often intuited as a distance metric, the KL divergence is
* not a true metric - for example, the KL from P to Q is not necessarily
* the same as the KL from Q to P.
*
* @param p a probability distribution.
* @param q a probability distribution.
* @return Kullback-Leibler divergence
*/
public static double KullbackLeiblerDivergence(SparseArray p, double[] q) {
if (p.isEmpty()) {
throw new IllegalArgumentException("p is empty.");
}
Iterator iter = p.iterator();
boolean intersection = false;
double kl = 0.0;
while (iter.hasNext()) {
SparseArray.Entry b = iter.next();
int i = b.index();
if (q[i] > 0) {
intersection = true;
kl += b.value() * Math.log(b.value() / q[i]);
}
}
if (intersection) {
return kl;
} else {
return Double.POSITIVE_INFINITY;
}
}
/**
* Jensen-Shannon divergence JS(P||Q) = (KL(P||M) + KL(Q||M)) / 2, where
* M = (P+Q)/2. The Jensen-Shannon divergence is a popular
* method of measuring the similarity between two probability distributions.
* It is also known as information radius or total divergence to the average.
* It is based on the Kullback-Leibler divergence, with the difference that
* it is always a finite value. The square root of the Jensen-Shannon divergence
* is a metric.
*
* @param p a probability distribution.
* @param q a probability distribution.
* @return Jensen-Shannon divergence
*/
public static double JensenShannonDivergence(double[] p, double[] q) {
double[] m = new double[p.length];
for (int i = 0; i < m.length; i++) {
m[i] = (p[i] + q[i]) / 2;
}
return (KullbackLeiblerDivergence(p, m) + KullbackLeiblerDivergence(q, m)) / 2;
}
/**
* Jensen-Shannon divergence JS(P||Q) = (KL(P||M) + KL(Q||M)) / 2, where
* M = (P+Q)/2. The Jensen-Shannon divergence is a popular
* method of measuring the similarity between two probability distributions.
* It is also known as information radius or total divergence to the average.
* It is based on the Kullback-Leibler divergence, with the difference that
* it is always a finite value. The square root of the Jensen-Shannon divergence
* is a metric.
*
* @param p a probability distribution.
* @param q a probability distribution.
* @return Jensen-Shannon divergence
*/
public static double JensenShannonDivergence(SparseArray p, SparseArray q) {
if (p.isEmpty()) {
throw new IllegalArgumentException("p is empty.");
}
if (q.isEmpty()) {
throw new IllegalArgumentException("q is empty.");
}
Iterator pIter = p.iterator();
Iterator qIter = q.iterator();
SparseArray.Entry a = pIter.hasNext() ? pIter.next() : null;
SparseArray.Entry b = qIter.hasNext() ? qIter.next() : null;
double js = 0.0;
while (a != null && b != null) {
if (a.index() < b.index()) {
double mi = a.value() / 2;
js += a.value() * Math.log(a.value() / mi);
a = pIter.hasNext() ? pIter.next() : null;
} else if (a.index() > b.index()) {
double mi = b.value() / 2;
js += b.value() * Math.log(b.value() / mi);
b = qIter.hasNext() ? qIter.next() : null;
} else {
double mi = (a.value() + b.value()) / 2;
js += a.value() * Math.log(a.value() / mi) + b.value() * Math.log(b.value() / mi);
a = pIter.hasNext() ? pIter.next() : null;
b = qIter.hasNext() ? qIter.next() : null;
}
}
return js / 2;
}
/**
* Jensen-Shannon divergence JS(P||Q) = (KL(P||M) + KL(Q||M)) / 2, where
* M = (P+Q)/2. The Jensen-Shannon divergence is a popular
* method of measuring the similarity between two probability distributions.
* It is also known as information radius or total divergence to the average.
* It is based on the Kullback-Leibler divergence, with the difference that
* it is always a finite value. The square root of the Jensen-Shannon divergence
* is a metric.
*
* @param p a probability distribution.
* @param q a probability distribution.
* @return Jensen-Shannon divergence
*/
public static double JensenShannonDivergence(double[] p, SparseArray q) {
return JensenShannonDivergence(q, p);
}
/**
* Jensen-Shannon divergence JS(P||Q) = (KL(P||M) + KL(Q||M)) / 2, where
* M = (P+Q)/2. The Jensen-Shannon divergence is a popular
* method of measuring the similarity between two probability distributions.
* It is also known as information radius or total divergence to the average.
* It is based on the Kullback-Leibler divergence, with the difference that
* it is always a finite value. The square root of the Jensen-Shannon divergence
* is a metric.
*
* @param p a probability distribution.
* @param q a probability distribution.
* @return Jensen-Shannon divergence
*/
public static double JensenShannonDivergence(SparseArray p, double[] q) {
if (p.isEmpty()) {
throw new IllegalArgumentException("p is empty.");
}
Iterator iter = p.iterator();
double js = 0.0;
while (iter.hasNext()) {
SparseArray.Entry b = iter.next();
int i = b.index();
double mi = (b.value() + q[i]) / 2;
js += b.value() * Math.log(b.value() / mi);
if (q[i] > 0) {
js += q[i] * Math.log(q[i] / mi);
}
}
return js / 2;
}
/**
* Returns the dot product between two binary sparse arrays,
* which are the indices of nonzero elements in ascending order.
* @param a a binary sparse vector.
* @param b a binary sparse vector.
* @return the dot product.
*/
public static int dot(int[] a, int[] b) {
int sum = 0;
for (int p1 = 0, p2 = 0; p1 < a.length && p2 < b.length; ) {
int i1 = a[p1];
int i2 = b[p2];
if (i1 == i2) {
sum++;
p1++;
p2++;
} else if (i1 > i2) {
p2++;
} else {
p1++;
}
}
return sum;
}
/**
* Returns the dot product between two vectors.
* @param a a vector.
* @param b a vector.
* @return the dot product.
*/
public static float dot(float[] a, float[] b) {
if (a.length != b.length) {
throw new IllegalArgumentException("Arrays have different length.");
}
float sum = 0.0F;
for (int i = 0; i < a.length; i++) {
sum += a[i] * b[i];
}
return sum;
}
/**
* Returns the dot product between two vectors.
* @param a a vector.
* @param b a vector.
* @return the dot product.
*/
public static double dot(double[] a, double[] b) {
if (a.length != b.length) {
throw new IllegalArgumentException("Arrays have different length.");
}
double sum = 0.0;
for (int i = 0; i < a.length; i++) {
sum += a[i] * b[i];
}
return sum;
}
/**
* Returns the dot product between two sparse arrays.
* @param x a sparse vector.
* @param y a sparse vector.
* @return the dot product.
*/
public static double dot(SparseArray x, SparseArray y) {
Iterator it1 = x.iterator();
Iterator it2 = y.iterator();
SparseArray.Entry e1 = it1.hasNext() ? it1.next() : null;
SparseArray.Entry e2 = it2.hasNext() ? it2.next() : null;
double sum = 0.0;
while (e1 != null && e2 != null) {
if (e1.index() == e2.index()) {
sum += e1.value() * e2.value();
e1 = it1.hasNext() ? it1.next() : null;
e2 = it2.hasNext() ? it2.next() : null;
} else if (e1.index() > e2.index()) {
e2 = it2.hasNext() ? it2.next() : null;
} else {
e1 = it1.hasNext() ? it1.next() : null;
}
}
return sum;
}
/**
* Returns the pairwise dot product matrix of binary sparse vectors.
* @param x the binary sparse vectors, which are the indices of
* nonzero elements in ascending order.
* @return the dot product matrix.
*/
public static Matrix pdot(int[][] x) {
int n = x.length;
Matrix matrix = new Matrix(n, n);
matrix.uplo(UPLO.LOWER);
IntStream.range(0, n).parallel().forEach(j -> {
int[] xj = x[j];
for (int i = 0; i < n; i++) {
matrix.set(i, j, dot(x[i], xj));
}
});
return matrix;
}
/**
* Returns the pairwise dot product matrix of float vectors.
* @param x the vectors.
* @return the dot product matrix.
*/
public static Matrix pdot(float[][] x) {
int n = x.length;
Matrix matrix = new Matrix(n, n);
matrix.uplo(UPLO.LOWER);
IntStream.range(0, n).parallel().forEach(j -> {
float[] xj = x[j];
for (int i = 0; i < n; i++) {
matrix.set(i, j, dot(x[i], xj));
}
});
return matrix;
}
/**
* Returns the pairwise dot product matrix of double vectors.
* @param x the vectors.
* @return the dot product matrix.
*/
public static Matrix pdot(double[][] x) {
int n = x.length;
Matrix matrix = new Matrix(n, n);
matrix.uplo(UPLO.LOWER);
IntStream.range(0, n).parallel().forEach(j -> {
double[] xj = x[j];
for (int i = 0; i < n; i++) {
matrix.set(i, j, dot(x[i], xj));
}
});
return matrix;
}
/**
* Returns the pairwise dot product matrix of multiple vectors.
* @param x the sparse vectors.
* @return the dot product matrix.
*/
public static Matrix pdot(SparseArray[] x) {
int n = x.length;
Matrix matrix = new Matrix(n, n);
matrix.uplo(UPLO.LOWER);
IntStream.range(0, n).parallel().forEach(j -> {
SparseArray xj = x[j];
for (int i = 0; i < n; i++) {
matrix.set(i, j, dot(x[i], xj));
}
});
return matrix;
}
/**
* Returns the covariance between two vectors.
* @param x a vector.
* @param y a vector.
* @return the covariance.
*/
public static double cov(int[] x, int[] y) {
if (x.length != y.length) {
throw new IllegalArgumentException("Arrays have different length.");
}
if (x.length < 3) {
throw new IllegalArgumentException("array length has to be at least 3.");
}
double mx = mean(x);
double my = mean(y);
double Sxy = 0.0;
for (int i = 0; i < x.length; i++) {
double dx = x[i] - mx;
double dy = y[i] - my;
Sxy += dx * dy;
}
return Sxy / (x.length - 1);
}
/**
* Returns the covariance between two vectors.
* @param x a vector.
* @param y a vector.
* @return the covariance.
*/
public static double cov(float[] x, float[] y) {
if (x.length != y.length) {
throw new IllegalArgumentException("Arrays have different length.");
}
if (x.length < 3) {
throw new IllegalArgumentException("array length has to be at least 3.");
}
double mx = mean(x);
double my = mean(y);
double Sxy = 0.0;
for (int i = 0; i < x.length; i++) {
double dx = x[i] - mx;
double dy = y[i] - my;
Sxy += dx * dy;
}
return Sxy / (x.length - 1);
}
/**
* Returns the covariance between two vectors.
* @param x a vector.
* @param y a vector.
* @return the covariance.
*/
public static double cov(double[] x, double[] y) {
if (x.length != y.length) {
throw new IllegalArgumentException("Arrays have different length.");
}
if (x.length < 3) {
throw new IllegalArgumentException("array length has to be at least 3.");
}
double mx = mean(x);
double my = mean(y);
double Sxy = 0.0;
for (int i = 0; i < x.length; i++) {
double dx = x[i] - mx;
double dy = y[i] - my;
Sxy += dx * dy;
}
return Sxy / (x.length - 1);
}
/**
* Returns the sample covariance matrix.
* @param data the samples
* @return the covariance matrix.
*/
public static double[][] cov(double[][] data) {
return cov(data, colMeans(data));
}
/**
* Returns the sample covariance matrix.
* @param data the samples
* @param mu the known mean of data.
* @return the covariance matrix.
*/
public static double[][] cov(double[][] data, double[] mu) {
double[][] sigma = new double[data[0].length][data[0].length];
for (double[] datum : data) {
for (int j = 0; j < mu.length; j++) {
for (int k = 0; k <= j; k++) {
sigma[j][k] += (datum[j] - mu[j]) * (datum[k] - mu[k]);
}
}
}
int n = data.length - 1;
for (int j = 0; j < mu.length; j++) {
for (int k = 0; k <= j; k++) {
sigma[j][k] /= n;
sigma[k][j] = sigma[j][k];
}
}
return sigma;
}
/**
* Returns the correlation coefficient between two vectors.
* @param x a vector.
* @param y a vector.
* @return the correlation coefficient.
*/
public static double cor(int[] x, int[] y) {
if (x.length != y.length) {
throw new IllegalArgumentException("Arrays have different length.");
}
if (x.length < 3) {
throw new IllegalArgumentException("array length has to be at least 3.");
}
double Sxy = cov(x, y);
double Sxx = var(x);
double Syy = var(y);
if (Sxx == 0 || Syy == 0) {
return Double.NaN;
}
return Sxy / sqrt(Sxx * Syy);
}
/**
* Returns the correlation coefficient between two vectors.
* @param x a vector.
* @param y a vector.
* @return the correlation coefficient.
*/
public static double cor(float[] x, float[] y) {
if (x.length != y.length) {
throw new IllegalArgumentException("Arrays have different length.");
}
if (x.length < 3) {
throw new IllegalArgumentException("array length has to be at least 3.");
}
double Sxy = cov(x, y);
double Sxx = var(x);
double Syy = var(y);
if (Sxx == 0 || Syy == 0) {
return Double.NaN;
}
return Sxy / sqrt(Sxx * Syy);
}
/**
* Returns the correlation coefficient between two vectors.
* @param x a vector.
* @param y a vector.
* @return the correlation coefficient.
*/
public static double cor(double[] x, double[] y) {
if (x.length != y.length) {
throw new IllegalArgumentException("Arrays have different length.");
}
if (x.length < 3) {
throw new IllegalArgumentException("array length has to be at least 3.");
}
double Sxy = cov(x, y);
double Sxx = var(x);
double Syy = var(y);
if (Sxx == 0 || Syy == 0) {
return Double.NaN;
}
return Sxy / sqrt(Sxx * Syy);
}
/**
* Returns the sample correlation matrix.
* @param data the samples
* @return the correlation matrix.
*/
public static double[][] cor(double[][] data) {
return cor(data, colMeans(data));
}
/**
* Returns the sample correlation matrix.
* @param data the samples
* @param mu the known mean of data.
* @return the correlation matrix.
*/
public static double[][] cor(double[][] data, double[] mu) {
double[][] sigma = cov(data, mu);
int n = data[0].length;
double[] sd = new double[n];
for (int i = 0; i < n; i++) {
sd[i] = sqrt(sigma[i][i]);
}
for (int i = 0; i < n; i++) {
for (int j = 0; j <= i; j++) {
sigma[i][j] /= sd[i] * sd[j];
sigma[j][i] = sigma[i][j];
}
}
return sigma;
}
/**
* Given a sorted array, replaces the elements by their rank, including
* midranking of ties, and returns as s the sum of f3 - f, where
* f is the number of elements in each tie.
*/
private static double crank(double[] w) {
int n = w.length;
double s = 0.0;
int j = 1;
while (j < n) {
if (w[j] != w[j - 1]) {
w[j - 1] = j;
++j;
} else {
int jt = j + 1;
while (jt <= n && w[jt - 1] == w[j - 1]) {
jt++;
}
double rank = 0.5 * (j + jt - 1);
for (int ji = j; ji <= (jt - 1); ji++) {
w[ji - 1] = rank;
}
double t = jt - j;
s += (t * t * t - t);
j = jt;
}
}
if (j == n) {
w[n - 1] = n;
}
return s;
}
/**
* The Spearman Rank Correlation Coefficient is a form of the Pearson
* coefficient with the data converted to rankings (i.e. when variables
* are ordinal). It can be used when there is non-parametric data and hence
* Pearson cannot be used.
* @param x a vector.
* @param y a vector.
* @return the Spearman rank correlation coefficient.
*/
public static double spearman(int[] x, int[] y) {
if (x.length != y.length) {
throw new IllegalArgumentException("Input vector sizes are different.");
}
int n = x.length;
double[] wksp1 = new double[n];
double[] wksp2 = new double[n];
for (int j = 0; j < n; j++) {
wksp1[j] = x[j];
wksp2[j] = y[j];
}
QuickSort.sort(wksp1, wksp2);
crank(wksp1);
QuickSort.sort(wksp2, wksp1);
crank(wksp2);
return cor(wksp1, wksp2);
}
/**
* The Spearman Rank Correlation Coefficient is a form of the Pearson
* coefficient with the data converted to rankings (i.e. when variables
* are ordinal). It can be used when there is non-parametric data and hence
* Pearson cannot be used.
* @param x a vector.
* @param y a vector.
* @return the Spearman rank correlation coefficient.
*/
public static double spearman(float[] x, float[] y) {
if (x.length != y.length) {
throw new IllegalArgumentException("Input vector sizes are different.");
}
int n = x.length;
double[] wksp1 = new double[n];
double[] wksp2 = new double[n];
for (int j = 0; j < n; j++) {
wksp1[j] = x[j];
wksp2[j] = y[j];
}
QuickSort.sort(wksp1, wksp2);
crank(wksp1);
QuickSort.sort(wksp2, wksp1);
crank(wksp2);
return cor(wksp1, wksp2);
}
/**
* The Spearman Rank Correlation Coefficient is a form of the Pearson
* coefficient with the data converted to rankings (i.e. when variables
* are ordinal). It can be used when there is non-parametric data and hence
* Pearson cannot be used.
* @param x a vector.
* @param y a vector.
* @return the Spearman rank correlation coefficient.
*/
public static double spearman(double[] x, double[] y) {
if (x.length != y.length) {
throw new IllegalArgumentException("Input vector sizes are different.");
}
double[] wksp1 = x.clone();
double[] wksp2 = y.clone();
QuickSort.sort(wksp1, wksp2);
crank(wksp1);
QuickSort.sort(wksp2, wksp1);
crank(wksp2);
return cor(wksp1, wksp2);
}
/**
* The Kendall Tau Rank Correlation Coefficient is used to measure the
* degree of correspondence between sets of rankings where the measures
* are not equidistant. It is used with non-parametric data.
* @param x a vector.
* @param y a vector.
* @return the Kendall rank correlation coefficient.
*/
public static double kendall(int[] x, int[] y) {
if (x.length != y.length) {
throw new IllegalArgumentException("Input vector sizes are different.");
}
int is = 0, n2 = 0, n1 = 0, n = x.length;
double aa, a2, a1;
for (int j = 0; j < n - 1; j++) {
for (int k = j + 1; k < n; k++) {
a1 = x[j] - x[k];
a2 = y[j] - y[k];
aa = a1 * a2;
if (aa != 0.0) {
++n1;
++n2;
if (aa > 0) {
++is;
} else {
--is;
}
} else {
if (a1 != 0.0) {
++n1;
}
if (a2 != 0.0) {
++n2;
}
}
}
}
return is / (sqrt(n1) * sqrt(n2));
}
/**
* The Kendall Tau Rank Correlation Coefficient is used to measure the
* degree of correspondence between sets of rankings where the measures
* are not equidistant. It is used with non-parametric data.
* @param x a vector.
* @param y a vector.
* @return the Kendall rank correlation coefficient.
*/
public static double kendall(float[] x, float[] y) {
if (x.length != y.length) {
throw new IllegalArgumentException("Input vector sizes are different.");
}
int is = 0, n2 = 0, n1 = 0, n = x.length;
double aa, a2, a1;
for (int j = 0; j < n - 1; j++) {
for (int k = j + 1; k < n; k++) {
a1 = x[j] - x[k];
a2 = y[j] - y[k];
aa = a1 * a2;
if (aa != 0.0) {
++n1;
++n2;
if (aa > 0) {
++is;
} else {
--is;
}
} else {
if (a1 != 0.0) {
++n1;
}
if (a2 != 0.0) {
++n2;
}
}
}
}
return is / (sqrt(n1) * sqrt(n2));
}
/**
* The Kendall Tau Rank Correlation Coefficient is used to measure the
* degree of correspondence between sets of rankings where the measures
* are not equidistant. It is used with non-parametric data.
* @param x a vector.
* @param y a vector.
* @return the Kendall rank correlation coefficient.
*/
public static double kendall(double[] x, double[] y) {
if (x.length != y.length) {
throw new IllegalArgumentException("Input vector sizes are different.");
}
int is = 0, n2 = 0, n1 = 0, n = x.length;
double aa, a2, a1;
for (int j = 0; j < n - 1; j++) {
for (int k = j + 1; k < n; k++) {
a1 = x[j] - x[k];
a2 = y[j] - y[k];
aa = a1 * a2;
if (aa != 0.0) {
++n1;
++n2;
if (aa > 0) {
++is;
} else {
--is;
}
} else {
if (a1 != 0.0) {
++n1;
}
if (a2 != 0.0) {
++n2;
}
}
}
}
return is / (sqrt(n1) * sqrt(n2));
}
/**
* L1 vector norm.
* @param x a vector.
* @return L1 norm.
*/
public static float norm1(float[] x) {
float norm = 0.0F;
for (float n : x) {
norm += abs(n);
}
return norm;
}
/**
* L1 vector norm.
* @param x a vector.
* @return L1 norm.
*/
public static double norm1(double[] x) {
double norm = 0.0;
for (double n : x) {
norm += abs(n);
}
return norm;
}
/**
* L2 vector norm.
* @param x a vector.
* @return L2 norm.
*/
public static float norm2(float[] x) {
float norm = 0.0F;
for (float n : x) {
norm += n * n;
}
return (float) sqrt(norm);
}
/**
* L2 vector norm.
* @param x a vector.
* @return L2 norm.
*/
public static double norm2(double[] x) {
double norm = 0.0;
for (double n : x) {
norm += n * n;
}
return sqrt(norm);
}
/**
* L∞ vector norm that is the maximum absolute value.
* @param x a vector.
* @return L∞ norm.
*/
public static float normInf(float[] x) {
int n = x.length;
float f = abs(x[0]);
for (int i = 1; i < n; i++) {
f = Math.max(f, abs(x[i]));
}
return f;
}
/**
* L∞ vector norm. Maximum absolute value.
* @param x a vector.
* @return L∞ norm.
*/
public static double normInf(double[] x) {
int n = x.length;
double f = abs(x[0]);
for (int i = 1; i < n; i++) {
f = Math.max(f, abs(x[i]));
}
return f;
}
/**
* L2 vector norm.
* @param x a vector.
* @return L2 norm.
*/
public static float norm(float[] x) {
return norm2(x);
}
/**
* L2 vector norm.
* @param x a vector.
* @return L2 norm.
*/
public static double norm(double[] x) {
return norm2(x);
}
/**
* Returns the cosine similarity.
* @param a a vector.
* @param b a vector.
* @return the cosine similarity.
*/
public static float cosine(float[] a, float[] b) {
return dot(a, b) / (norm2(a) * norm2(b));
}
/**
* Returns the cosine similarity.
* @param a a vector.
* @param b a vector.
* @return the cosine similarity.
*/
public static double cosine(double[] a, double[] b) {
return dot(a, b) / (norm2(a) * norm2(b));
}
/**
* Returns the angular distance.
* @param a a vector.
* @param b a vector.
* @return the angular distance.
*/
public static float angular(float[] a, float[] b) {
return (float) (Math.acos(cosine(a, b)) / Math.PI);
}
/**
* Returns the angular distance.
* @param a a vector.
* @param b a vector.
* @return the angular distance.
*/
public static double angular(double[] a, double[] b) {
return Math.acos(cosine(a, b)) / Math.PI;
}
/**
* Normalizes an array to norm 1.
* @param array the array.
*/
public static void normalize(double[] array) {
double norm = norm(array);
if (isZero(norm)) {
logger.warn("array has norm of 0.");
norm = 1;
}
for (int i = 0; i < array.length; i++) {
array[i] /= norm;
}
}
/**
* Standardizes an array to mean 0 and variance 1.
* @param array the array.
*/
public static void standardize(double[] array) {
double mu = mean(array);
double sigma = sd(array);
if (isZero(sigma)) {
logger.warn("array has variance of 0.");
sigma = 1;
}
for (int i = 0; i < array.length; i++) {
array[i] = (array[i] - mu) / sigma;
}
}
/**
* Scales each column of a matrix to range [0, 1].
* @param matrix the matrix.
*/
public static void scale(double[][] matrix) {
int n = matrix.length;
int p = matrix[0].length;
double[] min = colMin(matrix);
double[] max = colMax(matrix);
for (int j = 0; j < p; j++) {
double scale = max[j] - min[j];
if (!isZero(scale)) {
for (int i = 0; i < n; i++) {
matrix[i][j] = (matrix[i][j] - min[j]) / scale;
}
} else {
for (int i = 0; i < n; i++) {
matrix[i][j] = 0.5;
}
}
}
}
/**
* Standardizes each column of a matrix to 0 mean and unit variance.
* @param matrix the matrix.
*/
public static void standardize(double[][] matrix) {
int n = matrix.length;
int p = matrix[0].length;
double[] center = colMeans(matrix);
for (int i = 0; i < n; i++) {
for (int j = 0; j < p; j++) {
matrix[i][j] = matrix[i][j] - center[j];
}
}
double[] scale = new double[p];
for (int j = 0; j < p; j++) {
for (double[] xi : matrix) {
scale[j] += pow2(xi[j]);
}
scale[j] = sqrt(scale[j] / (n-1));
if (!isZero(scale[j])) {
for (int i = 0; i < n; i++) {
matrix[i][j] /= scale[j];
}
}
}
}
/**
* Unitizes each column of a matrix to unit length (L_2 norm).
* @param matrix the matrix.
*/
public static void normalize(double[][] matrix) {
normalize(matrix, false);
}
/**
* Unitizes each column of a matrix to unit length (L_2 norm).
* @param matrix the matrix.
* @param centerizing If true, centerize each column to 0 mean.
*/
public static void normalize(double[][] matrix, boolean centerizing) {
int n = matrix.length;
int p = matrix[0].length;
if (centerizing) {
double[] center = colMeans(matrix);
for (int i = 0; i < n; i++) {
for (int j = 0; j < p; j++) {
matrix[i][j] = matrix[i][j] - center[j];
}
}
}
double[] scale = new double[p];
for (int j = 0; j < p; j++) {
for (double[] xi : matrix) {
scale[j] += pow2(xi[j]);
}
scale[j] = sqrt(scale[j]);
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < p; j++) {
if (!isZero(scale[j])) {
matrix[i][j] /= scale[j];
}
}
}
}
/**
* Unitize an array so that L2 norm of array = 1.
*
* @param array the vector.
*/
public static void unitize(double[] array) {
unitize2(array);
}
/**
* Unitize an array so that L1 norm of array is 1.
*
* @param array the vector.
*/
public static void unitize1(double[] array) {
double n = norm1(array);
for (int i = 0; i < array.length; i++) {
array[i] /= n;
}
}
/**
* Unitize an array so that L2 norm of array = 1.
*
* @param array the vector.
*/
public static void unitize2(double[] array) {
double n = norm(array);
for (int i = 0; i < array.length; i++) {
array[i] /= n;
}
}
/**
* Check if x element-wisely equals y with default epsilon 1E-7.
* @param x an array.
* @param y an array.
* @return true if x element-wisely equals y.
*/
public static boolean equals(float[] x, float[] y) {
return equals(x, y, 1.0E-7f);
}
/**
* Check if x element-wisely equals y in given precision.
* @param x an array.
* @param y an array.
* @param epsilon a number close to zero.
* @return true if x element-wisely equals y.
*/
public static boolean equals(float[] x, float[] y, float epsilon) {
if (x.length != y.length) {
throw new IllegalArgumentException(String.format("Arrays have different length: x[%d], y[%d]", x.length, y.length));
}
for (int i = 0; i < x.length; i++) {
if (abs(x[i] - y[i]) > epsilon) {
return false;
}
}
return true;
}
/**
* Check if x element-wisely equals y with default epsilon 1E-10.
* @param x an array.
* @param y an array.
* @return true if x element-wisely equals y.
*/
public static boolean equals(double[] x, double[] y) {
return equals(x, y, 1.0E-10);
}
/**
* Check if x element-wisely equals y in given precision.
* @param x an array.
* @param y an array.
* @param epsilon a number close to zero.
* @return true if x element-wisely equals y.
*/
public static boolean equals(double[] x, double[] y, double epsilon) {
if (x.length != y.length) {
throw new IllegalArgumentException(String.format("Arrays have different length: x[%d], y[%d]", x.length, y.length));
}
if (epsilon <= 0.0) {
throw new IllegalArgumentException("Invalid epsilon: " + epsilon);
}
for (int i = 0; i < x.length; i++) {
if (abs(x[i] - y[i]) > epsilon) {
return false;
}
}
return true;
}
/**
* Check if x element-wisely equals y with default epsilon 1E-7.
* @param x a two-dimensional array.
* @param y a two-dimensional array.
* @return true if x element-wisely equals y.
*/
public static boolean equals(float[][] x, float[][] y) {
return equals(x, y, 1.0E-7f);
}
/**
* Check if x element-wisely equals y in given precision.
* @param x a two-dimensional array.
* @param y a two-dimensional array.
* @param epsilon a number close to zero.
* @return true if x element-wisely equals y.
*/
public static boolean equals(float[][] x, float[][] y, float epsilon) {
if (x.length != y.length || x[0].length != y[0].length) {
throw new IllegalArgumentException(String.format("Matrices have different rows: %d x %d vs %d x %d", x.length, x[0].length, y.length, y[0].length));
}
for (int i = 0; i < x.length; i++) {
for (int j = 0; j < x[i].length; j++) {
if (abs(x[i][j] - y[i][j]) > epsilon) {
return false;
}
}
}
return true;
}
/**
* Check if x element-wisely equals y with default epsilon 1E-10.
* @param x a two-dimensional array.
* @param y a two-dimensional array.
* @return true if x element-wisely equals y.
*/
public static boolean equals(double[][] x, double[][] y) {
return equals(x, y, 1.0E-10);
}
/**
* Check if x element-wisely equals y in given precision.
* @param x a two-dimensional array.
* @param y a two-dimensional array.
* @param epsilon a number close to zero.
* @return true if x element-wisely equals y.
*/
public static boolean equals(double[][] x, double[][] y, double epsilon) {
if (x.length != y.length || x[0].length != y[0].length) {
throw new IllegalArgumentException(String.format("Matrices have different rows: %d x %d vs %d x %d", x.length, x[0].length, y.length, y[0].length));
}
if (epsilon <= 0.0) {
throw new IllegalArgumentException("Invalid epsilon: " + epsilon);
}
for (int i = 0; i < x.length; i++) {
for (int j = 0; j < x[i].length; j++) {
if (abs(x[i][j] - y[i][j]) > epsilon) {
return false;
}
}
}
return true;
}
/**
* Tests if a floating number is zero in machine precision.
* @param x a real number.
* @return true if x is zero in machine precision.
*/
public static boolean isZero(float x) {
return isZero(x, FLOAT_EPSILON);
}
/**
* Tests if a floating number is zero in given precision.
* @param x a real number.
* @param epsilon a number close to zero.
* @return true if x is zero in epsilon
precision.
*/
public static boolean isZero(float x, float epsilon) {
return abs(x) < epsilon;
}
/**
* Tests if a floating number is zero in machine precision.
* @param x a real number.
* @return true if x is zero in machine precision.
*/
public static boolean isZero(double x) {
return isZero(x, EPSILON);
}
/**
* Tests if a floating number is zero in given precision.
* @param x a real number.
* @param epsilon a number close to zero.
* @return true if x is zero in epsilon
precision.
*/
public static boolean isZero(double x, double epsilon) {
return abs(x) < epsilon;
}
/**
* Deep clone a two-dimensional array.
* @param x a two-dimensional array.
* @return the deep clone.
*/
public static int[][] clone(int[][] x) {
int[][] matrix = new int[x.length][];
for (int i = 0; i < x.length; i++) {
matrix[i] = x[i].clone();
}
return matrix;
}
/**
* Deep clone a two-dimensional array.
* @param x a two-dimensional array.
* @return the deep clone.
*/
public static float[][] clone(float[][] x) {
float[][] matrix = new float[x.length][];
for (int i = 0; i < x.length; i++) {
matrix[i] = x[i].clone();
}
return matrix;
}
/**
* Deep clone a two-dimensional array.
* @param x a two-dimensional array.
* @return the deep clone.
*/
public static double[][] clone(double[][] x) {
double[][] matrix = new double[x.length][];
for (int i = 0; i < x.length; i++) {
matrix[i] = x[i].clone();
}
return matrix;
}
/**
* Swap two elements of an array.
* @param x an array.
* @param i the index of first element.
* @param j the index of second element.
*/
public static void swap(int[] x, int i, int j) {
int s = x[i];
x[i] = x[j];
x[j] = s;
}
/**
* Swap two elements of an array.
* @param x an array.
* @param i the index of first element.
* @param j the index of second element.
*/
public static void swap(float[] x, int i, int j) {
float s = x[i];
x[i] = x[j];
x[j] = s;
}
/**
* Swap two elements of an array.
* @param x an array.
* @param i the index of first element.
* @param j the index of second element.
*/
public static void swap(double[] x, int i, int j) {
double s = x[i];
x[i] = x[j];
x[j] = s;
}
/**
* Swap two elements of an array.
* @param x an array.
* @param i the index of first element.
* @param j the index of second element.
*/
public static void swap(Object[] x, int i, int j) {
Object s = x[i];
x[i] = x[j];
x[j] = s;
}
/**
* Swap two arrays.
* @param x an array.
* @param y the other array.
*/
public static void swap(int[] x, int[] y) {
if (x.length != y.length) {
throw new IllegalArgumentException(String.format("Arrays have different length: x[%d], y[%d]", x.length, y.length));
}
for (int i = 0; i < x.length; i++) {
int s = x[i];
x[i] = y[i];
y[i] = s;
}
}
/**
* Swap two arrays.
* @param x an array.
* @param y the other array.
*/
public static void swap(float[] x, float[] y) {
if (x.length != y.length) {
throw new IllegalArgumentException(String.format("Arrays have different length: x[%d], y[%d]", x.length, y.length));
}
for (int i = 0; i < x.length; i++) {
float s = x[i];
x[i] = y[i];
y[i] = s;
}
}
/**
* Swap two arrays.
* @param x an array.
* @param y the other array.
*/
public static void swap(double[] x, double[] y) {
if (x.length != y.length) {
throw new IllegalArgumentException(String.format("Arrays have different length: x[%d], y[%d]", x.length, y.length));
}
for (int i = 0; i < x.length; i++) {
double s = x[i];
x[i] = y[i];
y[i] = s;
}
}
/**
* Swap two arrays.
* @param x an array.
* @param y the other array.
* @param the data type of array elements.
*/
public static void swap(E[] x, E[] y) {
if (x.length != y.length) {
throw new IllegalArgumentException(String.format("Arrays have different length: x[%d], y[%d]", x.length, y.length));
}
for (int i = 0; i < x.length; i++) {
E s = x[i];
x[i] = y[i];
y[i] = s;
}
}
/**
* Copy x into y.
* @param x the input matrix.
* @param y the output matrix.
*/
public static void copy(int[][] x, int[][] y) {
if (x.length != y.length || x[0].length != y[0].length) {
throw new IllegalArgumentException(String.format("Matrices have different rows: %d x %d vs %d x %d", x.length, x[0].length, y.length, y[0].length));
}
for (int i = 0; i < x.length; i++) {
System.arraycopy(x[i], 0, y[i], 0, x[i].length);
}
}
/**
* Deep copy x into y.
* @param x the input matrix.
* @param y the output matrix.
*/
public static void copy(float[][] x, float[][] y) {
if (x.length != y.length || x[0].length != y[0].length) {
throw new IllegalArgumentException(String.format("Matrices have different rows: %d x %d vs %d x %d", x.length, x[0].length, y.length, y[0].length));
}
for (int i = 0; i < x.length; i++) {
System.arraycopy(x[i], 0, y[i], 0, x[i].length);
}
}
/**
* Deep copy x into y.
* @param x the input matrix.
* @param y the output matrix.
*/
public static void copy(double[][] x, double[][] y) {
if (x.length != y.length || x[0].length != y[0].length) {
throw new IllegalArgumentException(String.format("Matrices have different rows: %d x %d vs %d x %d", x.length, x[0].length, y.length, y[0].length));
}
for (int i = 0; i < x.length; i++) {
System.arraycopy(x[i], 0, y[i], 0, x[i].length);
}
}
/**
* Element-wise sum of two arrays y = x + y.
* @param x a vector.
* @param y avector.
*/
public static void add(double[] y, double[] x) {
if (x.length != y.length) {
throw new IllegalArgumentException(String.format("Arrays have different length: x[%d], y[%d]", x.length, y.length));
}
for (int i = 0; i < x.length; i++) {
y[i] += x[i];
}
}
/**
* Element-wise subtraction of two arrays a -= b.
* @param a the minuend array.
* @param b the subtrahend array.
*/
public static void sub(double[] a, double[] b) {
if (b.length != a.length) {
throw new IllegalArgumentException(String.format("Arrays have different length: a[%d] != b[%d]", a.length, b.length));
}
for (int i = 0; i < b.length; i++) {
a[i] -= b[i];
}
}
/**
* Scale each element of an array by a constant x = a * x.
* @param a the scale factor.
* @param x the input and output vector.
*/
public static void scale(double a, double[] x) {
for (int i = 0; i < x.length; i++) {
x[i] *= a;
}
}
/**
* Scale each element of an array by a constant y = a * x.
* @param a the scale factor.
* @param x a vector.
* @param y the output vector.
*/
public static void scale(double a, double[] x, double[] y) {
for (int i = 0; i < x.length; i++) {
y[i] = a * x[i];
}
}
/**
* Update an array by adding a multiple of another array y = a * x + y.
* @param a the scale factor.
* @param x a vector.
* @param y the input and output vector.
* @return the vector y.
*/
public static double[] axpy(double a, double[] x, double[] y) {
if (x.length != y.length) {
throw new IllegalArgumentException(String.format("Arrays have different length: x[%d], y[%d]", x.length, y.length));
}
for (int i = 0; i < x.length; i++) {
y[i] += a * x[i];
}
return y;
}
/**
* Raise each element of an array to a scalar power.
* @param x the base array.
* @param n the scalar exponent.
* @return xn
*/
public static double[] pow(double[] x, double n) {
double[] y = new double[x.length];
for (int i = 0; i < x.length; i++) y[i] = Math.pow(x[i], n);
return y;
}
/**
* Find unique elements of vector.
* @param array an integer array.
* @return the same values as in x but with no repetitions.
*/
public static int[] unique(int[] array) {
return Arrays.stream(array).distinct().toArray();
}
/**
* Find unique elements of vector.
* @param array an array of strings.
* @return the same values as in x but with no repetitions.
*/
public static String[] unique(String[] array) {
return Arrays.stream(array).distinct().toArray(String[]::new);
}
/**
* Sorts each variable and returns the index of values in ascending order.
* Note that the order of original array is NOT altered.
*
* @param matrix a set of variables to be sorted. Each row is an instance. Each
* column is a variable.
* @return the index of values in ascending order
*/
public static int[][] sort(double[][] matrix) {
int n = matrix.length;
int p = matrix[0].length;
double[] a = new double[n];
int[][] index = new int[p][];
for (int j = 0; j < p; j++) {
for (int i = 0; i < n; i++) {
a[i] = matrix[i][j];
}
index[j] = QuickSort.sort(a);
}
return index;
}
/**
* Solve the tridiagonal linear set which is of diagonal dominance
* |bi| {@code >} |ai| + |ci|.
*
* | b0 c0 0 0 0 ... |
* | a1 b1 c1 0 0 ... |
* | 0 a2 b2 c2 0 ... |
* | ... |
* | ... a(n-2) b(n-2) c(n-2) |
* | ... 0 a(n-1) b(n-1) |
*
*
* @param a the lower part of tridiagonal matrix. a[0] is undefined and not
* referenced by the method.
* @param b the diagonal of tridiagonal matrix.
* @param c the upper of tridiagonal matrix. c[n-1] is undefined and not
* referenced by the method.
* @param r the right-hand side of linear equations.
* @return the solution.
*/
public static double[] solve(double[] a, double[] b, double[] c, double[] r) {
if (b[0] == 0.0) {
throw new IllegalArgumentException("Invalid value of b[0] == 0. The equations should be rewritten as a set of order n - 1.");
}
int n = a.length;
double[] u = new double[n];
double[] gam = new double[n];
double bet = b[0];
u[0] = r[0] / bet;
for (int j = 1; j < n; j++) {
gam[j] = c[j - 1] / bet;
bet = b[j] - a[j] * gam[j];
if (bet == 0.0) {
throw new IllegalArgumentException("The tridagonal matrix is not of diagonal dominance.");
}
u[j] = (r[j] - a[j] * u[j - 1]) / bet;
}
for (int j = (n - 2); j >= 0; j--) {
u[j] -= gam[j + 1] * u[j + 1];
}
return u;
}
}