http://git-wip-us.apache.org/repos/asf/ignite/blob/732dfea9/modules/ml/src/main/java/org/apache/ignite/math/decompositions/LUDecomposition.java ---------------------------------------------------------------------- diff --git a/modules/ml/src/main/java/org/apache/ignite/math/decompositions/LUDecomposition.java b/modules/ml/src/main/java/org/apache/ignite/math/decompositions/LUDecomposition.java new file mode 100644 index 0000000..82c90ec --- /dev/null +++ b/modules/ml/src/main/java/org/apache/ignite/math/decompositions/LUDecomposition.java @@ -0,0 +1,366 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.math.decompositions; + +import org.apache.ignite.math.Matrix; +import org.apache.ignite.math.Vector; +import org.apache.ignite.math.exceptions.CardinalityException; +import org.apache.ignite.math.exceptions.SingularMatrixException; + +/** + * Calculates the LU-decomposition of a square matrix. + * + * This class inspired by class from Apache Common Math with similar name. + * + * @see <a href="http://mathworld.wolfram.com/LUDecomposition.html">MathWorld</a> + * @see <a href="http://en.wikipedia.org/wiki/LU_decomposition">Wikipedia</a> + */ +public class LUDecomposition extends DecompositionSupport { + /** Default bound to determine effective singularity in LU decomposition. */ + private static final double DEFAULT_TOO_SMALL = 1e-11; + + /** Pivot permutation associated with LU decomposition. */ + private final Vector pivot; + /** Parity of the permutation associated with the LU decomposition. */ + private boolean even; + /** Singularity indicator. */ + private boolean singular; + /** Cached value of L. */ + private Matrix cachedL; + /** Cached value of U. */ + private Matrix cachedU; + /** Cached value of P. */ + private Matrix cachedP; + /** Original matrix. */ + private Matrix matrix; + /** Entries of LU decomposition. */ + private Matrix lu; + + /** + * Calculates the LU-decomposition of the given matrix. + * This constructor uses 1e-11 as default value for the singularity + * threshold. + * + * @param matrix Matrix to decompose. + * @throws CardinalityException if matrix is not square. + */ + public LUDecomposition(Matrix matrix) { + this(matrix, DEFAULT_TOO_SMALL); + } + + /** + * Calculates the LUP-decomposition of the given matrix. + * + * @param matrix Matrix to decompose. + * @param singularityThreshold threshold (based on partial row norm). + * @throws CardinalityException if matrix is not square. + */ + public LUDecomposition(Matrix matrix, double singularityThreshold) { + assert matrix != null; + + int rows = matrix.rowSize(); + int cols = matrix.columnSize(); + + if (rows != cols) + throw new CardinalityException(rows, cols); + + this.matrix = matrix; + + lu = copy(matrix); + + pivot = likeVector(matrix); + + for (int i = 0; i < pivot.size(); i++) + pivot.setX(i, i); + + even = true; + singular = false; + + cachedL = null; + cachedU = null; + cachedP = null; + + for (int col = 0; col < cols; col++) { + + //upper + for (int row = 0; row < col; row++) { + Vector luRow = lu.viewRow(row); + double sum = luRow.get(col); + + for (int i = 0; i < row; i++) + sum -= luRow.getX(i) * lu.getX(i, col); + + luRow.setX(col, sum); + } + + // permutation row + int max = col; + + double largest = Double.NEGATIVE_INFINITY; + + // lower + for (int row = col; row < rows; row++) { + Vector luRow = lu.viewRow(row); + double sum = luRow.getX(col); + + for (int i = 0; i < col; i++) + sum -= luRow.getX(i) * lu.getX(i, col); + + luRow.setX(col, sum); + + if (Math.abs(sum) > largest) { + largest = Math.abs(sum); + max = row; + } + } + + // Singularity check + if (Math.abs(lu.getX(max, col)) < singularityThreshold) { + singular = true; + return; + } + + // Pivot if necessary + if (max != col) { + double tmp; + Vector luMax = lu.viewRow(max); + Vector luCol = lu.viewRow(col); + + for (int i = 0; i < cols; i++) { + tmp = luMax.getX(i); + luMax.setX(i, luCol.getX(i)); + luCol.setX(i, tmp); + } + + int temp = (int)pivot.getX(max); + pivot.setX(max, pivot.getX(col)); + pivot.setX(col, temp); + + even = !even; + } + + // Divide the lower elements by the "winning" diagonal elt. + final double luDiag = lu.getX(col, col); + + for (int row = col + 1; row < cols; row++) { + double val = lu.getX(row, col) / luDiag; + lu.setX(row, col, val); + } + } + } + + /** + * Destroys decomposition components and other internal components of decomposition. + */ + @Override public void destroy() { + if (cachedL != null) + cachedL.destroy(); + if (cachedU != null) + cachedU.destroy(); + if (cachedP != null) + cachedP.destroy(); + lu.destroy(); + } + + /** + * Returns the matrix L of the decomposition. + * <p>L is a lower-triangular matrix</p> + * + * @return the L matrix (or null if decomposed matrix is singular). + */ + public Matrix getL() { + if ((cachedL == null) && !singular) { + final int m = pivot.size(); + + cachedL = like(matrix); + cachedL.assign(0.0); + + for (int i = 0; i < m; ++i) { + for (int j = 0; j < i; ++j) + cachedL.setX(i, j, lu.getX(i, j)); + + cachedL.setX(i, i, 1.0); + } + } + + return cachedL; + } + + /** + * Returns the matrix U of the decomposition. + * <p>U is an upper-triangular matrix</p> + * + * @return the U matrix (or null if decomposed matrix is singular). + */ + public Matrix getU() { + if ((cachedU == null) && !singular) { + final int m = pivot.size(); + + cachedU = like(matrix); + cachedU.assign(0.0); + + for (int i = 0; i < m; ++i) + for (int j = i; j < m; ++j) + cachedU.setX(i, j, lu.getX(i, j)); + } + + return cachedU; + } + + /** + * Returns the P rows permutation matrix. + * <p>P is a sparse matrix with exactly one element set to 1.0 in + * each row and each column, all other elements being set to 0.0.</p> + * <p>The positions of the 1 elements are given by the {@link #getPivot() + * pivot permutation vector}.</p> + * + * @return the P rows permutation matrix (or null if decomposed matrix is singular). + * @see #getPivot() + */ + public Matrix getP() { + if ((cachedP == null) && !singular) { + final int m = pivot.size(); + + cachedP = like(matrix); + cachedP.assign(0.0); + + for (int i = 0; i < m; ++i) + cachedP.setX(i, (int)pivot.get(i), 1.0); + } + + return cachedP; + } + + /** + * Returns the pivot permutation vector. + * + * @return the pivot permutation vector. + * @see #getP() + */ + public Vector getPivot() { + return pivot.copy(); + } + + /** + * Return the determinant of the matrix. + * + * @return determinant of the matrix. + */ + public double determinant() { + if (singular) + return 0; + + final int m = pivot.size(); + double determinant = even ? 1 : -1; + + for (int i = 0; i < m; i++) + determinant *= lu.getX(i, i); + + return determinant; + } + + /** */ + public Vector solve(Vector b) { + final int m = pivot.size(); + + if (b.size() != m) + throw new CardinalityException(b.size(), m); + + if (singular) + throw new SingularMatrixException(); + + final double[] bp = new double[m]; + + // Apply permutations to b + for (int row = 0; row < m; row++) + bp[row] = b.get((int)pivot.get(row)); + + // Solve LY = b + for (int col = 0; col < m; col++) { + final double bpCol = bp[col]; + + for (int i = col + 1; i < m; i++) + bp[i] -= bpCol * lu.get(i, col); + } + + // Solve UX = Y + for (int col = m - 1; col >= 0; col--) { + bp[col] /= lu.get(col, col); + final double bpCol = bp[col]; + + for (int i = 0; i < col; i++) + bp[i] -= bpCol * lu.get(i, col); + } + + return b.like(m).assign(bp); + } + + /** */ + public Matrix solve(Matrix b) { + final int m = pivot.size(); + + if (b.rowSize() != m) + throw new CardinalityException(b.rowSize(), m); + + if (singular) + throw new SingularMatrixException(); + + final int nColB = b.columnSize(); + + // Apply permutations to b + final double[][] bp = new double[m][nColB]; + for (int row = 0; row < m; row++) { + final double[] bpRow = bp[row]; + final int pRow = (int)pivot.get(row); + + for (int col = 0; col < nColB; col++) + bpRow[col] = b.get(pRow, col); + } + + // Solve LY = b + for (int col = 0; col < m; col++) { + final double[] bpCol = bp[col]; + for (int i = col + 1; i < m; i++) { + final double[] bpI = bp[i]; + final double luICol = lu.get(i, col); + + for (int j = 0; j < nColB; j++) + bpI[j] -= bpCol[j] * luICol; + } + } + + // Solve UX = Y + for (int col = m - 1; col >= 0; col--) { + final double[] bpCol = bp[col]; + final double luDiag = lu.getX(col, col); + + for (int j = 0; j < nColB; j++) + bpCol[j] /= luDiag; + + for (int i = 0; i < col; i++) { + final double[] bpI = bp[i]; + final double luICol = lu.get(i, col); + + for (int j = 0; j < nColB; j++) + bpI[j] -= bpCol[j] * luICol; + } + } + + return b.like(b.rowSize(), b.columnSize()).assign(bp); + } +}
http://git-wip-us.apache.org/repos/asf/ignite/blob/732dfea9/modules/ml/src/main/java/org/apache/ignite/math/decompositions/QRDecomposition.java ---------------------------------------------------------------------- diff --git a/modules/ml/src/main/java/org/apache/ignite/math/decompositions/QRDecomposition.java b/modules/ml/src/main/java/org/apache/ignite/math/decompositions/QRDecomposition.java new file mode 100644 index 0000000..9608ed5 --- /dev/null +++ b/modules/ml/src/main/java/org/apache/ignite/math/decompositions/QRDecomposition.java @@ -0,0 +1,186 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.math.decompositions; + +import org.apache.ignite.math.Matrix; +import org.apache.ignite.math.Vector; +import org.apache.ignite.math.functions.Functions; + +/** + * For an {@code m x n} matrix {@code A} with {@code m >= n}, the QR decomposition + * is an {@code m x n} orthogonal matrix {@code Q} and an {@code n x n} upper + * triangular matrix {@code R} so that {@code A = Q*R}. + */ +public class QRDecomposition extends DecompositionSupport { + /** */ + private final Matrix q; + /** */ + private final Matrix r; + + /** */ + private final Matrix mType; + /** */ + private final boolean fullRank; + + /** */ + private final int rows; + /** */ + private final int cols; + + /** + * @param v Value to be checked for being an ordinary double. + */ + private void checkDouble(double v) { + if (Double.isInfinite(v) || Double.isNaN(v)) + throw new ArithmeticException("Invalid intermediate result"); + } + + /** + * Constructs a new QR decomposition object computed by Householder reflections. + * + * @param mtx A rectangular matrix. + */ + public QRDecomposition(Matrix mtx) { + assert mtx != null; + + rows = mtx.rowSize(); + + int min = Math.min(mtx.rowSize(), mtx.columnSize()); + + cols = mtx.columnSize(); + + mType = like(mtx, 1, 1); + + Matrix qTmp = copy(mtx); + + boolean fullRank = true; + + r = like(mtx, min, cols); + + for (int i = 0; i < min; i++) { + Vector qi = qTmp.viewColumn(i); + + double alpha = qi.kNorm(2); + + if (Math.abs(alpha) > Double.MIN_VALUE) + qi.map(Functions.div(alpha)); + else { + checkDouble(alpha); + + fullRank = false; + } + + r.set(i, i, alpha); + + for (int j = i + 1; j < cols; j++) { + Vector qj = qTmp.viewColumn(j); + + double norm = qj.kNorm(2); + + if (Math.abs(norm) > Double.MIN_VALUE) { + double beta = qi.dot(qj); + + r.set(i, j, beta); + + if (j < min) + qj.map(qi, Functions.plusMult(-beta)); + } + else + checkDouble(norm); + } + } + + if (cols > min) + q = qTmp.viewPart(0, rows, 0, min).copy(); + else + q = qTmp; + + this.fullRank = fullRank; + } + + /** {@inheritDoc} */ + @Override public void destroy() { + q.destroy(); + r.destroy(); + mType.destroy(); + } + + /** + * Gets orthogonal factor {@code Q}. + */ + public Matrix getQ() { + return q; + } + + /** + * Gets triangular factor {@code R}. + */ + public Matrix getR() { + return r; + } + + /** + * Returns whether the matrix {@code A} has full rank. + * + * @return true if {@code R}, and hence {@code A} , has full rank. + */ + public boolean hasFullRank() { + return fullRank; + } + + /** + * Least squares solution of {@code A*X = B}; {@code returns X}. + * + * @param mtx A matrix with as many rows as {@code A} and any number of cols. + * @return {@code X<} that minimizes the two norm of {@code Q*R*X - B}. + * @throws IllegalArgumentException if {@code B.rows() != A.rows()}. + */ + public Matrix solve(Matrix mtx) { + if (mtx.rowSize() != rows) + throw new IllegalArgumentException("Matrix row dimensions must agree."); + + int cols = mtx.columnSize(); + + Matrix x = like(mType, this.cols, cols); + + Matrix qt = getQ().transpose(); + Matrix y = qt.times(mtx); + + Matrix r = getR(); + + for (int k = Math.min(this.cols, rows) - 1; k > 0; k--) { + // X[k,] = Y[k,] / R[k,k], note that X[k,] starts with 0 so += is same as = + x.viewRow(k).map(y.viewRow(k), Functions.plusMult(1 / r.get(k, k))); + + // Y[0:(k-1),] -= R[0:(k-1),k] * X[k,] + Vector rCol = r.viewColumn(k).viewPart(0, k); + + for (int c = 0; c < cols; c++) + y.viewColumn(c).viewPart(0, k).map(rCol, Functions.plusMult(-x.get(k, c))); + } + + return x; + } + + /** + * Returns a rough string rendition of a QR. + */ + @Override public String toString() { + return String.format("QR(%d x %d, fullRank=%s)", rows, cols, hasFullRank()); + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/732dfea9/modules/ml/src/main/java/org/apache/ignite/math/decompositions/SingularValueDecomposition.java ---------------------------------------------------------------------- diff --git a/modules/ml/src/main/java/org/apache/ignite/math/decompositions/SingularValueDecomposition.java b/modules/ml/src/main/java/org/apache/ignite/math/decompositions/SingularValueDecomposition.java new file mode 100644 index 0000000..75eb206 --- /dev/null +++ b/modules/ml/src/main/java/org/apache/ignite/math/decompositions/SingularValueDecomposition.java @@ -0,0 +1,620 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.math.decompositions; + +import org.apache.ignite.math.Algebra; +import org.apache.ignite.math.Matrix; + +/** + * Compute a singular value decomposition (SVD) of {@code (l x k)} matrix {@code m}. + * <p>This decomposition can be thought + * as an extension of {@link EigenDecomposition} to rectangular matrices. The factorization we get is following:</p> + * <p>{@code m = u * s * v^{*}}, where</p> + * <ul><li>{@code u} is a real or complex unitary matrix.</li> + * <li>{@code s} is a rectangular diagonal matrix with non-negative real numbers on diagonal + * (these numbers are singular values of {@code m}).</li> + * <li>{@code v} is a real or complex unitary matrix.</li></ul> + * <p>If {@code m} is real then {@code u} and {@code v} are also real.</p> + * <p>See also: <a href="https://en.wikipedia.org/wiki/Singular_value_decomposition">Wikipedia article on SVD</a>.</p> + * <p>Note: complex case is currently not supported.</p> + */ +public class SingularValueDecomposition extends DecompositionSupport { + // U and V. + /** */ + private final double[][] u; + /** */ + private final double[][] v; + + /** Singular values. */ + private final double[] s; + + /** Row dimension. */ + private final int m; + /** Column dimension. */ + private final int n; + + /** */ + private Matrix arg; + + /** */ + private boolean transpositionNeeded; + + /** + * Singular value decomposition object. + * + * @param arg A rectangular matrix. + */ + public SingularValueDecomposition(Matrix arg) { + assert arg != null; + + this.arg = arg; + + if (arg.rowSize() < arg.columnSize()) + transpositionNeeded = true; + + double[][] a; + + if (transpositionNeeded) { + // Use the transpose matrix. + m = arg.columnSize(); + n = arg.rowSize(); + + a = new double[m][n]; + + for (int i = 0; i < m; i++) + for (int j = 0; j < n; j++) + a[i][j] = arg.get(j, i); + } + else { + m = arg.rowSize(); + n = arg.columnSize(); + + a = new double[m][n]; + + for (int i = 0; i < m; i++) + for (int j = 0; j < n; j++) + a[i][j] = arg.get(i, j); + } + + int nu = Math.min(m, n); + + s = new double[Math.min(m + 1, n)]; + u = new double[m][nu]; + v = new double[n][n]; + + double[] e = new double[n]; + double[] work = new double[m]; + + int nct = Math.min(m - 1, n); + int nrt = Math.max(0, Math.min(n - 2, m)); + + for (int k = 0; k < Math.max(nct, nrt); k++) { + if (k < nct) { + // Compute the transformation for the k-th column and + // place the k-th diagonal in s[k]. Compute 2-norm of k-th + // column without under/overflow. + s[k] = 0; + + for (int i = k; i < m; i++) + s[k] = Algebra.hypot(s[k], a[i][k]); + + if (s[k] != 0.0) { + if (a[k][k] < 0.0) + s[k] = -s[k]; + + for (int i = k; i < m; i++) + a[i][k] /= s[k]; + + a[k][k] += 1.0; + } + + s[k] = -s[k]; + } + + for (int j = k + 1; j < n; j++) { + if (k < nct && s[k] != 0.0) { + // Apply the transformation. + double t = 0; + + for (int i = k; i < m; i++) + t += a[i][k] * a[i][j]; + + t = -t / a[k][k]; + + for (int i = k; i < m; i++) + a[i][j] += t * a[i][k]; + } + + // Place the k-th row of A into e for the + // subsequent calculation of the row transformation. + e[j] = a[k][j]; + } + + if (k < nct) + // Place the transformation in U for subsequent back + // multiplication. + for (int i = k; i < m; i++) + u[i][k] = a[i][k]; + + if (k < nrt) { + // Compute the k-th row transformation and place the + // k-th super-diagonal in e[k]. + // Compute 2-norm without under/overflow. + e[k] = 0; + + for (int i = k + 1; i < n; i++) + e[k] = Algebra.hypot(e[k], e[i]); + + if (e[k] != 0.0) { + if (e[k + 1] < 0.0) + e[k] = -e[k]; + + for (int i = k + 1; i < n; i++) + e[i] /= e[k]; + + e[k + 1] += 1.0; + } + + e[k] = -e[k]; + + if (k + 1 < m && e[k] != 0.0) { + // Apply the transformation. + for (int i = k + 1; i < m; i++) + work[i] = 0.0; + + for (int j = k + 1; j < n; j++) + for (int i = k + 1; i < m; i++) + work[i] += e[j] * a[i][j]; + + for (int j = k + 1; j < n; j++) { + double t = -e[j] / e[k + 1]; + + for (int i = k + 1; i < m; i++) + a[i][j] += t * work[i]; + } + } + + // Place the transformation in V for subsequent + // back multiplication. + for (int i = k + 1; i < n; i++) + v[i][k] = e[i]; + } + } + + // Set up the final bi-diagonal matrix or order p. + int p = Math.min(n, m + 1); + + if (nct < n) + s[nct] = a[nct][nct]; + + if (m < p) + s[p - 1] = 0.0; + + if (nrt + 1 < p) + e[nrt] = a[nrt][p - 1]; + + e[p - 1] = 0.0; + + // Generate U. + for (int j = nct; j < nu; j++) { + for (int i = 0; i < m; i++) + u[i][j] = 0.0; + + u[j][j] = 1.0; + } + + for (int k = nct - 1; k >= 0; k--) { + if (s[k] != 0.0) { + for (int j = k + 1; j < nu; j++) { + double t = 0; + + for (int i = k; i < m; i++) + t += u[i][k] * u[i][j]; + + t = -t / u[k][k]; + + for (int i = k; i < m; i++) + u[i][j] += t * u[i][k]; + } + + for (int i = k; i < m; i++) + u[i][k] = -u[i][k]; + + u[k][k] = 1.0 + u[k][k]; + + for (int i = 0; i < k - 1; i++) + u[i][k] = 0.0; + } + else { + for (int i = 0; i < m; i++) + u[i][k] = 0.0; + + u[k][k] = 1.0; + } + } + + // Generate V. + for (int k = n - 1; k >= 0; k--) { + if (k < nrt && e[k] != 0.0) { + for (int j = k + 1; j < nu; j++) { + double t = 0; + + for (int i = k + 1; i < n; i++) + t += v[i][k] * v[i][j]; + + t = -t / v[k + 1][k]; + + for (int i = k + 1; i < n; i++) + v[i][j] += t * v[i][k]; + } + } + + for (int i = 0; i < n; i++) + v[i][k] = 0.0; + + v[k][k] = 1.0; + } + + // Main iteration loop for the singular values. + int pp = p - 1; + int iter = 0; + + double eps = Math.pow(2.0, -52.0); + double tiny = Math.pow(2.0, -966.0); + + while (p > 0) { + int k; + + for (k = p - 2; k >= -1; k--) { + if (k == -1) + break; + + if (Math.abs(e[k]) <= tiny + eps * (Math.abs(s[k]) + Math.abs(s[k + 1]))) { + e[k] = 0.0; + + break; + } + } + + int kase; + + if (k == p - 2) + kase = 4; + else { + int ks; + + for (ks = p - 1; ks >= k; ks--) { + if (ks == k) + break; + + double t = + (ks != p ? Math.abs(e[ks]) : 0.) + + (ks != k + 1 ? Math.abs(e[ks - 1]) : 0.); + + if (Math.abs(s[ks]) <= tiny + eps * t) { + s[ks] = 0.0; + + break; + } + } + + if (ks == k) + kase = 3; + else if (ks == p - 1) + kase = 1; + else { + kase = 2; + + k = ks; + } + } + + k++; + + // Perform the task indicated by kase. + switch (kase) { + // Deflate negligible s(p). + case 1: { + double f = e[p - 2]; + + e[p - 2] = 0.0; + + for (int j = p - 2; j >= k; j--) { + double t = Algebra.hypot(s[j], f); + double cs = s[j] / t; + double sn = f / t; + + s[j] = t; + + if (j != k) { + f = -sn * e[j - 1]; + e[j - 1] = cs * e[j - 1]; + } + + for (int i = 0; i < n; i++) { + t = cs * v[i][j] + sn * v[i][p - 1]; + + v[i][p - 1] = -sn * v[i][j] + cs * v[i][p - 1]; + v[i][j] = t; + } + } + } + + break; + + // Split at negligible s(k). + case 2: { + double f = e[k - 1]; + e[k - 1] = 0.0; + + for (int j = k; j < p; j++) { + double t = Algebra.hypot(s[j], f); + double cs = s[j] / t; + double sn = f / t; + + s[j] = t; + f = -sn * e[j]; + e[j] = cs * e[j]; + + for (int i = 0; i < m; i++) { + t = cs * u[i][j] + sn * u[i][k - 1]; + + u[i][k - 1] = -sn * u[i][j] + cs * u[i][k - 1]; + u[i][j] = t; + } + } + } + + break; + + // Perform one qr step. + case 3: { + // Calculate the shift. + double scale = Math.max(Math.max(Math.max(Math.max( + Math.abs(s[p - 1]), Math.abs(s[p - 2])), Math.abs(e[p - 2])), + Math.abs(s[k])), Math.abs(e[k])); + + double sp = s[p - 1] / scale; + double spm1 = s[p - 2] / scale; + double epm1 = e[p - 2] / scale; + double sk = s[k] / scale; + double ek = e[k] / scale; + double b = ((spm1 + sp) * (spm1 - sp) + epm1 * epm1) / 2.0; + double c = sp * epm1 * sp * epm1; + double shift = 0.0; + + if (b != 0.0 || c != 0.0) { + shift = Math.sqrt(b * b + c); + + if (b < 0.0) + shift = -shift; + + shift = c / (b + shift); + } + + double f = (sk + sp) * (sk - sp) + shift; + double g = sk * ek; + + // Chase zeros. + for (int j = k; j < p - 1; j++) { + double t = Algebra.hypot(f, g); + double cs = f / t; + double sn = g / t; + + if (j != k) + e[j - 1] = t; + + f = cs * s[j] + sn * e[j]; + e[j] = cs * e[j] - sn * s[j]; + g = sn * s[j + 1]; + s[j + 1] = cs * s[j + 1]; + + for (int i = 0; i < n; i++) { + t = cs * v[i][j] + sn * v[i][j + 1]; + + v[i][j + 1] = -sn * v[i][j] + cs * v[i][j + 1]; + v[i][j] = t; + } + + t = Algebra.hypot(f, g); + cs = f / t; + sn = g / t; + s[j] = t; + f = cs * e[j] + sn * s[j + 1]; + s[j + 1] = -sn * e[j] + cs * s[j + 1]; + g = sn * e[j + 1]; + e[j + 1] = cs * e[j + 1]; + + if (j < m - 1) + for (int i = 0; i < m; i++) { + t = cs * u[i][j] + sn * u[i][j + 1]; + + u[i][j + 1] = -sn * u[i][j] + cs * u[i][j + 1]; + u[i][j] = t; + } + } + + e[p - 2] = f; + iter = iter + 1; + } + + break; + + // Convergence. + case 4: { + // Make the singular values positive. + if (s[k] <= 0.0) { + s[k] = s[k] < 0.0 ? -s[k] : 0.0; + + for (int i = 0; i <= pp; i++) + v[i][k] = -v[i][k]; + } + + // Order the singular values. + while (k < pp) { + if (s[k] >= s[k + 1]) + break; + + double t = s[k]; + + s[k] = s[k + 1]; + s[k + 1] = t; + + if (k < n - 1) + for (int i = 0; i < n; i++) { + t = v[i][k + 1]; + + v[i][k + 1] = v[i][k]; + v[i][k] = t; + } + + if (k < m - 1) + for (int i = 0; i < m; i++) { + t = u[i][k + 1]; + + u[i][k + 1] = u[i][k]; + u[i][k] = t; + } + + k++; + } + + iter = 0; + p--; + } + + break; + + default: + throw new IllegalStateException(); + } + } + } + + /** + * Gets the two norm condition number, which is {@code max(S) / min(S)} . + */ + public double cond() { + return s[0] / s[Math.min(m, n) - 1]; + } + + /** + * @return the diagonal matrix of singular values. + */ + public Matrix getS() { + double[][] s = new double[n][n]; + + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) + s[i][j] = 0.0; + + s[i][i] = this.s[i]; + } + + return like(arg, n, n).assign(s); + } + + /** + * Gets the diagonal of {@code S}, which is a one-dimensional array of + * singular values. + * + * @return diagonal of {@code S}. + */ + public double[] getSingularValues() { + return s; + } + + /** + * Gets the left singular vectors {@code U}. + * + * @return {@code U} + */ + public Matrix getU() { + if (transpositionNeeded) + return like(arg, v.length, v.length).assign(v); + else { + int numCols = Math.min(m + 1, n); + + Matrix r = like(arg, m, numCols); + + for (int i = 0; i < m; i++) + for (int j = 0; j < numCols; j++) + r.set(i, j, u[i][j]); + + return r; + } + } + + /** + * Gets the right singular vectors {@code V}. + * + * @return {@code V} + */ + public Matrix getV() { + if (transpositionNeeded) { + int numCols = Math.min(m + 1, n); + + Matrix r = like(arg, m, numCols); + + for (int i = 0; i < m; i++) + for (int j = 0; j < numCols; j++) + r.set(i, j, u[i][j]); + + return r; + } + else + return like(arg, v.length, v.length).assign(v); + } + + /** + * Gets the two norm, which is {@code max(S)}. + */ + public double norm2() { + return s[0]; + } + + /** + * Gets effective numerical matrix rank. + */ + public int rank() { + double eps = Math.pow(2.0, -52.0); + double tol = Math.max(m, n) * s[0] * eps; + int r = 0; + + for (double value : s) + if (value > tol) + r++; + + return r; + } + + /** + * Gets [n à n] covariance matrix. + * + * @param minSingularVal Value below which singular values are ignored. + */ + Matrix getCovariance(double minSingularVal) { + Matrix j = like(arg, s.length, s.length); + Matrix vMat = like(arg, v.length, v.length).assign(v); + + for (int i = 0; i < s.length; i++) + j.set(i, i, s[i] >= minSingularVal ? 1 / (s[i] * s[i]) : 0.0); + + return vMat.times(j).times(vMat.transpose()); + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/732dfea9/modules/ml/src/main/java/org/apache/ignite/math/decompositions/package-info.java ---------------------------------------------------------------------- diff --git a/modules/ml/src/main/java/org/apache/ignite/math/decompositions/package-info.java b/modules/ml/src/main/java/org/apache/ignite/math/decompositions/package-info.java new file mode 100644 index 0000000..dcfa0f8 --- /dev/null +++ b/modules/ml/src/main/java/org/apache/ignite/math/decompositions/package-info.java @@ -0,0 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * <!-- Package description. --> + * Contains matrix decompositions for distributed code algebra. + */ +package org.apache.ignite.math.decompositions; http://git-wip-us.apache.org/repos/asf/ignite/blob/732dfea9/modules/ml/src/main/java/org/apache/ignite/math/exceptions/CardinalityException.java ---------------------------------------------------------------------- diff --git a/modules/ml/src/main/java/org/apache/ignite/math/exceptions/CardinalityException.java b/modules/ml/src/main/java/org/apache/ignite/math/exceptions/CardinalityException.java new file mode 100644 index 0000000..fc87a27 --- /dev/null +++ b/modules/ml/src/main/java/org/apache/ignite/math/exceptions/CardinalityException.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.math.exceptions; + +import org.apache.ignite.IgniteException; + +/** + * Indicates a cardinality mismatch in matrix or vector operations. + */ +public class CardinalityException extends IgniteException { + /** */ + private static final long serialVersionUID = 0L; + + /** + * Creates new cardinality violation exception. + * + * @param exp Expected cardinality. + * @param act Actual cardinality. + */ + public CardinalityException(int exp, int act) { + super("Cardinality violation [expected=" + exp + ", actual=" + act + "]"); + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/732dfea9/modules/ml/src/main/java/org/apache/ignite/math/exceptions/ColumnIndexException.java ---------------------------------------------------------------------- diff --git a/modules/ml/src/main/java/org/apache/ignite/math/exceptions/ColumnIndexException.java b/modules/ml/src/main/java/org/apache/ignite/math/exceptions/ColumnIndexException.java new file mode 100644 index 0000000..7670caf --- /dev/null +++ b/modules/ml/src/main/java/org/apache/ignite/math/exceptions/ColumnIndexException.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.math.exceptions; + +import org.apache.ignite.IgniteException; + +/** + * This exception is used to indicate any error condition accessing matrix elements by invalid column index. + */ +public class ColumnIndexException extends IgniteException { + /** */ + private static final long serialVersionUID = 0L; + + /** + * @param idx Index value that caused this exception. + */ + public ColumnIndexException(int idx) { + super("Invalid (out of bound) column index: " + idx); + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/732dfea9/modules/ml/src/main/java/org/apache/ignite/math/exceptions/IndexException.java ---------------------------------------------------------------------- diff --git a/modules/ml/src/main/java/org/apache/ignite/math/exceptions/IndexException.java b/modules/ml/src/main/java/org/apache/ignite/math/exceptions/IndexException.java new file mode 100644 index 0000000..9ada706 --- /dev/null +++ b/modules/ml/src/main/java/org/apache/ignite/math/exceptions/IndexException.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.math.exceptions; + +import org.apache.ignite.IgniteException; + +/** + * Indicates an invalid, i.e. out of bound, index on matrix or vector operations. + */ +public class IndexException extends IgniteException { + /** */ + private static final long serialVersionUID = 0L; + + /** + * @param idx Index value that caused this exception. + */ + public IndexException(int idx) { + super("Invalid (out of bound) index: " + idx); + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/732dfea9/modules/ml/src/main/java/org/apache/ignite/math/exceptions/NonPositiveDefiniteMatrixException.java ---------------------------------------------------------------------- diff --git a/modules/ml/src/main/java/org/apache/ignite/math/exceptions/NonPositiveDefiniteMatrixException.java b/modules/ml/src/main/java/org/apache/ignite/math/exceptions/NonPositiveDefiniteMatrixException.java new file mode 100644 index 0000000..b6017c2 --- /dev/null +++ b/modules/ml/src/main/java/org/apache/ignite/math/exceptions/NonPositiveDefiniteMatrixException.java @@ -0,0 +1,20 @@ +package org.apache.ignite.math.exceptions; + +import org.apache.ignite.IgniteException; + +/** + * This exception is used to indicate error condition of matrix elements failing the positivity check. + */ +public class NonPositiveDefiniteMatrixException extends IgniteException { + /** + * Construct an exception. + * + * @param wrong Value that fails the positivity check. + * @param idx Row (and column) index. + * @param threshold Absolute positivity threshold. + */ + public NonPositiveDefiniteMatrixException(double wrong, int idx, double threshold) { + super("Matrix must be positive, wrong element located on diagonal with index " + + idx + " and has value " + wrong + " with this threshold " + threshold); + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/732dfea9/modules/ml/src/main/java/org/apache/ignite/math/exceptions/NonSymmetricMatrixException.java ---------------------------------------------------------------------- diff --git a/modules/ml/src/main/java/org/apache/ignite/math/exceptions/NonSymmetricMatrixException.java b/modules/ml/src/main/java/org/apache/ignite/math/exceptions/NonSymmetricMatrixException.java new file mode 100644 index 0000000..8b4cbdb --- /dev/null +++ b/modules/ml/src/main/java/org/apache/ignite/math/exceptions/NonSymmetricMatrixException.java @@ -0,0 +1,18 @@ +package org.apache.ignite.math.exceptions; + +import org.apache.ignite.IgniteException; + +/** + * This exception is used to indicate error condition of matrix failing the symmetry check. + */ +public class NonSymmetricMatrixException extends IgniteException { + /** + * @param row Row. + * @param col Column. + * @param threshold Threshold. + */ + public NonSymmetricMatrixException(int row, int col, double threshold) { + super("Symmetric matrix expected, the symmetry is broken on row " + + row + " and col " + col + " with this threshold " + threshold); + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/732dfea9/modules/ml/src/main/java/org/apache/ignite/math/exceptions/RowIndexException.java ---------------------------------------------------------------------- diff --git a/modules/ml/src/main/java/org/apache/ignite/math/exceptions/RowIndexException.java b/modules/ml/src/main/java/org/apache/ignite/math/exceptions/RowIndexException.java new file mode 100644 index 0000000..f74ae2c --- /dev/null +++ b/modules/ml/src/main/java/org/apache/ignite/math/exceptions/RowIndexException.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.math.exceptions; + +import org.apache.ignite.IgniteException; + +/** + * This exception is used to indicate any error condition accessing matrix elements by invalid row index. + */ +public class RowIndexException extends IgniteException { + /** */ + private static final long serialVersionUID = 0L; + + /** + * @param idx Index value that caused this exception. + */ + public RowIndexException(int idx) { + super("Invalid (out of bound) row index: " + idx); + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/732dfea9/modules/ml/src/main/java/org/apache/ignite/math/exceptions/SingularMatrixException.java ---------------------------------------------------------------------- diff --git a/modules/ml/src/main/java/org/apache/ignite/math/exceptions/SingularMatrixException.java b/modules/ml/src/main/java/org/apache/ignite/math/exceptions/SingularMatrixException.java new file mode 100644 index 0000000..4ed3410 --- /dev/null +++ b/modules/ml/src/main/java/org/apache/ignite/math/exceptions/SingularMatrixException.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.math.exceptions; + +import org.apache.ignite.IgniteException; + +/** + * Exception to be thrown when a non-singular matrix is expected. + */ +public class SingularMatrixException extends IgniteException { + /** */ + public SingularMatrixException() { + super("Regular (or non-singular) matrix expected."); + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/732dfea9/modules/ml/src/main/java/org/apache/ignite/math/exceptions/UnknownProviderException.java ---------------------------------------------------------------------- diff --git a/modules/ml/src/main/java/org/apache/ignite/math/exceptions/UnknownProviderException.java b/modules/ml/src/main/java/org/apache/ignite/math/exceptions/UnknownProviderException.java new file mode 100644 index 0000000..3e6498a --- /dev/null +++ b/modules/ml/src/main/java/org/apache/ignite/math/exceptions/UnknownProviderException.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.math.exceptions; + +import org.apache.ignite.IgniteException; + +/** + * Indicates that no provider has been found for a given vector or matrix flavor. + */ +public class UnknownProviderException extends IgniteException { + /** */ + private static final long serialVersionUID = 0L; + + /** + * @param flv Flavor (a.k.a. operation performance hints) that has no registered provider for. + */ + public UnknownProviderException(String flv) { + super("No provider has been found for the flavor: " + flv); + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/732dfea9/modules/ml/src/main/java/org/apache/ignite/math/exceptions/UnsupportedOperationException.java ---------------------------------------------------------------------- diff --git a/modules/ml/src/main/java/org/apache/ignite/math/exceptions/UnsupportedOperationException.java b/modules/ml/src/main/java/org/apache/ignite/math/exceptions/UnsupportedOperationException.java new file mode 100644 index 0000000..be5264c --- /dev/null +++ b/modules/ml/src/main/java/org/apache/ignite/math/exceptions/UnsupportedOperationException.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.math.exceptions; + +import org.apache.ignite.IgniteException; + +/** + * Indicate that a specific operation is not supported by the underlying implementation. + * In some cases, an operation may be unsupported only in certain cases where, for example, + * it could not be deterministically completed in polynomial time. + */ +public class UnsupportedOperationException extends IgniteException { + /** */ + private static final long serialVersionUID = 0L; + + /** + * @param errMsg Error message. + */ + public UnsupportedOperationException(String errMsg) { + super(errMsg); + } + + /** + * + */ + public UnsupportedOperationException() { + this("Unsupported operation."); + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/732dfea9/modules/ml/src/main/java/org/apache/ignite/math/exceptions/package-info.java ---------------------------------------------------------------------- diff --git a/modules/ml/src/main/java/org/apache/ignite/math/exceptions/package-info.java b/modules/ml/src/main/java/org/apache/ignite/math/exceptions/package-info.java new file mode 100644 index 0000000..83f3fa4 --- /dev/null +++ b/modules/ml/src/main/java/org/apache/ignite/math/exceptions/package-info.java @@ -0,0 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * <!-- Package description. --> + * Contains exceptions for distributed code algebra. + */ +package org.apache.ignite.math.exceptions; \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ignite/blob/732dfea9/modules/ml/src/main/java/org/apache/ignite/math/functions/Functions.java ---------------------------------------------------------------------- diff --git a/modules/ml/src/main/java/org/apache/ignite/math/functions/Functions.java b/modules/ml/src/main/java/org/apache/ignite/math/functions/Functions.java new file mode 100644 index 0000000..7100908 --- /dev/null +++ b/modules/ml/src/main/java/org/apache/ignite/math/functions/Functions.java @@ -0,0 +1,136 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.math.functions; + +/** + * Compatibility with Apache Mahout. + */ +public final class Functions { + /** Function that returns {@code Math.abs(a)}. */ + public static final IgniteDoubleFunction<Double> ABS = Math::abs; + + /** Function that returns its argument. */ + public static final IgniteDoubleFunction<Double> IDENTITY = (a) -> a; + + /** Function that returns {@code Math.log(a) / Math.log(2)}. */ + public static final IgniteDoubleFunction<Double> LOG2 = (a) -> Math.log(a) * 1.4426950408889634; + + /** Function that returns {@code -a}. */ + public static final IgniteDoubleFunction<Double> NEGATE = (a) -> -a; + + /** Function that returns {@code a < 0 ? -1 : a > 0 ? 1 : 0 }. */ + public static final IgniteDoubleFunction<Double> SIGN = (a) -> a < 0.0 ? -1.0 : a > 0.0 ? 1.0 : 0.0; + + /** Function that returns {@code a * a}. */ + public static final IgniteDoubleFunction<Double> SQUARE = (a) -> a * a; + + /** Function that returns {@code 1 / (1 + exp(-a) } */ + public static final IgniteDoubleFunction<Double> SIGMOID = (a) -> 1.0 / (1.0 + Math.exp(-a)); + + /** Function that returns {@code 1 / a } */ + public static final IgniteDoubleFunction<Double> INV = (a) -> 1.0 / a; + + /** Function that returns {@code a * (1-a) } */ + public static final IgniteDoubleFunction<Double> SIGMOIDGRADIENT = (a) -> a * (1.0 - a); + + /** Function that returns {@code a % b}. */ + public static final IgniteBiFunction<Double, Double, Double> MOD = (a, b) -> a % b; + + /** Function that returns {@code a * b}. */ + public static final IgniteBiFunction<Double, Double, Double> MULT = (a, b) -> a * b; + + /** Function that returns {@code Math.log(a) / Math.log(b)}. */ + public static final IgniteBiFunction<Double, Double, Double> LG = (a, b) -> Math.log(a) / Math.log(b); + + /** Function that returns {@code a + b}. */ + public static final IgniteBiFunction<Double, Double, Double> PLUS = (a, b) -> a + b; + + /** Function that returns {@code a - b}. */ + public static final IgniteBiFunction<Double, Double, Double> MINUS = (a, b) -> a - b; + + /** Function that returns {@code abs(a - b)}. */ + public static final IgniteBiFunction<Double, Double, Double> MINUS_ABS = (a, b) -> Math.abs(a - b); + + /** Function that returns {@code max(abs(a), abs(b))}. */ + public static final IgniteBiFunction<Double, Double, Double> MAX_ABS = (a, b) -> Math.max(Math.abs(a), Math.abs(b)); + + /** Function that returns {@code min(abs(a), abs(b))}. */ + public static final IgniteBiFunction<Double, Double, Double> MIN_ABS = (a, b) -> Math.min(Math.abs(a), Math.abs(b)); + + /** Function that returns {@code Math.abs(a) + Math.abs(b)}. */ + public static final IgniteBiFunction<Double, Double, Double> PLUS_ABS = (a, b) -> Math.abs(a) + Math.abs(b); + + /** Function that returns {@code (a - b) * (a - b)} */ + public static final IgniteBiFunction<Double, Double, Double> MINUS_SQUARED = (a, b) -> (a - b) * (a - b); + + /** + * Function that returns {@code a < b ? -1 : a > b ? 1 : 0}. + */ + public static final IgniteBiFunction<Double, Double, Double> COMPARE = (a, b) -> a < b ? -1.0 : a > b ? 1.0 : 0.0; + + /** + * Function that returns {@code a + b}. {@code a} is a variable, {@code b} is fixed. + * + * @param b + */ + public static IgniteDoubleFunction<Double> plus(final double b) { + return (a) -> a + b; + } + + /** + * Function that returns {@code a * b}. {@code a} is a variable, {@code b} is fixed. + * + * @param b + */ + public static IgniteDoubleFunction<Double> mult(final double b) { + return (a) -> a * b; + } + + /** Function that returns {@code a / b}. {@code a} is a variable, {@code b} is fixed. */ + public static IgniteDoubleFunction<Double> div(double b) { + return mult(1 / b); + } + + /** + * Function that returns {@code a + b*constant}. {@code a} and {@code b} are variables, + * {@code constant} is fixed. + */ + public static IgniteBiFunction<Double, Double, Double> plusMult(double constant) { + return (a, b) -> a + b * constant; + } + + /** + * Function that returns {@code a - b*constant}. {@code a} and {@code b} are variables, + * {@code constant} is fixed. + */ + public static IgniteBiFunction<Double, Double, Double> minusMult(double constant) { + return (a, b) -> a - b * constant; + } + + /** + * @param b + */ + public static IgniteDoubleFunction<Double> pow(final double b) { + return (a) -> { + if (b == 2) + return a * a; + else + return Math.pow(a, b); + }; + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/732dfea9/modules/ml/src/main/java/org/apache/ignite/math/functions/IgniteBiConsumer.java ---------------------------------------------------------------------- diff --git a/modules/ml/src/main/java/org/apache/ignite/math/functions/IgniteBiConsumer.java b/modules/ml/src/main/java/org/apache/ignite/math/functions/IgniteBiConsumer.java new file mode 100644 index 0000000..22e8274 --- /dev/null +++ b/modules/ml/src/main/java/org/apache/ignite/math/functions/IgniteBiConsumer.java @@ -0,0 +1,12 @@ +package org.apache.ignite.math.functions; + +import java.io.Serializable; +import java.util.function.BiConsumer; + +/** + * Serializable binary consumer. + * + * @see java.util.function.BiConsumer + */ +public interface IgniteBiConsumer<T, U> extends BiConsumer<T, U>, Serializable { +} http://git-wip-us.apache.org/repos/asf/ignite/blob/732dfea9/modules/ml/src/main/java/org/apache/ignite/math/functions/IgniteBiFunction.java ---------------------------------------------------------------------- diff --git a/modules/ml/src/main/java/org/apache/ignite/math/functions/IgniteBiFunction.java b/modules/ml/src/main/java/org/apache/ignite/math/functions/IgniteBiFunction.java new file mode 100644 index 0000000..9d9c147 --- /dev/null +++ b/modules/ml/src/main/java/org/apache/ignite/math/functions/IgniteBiFunction.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.math.functions; + +import java.io.Serializable; +import java.util.function.BiFunction; + +/** + * Serializable binary function. + * + * @see java.util.function.BiFunction + */ +public interface IgniteBiFunction<A, B, T> extends BiFunction<A, B, T>, Serializable { +} http://git-wip-us.apache.org/repos/asf/ignite/blob/732dfea9/modules/ml/src/main/java/org/apache/ignite/math/functions/IgniteConsumer.java ---------------------------------------------------------------------- diff --git a/modules/ml/src/main/java/org/apache/ignite/math/functions/IgniteConsumer.java b/modules/ml/src/main/java/org/apache/ignite/math/functions/IgniteConsumer.java new file mode 100644 index 0000000..1f7ca07 --- /dev/null +++ b/modules/ml/src/main/java/org/apache/ignite/math/functions/IgniteConsumer.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.math.functions; + +import java.io.Serializable; +import java.util.function.Consumer; + +/** + * Serializable consumer. + * + * @see java.util.function.Consumer + */ +public interface IgniteConsumer<T> extends Consumer<T>, Serializable { +} http://git-wip-us.apache.org/repos/asf/ignite/blob/732dfea9/modules/ml/src/main/java/org/apache/ignite/math/functions/IgniteDoubleFunction.java ---------------------------------------------------------------------- diff --git a/modules/ml/src/main/java/org/apache/ignite/math/functions/IgniteDoubleFunction.java b/modules/ml/src/main/java/org/apache/ignite/math/functions/IgniteDoubleFunction.java new file mode 100644 index 0000000..7a23d50 --- /dev/null +++ b/modules/ml/src/main/java/org/apache/ignite/math/functions/IgniteDoubleFunction.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.math.functions; + +import java.io.Serializable; +import java.util.function.DoubleFunction; + +/** + * Serializable double function. + * + * @see java.util.function.DoubleFunction + */ +public interface IgniteDoubleFunction<Double> extends DoubleFunction<Double>, Serializable { +} http://git-wip-us.apache.org/repos/asf/ignite/blob/732dfea9/modules/ml/src/main/java/org/apache/ignite/math/functions/IgniteFunction.java ---------------------------------------------------------------------- diff --git a/modules/ml/src/main/java/org/apache/ignite/math/functions/IgniteFunction.java b/modules/ml/src/main/java/org/apache/ignite/math/functions/IgniteFunction.java new file mode 100644 index 0000000..cfe89a4 --- /dev/null +++ b/modules/ml/src/main/java/org/apache/ignite/math/functions/IgniteFunction.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.math.functions; + +import java.io.Serializable; +import java.util.function.Function; + +/** + * Serializable function. + * + * @see java.util.function.Function + */ +public interface IgniteFunction<T, R> extends Function<T, R>, Serializable { + +} http://git-wip-us.apache.org/repos/asf/ignite/blob/732dfea9/modules/ml/src/main/java/org/apache/ignite/math/functions/IntDoubleToVoidFunction.java ---------------------------------------------------------------------- diff --git a/modules/ml/src/main/java/org/apache/ignite/math/functions/IntDoubleToVoidFunction.java b/modules/ml/src/main/java/org/apache/ignite/math/functions/IntDoubleToVoidFunction.java new file mode 100644 index 0000000..e5d69c7 --- /dev/null +++ b/modules/ml/src/main/java/org/apache/ignite/math/functions/IntDoubleToVoidFunction.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.math.functions; + +/** + * Setter function for the vector. + */ +public interface IntDoubleToVoidFunction extends IgniteBiConsumer<Integer, Double> { + +} http://git-wip-us.apache.org/repos/asf/ignite/blob/732dfea9/modules/ml/src/main/java/org/apache/ignite/math/functions/IntIntDoubleToVoidFunction.java ---------------------------------------------------------------------- diff --git a/modules/ml/src/main/java/org/apache/ignite/math/functions/IntIntDoubleToVoidFunction.java b/modules/ml/src/main/java/org/apache/ignite/math/functions/IntIntDoubleToVoidFunction.java new file mode 100644 index 0000000..cad8c3c --- /dev/null +++ b/modules/ml/src/main/java/org/apache/ignite/math/functions/IntIntDoubleToVoidFunction.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.math.functions; + +import java.io.Serializable; + +/** + * Setter function for matrices. + */ +public interface IntIntDoubleToVoidFunction extends Serializable { + /** */ + public void apply(int x, int y, double v); +} http://git-wip-us.apache.org/repos/asf/ignite/blob/732dfea9/modules/ml/src/main/java/org/apache/ignite/math/functions/IntIntToDoubleFunction.java ---------------------------------------------------------------------- diff --git a/modules/ml/src/main/java/org/apache/ignite/math/functions/IntIntToDoubleFunction.java b/modules/ml/src/main/java/org/apache/ignite/math/functions/IntIntToDoubleFunction.java new file mode 100644 index 0000000..b31d9f9 --- /dev/null +++ b/modules/ml/src/main/java/org/apache/ignite/math/functions/IntIntToDoubleFunction.java @@ -0,0 +1,24 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.math.functions; + +/** + * Getters functions for matrices. + */ +public interface IntIntToDoubleFunction extends IgniteBiFunction<Integer, Integer, Double> { +} http://git-wip-us.apache.org/repos/asf/ignite/blob/732dfea9/modules/ml/src/main/java/org/apache/ignite/math/functions/package-info.java ---------------------------------------------------------------------- diff --git a/modules/ml/src/main/java/org/apache/ignite/math/functions/package-info.java b/modules/ml/src/main/java/org/apache/ignite/math/functions/package-info.java new file mode 100644 index 0000000..133e62c --- /dev/null +++ b/modules/ml/src/main/java/org/apache/ignite/math/functions/package-info.java @@ -0,0 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * <!-- Package description. --> + * Contains serializable functions for distributed code algebra. + */ +package org.apache.ignite.math.functions; \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ignite/blob/732dfea9/modules/ml/src/main/java/org/apache/ignite/math/impls/CacheUtils.java ---------------------------------------------------------------------- diff --git a/modules/ml/src/main/java/org/apache/ignite/math/impls/CacheUtils.java b/modules/ml/src/main/java/org/apache/ignite/math/impls/CacheUtils.java new file mode 100644 index 0000000..df33895 --- /dev/null +++ b/modules/ml/src/main/java/org/apache/ignite/math/impls/CacheUtils.java @@ -0,0 +1,356 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.math.impls; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import javax.cache.Cache; +import org.apache.ignite.Ignite; +import org.apache.ignite.IgniteCache; +import org.apache.ignite.Ignition; +import org.apache.ignite.cache.affinity.Affinity; +import org.apache.ignite.cache.query.ScanQuery; +import org.apache.ignite.cluster.ClusterGroup; +import org.apache.ignite.cluster.ClusterNode; +import org.apache.ignite.lang.IgniteCallable; +import org.apache.ignite.lang.IgniteRunnable; +import org.apache.ignite.math.KeyMapper; +import org.apache.ignite.math.ValueMapper; +import org.apache.ignite.math.functions.IgniteBiFunction; +import org.apache.ignite.math.functions.IgniteConsumer; +import org.apache.ignite.math.functions.IgniteFunction; + +/** + * Distribution-related misc. support. + */ +public class CacheUtils { + /** + * Cache entry support. + * + * @param <K> + * @param <V> + */ + public static class CacheEntry<K, V> { + /** */ + private Cache.Entry<K, V> entry; + /** */ + private IgniteCache<K, V> cache; + + /** + * @param entry Original cache entry. + * @param cache Cache instance. + */ + CacheEntry(Cache.Entry<K, V> entry, IgniteCache<K, V> cache) { + this.entry = entry; + this.cache = cache; + } + + /** + * + * + */ + public Cache.Entry<K, V> entry() { + return entry; + } + + /** + * + * + */ + public IgniteCache<K, V> cache() { + return cache; + } + } + + /** + * Gets local Ignite instance. + */ + public static Ignite ignite() { + return Ignition.localIgnite(); + } + + /** + * @param cacheName Cache name. + * @param k Key into the cache. + * @param <K> Key type. + * @return Cluster group for given key. + */ + public static <K> ClusterGroup groupForKey(String cacheName, K k) { + return ignite().cluster().forNode(ignite().affinity(cacheName).mapKeyToNode(k)); + } + + /** + * @param cacheName Cache name. + * @param keyMapper {@link KeyMapper} to validate cache key. + * @param valMapper {@link ValueMapper} to obtain double value for given cache key. + * @param <K> Cache key object type. + * @param <V> Cache value object type. + * @return Sum of the values obtained for valid keys. + */ + public static <K, V> double sum(String cacheName, KeyMapper<K> keyMapper, ValueMapper<V> valMapper) { + Collection<Double> subSums = fold(cacheName, (CacheEntry<K, V> ce, Double acc) -> { + if (keyMapper.isValid(ce.entry().getKey())) { + double v = valMapper.toDouble(ce.entry().getValue()); + + return acc == null ? v : acc + v; + } + else + return acc; + }); + + return sum(subSums); + } + + /** + * @param cacheName Cache name. + * @return Sum obtained using sparse logic. + */ + public static <K, V> double sparseSum(String cacheName) { + Collection<Double> subSums = fold(cacheName, (CacheEntry<Integer, Map<Integer, Double>> ce, Double acc) -> { + Map<Integer, Double> map = ce.entry().getValue(); + + double sum = sum(map.values()); + + return acc == null ? sum : acc + sum; + }); + + return sum(subSums); + } + + /** + * @param c {@link Collection} of double values to sum. + * @return Sum of the values. + */ + private static double sum(Collection<Double> c) { + double sum = 0.0; + + for (double d : c) + sum += d; + + return sum; + } + + /** + * @param cacheName Cache name. + * @param keyMapper {@link KeyMapper} to validate cache key. + * @param valMapper {@link ValueMapper} to obtain double value for given cache key. + * @param <K> Cache key object type. + * @param <V> Cache value object type. + * @return Minimum value for valid keys. + */ + public static <K, V> double min(String cacheName, KeyMapper<K> keyMapper, ValueMapper<V> valMapper) { + Collection<Double> mins = fold(cacheName, (CacheEntry<K, V> ce, Double acc) -> { + if (keyMapper.isValid(ce.entry().getKey())) { + double v = valMapper.toDouble(ce.entry().getValue()); + + if (acc == null) + return v; + else + return Math.min(acc, v); + } + else + return acc; + }); + + return Collections.min(mins); + } + + /** + * @param cacheName Cache name. + * @return Minimum value obtained using sparse logic. + */ + public static <K, V> double sparseMin(String cacheName) { + Collection<Double> mins = fold(cacheName, (CacheEntry<Integer, Map<Integer, Double>> ce, Double acc) -> { + Map<Integer, Double> map = ce.entry().getValue(); + + double min = Collections.min(map.values()); + + if (acc == null) + return min; + else + return Math.min(acc, min); + }); + + return Collections.min(mins); + } + + /** + * @param cacheName Cache name. + * @return Maximum value obtained using sparse logic. + */ + public static <K, V> double sparseMax(String cacheName) { + Collection<Double> maxes = fold(cacheName, (CacheEntry<Integer, Map<Integer, Double>> ce, Double acc) -> { + Map<Integer, Double> map = ce.entry().getValue(); + + double max = Collections.max(map.values()); + + if (acc == null) + return max; + else + return Math.max(acc, max); + }); + + return Collections.max(maxes); + } + + /** + * @param cacheName Cache name. + * @param keyMapper {@link KeyMapper} to validate cache key. + * @param valMapper {@link ValueMapper} to obtain double value for given cache key. + * @param <K> Cache key object type. + * @param <V> Cache value object type. + * @return Maximum value for valid keys. + */ + public static <K, V> double max(String cacheName, KeyMapper<K> keyMapper, ValueMapper<V> valMapper) { + Collection<Double> maxes = fold(cacheName, (CacheEntry<K, V> ce, Double acc) -> { + if (keyMapper.isValid(ce.entry().getKey())) { + double v = valMapper.toDouble(ce.entry().getValue()); + + if (acc == null) + return v; + else + return Math.max(acc, v); + } + else + return acc; + }); + + return Collections.max(maxes); + } + + /** + * @param cacheName Cache name. + * @param keyMapper {@link KeyMapper} to validate cache key. + * @param valMapper {@link ValueMapper} to obtain double value for given cache key. + * @param mapper Mapping {@link IgniteFunction}. + * @param <K> Cache key object type. + * @param <V> Cache value object type. + */ + public static <K, V> void map(String cacheName, KeyMapper<K> keyMapper, ValueMapper<V> valMapper, + IgniteFunction<Double, Double> mapper) { + foreach(cacheName, (CacheEntry<K, V> ce) -> { + K k = ce.entry().getKey(); + + if (keyMapper.isValid(k)) + // Actual assignment. + ce.cache().put(k, valMapper.fromDouble(mapper.apply(valMapper.toDouble(ce.entry().getValue())))); + }); + } + + /** + * @param cacheName Cache name. + * @param mapper Mapping {@link IgniteFunction}. + */ + public static <K, V> void sparseMap(String cacheName, IgniteFunction<Double, Double> mapper) { + foreach(cacheName, (CacheEntry<Integer, Map<Integer, Double>> ce) -> { + Integer k = ce.entry().getKey(); + Map<Integer, Double> v = ce.entry().getValue(); + + for (Map.Entry<Integer, Double> e : v.entrySet()) + e.setValue(mapper.apply(e.getValue())); + + ce.cache().put(k, v); + }); + } + + /** + * @param cacheName Cache name. + * @param fun An operation that accepts a cache entry and processes it. + * @param <K> Cache key object type. + * @param <V> Cache value object type. + */ + public static <K, V> void foreach(String cacheName, IgniteConsumer<CacheEntry<K, V>> fun) { + bcast(cacheName, () -> { + Ignite ignite = Ignition.localIgnite(); + IgniteCache<K, V> cache = ignite.getOrCreateCache(cacheName); + + int partsCnt = ignite.affinity(cacheName).partitions(); + + // Use affinity in filter for scan query. Otherwise we accept consumer in each node which is wrong. + Affinity affinity = ignite.affinity(cacheName); + ClusterNode locNode = ignite.cluster().localNode(); + + // Iterate over all partitions. Some of them will be stored on that local node. + for (int part = 0; part < partsCnt; part++) { + int p = part; + + // Iterate over given partition. + // Query returns an empty cursor if this partition is not stored on this node. + for (Cache.Entry<K, V> entry : cache.query(new ScanQuery<K, V>(part, + (k, v) -> affinity.mapPartitionToNode(p) == locNode))) + fun.accept(new CacheEntry<>(entry, cache)); + } + }); + } + + /** + * <b>Currently fold supports only commutative operations.<b/> + * + * @param cacheName Cache name. + * @param folder Fold function operating over cache entries. + * @param <K> Cache key object type. + * @param <V> Cache value object type. + * @param <A> Fold result type. + * @return Fold operation result. + */ + public static <K, V, A> Collection<A> fold(String cacheName, IgniteBiFunction<CacheEntry<K, V>, A, A> folder) { + return bcast(cacheName, () -> { + Ignite ignite = Ignition.localIgnite(); + IgniteCache<K, V> cache = ignite.getOrCreateCache(cacheName); + + int partsCnt = ignite.affinity(cacheName).partitions(); + + // Use affinity in filter for ScanQuery. Otherwise we accept consumer in each node which is wrong. + Affinity affinity = ignite.affinity(cacheName); + ClusterNode locNode = ignite.cluster().localNode(); + + A a = null; + + // Iterate over all partitions. Some of them will be stored on that local node. + for (int part = 0; part < partsCnt; part++) { + int p = part; + + // Iterate over given partition. + // Query returns an empty cursor if this partition is not stored on this node. + for (Cache.Entry<K, V> entry : cache.query(new ScanQuery<K, V>(part, + (k, v) -> affinity.mapPartitionToNode(p) == locNode))) + a = folder.apply(new CacheEntry<>(entry, cache), a); + } + + return a; + }); + } + + /** + * @param cacheName Cache name. + * @param run {@link Runnable} to broadcast to cache nodes for given cache name. + */ + public static void bcast(String cacheName, IgniteRunnable run) { + ignite().compute(ignite().cluster().forCacheNodes(cacheName)).broadcast(run); + } + + /** + * @param cacheName Cache name. + * @param call {@link IgniteCallable} to broadcast to cache nodes for given cache name. + * @param <A> Type returned by the callable. + */ + public static <A> Collection<A> bcast(String cacheName, IgniteCallable<A> call) { + return ignite().compute(ignite().cluster().forCacheNodes(cacheName)).broadcast(call); + } +}
