You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by sb...@apache.org on 2017/04/19 08:45:54 UTC

[13/50] [abbrv] ignite git commit: IGNITE-5000 Rename Ignite Math module to Ignite ML module added missed licenses renamed packages fixed wrong ml profile activation

http://git-wip-us.apache.org/repos/asf/ignite/blob/d78e071a/modules/ml/src/test/java/org/apache/ignite/ml/math/decompositions/EigenDecompositionTest.java
----------------------------------------------------------------------
diff --git a/modules/ml/src/test/java/org/apache/ignite/ml/math/decompositions/EigenDecompositionTest.java b/modules/ml/src/test/java/org/apache/ignite/ml/math/decompositions/EigenDecompositionTest.java
new file mode 100644
index 0000000..d283ce7
--- /dev/null
+++ b/modules/ml/src/test/java/org/apache/ignite/ml/math/decompositions/EigenDecompositionTest.java
@@ -0,0 +1,193 @@
+/*
+ * 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.impls.matrix.DenseLocalOnHeapMatrix;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests for {@link EigenDecomposition}
+ */
+public class EigenDecompositionTest {
+    /** */
+    private static final double EPSILON = 1e-11;
+
+    /** */
+    @Test
+    public void testMatrixWithRealEigenvalues() {
+        test(new double[][] {
+                {1.0d, 0.0d, 0.0d, 0.0d},
+                {0.0d, 1.0d, 0.0d, 0.0d},
+                {0.0d, 0.0d, 2.0d, 0.0d},
+                {1.0d, 1.0d, 0.0d, 2.0d}},
+            new double[] {1, 2, 2, 1});
+    }
+
+    /** */
+    @Test
+    public void testSymmetricMatrix() {
+        EigenDecomposition decomposition = new EigenDecomposition(new DenseLocalOnHeapMatrix(new double[][] {
+            {1.0d, 0.0d, 0.0d, 1.0d},
+            {0.0d, 1.0d, 0.0d, 1.0d},
+            {0.0d, 0.0d, 2.0d, 0.0d},
+            {1.0d, 1.0d, 0.0d, 2.0d}}));
+
+        Matrix d = decomposition.getD();
+        Matrix v = decomposition.getV();
+
+        assertNotNull("Matrix d is expected to be not null.", d);
+        assertNotNull("Matrix v is expected to be not null.", v);
+
+        assertEquals("Unexpected rows in d matrix.", 4, d.rowSize());
+        assertEquals("Unexpected cols in d matrix.", 4, d.columnSize());
+
+        assertEquals("Unexpected rows in v matrix.", 4, v.rowSize());
+        assertEquals("Unexpected cols in v matrix.", 4, v.columnSize());
+
+        assertIsDiagonalNonZero(d);
+
+        decomposition.destroy();
+    }
+
+    /** */
+    @Test
+    public void testNonSquareMatrix() {
+        EigenDecomposition decomposition = new EigenDecomposition(new DenseLocalOnHeapMatrix(new double[][] {
+            {1.0d, 0.0d, 0.0d},
+            {0.0d, 1.0d, 0.0d},
+            {0.0d, 0.0d, 2.0d},
+            {1.0d, 1.0d, 0.0d}}));
+        // todo find out why decomposition of 3X4 matrix throws row index exception
+
+        Matrix d = decomposition.getD();
+        Matrix v = decomposition.getV();
+
+        assertNotNull("Matrix d is expected to be not null.", d);
+        assertNotNull("Matrix v is expected to be not null.", v);
+
+        assertEquals("Unexpected rows in d matrix.", 4, d.rowSize());
+        assertEquals("Unexpected cols in d matrix.", 4, d.columnSize());
+
+        assertEquals("Unexpected rows in v matrix.", 4, v.rowSize());
+        assertEquals("Unexpected cols in v matrix.", 3, v.columnSize());
+
+        assertIsDiagonal(d, true);
+
+        decomposition.destroy();
+    }
+
+    /** */
+    private void test(double[][] mRaw, double[] expRealEigenValues) {
+        DenseLocalOnHeapMatrix m = new DenseLocalOnHeapMatrix(mRaw);
+        EigenDecomposition decomposition = new EigenDecomposition(m);
+
+        Matrix d = decomposition.getD();
+        Matrix v = decomposition.getV();
+
+        assertIsDiagonalNonZero(d);
+
+        // check that d's diagonal consists of eigenvalues of m.
+        assertDiagonalConsistsOfEigenvalues(m, d, v);
+
+        // m = v d v^{-1} is equivalent to
+        // m v = v d
+        assertMatricesAreEqual(m.times(v), v.times(d));
+
+        assertEigenvalues(decomposition, expRealEigenValues);
+
+        decomposition.destroy();
+    }
+
+    /** */
+    private void assertEigenvalues(EigenDecomposition decomposition, double[] expRealEigenValues) {
+        Vector real = decomposition.getRealEigenValues();
+        Vector imag = decomposition.getImagEigenvalues();
+
+        assertEquals("Real values size differs from expected.", expRealEigenValues.length, real.size());
+        assertEquals("Imag values size differs from expected.", expRealEigenValues.length, imag.size());
+
+        for (int idx = 0; idx < expRealEigenValues.length; idx++) {
+            assertEquals("Real eigen value differs from expected at " + idx,
+                expRealEigenValues[idx], real.get(idx), 0d);
+
+            assertEquals("Imag eigen value differs from expected at " + idx,
+                0d, imag.get(idx), 0d);
+        }
+
+    }
+
+    /** */
+    private void assertDiagonalConsistsOfEigenvalues(DenseLocalOnHeapMatrix m, Matrix d, Matrix v) {
+        int n = m.columnSize();
+        for (int i = 0; i < n; i++) {
+            Vector eigenVector = v.viewColumn(i);
+            double eigenVal = d.getX(i, i);
+            assertVectorsAreEqual(m.times(eigenVector), eigenVector.times(eigenVal));
+        }
+
+    }
+
+    /** */
+    private void assertMatricesAreEqual(Matrix exp, Matrix actual) {
+        assertTrue("The row sizes of matrices are not equal", exp.rowSize() == actual.rowSize());
+        assertTrue("The col sizes of matrices are not equal", exp.columnSize() == actual.columnSize());
+
+        // Since matrix is square, we need only one dimension
+        int n = exp.columnSize();
+
+        for (int i = 0; i < n; i++)
+            for (int j = 0; j < n; j++)
+                assertEquals("Values should be equal", exp.getX(i, j), actual.getX(i, j), EPSILON);
+    }
+
+    /** */
+    private void assertVectorsAreEqual(Vector exp, Vector actual) {
+        assertTrue("Vectors sizes are not equal", exp.size() == actual.size());
+
+        // Since matrix is square, we need only one dimension
+        int n = exp.size();
+
+        for (int i = 0; i < n; i++)
+            assertEquals("Values should be equal", exp.getX(i), actual.getX(i), EPSILON);
+    }
+
+    /** */
+    private void assertIsDiagonalNonZero(Matrix m) {
+        assertIsDiagonal(m, false);
+    }
+
+    /** */
+    private void assertIsDiagonal(Matrix m, boolean zeroesAllowed) {
+        // Since matrix is square, we need only one dimension
+        int n = m.columnSize();
+
+        assertEquals("Diagonal matrix is not square", n, m.rowSize());
+
+        for (int i = 0; i < n; i++)
+            for (int j = 0; j < n; j++)
+                assertTrue("Matrix is not diagonal, violation at (" + i + "," + j + ")",
+                    ((i == j) && (zeroesAllowed || m.getX(i, j) != 0))
+                        || ((i != j) && m.getX(i, j) == 0));
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/d78e071a/modules/ml/src/test/java/org/apache/ignite/ml/math/decompositions/LUDecompositionTest.java
----------------------------------------------------------------------
diff --git a/modules/ml/src/test/java/org/apache/ignite/ml/math/decompositions/LUDecompositionTest.java b/modules/ml/src/test/java/org/apache/ignite/ml/math/decompositions/LUDecompositionTest.java
new file mode 100644
index 0000000..fc76c39
--- /dev/null
+++ b/modules/ml/src/test/java/org/apache/ignite/ml/math/decompositions/LUDecompositionTest.java
@@ -0,0 +1,250 @@
+/*
+ * 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;
+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.vector.DenseLocalOnHeapVector;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests for {@link LUDecomposition}.
+ */
+public class LUDecompositionTest {
+    /** */
+    private Matrix testL;
+    /** */
+    private Matrix testU;
+    /** */
+    private Matrix testP;
+    /** */
+    private Matrix testMatrix;
+    /** */
+    private int[] rawPivot;
+
+    /** */
+    @Before
+    public void setUp() {
+        double[][] rawMatrix = new double[][] {
+            {2.0d, 1.0d, 1.0d, 0.0d},
+            {4.0d, 3.0d, 3.0d, 1.0d},
+            {8.0d, 7.0d, 9.0d, 5.0d},
+            {6.0d, 7.0d, 9.0d, 8.0d}};
+        double[][] rawL = {
+            {1.0d, 0.0d, 0.0d, 0.0d},
+            {3.0d / 4.0d, 1.0d, 0.0d, 0.0d},
+            {1.0d / 2.0d, -2.0d / 7.0d, 1.0d, 0.0d},
+            {1.0d / 4.0d, -3.0d / 7.0d, 1.0d / 3.0d, 1.0d}};
+        double[][] rawU = {
+            {8.0d, 7.0d, 9.0d, 5.0d},
+            {0.0d, 7.0d / 4.0d, 9.0d / 4.0d, 17.0d / 4.0d},
+            {0.0d, 0.0d, -6.0d / 7.0d, -2.0d / 7.0d},
+            {0.0d, 0.0d, 0.0d, 2.0d / 3.0d}};
+        double[][] rawP = new double[][] {
+            {0, 0, 1.0d, 0},
+            {0, 0, 0, 1.0d},
+            {0, 1.0d, 0, 0},
+            {1.0d, 0, 0, 0}};
+
+        rawPivot = new int[] {3, 4, 2, 1};
+
+        testMatrix = new DenseLocalOnHeapMatrix(rawMatrix);
+        testL = new DenseLocalOnHeapMatrix(rawL);
+        testU = new DenseLocalOnHeapMatrix(rawU);
+        testP = new DenseLocalOnHeapMatrix(rawP);
+    }
+
+    /** */
+    @Test
+    public void getL() throws Exception {
+        Matrix luDecompositionL = new LUDecomposition(testMatrix).getL();
+
+        assertEquals("Unexpected row size.", testL.rowSize(), luDecompositionL.rowSize());
+        assertEquals("Unexpected column size.", testL.columnSize(), luDecompositionL.columnSize());
+
+        for (int i = 0; i < testL.rowSize(); i++)
+            for (int j = 0; j < testL.columnSize(); j++)
+                assertEquals("Unexpected value at (" + i + "," + j + ").",
+                    testL.getX(i, j), luDecompositionL.getX(i, j), 0.0000001d);
+
+        luDecompositionL.destroy();
+    }
+
+    /** */
+    @Test
+    public void getU() throws Exception {
+        Matrix luDecompositionU = new LUDecomposition(testMatrix).getU();
+
+        assertEquals("Unexpected row size.", testU.rowSize(), luDecompositionU.rowSize());
+        assertEquals("Unexpected column size.", testU.columnSize(), luDecompositionU.columnSize());
+
+        for (int i = 0; i < testU.rowSize(); i++)
+            for (int j = 0; j < testU.columnSize(); j++)
+                assertEquals("Unexpected value at (" + i + "," + j + ").",
+                    testU.getX(i, j), luDecompositionU.getX(i, j), 0.0000001d);
+
+        luDecompositionU.destroy();
+    }
+
+    /** */
+    @Test
+    public void getP() throws Exception {
+        Matrix luDecompositionP = new LUDecomposition(testMatrix).getP();
+
+        assertEquals("Unexpected row size.", testP.rowSize(), luDecompositionP.rowSize());
+        assertEquals("Unexpected column size.", testP.columnSize(), luDecompositionP.columnSize());
+
+        for (int i = 0; i < testP.rowSize(); i++)
+            for (int j = 0; j < testP.columnSize(); j++)
+                assertEquals("Unexpected value at (" + i + "," + j + ").",
+                    testP.getX(i, j), luDecompositionP.getX(i, j), 0.0000001d);
+
+        luDecompositionP.destroy();
+    }
+
+    /** */
+    @Test
+    public void getPivot() throws Exception {
+        Vector pivot = new LUDecomposition(testMatrix).getPivot();
+
+        assertEquals("Unexpected pivot size.", rawPivot.length, pivot.size());
+
+        for (int i = 0; i < testU.rowSize(); i++)
+            assertEquals("Unexpected value at " + i, rawPivot[i], (int)pivot.get(i) + 1);
+    }
+
+    /**
+     * Test for {@link DecompositionSupport} features.
+     */
+    @Test
+    public void decompositionSupportTest() {
+        LUDecomposition dec = new LUDecomposition(new PivotedMatrixView(testMatrix));
+        Matrix luDecompositionL = dec.getL();
+
+        assertEquals("Unexpected L row size.", testL.rowSize(), luDecompositionL.rowSize());
+        assertEquals("Unexpected L column size.", testL.columnSize(), luDecompositionL.columnSize());
+
+        for (int i = 0; i < testL.rowSize(); i++)
+            for (int j = 0; j < testL.columnSize(); j++)
+                assertEquals("Unexpected L value at (" + i + "," + j + ").",
+                    testL.getX(i, j), luDecompositionL.getX(i, j), 0.0000001d);
+
+        Matrix luDecompositionU = dec.getU();
+
+        assertEquals("Unexpected U row size.", testU.rowSize(), luDecompositionU.rowSize());
+        assertEquals("Unexpected U column size.", testU.columnSize(), luDecompositionU.columnSize());
+
+        for (int i = 0; i < testU.rowSize(); i++)
+            for (int j = 0; j < testU.columnSize(); j++)
+                assertEquals("Unexpected U value at (" + i + "," + j + ").",
+                    testU.getX(i, j), luDecompositionU.getX(i, j), 0.0000001d);
+
+        Matrix luDecompositionP = dec.getP();
+
+        assertEquals("Unexpected P row size.", testP.rowSize(), luDecompositionP.rowSize());
+        assertEquals("Unexpected P column size.", testP.columnSize(), luDecompositionP.columnSize());
+
+        for (int i = 0; i < testP.rowSize(); i++)
+            for (int j = 0; j < testP.columnSize(); j++)
+                assertEquals("Unexpected P value at (" + i + "," + j + ").",
+                    testP.getX(i, j), luDecompositionP.getX(i, j), 0.0000001d);
+
+        dec.destroy();
+    }
+
+    /** */
+    @Test
+    public void singularDeterminant() throws Exception {
+        assertEquals("Unexpected determinant for singular matrix decomposition.",
+            0d, new LUDecomposition(new DenseLocalOnHeapMatrix(2, 2)).determinant(), 0d);
+    }
+
+    /** */
+    @Test(expected = CardinalityException.class)
+    public void solveVecWrongSize() throws Exception {
+        new LUDecomposition(testMatrix).solve(new DenseLocalOnHeapVector(testMatrix.rowSize() + 1));
+    }
+
+    /** */
+    @Test(expected = SingularMatrixException.class)
+    public void solveVecSingularMatrix() throws Exception {
+        new LUDecomposition(new DenseLocalOnHeapMatrix(testMatrix.rowSize(), testMatrix.rowSize()))
+            .solve(new DenseLocalOnHeapVector(testMatrix.rowSize()));
+    }
+
+    /** */
+    @Test
+    public void solveVec() throws Exception {
+        Vector sol = new LUDecomposition(new PivotedMatrixView(testMatrix))
+            .solve(new DenseLocalOnHeapVector(testMatrix.rowSize()));
+
+        assertEquals("Wrong solution vector size.", testMatrix.rowSize(), sol.size());
+
+        for (int i = 0; i < sol.size(); i++)
+            assertEquals("Unexpected value at index " + i, 0d, sol.getX(i), 0.0000001d);
+    }
+
+    /** */
+    @Test(expected = CardinalityException.class)
+    public void solveMtxWrongSize() throws Exception {
+        new LUDecomposition(testMatrix).solve(
+            new DenseLocalOnHeapMatrix(testMatrix.rowSize() + 1, testMatrix.rowSize()));
+    }
+
+    /** */
+    @Test(expected = SingularMatrixException.class)
+    public void solveMtxSingularMatrix() throws Exception {
+        new LUDecomposition(new DenseLocalOnHeapMatrix(testMatrix.rowSize(), testMatrix.rowSize()))
+            .solve(new DenseLocalOnHeapMatrix(testMatrix.rowSize(), testMatrix.rowSize()));
+    }
+
+    /** */
+    @Test
+    public void solveMtx() throws Exception {
+        Matrix sol = new LUDecomposition(new PivotedMatrixView(testMatrix))
+            .solve(new DenseLocalOnHeapMatrix(testMatrix.rowSize(), testMatrix.rowSize()));
+
+        assertEquals("Wrong solution matrix row size.", testMatrix.rowSize(), sol.rowSize());
+
+        assertEquals("Wrong solution matrix column size.", testMatrix.rowSize(), sol.columnSize());
+
+        for (int row = 0; row < sol.rowSize(); row++)
+            for (int col = 0; col < sol.columnSize(); col++)
+                assertEquals("Unexpected P value at (" + row + "," + col + ").",
+                    0d, sol.getX(row, col), 0.0000001d);
+    }
+
+    /** */
+    @Test(expected = AssertionError.class)
+    public void nullMatrixTest() {
+        new LUDecomposition(null);
+    }
+
+    /** */
+    @Test(expected = CardinalityException.class)
+    public void nonSquareMatrixTest() {
+        new LUDecomposition(new DenseLocalOnHeapMatrix(2, 3));
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/d78e071a/modules/ml/src/test/java/org/apache/ignite/ml/math/decompositions/QRDecompositionTest.java
----------------------------------------------------------------------
diff --git a/modules/ml/src/test/java/org/apache/ignite/ml/math/decompositions/QRDecompositionTest.java b/modules/ml/src/test/java/org/apache/ignite/ml/math/decompositions/QRDecompositionTest.java
new file mode 100644
index 0000000..589d5d1
--- /dev/null
+++ b/modules/ml/src/test/java/org/apache/ignite/ml/math/decompositions/QRDecompositionTest.java
@@ -0,0 +1,139 @@
+/*
+ * 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.impls.matrix.DenseLocalOnHeapMatrix;
+import org.apache.ignite.ml.math.impls.matrix.PivotedMatrixView;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/** */
+public class QRDecompositionTest {
+    /** */
+    @Test
+    public void basicTest() {
+        basicTest(new DenseLocalOnHeapMatrix(new double[][] {
+            {2.0d, -1.0d, 0.0d},
+            {-1.0d, 2.0d, -1.0d},
+            {0.0d, -1.0d, 2.0d}
+        }));
+    }
+
+    /**
+     * Test for {@link DecompositionSupport} features.
+     */
+    @Test
+    public void decompositionSupportTest() {
+        basicTest(new PivotedMatrixView(new DenseLocalOnHeapMatrix(new double[][] {
+            {2.0d, -1.0d, 0.0d},
+            {-1.0d, 2.0d, -1.0d},
+            {0.0d, -1.0d, 2.0d}
+        })));
+    }
+
+    /** */
+    @Test(expected = AssertionError.class)
+    public void nullMatrixTest() {
+        new QRDecomposition(null);
+    }
+
+    /** */
+    @Test(expected = IllegalArgumentException.class)
+    public void solveWrongMatrixSizeTest() {
+        new QRDecomposition(new DenseLocalOnHeapMatrix(new double[][] {
+            {2.0d, -1.0d, 0.0d},
+            {-1.0d, 2.0d, -1.0d},
+            {0.0d, -1.0d, 2.0d}
+        })).solve(new DenseLocalOnHeapMatrix(2, 3));
+    }
+
+    /** */
+    private void basicTest(Matrix m) {
+        QRDecomposition dec = new QRDecomposition(m);
+        assertTrue("Unexpected value for full rank in decomposition " + dec, dec.hasFullRank());
+
+        Matrix q = dec.getQ();
+        Matrix r = dec.getR();
+
+        assertNotNull("Matrix q is expected to be not null.", q);
+        assertNotNull("Matrix r is expected to be not null.", r);
+
+        Matrix qSafeCp = safeCopy(q);
+
+        Matrix expIdentity = qSafeCp.times(qSafeCp.transpose());
+
+        final double delta = 0.0001;
+
+        for (int row = 0; row < expIdentity.rowSize(); row++)
+            for (int col = 0; col < expIdentity.columnSize(); col++)
+                assertEquals("Unexpected identity matrix value at (" + row + "," + col + ").",
+                    row == col ? 1d : 0d, expIdentity.get(col, row), delta);
+
+        for (int row = 0; row < r.rowSize(); row++)
+            for (int col = 0; col < row - 1; col++)
+                assertEquals("Unexpected upper triangular matrix value at (" + row + "," + col + ").",
+                    0d, r.get(row, col), delta);
+
+        Matrix recomposed = qSafeCp.times(r);
+
+        for (int row = 0; row < m.rowSize(); row++)
+            for (int col = 0; col < m.columnSize(); col++)
+                assertEquals("Unexpected recomposed matrix value at (" + row + "," + col + ").",
+                    m.get(row, col), recomposed.get(row, col), delta);
+
+        Matrix sol = dec.solve(new DenseLocalOnHeapMatrix(3, 10));
+        assertEquals("Unexpected rows in solution matrix.", 3, sol.rowSize());
+        assertEquals("Unexpected cols in solution matrix.", 10, sol.columnSize());
+
+        for (int row = 0; row < sol.rowSize(); row++)
+            for (int col = 0; col < sol.columnSize(); col++)
+                assertEquals("Unexpected solution matrix value at (" + row + "," + col + ").",
+                    0d, sol.get(row, col), delta);
+
+        dec.destroy();
+
+        QRDecomposition dec1 = new QRDecomposition(new DenseLocalOnHeapMatrix(new double[][] {
+            {2.0d, -1.0d},
+            {-1.0d, 2.0d},
+            {0.0d, -1.0d}
+        }));
+
+        assertTrue("Unexpected value for full rank in decomposition " + dec1, dec1.hasFullRank());
+
+        dec1.destroy();
+
+        QRDecomposition dec2 = new QRDecomposition(new DenseLocalOnHeapMatrix(new double[][] {
+            {2.0d, -1.0d, 0.0d, 0.0d},
+            {-1.0d, 2.0d, -1.0d, 0.0d},
+            {0.0d, -1.0d, 2.0d, 0.0d}
+        }));
+
+        assertTrue("Unexpected value for full rank in decomposition " + dec2, dec2.hasFullRank());
+
+        dec2.destroy();
+    }
+
+    /** */
+    private Matrix safeCopy(Matrix orig) {
+        return new DenseLocalOnHeapMatrix(orig.rowSize(), orig.columnSize()).assign(orig);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/d78e071a/modules/ml/src/test/java/org/apache/ignite/ml/math/decompositions/SingularValueDecompositionTest.java
----------------------------------------------------------------------
diff --git a/modules/ml/src/test/java/org/apache/ignite/ml/math/decompositions/SingularValueDecompositionTest.java b/modules/ml/src/test/java/org/apache/ignite/ml/math/decompositions/SingularValueDecompositionTest.java
new file mode 100644
index 0000000..f9843ae
--- /dev/null
+++ b/modules/ml/src/test/java/org/apache/ignite/ml/math/decompositions/SingularValueDecompositionTest.java
@@ -0,0 +1,120 @@
+/*
+ * 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.impls.matrix.DenseLocalOnHeapMatrix;
+import org.apache.ignite.ml.math.impls.matrix.PivotedMatrixView;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/** */
+public class SingularValueDecompositionTest {
+    /** */
+    @Test
+    public void basicTest() {
+        basicTest(new DenseLocalOnHeapMatrix(new double[][] {
+            {2.0d, -1.0d, 0.0d},
+            {-1.0d, 2.0d, -1.0d},
+            {0.0d, -1.0d, 2.0d}
+        }));
+    }
+
+    /**
+     * Test for {@link DecompositionSupport} features.
+     */
+    @Test
+    public void decompositionSupportTest() {
+        basicTest(new PivotedMatrixView(new DenseLocalOnHeapMatrix(new double[][] {
+            {2.0d, -1.0d, 0.0d},
+            {-1.0d, 2.0d, -1.0d},
+            {0.0d, -1.0d, 2.0d}
+        })));
+    }
+
+    /** */
+    @Test
+    public void rowsLessThanColumnsTest() {
+        DenseLocalOnHeapMatrix m = new DenseLocalOnHeapMatrix(new double[][] {
+            {2.0d, -1.0d, 0.0d},
+            {-1.0d, 2.0d, -1.0d}
+        });
+
+        SingularValueDecomposition dec = new SingularValueDecomposition(m);
+        assertEquals("Unexpected value for singular values size.",
+            2, dec.getSingularValues().length);
+
+        Matrix s = dec.getS();
+        Matrix u = dec.getU();
+        Matrix v = dec.getV();
+        Matrix covariance = dec.getCovariance(0.5);
+
+        assertNotNull("Matrix s is expected to be not null.", s);
+        assertNotNull("Matrix u is expected to be not null.", u);
+        assertNotNull("Matrix v is expected to be not null.", v);
+        assertNotNull("Covariance matrix is expected to be not null.", covariance);
+
+        dec.destroy();
+    }
+
+    /** */
+    @Test(expected = AssertionError.class)
+    public void nullMatrixTest() {
+        new SingularValueDecomposition(null);
+    }
+
+    /** */
+    private void basicTest(Matrix m) {
+        SingularValueDecomposition dec = new SingularValueDecomposition(m);
+        assertEquals("Unexpected value for singular values size.",
+            3, dec.getSingularValues().length);
+
+        Matrix s = dec.getS();
+        Matrix u = dec.getU();
+        Matrix v = dec.getV();
+        Matrix covariance = dec.getCovariance(0.5);
+
+        assertNotNull("Matrix s is expected to be not null.", s);
+        assertNotNull("Matrix u is expected to be not null.", u);
+        assertNotNull("Matrix v is expected to be not null.", v);
+        assertNotNull("Covariance matrix is expected to be not null.", covariance);
+
+        assertTrue("Decomposition cond is expected to be positive.", dec.cond() > 0);
+        assertTrue("Decomposition norm2 is expected to be positive.", dec.norm2() > 0);
+        assertEquals("Decomposition rank differs from expected.", 3, dec.rank());
+        assertEquals("Decomposition singular values size differs from expected.",
+            3, dec.getSingularValues().length);
+
+        Matrix recomposed = (u.times(s).times(v.transpose()));
+
+        for (int row = 0; row < m.rowSize(); row++)
+            for (int col = 0; col < m.columnSize(); col++)
+                assertEquals("Unexpected recomposed matrix value at (" + row + "," + col + ").",
+                    m.get(row, col), recomposed.get(row, col), 0.001);
+
+        for (int row = 0; row < covariance.rowSize(); row++)
+            for (int col = row + 1; col < covariance.columnSize(); col++)
+                assertEquals("Unexpected covariance matrix value at (" + row + "," + col + ").",
+                    covariance.get(row, col), covariance.get(col, row), 0.001);
+
+        dec.destroy();
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/d78e071a/modules/ml/src/test/java/org/apache/ignite/ml/math/impls/MathTestConstants.java
----------------------------------------------------------------------
diff --git a/modules/ml/src/test/java/org/apache/ignite/ml/math/impls/MathTestConstants.java b/modules/ml/src/test/java/org/apache/ignite/ml/math/impls/MathTestConstants.java
new file mode 100644
index 0000000..d26733e
--- /dev/null
+++ b/modules/ml/src/test/java/org/apache/ignite/ml/math/impls/MathTestConstants.java
@@ -0,0 +1,88 @@
+/*
+ * 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.impls;
+
+/**
+ * Collect constants for org.apache.ignite.math tests
+ */
+public interface MathTestConstants {
+    /** */
+    public double SECOND_ARG = 1d;
+
+    /**
+     * We assume that we will check calculation precision in other tests.
+     */
+    public double EXP_DELTA = 0.1d;
+
+    /** */
+    public String UNEXPECTED_VAL = "Unexpected value.";
+
+    /** */
+    public String NULL_GUID = "Null GUID.";
+
+    /** */
+    public String UNEXPECTED_GUID_VAL = "Unexpected GUID value.";
+
+    /** */
+    public String EMPTY_GUID = "Empty GUID.";
+
+    /** */
+    public String VALUES_SHOULD_BE_NOT_EQUALS = "Values should be not equals.";
+
+    /** */
+    public String NULL_VAL = "Null value.";
+
+    /** */
+    public String NULL_VALUES = "Null values.";
+
+    /** */
+    public String NOT_NULL_VAL = "Not null value.";
+
+    /** */
+    public double TEST_VAL = 1d;
+
+    /** */
+    public String VAL_NOT_EQUALS = "Values not equals.";
+
+    /** */
+    public String NO_NEXT_ELEMENT = "No next element.";
+
+    /** */
+    public int STORAGE_SIZE = 100;
+
+    /** */
+    public String WRONG_ATTRIBUTE_VAL = "Wrong attribute value.";
+
+    /** */
+    public String NULL_DATA_ELEMENT = "Null data element.";
+
+    /** */
+    public String WRONG_DATA_ELEMENT = "Wrong data element.";
+
+    /** */
+    public double NIL_DELTA = 0d;
+
+    /** */
+    public String NULL_DATA_STORAGE = "Null data storage.";
+
+    /** */
+    public String WRONG_DATA_SIZE = "Wrong data size.";
+
+    /** */
+    public String UNEXPECTED_DATA_VAL = "Unexpected data value.";
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/d78e071a/modules/ml/src/test/java/org/apache/ignite/ml/math/impls/matrix/CacheMatrixTest.java
----------------------------------------------------------------------
diff --git a/modules/ml/src/test/java/org/apache/ignite/ml/math/impls/matrix/CacheMatrixTest.java b/modules/ml/src/test/java/org/apache/ignite/ml/math/impls/matrix/CacheMatrixTest.java
new file mode 100644
index 0000000..3d4d05e
--- /dev/null
+++ b/modules/ml/src/test/java/org/apache/ignite/ml/math/impls/matrix/CacheMatrixTest.java
@@ -0,0 +1,369 @@
+/*
+ * 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.impls.matrix;
+
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.internal.util.IgniteUtils;
+import org.apache.ignite.ml.math.ExternalizeTest;
+import org.apache.ignite.ml.math.IdentityValueMapper;
+import org.apache.ignite.ml.math.Matrix;
+import org.apache.ignite.ml.math.MatrixKeyMapper;
+import org.apache.ignite.ml.math.exceptions.UnsupportedOperationException;
+import org.apache.ignite.ml.math.impls.MathTestConstants;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.apache.ignite.testframework.junits.common.GridCommonTest;
+
+/**
+ * Tests for {@link CacheMatrix}.
+ */
+@GridCommonTest(group = "Distributed Models")
+public class CacheMatrixTest extends GridCommonAbstractTest {
+    /** Number of nodes in grid */
+    private static final int NODE_COUNT = 3;
+    /** Cache name. */
+    private static final String CACHE_NAME = "test-cache";
+    /** */
+    private static final String UNEXPECTED_ATTRIBUTE_VALUE = "Unexpected attribute value.";
+    /** Grid instance. */
+    private Ignite ignite;
+    /** Matrix rows */
+    private final int rows = MathTestConstants.STORAGE_SIZE;
+    /** Matrix cols */
+    private final int cols = MathTestConstants.STORAGE_SIZE;
+
+    /**
+     * Default constructor.
+     */
+    public CacheMatrixTest() {
+        super(false);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTestsStarted() throws Exception {
+        for (int i = 1; i <= NODE_COUNT; i++)
+            startGrid(i);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTestsStopped() throws Exception {
+        stopAllGrids();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override protected void beforeTest() throws Exception {
+        ignite = grid(NODE_COUNT);
+
+        ignite.configuration().setPeerClassLoadingEnabled(true);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTest() throws Exception {
+        ignite.destroyCache(CACHE_NAME);
+    }
+
+    /** */
+    public void testGetSet() throws Exception {
+        MatrixKeyMapper<Integer> keyMapper = getKeyMapper(rows, cols);
+        IgniteCache<Integer, Double> cache = getCache();
+        CacheMatrix<Integer, Double> cacheMatrix = new CacheMatrix<>(rows, cols, cache, keyMapper, new IdentityValueMapper());
+
+        for (int i = 0; i < rows; i++) {
+            for (int j = 0; j < cols; j++) {
+                double v = Math.random();
+                cacheMatrix.set(i, j, v);
+
+                assert Double.compare(v, cacheMatrix.get(i, j)) == 0;
+                assert Double.compare(v, cache.get(keyMapper.apply(i, j))) == 0;
+            }
+        }
+    }
+
+    /** */
+    public void testCopy() throws Exception {
+        MatrixKeyMapper<Integer> keyMapper = getKeyMapper(rows, cols);
+        IgniteCache<Integer, Double> cache = getCache();
+        CacheMatrix<Integer, Double> cacheMatrix = new CacheMatrix<>(rows, cols, cache, keyMapper, new IdentityValueMapper());
+
+        fillMatrix(cacheMatrix);
+
+        try {
+            cacheMatrix.copy();
+
+            fail("UnsupportedOperationException expected");
+        }
+        catch (UnsupportedOperationException e) {
+            // No-op.
+        }
+    }
+
+    /** */
+    public void testLike() throws Exception {
+        MatrixKeyMapper<Integer> keyMapper = getKeyMapper(rows, cols);
+        IgniteCache<Integer, Double> cache = getCache();
+        CacheMatrix<Integer, Double> cacheMatrix = new CacheMatrix<>(rows, cols, cache, keyMapper, new IdentityValueMapper());
+
+        try {
+            cacheMatrix.like(rows, cols);
+
+            fail("UnsupportedOperationException expected");
+        }
+        catch (UnsupportedOperationException e) {
+            // No-op.
+        }
+    }
+
+    /** */
+    public void testLikeVector() throws Exception {
+        MatrixKeyMapper<Integer> keyMapper = getKeyMapper(rows, cols);
+        IgniteCache<Integer, Double> cache = getCache();
+        CacheMatrix<Integer, Double> cacheMatrix = new CacheMatrix<>(rows, cols, cache, keyMapper, new IdentityValueMapper());
+
+        try {
+            cacheMatrix.likeVector(cols);
+
+            fail("UnsupportedOperationException expected");
+        }
+        catch (UnsupportedOperationException e) {
+            // No-op.
+        }
+    }
+
+    /** */
+    public void testPlus() {
+        IgniteUtils.setCurrentIgniteName(ignite.configuration().getIgniteInstanceName());
+
+        double plusVal = 2;
+
+        MatrixKeyMapper<Integer> keyMapper = getKeyMapper(rows, cols);
+        IgniteCache<Integer, Double> cache = getCache();
+        CacheMatrix<Integer, Double> cacheMatrix = new CacheMatrix<>(rows, cols, cache, keyMapper, new IdentityValueMapper());
+
+        initMatrix(cacheMatrix);
+
+        cacheMatrix.plus(plusVal);
+
+        for (int i = 0; i < rows; i++)
+            for (int j = 0; j < cols; j++)
+                assertEquals(plusVal, cacheMatrix.get(i, j));
+    }
+
+    /** */
+    public void testDivide() {
+        IgniteUtils.setCurrentIgniteName(ignite.configuration().getIgniteInstanceName());
+
+        double initVal = 1;
+        double divVal = 2;
+
+        MatrixKeyMapper<Integer> keyMapper = getKeyMapper(rows, cols);
+        IgniteCache<Integer, Double> cache = getCache();
+        CacheMatrix<Integer, Double> cacheMatrix = new CacheMatrix<>(rows, cols, cache, keyMapper, new IdentityValueMapper());
+
+        initMatrix(cacheMatrix);
+        cacheMatrix.assign(initVal);
+        cacheMatrix.divide(divVal);
+
+        for (int i = 0; i < rows; i++)
+            for (int j = 0; j < cols; j++)
+                assertTrue(Double.compare(cacheMatrix.get(i, j), initVal / divVal) == 0);
+    }
+
+    /** */
+    public void testTimes() {
+        IgniteUtils.setCurrentIgniteName(ignite.configuration().getIgniteInstanceName());
+
+        double initVal = 1;
+        double timVal = 2;
+
+        MatrixKeyMapper<Integer> keyMapper = getKeyMapper(rows, cols);
+        IgniteCache<Integer, Double> cache = getCache();
+        CacheMatrix<Integer, Double> cacheMatrix = new CacheMatrix<>(rows, cols, cache, keyMapper, new IdentityValueMapper());
+
+        initMatrix(cacheMatrix);
+        cacheMatrix.assign(initVal);
+        cacheMatrix.times(timVal);
+
+        for (int i = 0; i < rows; i++)
+            for (int j = 0; j < cols; j++)
+                assertTrue(Double.compare(cacheMatrix.get(i, j), initVal * timVal) == 0);
+    }
+
+    /** */
+    public void testSum() {
+        IgniteUtils.setCurrentIgniteName(ignite.configuration().getIgniteInstanceName());
+
+        double initVal = 1;
+
+        MatrixKeyMapper<Integer> keyMapper = getKeyMapper(rows, cols);
+        IgniteCache<Integer, Double> cache = getCache();
+        CacheMatrix<Integer, Double> cacheMatrix = new CacheMatrix<>(rows, cols, cache, keyMapper, new IdentityValueMapper());
+
+        double sum = 0;
+
+        initMatrix(cacheMatrix);
+        sum = cacheMatrix.sum();
+
+        assertTrue(Double.compare(sum, 0d) == 0);
+
+        cacheMatrix.assign(1d);
+        sum = cacheMatrix.sum();
+
+        assertTrue(Double.compare(sum, rows * cols) == 0);
+    }
+
+    /** */
+    public void testAssignSingleValue() {
+        IgniteUtils.setCurrentIgniteName(ignite.configuration().getIgniteInstanceName());
+
+        double initVal = 1;
+
+        MatrixKeyMapper<Integer> keyMapper = getKeyMapper(rows, cols);
+        IgniteCache<Integer, Double> cache = getCache();
+        CacheMatrix<Integer, Double> cacheMatrix = new CacheMatrix<>(rows, cols, cache, keyMapper, new IdentityValueMapper());
+
+        initMatrix(cacheMatrix);
+
+        cacheMatrix.assign(initVal);
+
+        for (int i = 0; i < rows; i++)
+            for (int j = 0; j < cols; j++)
+                assertEquals(initVal, cacheMatrix.get(i, j));
+    }
+
+    /** */
+    public void testAssignArray() {
+        IgniteUtils.setCurrentIgniteName(ignite.configuration().getIgniteInstanceName());
+
+        double[][] initVal = new double[rows][cols];
+
+        MatrixKeyMapper<Integer> keyMapper = getKeyMapper(rows, cols);
+        IgniteCache<Integer, Double> cache = getCache();
+        CacheMatrix<Integer, Double> cacheMatrix = new CacheMatrix<>(rows, cols, cache, keyMapper, new IdentityValueMapper());
+
+        for (int i = 0; i < rows; i++)
+            for (int j = 0; j < cols; j++)
+                initVal[i][j] = Math.random();
+
+        cacheMatrix.assign(initVal);
+
+        for (int i = 0; i < rows; i++)
+            for (int j = 0; j < cols; j++)
+                assertEquals(initVal[i][j], cacheMatrix.get(i, j));
+    }
+
+    /** */
+    public void testAttributes() {
+        IgniteUtils.setCurrentIgniteName(ignite.configuration().getIgniteInstanceName());
+
+        MatrixKeyMapper<Integer> keyMapper = getKeyMapper(rows, cols);
+        IgniteCache<Integer, Double> cache = getCache();
+        CacheMatrix<Integer, Double> cacheMatrix = new CacheMatrix<>(rows, cols, cache, keyMapper, new IdentityValueMapper());
+
+        assertFalse(UNEXPECTED_ATTRIBUTE_VALUE, cacheMatrix.isSequentialAccess());
+        assertFalse(UNEXPECTED_ATTRIBUTE_VALUE, cacheMatrix.isDense());
+        assertFalse(UNEXPECTED_ATTRIBUTE_VALUE, cacheMatrix.isArrayBased());
+        assertTrue(UNEXPECTED_ATTRIBUTE_VALUE, cacheMatrix.isRandomAccess());
+        assertTrue(UNEXPECTED_ATTRIBUTE_VALUE, cacheMatrix.isDistributed());
+    }
+
+    /** */
+    public void testExternalization() {
+        IgniteUtils.setCurrentIgniteName(ignite.configuration().getIgniteInstanceName());
+
+        MatrixKeyMapper<Integer> keyMapper = getKeyMapper(rows, cols);
+        IgniteCache<Integer, Double> cache = getCache();
+        final CacheMatrix<Integer, Double> cacheMatrix = new CacheMatrix<>(rows, cols, cache, keyMapper, new IdentityValueMapper());
+
+        ExternalizeTest<CacheMatrix<Integer, Double>> externalizeTest = new ExternalizeTest<CacheMatrix<Integer, Double>>() {
+
+            @Override public void externalizeTest() {
+                super.externalizeTest(cacheMatrix);
+            }
+        };
+
+        externalizeTest.externalizeTest();
+    }
+
+    /** */
+    public void testMinMax() {
+        IgniteUtils.setCurrentIgniteName(ignite.configuration().getIgniteInstanceName());
+
+        MatrixKeyMapper<Integer> keyMapper = getKeyMapper(rows, cols);
+        IgniteCache<Integer, Double> cache = getCache();
+        final CacheMatrix<Integer, Double> cacheMatrix = new CacheMatrix<>(rows, cols, cache, keyMapper, new IdentityValueMapper());
+
+        for (int i = 0; i < rows; i++)
+            for (int j = 0; j < cols; j++)
+                cacheMatrix.set(i, j, i * rows + j);
+
+        assertEquals(0.0, cacheMatrix.minValue(), 0.0);
+        assertEquals(rows * cols - 1, cacheMatrix.maxValue(), 0.0);
+    }
+
+    /** */
+    public void testMap() {
+        IgniteUtils.setCurrentIgniteName(ignite.configuration().getIgniteInstanceName());
+
+        MatrixKeyMapper<Integer> keyMapper = getKeyMapper(rows, cols);
+        IgniteCache<Integer, Double> cache = getCache();
+        final CacheMatrix<Integer, Double> cacheMatrix = new CacheMatrix<>(rows, cols, cache, keyMapper, new IdentityValueMapper());
+
+        initMatrix(cacheMatrix);
+
+        cacheMatrix.map(value -> value + 10);
+
+        for (int i = 0; i < rows; i++)
+            for (int j = 0; j < cols; j++)
+                assertEquals(10.0, cacheMatrix.getX(i, j), 0.0);
+    }
+
+    /** */
+    private IgniteCache<Integer, Double> getCache() {
+        assert ignite != null;
+
+        CacheConfiguration cfg = new CacheConfiguration();
+        cfg.setName(CACHE_NAME);
+
+        IgniteCache<Integer, Double> cache = ignite.getOrCreateCache(CACHE_NAME);
+
+        assert cache != null;
+        return cache;
+    }
+
+    /** */
+    private MatrixKeyMapper<Integer> getKeyMapper(final int rows, final int cols) {
+        return new MatrixKeyMapperForTests(rows, cols);
+    }
+
+    /** Init the given matrix by random values. */
+    private void fillMatrix(Matrix m) {
+        for (int i = 0; i < m.rowSize(); i++)
+            for (int j = 0; j < m.columnSize(); j++)
+                m.set(i, j, Math.random());
+    }
+
+    /** Init the given matrix by zeros. */
+    private void initMatrix(Matrix m) {
+        for (int i = 0; i < m.rowSize(); i++)
+            for (int j = 0; j < m.columnSize(); j++)
+                m.set(i, j, 0d);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/d78e071a/modules/ml/src/test/java/org/apache/ignite/ml/math/impls/matrix/DenseLocalOffHeapMatrixConstructorTest.java
----------------------------------------------------------------------
diff --git a/modules/ml/src/test/java/org/apache/ignite/ml/math/impls/matrix/DenseLocalOffHeapMatrixConstructorTest.java b/modules/ml/src/test/java/org/apache/ignite/ml/math/impls/matrix/DenseLocalOffHeapMatrixConstructorTest.java
new file mode 100644
index 0000000..88bf8d0
--- /dev/null
+++ b/modules/ml/src/test/java/org/apache/ignite/ml/math/impls/matrix/DenseLocalOffHeapMatrixConstructorTest.java
@@ -0,0 +1,65 @@
+/*
+ * 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.impls.matrix;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/** */
+public class DenseLocalOffHeapMatrixConstructorTest {
+    /** */
+    @Test
+    public void invalidArgsTest() {
+        DenseLocalOnHeapMatrixConstructorTest.verifyAssertionError(() -> new DenseLocalOffHeapMatrix(0, 1), "invalid row parameter");
+
+        DenseLocalOnHeapMatrixConstructorTest.verifyAssertionError(() -> new DenseLocalOffHeapMatrix(1, 0), "invalid col parameter");
+
+        //noinspection ConstantConditions
+        DenseLocalOnHeapMatrixConstructorTest.verifyAssertionError(() -> new DenseLocalOffHeapMatrix(null), "null matrix parameter");
+
+        DenseLocalOnHeapMatrixConstructorTest.verifyAssertionError(() -> new DenseLocalOffHeapMatrix(new double[][] {null, new double[1]}),
+            "null row in matrix");
+    }
+
+    /** */
+    @Test
+    public void basicTest() {
+        assertEquals("Expected number of rows, int parameters.", 1,
+            new DenseLocalOffHeapMatrix(1, 2).rowSize());
+
+        assertEquals("Expected number of rows, double[][] parameter.", 1,
+            new DenseLocalOffHeapMatrix(new double[][] {new double[2]}).rowSize());
+
+        assertEquals("Expected number of cols, int parameters.", 1,
+            new DenseLocalOffHeapMatrix(2, 1).columnSize());
+
+        assertEquals("Expected number of cols, double[][] parameter.", 1,
+            new DenseLocalOffHeapMatrix(new double[][] {new double[1], new double[1]}).columnSize());
+
+        double[][] data1 = new double[][] {{1, 2}, {3, 4}}, data2 = new double[][] {{1, 2}, {3, 5}};
+
+        assertTrue("Matrices with same values are expected to be equal",
+            new DenseLocalOffHeapMatrix(data1).equals(new DenseLocalOffHeapMatrix(data1)));
+
+        assertFalse("Matrices with same values are expected to be equal",
+            new DenseLocalOffHeapMatrix(data1).equals(new DenseLocalOffHeapMatrix(data2)));
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/d78e071a/modules/ml/src/test/java/org/apache/ignite/ml/math/impls/matrix/DenseLocalOnHeapMatrixConstructorTest.java
----------------------------------------------------------------------
diff --git a/modules/ml/src/test/java/org/apache/ignite/ml/math/impls/matrix/DenseLocalOnHeapMatrixConstructorTest.java b/modules/ml/src/test/java/org/apache/ignite/ml/math/impls/matrix/DenseLocalOnHeapMatrixConstructorTest.java
new file mode 100644
index 0000000..1cf6464
--- /dev/null
+++ b/modules/ml/src/test/java/org/apache/ignite/ml/math/impls/matrix/DenseLocalOnHeapMatrixConstructorTest.java
@@ -0,0 +1,71 @@
+/*
+ * 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.impls.matrix;
+
+import java.util.function.Supplier;
+import org.apache.ignite.ml.math.Matrix;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+/** */
+public class DenseLocalOnHeapMatrixConstructorTest {
+    /** */
+    @Test
+    public void invalidArgsTest() {
+        verifyAssertionError(() -> new DenseLocalOnHeapMatrix(0, 1), "invalid row parameter");
+
+        verifyAssertionError(() -> new DenseLocalOnHeapMatrix(1, 0), "invalid col parameter");
+
+        //noinspection ConstantConditions
+        verifyAssertionError(() -> new DenseLocalOnHeapMatrix(null), "null matrix parameter");
+
+        verifyAssertionError(() -> new DenseLocalOnHeapMatrix(new double[][] {null, new double[1]}),
+            "null row in matrix");
+    }
+
+    /** */
+    @Test
+    public void basicTest() {
+        assertEquals("Expected number of rows, int parameters.", 1,
+            new DenseLocalOnHeapMatrix(1, 2).rowSize());
+
+        assertEquals("Expected number of rows, double[][] parameter.", 1,
+            new DenseLocalOnHeapMatrix(new double[][] {new double[2]}).rowSize());
+
+        assertEquals("Expected number of cols, int parameters.", 1,
+            new DenseLocalOnHeapMatrix(2, 1).columnSize());
+
+        assertEquals("Expected number of cols, double[][] parameter.", 1,
+            new DenseLocalOnHeapMatrix(new double[][] {new double[1], new double[1]}).columnSize());
+    }
+
+    /** */
+    static void verifyAssertionError(Supplier<Matrix> ctor, String desc) {
+        try {
+            assertNotNull("Unexpected null matrix in " + desc, ctor.get());
+        }
+        catch (AssertionError ae) {
+            return;
+        }
+
+        fail("Expected error not caught in " + desc);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/d78e071a/modules/ml/src/test/java/org/apache/ignite/ml/math/impls/matrix/DiagonalMatrixTest.java
----------------------------------------------------------------------
diff --git a/modules/ml/src/test/java/org/apache/ignite/ml/math/impls/matrix/DiagonalMatrixTest.java b/modules/ml/src/test/java/org/apache/ignite/ml/math/impls/matrix/DiagonalMatrixTest.java
new file mode 100644
index 0000000..a00403f
--- /dev/null
+++ b/modules/ml/src/test/java/org/apache/ignite/ml/math/impls/matrix/DiagonalMatrixTest.java
@@ -0,0 +1,209 @@
+/*
+ * 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.impls.matrix;
+
+import org.apache.ignite.ml.math.ExternalizeTest;
+import org.apache.ignite.ml.math.Matrix;
+import org.apache.ignite.ml.math.Vector;
+import org.apache.ignite.ml.math.exceptions.UnsupportedOperationException;
+import org.apache.ignite.ml.math.impls.MathTestConstants;
+import org.apache.ignite.ml.math.impls.vector.DenseLocalOnHeapVector;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * Tests for {@link DiagonalMatrix}.
+ */
+public class DiagonalMatrixTest extends ExternalizeTest<DiagonalMatrix> {
+    /** */
+    public static final String UNEXPECTED_VALUE = "Unexpected value";
+
+    /** */
+    private DiagonalMatrix testMatrix;
+
+    /** */
+    @Before
+    public void setup() {
+        DenseLocalOnHeapMatrix parent = new DenseLocalOnHeapMatrix(MathTestConstants.STORAGE_SIZE, MathTestConstants.STORAGE_SIZE);
+        fillMatrix(parent);
+        testMatrix = new DiagonalMatrix(parent);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void externalizeTest() {
+        externalizeTest(testMatrix);
+    }
+
+    /** */
+    @Test
+    public void testSetGetBasic() {
+        double testVal = 42;
+        for (int i = 0; i < MathTestConstants.STORAGE_SIZE; i++) {
+            testMatrix.set(i, i, testVal);
+
+            assertEquals(UNEXPECTED_VALUE + " at (" + i + "," + i + ")", testMatrix.get(i, i), testVal, 0d);
+        }
+
+        //noinspection EqualsWithItself
+        assertTrue("Matrix is expected to be equal to self.", testMatrix.equals(testMatrix));
+        //noinspection ObjectEqualsNull
+        assertFalse("Matrix is expected to be not equal to null.", testMatrix.equals(null));
+    }
+
+    /** */
+    @Test
+    public void testSetGet() {
+        verifyDiagonal(testMatrix);
+
+        final int size = MathTestConstants.STORAGE_SIZE;
+
+        for (Matrix m : new Matrix[] {
+            new DenseLocalOnHeapMatrix(size + 1, size),
+            new DenseLocalOnHeapMatrix(size, size + 1)}) {
+            fillMatrix(m);
+
+            verifyDiagonal(new DiagonalMatrix(m));
+        }
+
+        final double[] data = new double[size];
+
+        for (int i = 0; i < size; i++)
+            data[i] = 1 + i;
+
+        final Matrix m = new DiagonalMatrix(new DenseLocalOnHeapVector(data));
+
+        assertEquals("Rows in matrix constructed from vector", size, m.rowSize());
+        assertEquals("Cols in matrix constructed from vector", size, m.columnSize());
+
+        for (int i = 0; i < size; i++)
+            assertEquals(UNEXPECTED_VALUE + " at vector index " + i, data[i], m.get(i, i), 0d);
+
+        verifyDiagonal(m);
+
+        final Matrix m1 = new DiagonalMatrix(data);
+
+        assertEquals("Rows in matrix constructed from array", size, m1.rowSize());
+        assertEquals("Cols in matrix constructed from array", size, m1.columnSize());
+
+        for (int i = 0; i < size; i++)
+            assertEquals(UNEXPECTED_VALUE + " at array index " + i, data[i], m1.get(i, i), 0d);
+
+        verifyDiagonal(m1);
+    }
+
+    /** */
+    @Test
+    public void testConstant() {
+        final int size = MathTestConstants.STORAGE_SIZE;
+
+        for (double val : new double[] {-1.0, 0.0, 1.0}) {
+            Matrix m = new DiagonalMatrix(size, val);
+
+            assertEquals("Rows in matrix", size, m.rowSize());
+            assertEquals("Cols in matrix", size, m.columnSize());
+
+            for (int i = 0; i < size; i++)
+                assertEquals(UNEXPECTED_VALUE + " at index " + i, val, m.get(i, i), 0d);
+
+            verifyDiagonal(m, true);
+        }
+    }
+
+    /** */
+    @Test
+    public void testAttributes() {
+        assertTrue(UNEXPECTED_VALUE, testMatrix.rowSize() == MathTestConstants.STORAGE_SIZE);
+        assertTrue(UNEXPECTED_VALUE, testMatrix.columnSize() == MathTestConstants.STORAGE_SIZE);
+
+        assertFalse(UNEXPECTED_VALUE, testMatrix.isArrayBased());
+        assertTrue(UNEXPECTED_VALUE, testMatrix.isDense());
+        assertFalse(UNEXPECTED_VALUE, testMatrix.isDistributed());
+
+        assertEquals(UNEXPECTED_VALUE, testMatrix.isRandomAccess(), !testMatrix.isSequentialAccess());
+        assertTrue(UNEXPECTED_VALUE, testMatrix.isRandomAccess());
+    }
+
+    /** */
+    @Test
+    public void testNullParams() {
+        DenseLocalOnHeapMatrixConstructorTest.verifyAssertionError(() -> new DiagonalMatrix((Matrix)null), "Null Matrix parameter");
+
+        DenseLocalOnHeapMatrixConstructorTest.verifyAssertionError(() -> new DiagonalMatrix((Vector)null), "Null Vector parameter");
+
+        DenseLocalOnHeapMatrixConstructorTest.verifyAssertionError(() -> new DiagonalMatrix((double[])null), "Null double[] parameter");
+    }
+
+    /** */
+    private void verifyDiagonal(Matrix m, boolean readonly) {
+        final int rows = m.rowSize(), cols = m.columnSize();
+
+        final String sizeDetails = "rows" + "X" + "cols " + rows + "X" + cols;
+
+        for (int i = 0; i < rows; i++)
+            for (int j = 0; j < cols; j++) {
+                final String details = " at (" + i + "," + j + "), " + sizeDetails;
+
+                final boolean diagonal = i == j;
+
+                final double old = m.get(i, j);
+
+                if (!diagonal)
+                    assertEquals(UNEXPECTED_VALUE + details, 0, old, 0d);
+
+                final double exp = diagonal && !readonly ? old + 1 : old;
+
+                boolean expECaught = false;
+
+                try {
+                    m.set(i, j, exp);
+                }
+                catch (UnsupportedOperationException uoe) {
+                    if (diagonal && !readonly)
+                        throw uoe;
+
+                    expECaught = true;
+                }
+
+                if ((!diagonal || readonly) && !expECaught)
+                    fail("Expected exception was not caught " + details);
+
+                assertEquals(UNEXPECTED_VALUE + details, exp, m.get(i, j), 0d);
+            }
+    }
+
+    /** */
+    private void verifyDiagonal(Matrix m) {
+        verifyDiagonal(m, false);
+    }
+
+    /** */
+    private void fillMatrix(Matrix m) {
+        final int rows = m.rowSize(), cols = m.columnSize();
+
+        boolean negative = false;
+
+        for (int i = 0; i < rows; i++)
+            for (int j = 0; j < cols; j++)
+                m.set(i, j, (negative = !negative) ? -(i * cols + j + 1) : i * cols + j + 1);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/d78e071a/modules/ml/src/test/java/org/apache/ignite/ml/math/impls/matrix/FunctionMatrixConstructorTest.java
----------------------------------------------------------------------
diff --git a/modules/ml/src/test/java/org/apache/ignite/ml/math/impls/matrix/FunctionMatrixConstructorTest.java b/modules/ml/src/test/java/org/apache/ignite/ml/math/impls/matrix/FunctionMatrixConstructorTest.java
new file mode 100644
index 0000000..25de7d3
--- /dev/null
+++ b/modules/ml/src/test/java/org/apache/ignite/ml/math/impls/matrix/FunctionMatrixConstructorTest.java
@@ -0,0 +1,113 @@
+/*
+ * 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.impls.matrix;
+
+import org.apache.ignite.ml.math.Matrix;
+import org.apache.ignite.ml.math.exceptions.UnsupportedOperationException;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/** */
+public class FunctionMatrixConstructorTest {
+    /** */
+    @Test
+    public void invalidArgsTest() {
+        DenseLocalOnHeapMatrixConstructorTest.verifyAssertionError(() -> new FunctionMatrix(0, 1, (i, j) -> 0.0),
+            "Invalid row parameter.");
+
+        DenseLocalOnHeapMatrixConstructorTest.verifyAssertionError(() -> new FunctionMatrix(1, 0, (i, j) -> 0.0),
+            "Invalid col parameter.");
+
+        DenseLocalOnHeapMatrixConstructorTest.verifyAssertionError(() -> new FunctionMatrix(1, 1, null),
+            "Invalid func parameter.");
+
+        DenseLocalOnHeapMatrixConstructorTest.verifyAssertionError(() -> new FunctionMatrix(0, 1, (i, j) -> 0.0, null),
+            "Invalid row parameter, with setter func.");
+
+        DenseLocalOnHeapMatrixConstructorTest.verifyAssertionError(() -> new FunctionMatrix(1, 0, (i, j) -> 0.0, null),
+            "Invalid col parameter, with setter func.");
+
+        DenseLocalOnHeapMatrixConstructorTest.verifyAssertionError(() -> new FunctionMatrix(1, 1, null, null),
+            "Invalid func parameter, with setter func.");
+    }
+
+    /** */
+    @Test
+    public void basicTest() {
+        for (int rows : new int[] {1, 2, 3})
+            for (int cols : new int[] {1, 2, 3})
+                basicTest(rows, cols);
+
+        Matrix m = new FunctionMatrix(1, 1, (i, j) -> 1d);
+        //noinspection EqualsWithItself
+        assertTrue("Matrix is expected to be equal to self.", m.equals(m));
+        //noinspection ObjectEqualsNull
+        assertFalse("Matrix is expected to be not equal to null.", m.equals(null));
+    }
+
+    /** */
+    private void basicTest(int rows, int cols) {
+        double[][] data = new double[rows][cols];
+
+        for (int row = 0; row < rows; row++)
+            for (int col = 0; col < cols; col++)
+                data[row][col] = row * cols + row;
+
+        Matrix mReadOnly = new FunctionMatrix(rows, cols, (i, j) -> data[i][j]);
+
+        assertEquals("Rows in matrix.", rows, mReadOnly.rowSize());
+        assertEquals("Cols in matrix.", cols, mReadOnly.columnSize());
+
+        for (int row = 0; row < rows; row++)
+            for (int col = 0; col < cols; col++) {
+                assertEquals("Unexpected value at " + row + "x" + col, data[row][col], mReadOnly.get(row, col), 0d);
+
+                boolean expECaught = false;
+
+                try {
+                    mReadOnly.set(row, col, 0.0);
+                }
+                catch (UnsupportedOperationException uoe) {
+                    expECaught = true;
+                }
+
+                assertTrue("Expected exception wasn't thrown at " + row + "x" + col, expECaught);
+            }
+
+        Matrix m = new FunctionMatrix(rows, cols, (i, j) -> data[i][j], (i, j, val) -> data[i][j] = val);
+
+        assertEquals("Rows in matrix, with setter function.", rows, m.rowSize());
+        assertEquals("Cols in matrix, with setter function.", cols, m.columnSize());
+
+        for (int row = 0; row < rows; row++)
+            for (int col = 0; col < cols; col++) {
+                assertEquals("Unexpected value at " + row + "x" + col, data[row][col], m.get(row, col), 0d);
+
+                m.set(row, col, -data[row][col]);
+            }
+
+        for (int row = 0; row < rows; row++)
+            for (int col = 0; col < cols; col++)
+                assertEquals("Unexpected value set at " + row + "x" + col, -(row * cols + row), m.get(row, col), 0d);
+
+        assertTrue("Incorrect copy for empty matrix.", m.copy().equals(m));
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/d78e071a/modules/ml/src/test/java/org/apache/ignite/ml/math/impls/matrix/MatrixAttributeTest.java
----------------------------------------------------------------------
diff --git a/modules/ml/src/test/java/org/apache/ignite/ml/math/impls/matrix/MatrixAttributeTest.java b/modules/ml/src/test/java/org/apache/ignite/ml/math/impls/matrix/MatrixAttributeTest.java
new file mode 100644
index 0000000..79cb13a
--- /dev/null
+++ b/modules/ml/src/test/java/org/apache/ignite/ml/math/impls/matrix/MatrixAttributeTest.java
@@ -0,0 +1,156 @@
+/*
+ * 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.impls.matrix;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Function;
+import org.apache.ignite.ml.math.Matrix;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Attribute tests for matrices.
+ *
+ * TODO: WIP
+ */
+public class MatrixAttributeTest {
+    /** */
+    private final List<MatrixAttributeTest.AttrCfg> attrCfgs = Arrays.asList(
+        new AttrCfg("isDense", Matrix::isDense,
+            DenseLocalOnHeapMatrix.class, DenseLocalOffHeapMatrix.class, RandomMatrix.class, DiagonalMatrix.class),
+        new AttrCfg("isArrayBased", Matrix::isArrayBased, DenseLocalOnHeapMatrix.class),
+        new AttrCfg("isDistributed", Matrix::isDistributed),
+        new AttrCfg("isRandomAccess", Matrix::isRandomAccess, DenseLocalOnHeapMatrix.class, DenseLocalOffHeapMatrix.class, RandomMatrix.class, DiagonalMatrix.class, SparseLocalOnHeapMatrix.class),
+        new AttrCfg("isSequentialAccess", Matrix::isSequentialAccess, DiagonalMatrix.class)
+    );
+
+    /** */
+    private final List<MatrixAttributeTest.Specification> specFixture = Arrays.asList(
+        new Specification(new DenseLocalOnHeapMatrix(1, 1)),
+        new Specification(new DenseLocalOffHeapMatrix(1, 1)),
+        new Specification(new RandomMatrix(1, 1)),
+        new Specification(new DiagonalMatrix(new double[] {1.0})),
+        new Specification(new FunctionMatrix(1, 1, (x, y) -> 1.0)),
+        new Specification(new SparseLocalOnHeapMatrix(1, 1))
+    );
+
+    /** */
+    @Test
+    public void isDenseTest() {
+        assertAttribute("isDense");
+    }
+
+    /** */
+    @Test
+    public void isArrayBasedTest() {
+        assertAttribute("isArrayBased");
+    }
+
+    /** */
+    @Test
+    public void isSequentialAccessTest() {
+        assertAttribute("isSequentialAccess");
+    }
+
+    /** */
+    @Test
+    public void isRandomAccessTest() {
+        assertAttribute("isRandomAccess");
+    }
+
+    /** */
+    @Test
+    public void isDistributedTest() {
+        assertAttribute("isDistributed");
+    }
+
+    /** */
+    private void assertAttribute(String name) {
+        final MatrixAttributeTest.AttrCfg attr = attrCfg(name);
+
+        for (MatrixAttributeTest.Specification spec : specFixture)
+            spec.verify(attr);
+    }
+
+    /** */
+    private MatrixAttributeTest.AttrCfg attrCfg(String name) {
+        for (MatrixAttributeTest.AttrCfg attr : attrCfgs)
+            if (attr.name.equals(name))
+                return attr;
+
+        throw new IllegalArgumentException("Undefined attribute " + name);
+    }
+
+    /** See http://en.wikipedia.org/wiki/Specification_pattern */
+    private static class Specification {
+        /** */
+        private final Matrix m;
+        /** */
+        private final Class<? extends Matrix> underlyingType;
+        /** */
+        private final List<String> attrsFromUnderlying;
+        /** */
+        final String desc;
+
+        /** */
+        Specification(Matrix m, Class<? extends Matrix> underlyingType, String... attrsFromUnderlying) {
+            this.m = m;
+            this.underlyingType = underlyingType;
+            this.attrsFromUnderlying = Arrays.asList(attrsFromUnderlying);
+            final Class<? extends Matrix> clazz = m.getClass();
+            desc = clazz.getSimpleName() + (clazz.equals(underlyingType)
+                ? "" : " (underlying type " + underlyingType.getSimpleName() + ")");
+        }
+
+        /** */
+        Specification(Matrix m) {
+            this(m, m.getClass());
+        }
+
+        /** */
+        void verify(MatrixAttributeTest.AttrCfg attr) {
+            final boolean obtained = attr.obtain.apply(m);
+
+            final Class<? extends Matrix> typeToCheck
+                = attrsFromUnderlying.contains(attr.name) ? underlyingType : m.getClass();
+
+            final boolean exp = attr.trueInTypes.contains(typeToCheck);
+
+            assertEquals("Unexpected " + attr.name + " value for " + desc, exp, obtained);
+        }
+    }
+
+    /** */
+    private static class AttrCfg {
+        /** */
+        final String name;
+        /** */
+        final Function<Matrix, Boolean> obtain;
+        /** */
+        final List<Class> trueInTypes;
+
+        /** */
+        AttrCfg(String name, Function<Matrix, Boolean> obtain, Class... trueInTypes) {
+            this.name = name;
+            this.obtain = obtain;
+            this.trueInTypes = Arrays.asList(trueInTypes);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/d78e071a/modules/ml/src/test/java/org/apache/ignite/ml/math/impls/matrix/MatrixImplementationFixtures.java
----------------------------------------------------------------------
diff --git a/modules/ml/src/test/java/org/apache/ignite/ml/math/impls/matrix/MatrixImplementationFixtures.java b/modules/ml/src/test/java/org/apache/ignite/ml/math/impls/matrix/MatrixImplementationFixtures.java
new file mode 100644
index 0000000..52a0077
--- /dev/null
+++ b/modules/ml/src/test/java/org/apache/ignite/ml/math/impls/matrix/MatrixImplementationFixtures.java
@@ -0,0 +1,381 @@
+/*
+ * 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.impls.matrix;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import org.apache.ignite.ml.math.Matrix;
+import org.apache.ignite.ml.math.impls.storage.matrix.FunctionMatrixStorage;
+import org.jetbrains.annotations.NotNull;
+
+/** */
+class MatrixImplementationFixtures {
+    /** */
+    private static final List<Supplier<Iterable<Matrix>>> suppliers = Arrays.asList(
+        (Supplier<Iterable<Matrix>>)DenseLocalOnHeapMatrixFixture::new,
+        (Supplier<Iterable<Matrix>>)DenseLocalOffHeapMatrixFixture::new,
+        (Supplier<Iterable<Matrix>>)RandomMatrixFixture::new,
+        (Supplier<Iterable<Matrix>>)SparseLocalOnHeapMatrixFixture::new,
+        (Supplier<Iterable<Matrix>>)PivotedMatrixViewFixture::new,
+        (Supplier<Iterable<Matrix>>)MatrixViewFixture::new,
+        (Supplier<Iterable<Matrix>>)FunctionMatrixFixture::new,
+        (Supplier<Iterable<Matrix>>)DiagonalMatrixFixture::new,
+        (Supplier<Iterable<Matrix>>)TransposedMatrixViewFixture::new
+    );
+
+    /** */
+    void consumeSampleMatrix(BiConsumer<Matrix, String> consumer) {
+        for (Supplier<Iterable<Matrix>> fixtureSupplier : suppliers) {
+            final Iterable<Matrix> fixture = fixtureSupplier.get();
+
+            for (Matrix matrix : fixture) {
+                consumer.accept(matrix, fixture.toString());
+
+                matrix.destroy();
+            }
+        }
+    }
+
+    /** */
+    private static class DenseLocalOnHeapMatrixFixture extends MatrixSizeIterator {
+        /** */
+        DenseLocalOnHeapMatrixFixture() {
+            super(DenseLocalOnHeapMatrix::new, "DenseLocalOnHeapMatrix");
+        }
+    }
+
+    /** */
+    private static class DenseLocalOffHeapMatrixFixture extends MatrixSizeIterator {
+        /** */
+        DenseLocalOffHeapMatrixFixture() {
+            super(DenseLocalOffHeapMatrix::new, "DenseLocalOffHeapMatrix");
+        }
+    }
+
+    /** */
+    private static class RandomMatrixFixture extends MatrixSizeIterator {
+        /** */
+        RandomMatrixFixture() {
+            super(RandomMatrix::new, "RandomMatrix");
+        }
+    }
+
+    /** */
+    private static class SparseLocalOnHeapMatrixFixture extends MatrixSizeIterator {
+        /** */
+        SparseLocalOnHeapMatrixFixture() {
+            super(SparseLocalOnHeapMatrix::new, "SparseLocalOnHeapMatrix");
+        }
+    }
+
+    /** */
+    private static class PivotedMatrixViewFixture extends WrapperMatrixIterator {
+        /** */
+        PivotedMatrixViewFixture() {
+            super(PivotedMatrixView::new, "PivotedMatrixView over DenseLocalOnHeapMatrix");
+        }
+    }
+
+    /** */
+    private static class MatrixViewFixture extends WrapperMatrixIterator {
+        /** */
+        MatrixViewFixture() {
+            super((matrix) -> new MatrixView(matrix, 0, 0, matrix.rowSize(), matrix.columnSize()),
+                "MatrixView over DenseLocalOnHeapMatrix");
+        }
+    }
+
+    /** */
+    private static class FunctionMatrixFixture extends WrapperMatrixIterator {
+        /** */
+        FunctionMatrixFixture() {
+            super(FunctionMatrixForTest::new, "FunctionMatrix wrapping DenseLocalOnHeapMatrix");
+        }
+    }
+
+    /** */
+    private static class DiagonalMatrixFixture extends DiagonalIterator {
+        /** */
+        DiagonalMatrixFixture() {
+            super(DenseLocalOnHeapMatrix::new, "DiagonalMatrix over DenseLocalOnHeapMatrix");
+        }
+
+        /** {@inheritDoc} */
+        @NotNull
+        @Override public Iterator<Matrix> iterator() {
+            return new Iterator<Matrix>() {
+                /** {@inheritDoc} */
+                @Override public boolean hasNext() {
+                    return hasNextSize(getSizeIdx());
+                }
+
+                /** {@inheritDoc} */
+                @Override public Matrix next() {
+                    assert getSize(getSizeIdx()) == 1 : "Only size 1 allowed for diagonal matrix fixture.";
+
+                    Matrix matrix = getConstructor().apply(getSize(getSizeIdx()), getSize(getSizeIdx()));
+
+                    nextIdx();
+
+                    return new DiagonalMatrix(matrix);
+                }
+            };
+        }
+    }
+
+    /** */
+    private static class TransposedMatrixViewFixture extends WrapperMatrixIterator {
+        /** */
+        TransposedMatrixViewFixture() {
+            super(TransposedMatrixView::new, "TransposedMatrixView over DenseLocalOnHeapMatrix");
+        }
+    }
+
+    /** */
+    private static abstract class DiagonalIterator implements Iterable<Matrix> {
+        /** */
+        private final Integer[] sizes = new Integer[] {1, null};
+        /** */
+        private int sizeIdx = 0;
+
+        /** */
+        private BiFunction<Integer, Integer, ? extends Matrix> constructor;
+        /** */
+        private String desc;
+
+        /** */
+        DiagonalIterator(BiFunction<Integer, Integer, ? extends Matrix> constructor, String desc) {
+            this.constructor = constructor;
+            this.desc = desc;
+        }
+
+        /** */
+        public BiFunction<Integer, Integer, ? extends Matrix> getConstructor() {
+            return constructor;
+        }
+
+        /** */
+        int getSizeIdx() {
+            return sizeIdx;
+        }
+
+        /** */
+        @Override public String toString() {
+            return desc + "{rows=" + sizes[sizeIdx] + ", cols=" + sizes[sizeIdx] + "}";
+        }
+
+        /** */
+        boolean hasNextSize(int idx) {
+            return sizes[idx] != null;
+        }
+
+        /** */
+        Integer getSize(int idx) {
+            return sizes[idx];
+        }
+
+        /** */
+        void nextIdx() {
+            sizeIdx++;
+        }
+    }
+
+    /** */
+    private static class WrapperMatrixIterator extends MatrixSizeIterator {
+        /** */
+        private final Function<Matrix, Matrix> wrapperCtor;
+
+        /** */
+        WrapperMatrixIterator(Function<Matrix, Matrix> wrapperCtor, String desc) {
+            super(DenseLocalOnHeapMatrix::new, desc);
+
+            this.wrapperCtor = wrapperCtor;
+        }
+
+        /** {@inheritDoc} */
+        @NotNull
+        @Override public Iterator<Matrix> iterator() {
+            return new Iterator<Matrix>() {
+                /** {@inheritDoc} */
+                @Override public boolean hasNext() {
+                    return hasNextCol(getSizeIdx()) && hasNextRow(getSizeIdx());
+                }
+
+                /** {@inheritDoc} */
+                @Override public Matrix next() {
+                    Matrix matrix = getConstructor().apply(getRow(getSizeIdx()), getCol(getSizeIdx()));
+
+                    nextIdx();
+
+                    return wrapperCtor.apply(matrix);
+                }
+            };
+        }
+    }
+
+    /** */
+    private static class MatrixSizeIterator implements Iterable<Matrix> {
+        /** */
+        private final Integer[] rows = new Integer[] {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 512, 1024, null};
+        /** */
+        private final Integer[] cols = new Integer[] {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 1024, 512, null};
+        /** */
+        private int sizeIdx = 0;
+
+        /** */
+        private BiFunction<Integer, Integer, ? extends Matrix> constructor;
+        /** */
+        private String desc;
+
+        /** */
+        MatrixSizeIterator(BiFunction<Integer, Integer, ? extends Matrix> constructor, String desc) {
+            this.constructor = constructor;
+            this.desc = desc;
+        }
+
+        /** */
+        public BiFunction<Integer, Integer, ? extends Matrix> getConstructor() {
+            return constructor;
+        }
+
+        /** */
+        int getSizeIdx() {
+            return sizeIdx;
+        }
+
+        /** */
+        @Override public String toString() {
+            return desc + "{rows=" + rows[sizeIdx] + ", cols=" + cols[sizeIdx] + "}";
+        }
+
+        /** */
+        boolean hasNextRow(int idx) {
+            return rows[idx] != null;
+        }
+
+        /** */
+        boolean hasNextCol(int idx) {
+            return cols[idx] != null;
+        }
+
+        /** */
+        Integer getRow(int idx) {
+            return rows[idx];
+        }
+
+        /** */
+        int getCol(int idx) {
+            return cols[idx];
+        }
+
+        /** {@inheritDoc} */
+        @NotNull
+        @Override public Iterator<Matrix> iterator() {
+            return new Iterator<Matrix>() {
+                /** {@inheritDoc} */
+                @Override public boolean hasNext() {
+                    return hasNextCol(sizeIdx) && hasNextRow(sizeIdx);
+                }
+
+                /** {@inheritDoc} */
+                @Override public Matrix next() {
+                    Matrix matrix = constructor.apply(rows[sizeIdx], cols[sizeIdx]);
+
+                    nextIdx();
+
+                    return matrix;
+                }
+            };
+        }
+
+        /** */
+        void nextIdx() {
+            sizeIdx++;
+        }
+    }
+
+    /** Subclass tweaked for serialization */
+    private static class FunctionMatrixForTest extends FunctionMatrix {
+        /** */
+        Matrix underlying;
+
+        /** */
+        public FunctionMatrixForTest() {
+            // No-op.
+        }
+
+        /** */
+        FunctionMatrixForTest(Matrix underlying) {
+            super(underlying.rowSize(), underlying.columnSize(), underlying::get, underlying::set);
+
+            this.underlying = underlying;
+        }
+
+        /** {@inheritDoc} */
+        @Override public Matrix copy() {
+            return new FunctionMatrixForTest(underlying);
+        }
+
+        /** {@inheritDoc} */
+        @Override public void writeExternal(ObjectOutput out) throws IOException {
+            super.writeExternal(out);
+
+            out.writeObject(underlying);
+        }
+
+        /** {@inheritDoc} */
+        @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+            super.readExternal(in);
+
+            underlying = (Matrix)in.readObject();
+
+            setStorage(new FunctionMatrixStorage(underlying.rowSize(), underlying.columnSize(),
+                underlying::get, underlying::set));
+        }
+
+        /** {@inheritDoc} */
+        @Override public int hashCode() {
+            int res = 1;
+
+            res = res * 37 + underlying.hashCode();
+
+            return res;
+        }
+
+        /** {@inheritDoc} */
+        @Override public boolean equals(Object o) {
+            if (this == o)
+                return true;
+
+            if (o == null || getClass() != o.getClass())
+                return false;
+
+            FunctionMatrixForTest that = (FunctionMatrixForTest)o;
+
+            return underlying != null ? underlying.equals(that.underlying) : that.underlying == null;
+        }
+    }
+}