You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by av...@apache.org on 2017/04/18 14:54:50 UTC
[16/24] ignite git commit: IGNITE-5000 Rename Ignite Math module to
Ignite ML module added missed licenses renamed packages fixed wrong ml
profile activation (cherry picked from commit d78e071)
http://git-wip-us.apache.org/repos/asf/ignite/blob/0abf6601/modules/ml/src/main/java/org/apache/ignite/ml/math/decompositions/DecompositionSupport.java
----------------------------------------------------------------------
diff --git a/modules/ml/src/main/java/org/apache/ignite/ml/math/decompositions/DecompositionSupport.java b/modules/ml/src/main/java/org/apache/ignite/ml/math/decompositions/DecompositionSupport.java
new file mode 100644
index 0000000..20d6e79
--- /dev/null
+++ b/modules/ml/src/main/java/org/apache/ignite/ml/math/decompositions/DecompositionSupport.java
@@ -0,0 +1,105 @@
+/*
+ * 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.ml.math.decompositions;
+
+import org.apache.ignite.ml.math.Destroyable;
+import org.apache.ignite.ml.math.Matrix;
+import org.apache.ignite.ml.math.Vector;
+import org.apache.ignite.ml.math.impls.matrix.CacheMatrix;
+import org.apache.ignite.ml.math.impls.matrix.DenseLocalOnHeapMatrix;
+import org.apache.ignite.ml.math.impls.matrix.PivotedMatrixView;
+import org.apache.ignite.ml.math.impls.matrix.RandomMatrix;
+import org.apache.ignite.ml.math.impls.vector.DenseLocalOnHeapVector;
+
+/**
+ * Helper methods to support decomposition of matrix types having some functionality limited.
+ */
+public abstract class DecompositionSupport implements Destroyable {
+ /**
+ * Create the like matrix with read-only matrices support.
+ *
+ * @param matrix Matrix for like.
+ * @return Like matrix.
+ */
+ protected Matrix like(Matrix matrix) {
+ if (isCopyLikeSupport(matrix))
+ return new DenseLocalOnHeapMatrix(matrix.rowSize(), matrix.columnSize());
+ else
+ return matrix.like(matrix.rowSize(), matrix.columnSize());
+ }
+
+ /**
+ * Create the like matrix with specified size with read-only matrices support.
+ *
+ * @param matrix Matrix for like.
+ * @return Like matrix.
+ */
+ protected Matrix like(Matrix matrix, int rows, int cols) {
+ if (isCopyLikeSupport(matrix))
+ return new DenseLocalOnHeapMatrix(rows, cols);
+ else
+ return matrix.like(rows, cols);
+ }
+
+ /**
+ * Create the like vector with read-only matrices support.
+ *
+ * @param matrix Matrix for like.
+ * @param crd Cardinality of the vector.
+ * @return Like vector.
+ */
+ protected Vector likeVector(Matrix matrix, int crd) {
+ if (isCopyLikeSupport(matrix))
+ return new DenseLocalOnHeapVector(crd);
+ else
+ return matrix.likeVector(crd);
+ }
+
+ /**
+ * Create the like vector with read-only matrices support.
+ *
+ * @param matrix Matrix for like.
+ * @return Like vector.
+ */
+ protected Vector likeVector(Matrix matrix) {
+ return likeVector(matrix, matrix.rowSize());
+ }
+
+ /**
+ * Create the copy of matrix with read-only matrices support.
+ *
+ * @param matrix Matrix for copy.
+ * @return Copy.
+ */
+ protected Matrix copy(Matrix matrix) {
+ if (isCopyLikeSupport(matrix)) {
+ DenseLocalOnHeapMatrix cp = new DenseLocalOnHeapMatrix(matrix.rowSize(), matrix.columnSize());
+
+ cp.assign(matrix);
+
+ return cp;
+ }
+ else
+ return matrix.copy();
+ }
+
+ /** */
+ private boolean isCopyLikeSupport(Matrix matrix) {
+ return matrix instanceof RandomMatrix || matrix instanceof PivotedMatrixView || matrix instanceof CacheMatrix;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/0abf6601/modules/ml/src/main/java/org/apache/ignite/ml/math/decompositions/EigenDecomposition.java
----------------------------------------------------------------------
diff --git a/modules/ml/src/main/java/org/apache/ignite/ml/math/decompositions/EigenDecomposition.java b/modules/ml/src/main/java/org/apache/ignite/ml/math/decompositions/EigenDecomposition.java
new file mode 100644
index 0000000..01af989
--- /dev/null
+++ b/modules/ml/src/main/java/org/apache/ignite/ml/math/decompositions/EigenDecomposition.java
@@ -0,0 +1,923 @@
+/*
+ * 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.ml.math.decompositions;
+
+import org.apache.ignite.ml.math.Matrix;
+import org.apache.ignite.ml.math.Vector;
+import org.apache.ignite.ml.math.functions.Functions;
+
+/**
+ * This class provides EigenDecomposition of given matrix. The class is based on
+ * class with similar name from <a href="http://mahout.apache.org/">Apache Mahout</a> library.
+ *
+ * @see <a href=http://mathworld.wolfram.com/EigenDecomposition.html>MathWorld</a>
+ */
+public class EigenDecomposition extends DecompositionSupport {
+ /** Row and column dimension (square matrix). */
+ private final int n;
+
+ /** Array for internal storage of eigen vectors. */
+ private final Matrix v;
+
+ /** Array for internal storage of eigenvalues. */
+ private final Vector d;
+ /** Array for internal storage of eigenvalues. */
+ private final Vector e;
+
+ /** */
+ public EigenDecomposition(Matrix matrix) {
+ this(matrix, isSymmetric(matrix));
+ }
+
+ /** */
+ public EigenDecomposition(Matrix matrix, boolean isSymmetric) {
+ n = matrix.columnSize();
+
+ d = likeVector(matrix);
+ e = likeVector(matrix);
+ v = like(matrix);
+
+ if (isSymmetric) {
+ v.assign(matrix);
+
+ // Tridiagonalize.
+ tred2();
+
+ // Diagonalize.
+ tql2();
+
+ }
+ else
+ // Reduce to Hessenberg form.
+ // Reduce Hessenberg to real Schur form.
+ hqr2(orthes(matrix));
+ }
+
+ /**
+ * Return the eigen vector matrix
+ *
+ * @return V
+ */
+ public Matrix getV() {
+ return like(v).assign(v);
+ }
+
+ /**
+ * Return the real parts of the eigenvalues
+ */
+ public Vector getRealEigenValues() {
+ return d;
+ }
+
+ /**
+ * Return the imaginary parts of the eigenvalues
+ */
+ public Vector getImagEigenvalues() {
+ return e;
+ }
+
+ /**
+ * Return the block diagonal eigenvalue matrix
+ *
+ * @return D
+ */
+ public Matrix getD() {
+ Matrix res = like(v, d.size(), d.size());
+ res.assign(0);
+ res.viewDiagonal().assign(d);
+ for (int i = 0; i < n; i++) {
+ double v = e.getX(i);
+ if (v > 0)
+ res.setX(i, i + 1, v);
+ else if (v < 0)
+ res.setX(i, i - 1, v);
+ }
+ return res;
+ }
+
+ /**
+ * Destroys decomposition components and other internal components of decomposition.
+ */
+ @Override public void destroy() {
+ e.destroy();
+ v.destroy();
+ d.destroy();
+ }
+
+ /** */
+ private void tred2() {
+ // This is derived from the Algol procedures tred2 by
+ // Bowdler, Martin, Reinsch, and Wilkinson, Handbook for
+ // Auto. Comp., Vol.ii-Linear Algebra, and the corresponding
+ // Fortran subroutine in EISPACK.
+
+ d.assign(v.viewColumn(n - 1));
+
+ // Householder reduction to tridiagonal form.
+
+ for (int i = n - 1; i > 0; i--) {
+
+ // Scale to avoid under/overflow.
+ double scale = d.viewPart(0, i).kNorm(1);
+ double h = 0.0;
+
+ if (scale == 0.0) {
+ e.setX(i, d.getX(i - 1));
+ for (int j = 0; j < i; j++) {
+ d.setX(j, v.getX(i - 1, j));
+ v.setX(i, j, 0.0);
+ v.setX(j, i, 0.0);
+ }
+ }
+ else {
+
+ // Generate Householder vector.
+
+ for (int k = 0; k < i; k++) {
+ d.setX(k, d.getX(k) / scale);
+ h += d.getX(k) * d.getX(k);
+ }
+
+ double f = d.getX(i - 1);
+ double g = Math.sqrt(h);
+
+ if (f > 0)
+ g = -g;
+
+ e.setX(i, scale * g);
+ h -= f * g;
+ d.setX(i - 1, f - g);
+
+ for (int j = 0; j < i; j++)
+ e.setX(j, 0.0);
+
+ // Apply similarity transformation to remaining columns.
+
+ for (int j = 0; j < i; j++) {
+ f = d.getX(j);
+ v.setX(j, i, f);
+ g = e.getX(j) + v.getX(j, j) * f;
+
+ for (int k = j + 1; k <= i - 1; k++) {
+ g += v.getX(k, j) * d.getX(k);
+ e.setX(k, e.getX(k) + v.getX(k, j) * f);
+ }
+
+ e.setX(j, g);
+ }
+
+ f = 0.0;
+
+ for (int j = 0; j < i; j++) {
+ e.setX(j, e.getX(j) / h);
+ f += e.getX(j) * d.getX(j);
+ }
+
+ double hh = f / (h + h);
+
+ for (int j = 0; j < i; j++)
+ e.setX(j, e.getX(j) - hh * d.getX(j));
+
+ for (int j = 0; j < i; j++) {
+ f = d.getX(j);
+ g = e.getX(j);
+
+ for (int k = j; k <= i - 1; k++)
+ v.setX(k, j, v.getX(k, j) - (f * e.getX(k) + g * d.getX(k)));
+
+ d.setX(j, v.getX(i - 1, j));
+ v.setX(i, j, 0.0);
+ }
+ }
+
+ d.setX(i, h);
+ }
+ }
+
+ /** */
+ private Matrix orthes(Matrix matrix) {
+ // Working storage for nonsymmetric algorithm.
+ Vector ort = likeVector(matrix);
+ Matrix hessenBerg = like(matrix).assign(matrix);
+
+ // This is derived from the Algol procedures orthes and ortran,
+ // by Martin and Wilkinson, Handbook for Auto. Comp.,
+ // Vol.ii-Linear Algebra, and the corresponding
+ // Fortran subroutines in EISPACK.
+
+ int low = 0;
+ int high = n - 1;
+
+ for (int m = low + 1; m <= high - 1; m++) {
+
+ // Scale column.
+
+ Vector hCol = hessenBerg.viewColumn(m - 1).viewPart(m, high - m + 1);
+ double scale = hCol.kNorm(1);
+
+ if (scale != 0.0) {
+ // Compute Householder transformation.
+ ort.viewPart(m, high - m + 1).map(hCol, Functions.plusMult(1 / scale));
+ double h = ort.viewPart(m, high - m + 1).getLengthSquared();
+
+ double g = Math.sqrt(h);
+
+ if (ort.getX(m) > 0)
+ g = -g;
+
+ h -= ort.getX(m) * g;
+ ort.setX(m, ort.getX(m) - g);
+
+ // Apply Householder similarity transformation
+ // H = (I-u*u'/h)*H*(I-u*u')/h)
+
+ Vector ortPiece = ort.viewPart(m, high - m + 1);
+
+ for (int j = m; j < n; j++) {
+ double f = ortPiece.dot(hessenBerg.viewColumn(j).viewPart(m, high - m + 1)) / h;
+ hessenBerg.viewColumn(j).viewPart(m, high - m + 1).map(ortPiece, Functions.plusMult(-f));
+ }
+
+ for (int i = 0; i <= high; i++) {
+ double f = ortPiece.dot(hessenBerg.viewRow(i).viewPart(m, high - m + 1)) / h;
+ hessenBerg.viewRow(i).viewPart(m, high - m + 1).map(ortPiece, Functions.plusMult(-f));
+ }
+
+ ort.setX(m, scale * ort.getX(m));
+ hessenBerg.setX(m, m - 1, scale * g);
+ }
+ }
+
+ // Accumulate transformations (Algol's ortran).
+
+ v.assign(0);
+ v.viewDiagonal().assign(1);
+
+ for (int m = high - 1; m >= low + 1; m--) {
+ if (hessenBerg.getX(m, m - 1) != 0.0) {
+ ort.viewPart(m + 1, high - m).assign(hessenBerg.viewColumn(m - 1).viewPart(m + 1, high - m));
+
+ for (int j = m; j <= high; j++) {
+ double g = ort.viewPart(m, high - m + 1).dot(v.viewColumn(j).viewPart(m, high - m + 1));
+
+ // Double division avoids possible underflow
+ g = g / ort.getX(m) / hessenBerg.getX(m, m - 1);
+ v.viewColumn(j).viewPart(m, high - m + 1).map(ort.viewPart(m, high - m + 1), Functions.plusMult(g));
+ }
+ }
+ }
+
+ return hessenBerg;
+ }
+
+ /** Symmetric tridiagonal QL algorithm. */
+ private void tql2() {
+ // This is derived from the Algol procedures tql2, by
+ // Bowdler, Martin, Reinsch, and Wilkinson, Handbook for
+ // Auto. Comp., Vol.ii-Linear Algebra, and the corresponding
+ // Fortran subroutine in EISPACK.
+
+ e.viewPart(0, n - 1).assign(e.viewPart(1, n - 1));
+ e.setX(n - 1, 0.0);
+
+ double f = 0.0;
+ double tst1 = 0.0;
+ double eps = Math.pow(2.0, -52.0);
+
+ for (int l = 0; l < n; l++) {
+ // Find small subdiagonal element.
+
+ tst1 = Math.max(tst1, Math.abs(d.getX(l)) + Math.abs(e.getX(l)));
+ int m = l;
+
+ while (m < n) {
+ if (Math.abs(e.getX(m)) <= eps * tst1)
+ break;
+
+ m++;
+ }
+
+ // If m == l, d.getX(l) is an eigenvalue,
+ // otherwise, iterate.
+
+ if (m > l) {
+ do {
+ // Compute implicit shift
+
+ double g = d.getX(l);
+ double p = (d.getX(l + 1) - g) / (2.0 * e.getX(l));
+ double r = Math.hypot(p, 1.0);
+
+ if (p < 0)
+ r = -r;
+
+ d.setX(l, e.getX(l) / (p + r));
+ d.setX(l + 1, e.getX(l) * (p + r));
+ double dl1 = d.getX(l + 1);
+ double h = g - d.getX(l);
+
+ for (int i = l + 2; i < n; i++)
+ d.setX(i, d.getX(i) - h);
+
+ f += h;
+
+ // Implicit QL transformation.
+
+ p = d.getX(m);
+ double c = 1.0;
+ double c2 = c;
+ double c3 = c;
+ double el1 = e.getX(l + 1);
+ double s = 0.0;
+ double s2 = 0.0;
+
+ for (int i = m - 1; i >= l; i--) {
+ c3 = c2;
+ c2 = c;
+ s2 = s;
+ g = c * e.getX(i);
+ h = c * p;
+ r = Math.hypot(p, e.getX(i));
+ e.setX(i + 1, s * r);
+ s = e.getX(i) / r;
+ c = p / r;
+ p = c * d.getX(i) - s * g;
+ d.setX(i + 1, h + s * (c * g + s * d.getX(i)));
+
+ // Accumulate transformation.
+
+ for (int k = 0; k < n; k++) {
+ h = v.getX(k, i + 1);
+ v.setX(k, i + 1, s * v.getX(k, i) + c * h);
+ v.setX(k, i, c * v.getX(k, i) - s * h);
+ }
+ }
+
+ p = -s * s2 * c3 * el1 * e.getX(l) / dl1;
+ e.setX(l, s * p);
+ d.setX(l, c * p);
+
+ // Check for convergence.
+
+ }
+ while (Math.abs(e.getX(l)) > eps * tst1);
+ }
+
+ d.setX(l, d.getX(l) + f);
+ e.setX(l, 0.0);
+ }
+
+ // Sort eigenvalues and corresponding vectors.
+
+ for (int i = 0; i < n - 1; i++) {
+ int k = i;
+ double p = d.getX(i);
+
+ for (int j = i + 1; j < n; j++)
+ if (d.getX(j) > p) {
+ k = j;
+ p = d.getX(j);
+ }
+
+ if (k != i) {
+ d.setX(k, d.getX(i));
+ d.setX(i, p);
+
+ for (int j = 0; j < n; j++) {
+ p = v.getX(j, i);
+ v.setX(j, i, v.getX(j, k));
+ v.setX(j, k, p);
+ }
+ }
+ }
+ }
+
+ /** */
+ private void hqr2(Matrix h) {
+ // This is derived from the Algol procedure hqr2,
+ // by Martin and Wilkinson, Handbook for Auto. Comp.,
+ // Vol.ii-Linear Algebra, and the corresponding
+ // Fortran subroutine in EISPACK.
+
+ // Initialize
+
+ int nn = this.n;
+ int n = nn - 1;
+ int low = 0;
+ int high = nn - 1;
+ double eps = Math.pow(2.0, -52.0);
+ double exshift = 0.0;
+ double p = 0;
+ double q = 0;
+ double r = 0;
+ double s = 0;
+ double z = 0;
+ double w;
+ double x;
+ double y;
+
+ // Store roots isolated by balanc and compute matrix norm
+
+ double norm = h.foldMap(Functions.PLUS, Functions.ABS, 0.0);
+
+ // Outer loop over eigenvalue index
+
+ int iter = 0;
+ while (n >= low) {
+ // Look for single small sub-diagonal element
+ int l = n;
+
+ while (l > low) {
+ s = Math.abs(h.getX(l - 1, l - 1)) + Math.abs(h.getX(l, l));
+
+ if (s == 0.0)
+ s = norm;
+
+ if (Math.abs(h.getX(l, l - 1)) < eps * s)
+ break;
+
+ l--;
+ }
+
+ // Check for convergence
+
+ if (l == n) {
+ // One root found
+ h.setX(n, n, h.getX(n, n) + exshift);
+ d.setX(n, h.getX(n, n));
+ e.setX(n, 0.0);
+ n--;
+ iter = 0;
+ }
+ else if (l == n - 1) {
+ // Two roots found
+ w = h.getX(n, n - 1) * h.getX(n - 1, n);
+ p = (h.getX(n - 1, n - 1) - h.getX(n, n)) / 2.0;
+ q = p * p + w;
+ z = Math.sqrt(Math.abs(q));
+ h.setX(n, n, h.getX(n, n) + exshift);
+ h.setX(n - 1, n - 1, h.getX(n - 1, n - 1) + exshift);
+ x = h.getX(n, n);
+
+ // Real pair
+ if (q >= 0) {
+ if (p >= 0)
+ z = p + z;
+ else
+ z = p - z;
+
+ d.setX(n - 1, x + z);
+ d.setX(n, d.getX(n - 1));
+
+ if (z != 0.0)
+ d.setX(n, x - w / z);
+
+ e.setX(n - 1, 0.0);
+ e.setX(n, 0.0);
+ x = h.getX(n, n - 1);
+ s = Math.abs(x) + Math.abs(z);
+ p = x / s;
+ q = z / s;
+ r = Math.sqrt(p * p + q * q);
+ p /= r;
+ q /= r;
+
+ // Row modification
+
+ for (int j = n - 1; j < nn; j++) {
+ z = h.getX(n - 1, j);
+ h.setX(n - 1, j, q * z + p * h.getX(n, j));
+ h.setX(n, j, q * h.getX(n, j) - p * z);
+ }
+
+ // Column modification
+
+ for (int i = 0; i <= n; i++) {
+ z = h.getX(i, n - 1);
+ h.setX(i, n - 1, q * z + p * h.getX(i, n));
+ h.setX(i, n, q * h.getX(i, n) - p * z);
+ }
+
+ // Accumulate transformations
+
+ for (int i = low; i <= high; i++) {
+ z = v.getX(i, n - 1);
+ v.setX(i, n - 1, q * z + p * v.getX(i, n));
+ v.setX(i, n, q * v.getX(i, n) - p * z);
+ }
+
+ // Complex pair
+
+ }
+ else {
+ d.setX(n - 1, x + p);
+ d.setX(n, x + p);
+ e.setX(n - 1, z);
+ e.setX(n, -z);
+ }
+
+ n -= 2;
+ iter = 0;
+
+ // No convergence yet
+
+ }
+ else {
+ // Form shift
+ x = h.getX(n, n);
+ y = 0.0;
+ w = 0.0;
+
+ if (l < n) {
+ y = h.getX(n - 1, n - 1);
+ w = h.getX(n, n - 1) * h.getX(n - 1, n);
+ }
+
+ // Wilkinson's original ad hoc shift
+
+ if (iter == 10) {
+ exshift += x;
+
+ for (int i = low; i <= n; i++)
+ h.setX(i, i, x);
+
+ s = Math.abs(h.getX(n, n - 1)) + Math.abs(h.getX(n - 1, n - 2));
+ x = y = 0.75 * s;
+ w = -0.4375 * s * s;
+ }
+
+ // MATLAB's new ad hoc shift
+
+ if (iter == 30) {
+ s = (y - x) / 2.0;
+ s = s * s + w;
+
+ if (s > 0) {
+ s = Math.sqrt(s);
+
+ if (y < x)
+ s = -s;
+
+ s = x - w / ((y - x) / 2.0 + s);
+
+ for (int i = low; i <= n; i++)
+ h.setX(i, i, h.getX(i, i) - s);
+
+ exshift += s;
+ x = y = w = 0.964;
+ }
+ }
+
+ iter++; // (Could check iteration count here.)
+
+ // Look for two consecutive small sub-diagonal elements
+
+ int m = n - 2;
+
+ while (m >= l) {
+ z = h.getX(m, m);
+ r = x - z;
+ s = y - z;
+ p = (r * s - w) / h.getX(m + 1, m) + h.getX(m, m + 1);
+ q = h.getX(m + 1, m + 1) - z - r - s;
+ r = h.getX(m + 2, m + 1);
+ s = Math.abs(p) + Math.abs(q) + Math.abs(r);
+ p /= s;
+ q /= s;
+ r /= s;
+
+ if (m == l)
+ break;
+
+ double hmag = Math.abs(h.getX(m - 1, m - 1)) + Math.abs(h.getX(m + 1, m + 1));
+ double threshold = eps * Math.abs(p) * (Math.abs(z) + hmag);
+
+ if (Math.abs(h.getX(m, m - 1)) * (Math.abs(q) + Math.abs(r)) < threshold)
+ break;
+
+ m--;
+ }
+
+ for (int i = m + 2; i <= n; i++) {
+ h.setX(i, i - 2, 0.0);
+
+ if (i > m + 2)
+ h.setX(i, i - 3, 0.0);
+ }
+
+ // Double QR step involving rows l:n and columns m:n
+
+ for (int k = m; k <= n - 1; k++) {
+ boolean notlast = k != n - 1;
+
+ if (k != m) {
+ p = h.getX(k, k - 1);
+ q = h.getX(k + 1, k - 1);
+ r = notlast ? h.getX(k + 2, k - 1) : 0.0;
+ x = Math.abs(p) + Math.abs(q) + Math.abs(r);
+ if (x != 0.0) {
+ p /= x;
+ q /= x;
+ r /= x;
+ }
+ }
+
+ if (x == 0.0)
+ break;
+
+ s = Math.sqrt(p * p + q * q + r * r);
+
+ if (p < 0)
+ s = -s;
+
+ if (s != 0) {
+ if (k != m)
+ h.setX(k, k - 1, -s * x);
+ else if (l != m)
+ h.setX(k, k - 1, -h.getX(k, k - 1));
+
+ p += s;
+ x = p / s;
+ y = q / s;
+ z = r / s;
+ q /= p;
+ r /= p;
+
+ // Row modification
+
+ for (int j = k; j < nn; j++) {
+ p = h.getX(k, j) + q * h.getX(k + 1, j);
+
+ if (notlast) {
+ p += r * h.getX(k + 2, j);
+ h.setX(k + 2, j, h.getX(k + 2, j) - p * z);
+ }
+
+ h.setX(k, j, h.getX(k, j) - p * x);
+ h.setX(k + 1, j, h.getX(k + 1, j) - p * y);
+ }
+
+ // Column modification
+
+ for (int i = 0; i <= Math.min(n, k + 3); i++) {
+ p = x * h.getX(i, k) + y * h.getX(i, k + 1);
+
+ if (notlast) {
+ p += z * h.getX(i, k + 2);
+ h.setX(i, k + 2, h.getX(i, k + 2) - p * r);
+ }
+
+ h.setX(i, k, h.getX(i, k) - p);
+ h.setX(i, k + 1, h.getX(i, k + 1) - p * q);
+ }
+
+ // Accumulate transformations
+
+ for (int i = low; i <= high; i++) {
+ p = x * v.getX(i, k) + y * v.getX(i, k + 1);
+
+ if (notlast) {
+ p += z * v.getX(i, k + 2);
+ v.setX(i, k + 2, v.getX(i, k + 2) - p * r);
+ }
+
+ v.setX(i, k, v.getX(i, k) - p);
+ v.setX(i, k + 1, v.getX(i, k + 1) - p * q);
+ }
+ } // (s != 0)
+ } // k loop
+ } // check convergence
+ } // while (n >= low)
+
+ // Back substitute to find vectors of upper triangular form
+
+ if (norm == 0.0)
+ return;
+
+ for (n = nn - 1; n >= 0; n--) {
+ p = d.getX(n);
+ q = e.getX(n);
+
+ // Real vector
+
+ double t;
+
+ if (q == 0) {
+ int l = n;
+ h.setX(n, n, 1.0);
+
+ for (int i = n - 1; i >= 0; i--) {
+ w = h.getX(i, i) - p;
+ r = 0.0;
+
+ for (int j = l; j <= n; j++)
+ r += h.getX(i, j) * h.getX(j, n);
+
+ if (e.getX(i) < 0.0) {
+ z = w;
+ s = r;
+ }
+ else {
+ l = i;
+
+ if (e.getX(i) == 0.0) {
+ if (w == 0.0)
+ h.setX(i, n, -r / (eps * norm));
+ else
+ h.setX(i, n, -r / w);
+
+ // Solve real equations
+
+ }
+ else {
+ x = h.getX(i, i + 1);
+ y = h.getX(i + 1, i);
+ q = (d.getX(i) - p) * (d.getX(i) - p) + e.getX(i) * e.getX(i);
+ t = (x * s - z * r) / q;
+ h.setX(i, n, t);
+
+ if (Math.abs(x) > Math.abs(z))
+ h.setX(i + 1, n, (-r - w * t) / x);
+ else
+ h.setX(i + 1, n, (-s - y * t) / z);
+ }
+
+ // Overflow control
+
+ t = Math.abs(h.getX(i, n));
+
+ if (eps * t * t > 1) {
+ for (int j = i; j <= n; j++)
+ h.setX(j, n, h.getX(j, n) / t);
+ }
+ }
+ }
+
+ // Complex vector
+
+ }
+ else if (q < 0) {
+ int l = n - 1;
+
+ // Last vector component imaginary so matrix is triangular
+
+ if (Math.abs(h.getX(n, n - 1)) > Math.abs(h.getX(n - 1, n))) {
+ h.setX(n - 1, n - 1, q / h.getX(n, n - 1));
+ h.setX(n - 1, n, -(h.getX(n, n) - p) / h.getX(n, n - 1));
+ }
+ else {
+ cdiv(0.0, -h.getX(n - 1, n), h.getX(n - 1, n - 1) - p, q);
+ h.setX(n - 1, n - 1, cdivr);
+ h.setX(n - 1, n, cdivi);
+ }
+
+ h.setX(n, n - 1, 0.0);
+ h.setX(n, n, 1.0);
+
+ for (int i = n - 2; i >= 0; i--) {
+ double ra = 0.0;
+ double sa = 0.0;
+
+ for (int j = l; j <= n; j++) {
+ ra += h.getX(i, j) * h.getX(j, n - 1);
+ sa += h.getX(i, j) * h.getX(j, n);
+ }
+
+ w = h.getX(i, i) - p;
+
+ if (e.getX(i) < 0.0) {
+ z = w;
+ r = ra;
+ s = sa;
+ }
+ else {
+ l = i;
+
+ if (e.getX(i) == 0) {
+ cdiv(-ra, -sa, w, q);
+ h.setX(i, n - 1, cdivr);
+ h.setX(i, n, cdivi);
+ }
+ else {
+
+ // Solve complex equations
+
+ x = h.getX(i, i + 1);
+ y = h.getX(i + 1, i);
+
+ double vr = (d.getX(i) - p) * (d.getX(i) - p) + e.getX(i) * e.getX(i) - q * q;
+ double vi = (d.getX(i) - p) * 2.0 * q;
+
+ if (vr == 0.0 && vi == 0.0) {
+ double hmag = Math.abs(x) + Math.abs(y);
+ vr = eps * norm * (Math.abs(w) + Math.abs(q) + hmag + Math.abs(z));
+ }
+
+ cdiv(x * r - z * ra + q * sa, x * s - z * sa - q * ra, vr, vi);
+
+ h.setX(i, n - 1, cdivr);
+ h.setX(i, n, cdivi);
+
+ if (Math.abs(x) > (Math.abs(z) + Math.abs(q))) {
+ h.setX(i + 1, n - 1, (-ra - w * h.getX(i, n - 1) + q * h.getX(i, n)) / x);
+ h.setX(i + 1, n, (-sa - w * h.getX(i, n) - q * h.getX(i, n - 1)) / x);
+ }
+ else {
+ cdiv(-r - y * h.getX(i, n - 1), -s - y * h.getX(i, n), z, q);
+
+ h.setX(i + 1, n - 1, cdivr);
+ h.setX(i + 1, n, cdivi);
+ }
+ }
+
+ // Overflow control
+
+ t = Math.max(Math.abs(h.getX(i, n - 1)), Math.abs(h.getX(i, n)));
+
+ if (eps * t * t > 1)
+ for (int j = i; j <= n; j++) {
+ h.setX(j, n - 1, h.getX(j, n - 1) / t);
+ h.setX(j, n, h.getX(j, n) / t);
+ }
+ }
+ }
+ }
+ }
+
+ // Vectors of isolated roots
+
+ for (int i = 0; i < nn; i++)
+ if (i < low || i > high) {
+ for (int j = i; j < nn; j++)
+ v.setX(i, j, h.getX(i, j));
+ }
+
+ // Back transformation to get eigen vectors of original matrix
+
+ for (int j = nn - 1; j >= low; j--)
+ for (int i = low; i <= high; i++) {
+ z = 0.0;
+
+ for (int k = low; k <= Math.min(j, high); k++)
+ z += v.getX(i, k) * h.getX(k, j);
+
+ v.setX(i, j, z);
+ }
+ }
+
+ /** */
+ private static boolean isSymmetric(Matrix matrix) {
+ int cols = matrix.columnSize();
+ int rows = matrix.rowSize();
+
+ if (cols != rows)
+ return false;
+
+ for (int i = 0; i < cols; i++)
+ for (int j = 0; j < rows; j++) {
+ if (matrix.getX(i, j) != matrix.get(j, i))
+ return false;
+ }
+
+ return true;
+ }
+
+ /** Complex scalar division - real part. */
+ private double cdivr;
+ /** Complex scalar division - imaginary part. */
+ private double cdivi;
+
+ /** */
+ private void cdiv(double xr, double xi, double yr, double yi) {
+ double r;
+ double d;
+
+ if (Math.abs(yr) > Math.abs(yi)) {
+ r = yi / yr;
+ d = yr + r * yi;
+ cdivr = (xr + r * xi) / d;
+ cdivi = (xi - r * xr) / d;
+ }
+ else {
+ r = yr / yi;
+ d = yi + r * yr;
+ cdivr = (r * xr + xi) / d;
+ cdivi = (r * xi - xr) / d;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/0abf6601/modules/ml/src/main/java/org/apache/ignite/ml/math/decompositions/LUDecomposition.java
----------------------------------------------------------------------
diff --git a/modules/ml/src/main/java/org/apache/ignite/ml/math/decompositions/LUDecomposition.java b/modules/ml/src/main/java/org/apache/ignite/ml/math/decompositions/LUDecomposition.java
new file mode 100644
index 0000000..b1efc09
--- /dev/null
+++ b/modules/ml/src/main/java/org/apache/ignite/ml/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.ml.math.decompositions;
+
+import org.apache.ignite.ml.math.Matrix;
+import org.apache.ignite.ml.math.Vector;
+import org.apache.ignite.ml.math.exceptions.CardinalityException;
+import org.apache.ignite.ml.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/0abf6601/modules/ml/src/main/java/org/apache/ignite/ml/math/decompositions/QRDecomposition.java
----------------------------------------------------------------------
diff --git a/modules/ml/src/main/java/org/apache/ignite/ml/math/decompositions/QRDecomposition.java b/modules/ml/src/main/java/org/apache/ignite/ml/math/decompositions/QRDecomposition.java
new file mode 100644
index 0000000..39215e8
--- /dev/null
+++ b/modules/ml/src/main/java/org/apache/ignite/ml/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.ml.math.decompositions;
+
+import org.apache.ignite.ml.math.Matrix;
+import org.apache.ignite.ml.math.Vector;
+import org.apache.ignite.ml.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/0abf6601/modules/ml/src/main/java/org/apache/ignite/ml/math/decompositions/SingularValueDecomposition.java
----------------------------------------------------------------------
diff --git a/modules/ml/src/main/java/org/apache/ignite/ml/math/decompositions/SingularValueDecomposition.java b/modules/ml/src/main/java/org/apache/ignite/ml/math/decompositions/SingularValueDecomposition.java
new file mode 100644
index 0000000..1b04e4f
--- /dev/null
+++ b/modules/ml/src/main/java/org/apache/ignite/ml/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.ml.math.decompositions;
+
+import org.apache.ignite.ml.math.Algebra;
+import org.apache.ignite.ml.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/0abf6601/modules/ml/src/main/java/org/apache/ignite/ml/math/decompositions/package-info.java
----------------------------------------------------------------------
diff --git a/modules/ml/src/main/java/org/apache/ignite/ml/math/decompositions/package-info.java b/modules/ml/src/main/java/org/apache/ignite/ml/math/decompositions/package-info.java
new file mode 100644
index 0000000..d317ccd
--- /dev/null
+++ b/modules/ml/src/main/java/org/apache/ignite/ml/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.ml.math.decompositions;
http://git-wip-us.apache.org/repos/asf/ignite/blob/0abf6601/modules/ml/src/main/java/org/apache/ignite/ml/math/exceptions/CardinalityException.java
----------------------------------------------------------------------
diff --git a/modules/ml/src/main/java/org/apache/ignite/ml/math/exceptions/CardinalityException.java b/modules/ml/src/main/java/org/apache/ignite/ml/math/exceptions/CardinalityException.java
new file mode 100644
index 0000000..f03e5d8
--- /dev/null
+++ b/modules/ml/src/main/java/org/apache/ignite/ml/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.ml.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/0abf6601/modules/ml/src/main/java/org/apache/ignite/ml/math/exceptions/ColumnIndexException.java
----------------------------------------------------------------------
diff --git a/modules/ml/src/main/java/org/apache/ignite/ml/math/exceptions/ColumnIndexException.java b/modules/ml/src/main/java/org/apache/ignite/ml/math/exceptions/ColumnIndexException.java
new file mode 100644
index 0000000..08a67b5
--- /dev/null
+++ b/modules/ml/src/main/java/org/apache/ignite/ml/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.ml.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/0abf6601/modules/ml/src/main/java/org/apache/ignite/ml/math/exceptions/IndexException.java
----------------------------------------------------------------------
diff --git a/modules/ml/src/main/java/org/apache/ignite/ml/math/exceptions/IndexException.java b/modules/ml/src/main/java/org/apache/ignite/ml/math/exceptions/IndexException.java
new file mode 100644
index 0000000..93ca11e
--- /dev/null
+++ b/modules/ml/src/main/java/org/apache/ignite/ml/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.ml.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/0abf6601/modules/ml/src/main/java/org/apache/ignite/ml/math/exceptions/NonPositiveDefiniteMatrixException.java
----------------------------------------------------------------------
diff --git a/modules/ml/src/main/java/org/apache/ignite/ml/math/exceptions/NonPositiveDefiniteMatrixException.java b/modules/ml/src/main/java/org/apache/ignite/ml/math/exceptions/NonPositiveDefiniteMatrixException.java
new file mode 100644
index 0000000..b0cf294
--- /dev/null
+++ b/modules/ml/src/main/java/org/apache/ignite/ml/math/exceptions/NonPositiveDefiniteMatrixException.java
@@ -0,0 +1,37 @@
+/*
+ * 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.ml.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/0abf6601/modules/ml/src/main/java/org/apache/ignite/ml/math/exceptions/NonSymmetricMatrixException.java
----------------------------------------------------------------------
diff --git a/modules/ml/src/main/java/org/apache/ignite/ml/math/exceptions/NonSymmetricMatrixException.java b/modules/ml/src/main/java/org/apache/ignite/ml/math/exceptions/NonSymmetricMatrixException.java
new file mode 100644
index 0000000..7c563fe
--- /dev/null
+++ b/modules/ml/src/main/java/org/apache/ignite/ml/math/exceptions/NonSymmetricMatrixException.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.ml.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/0abf6601/modules/ml/src/main/java/org/apache/ignite/ml/math/exceptions/RowIndexException.java
----------------------------------------------------------------------
diff --git a/modules/ml/src/main/java/org/apache/ignite/ml/math/exceptions/RowIndexException.java b/modules/ml/src/main/java/org/apache/ignite/ml/math/exceptions/RowIndexException.java
new file mode 100644
index 0000000..ebbbca3
--- /dev/null
+++ b/modules/ml/src/main/java/org/apache/ignite/ml/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.ml.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/0abf6601/modules/ml/src/main/java/org/apache/ignite/ml/math/exceptions/SingularMatrixException.java
----------------------------------------------------------------------
diff --git a/modules/ml/src/main/java/org/apache/ignite/ml/math/exceptions/SingularMatrixException.java b/modules/ml/src/main/java/org/apache/ignite/ml/math/exceptions/SingularMatrixException.java
new file mode 100644
index 0000000..789b686
--- /dev/null
+++ b/modules/ml/src/main/java/org/apache/ignite/ml/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.ml.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/0abf6601/modules/ml/src/main/java/org/apache/ignite/ml/math/exceptions/UnknownProviderException.java
----------------------------------------------------------------------
diff --git a/modules/ml/src/main/java/org/apache/ignite/ml/math/exceptions/UnknownProviderException.java b/modules/ml/src/main/java/org/apache/ignite/ml/math/exceptions/UnknownProviderException.java
new file mode 100644
index 0000000..940b9aa
--- /dev/null
+++ b/modules/ml/src/main/java/org/apache/ignite/ml/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.ml.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);
+ }
+}