http://git-wip-us.apache.org/repos/asf/ignite/blob/26e40528/modules/ml/src/test/java/org/apache/ignite/ml/math/primitives/vector/AbstractVectorTest.java ---------------------------------------------------------------------- diff --git a/modules/ml/src/test/java/org/apache/ignite/ml/math/primitives/vector/AbstractVectorTest.java b/modules/ml/src/test/java/org/apache/ignite/ml/math/primitives/vector/AbstractVectorTest.java new file mode 100644 index 0000000..2550c6f --- /dev/null +++ b/modules/ml/src/test/java/org/apache/ignite/ml/math/primitives/vector/AbstractVectorTest.java @@ -0,0 +1,542 @@ +/* + * 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.primitives.vector; + +import java.util.Arrays; +import java.util.stream.StreamSupport; +import org.apache.ignite.ml.math.primitives.matrix.Matrix; +import org.apache.ignite.ml.math.exceptions.IndexException; +import org.apache.ignite.ml.math.functions.Functions; +import org.apache.ignite.ml.math.primitives.MathTestConstants; +import org.apache.ignite.ml.math.primitives.vector.storage.DenseVectorStorage; +import org.junit.Assert; +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.assertNotNull; +import static org.junit.Assert.assertTrue; + +/** + * Unit test for {@link AbstractVector}. + */ +public class AbstractVectorTest { + /** */ + private AbstractVector testVector; + + /** */ + @Before + public void setUp() { + testVector = getAbstractVector(); + } + + /** */ + @Test + public void setStorage() { + testVector.setStorage(createStorage()); + + assertTrue(testVector.size() == MathTestConstants.STORAGE_SIZE); + } + + /** */ + @Test + public void size() { + testVector.setStorage(createStorage()); + assertTrue(testVector.size() == MathTestConstants.STORAGE_SIZE); + + testVector.setStorage(new DenseVectorStorage(MathTestConstants.STORAGE_SIZE + MathTestConstants.STORAGE_SIZE)); + assertTrue(testVector.size() == MathTestConstants.STORAGE_SIZE + MathTestConstants.STORAGE_SIZE); + + testVector = getAbstractVector(createStorage()); + assertTrue(testVector.size() == MathTestConstants.STORAGE_SIZE); + } + + /** */ + @Test + public void getPositive() { + testVector = getAbstractVector(createStorage()); + + for (int i = 0; i < MathTestConstants.STORAGE_SIZE; i++) + assertNotNull(MathTestConstants.NULL_VALUES, testVector.get(i)); + + } + + /** */ + @Test(expected = NullPointerException.class) + public void getNegative0() { + testVector.get(0); + } + + /** */ + @Test(expected = IndexException.class) + public void getNegative1() { + testVector.setStorage(createStorage()); + + testVector.get(-1); + } + + /** */ + @Test(expected = IndexException.class) + public void getNegative2() { + testVector.setStorage(createStorage()); + + testVector.get(testVector.size() + 1); + } + + /** */ + @Test(expected = NullPointerException.class) + public void getXNegative0() { + testVector.getX(0); + } + + /** */ + @Test(expected = ArrayIndexOutOfBoundsException.class) + public void getXNegative1() { + testVector.setStorage(createStorage()); + + testVector.getX(-1); + } + + /** */ + @Test(expected = ArrayIndexOutOfBoundsException.class) + public void getXNegative2() { + testVector.setStorage(createStorage()); + + testVector.getX(MathTestConstants.STORAGE_SIZE + 1); + } + + /** */ + @Test + public void set() { + double[] data = initVector(); + + for (int i = 0; i < MathTestConstants.STORAGE_SIZE; i++) + testVector.set(i, Math.exp(data[i])); + + for (int i = 0; i < MathTestConstants.STORAGE_SIZE; i++) + assertEquals(MathTestConstants.VAL_NOT_EQUALS, testVector.get(i), Math.exp(data[i]), MathTestConstants.NIL_DELTA); + } + + /** */ + @Test(expected = IndexException.class) + public void setNegative0() { + testVector.set(-1, -1); + } + + /** */ + @Test(expected = IndexException.class) + public void setNegative1() { + initVector(); + + testVector.set(-1, -1); + } + + /** */ + @Test(expected = IndexException.class) + public void setNegative2() { + initVector(); + + testVector.set(MathTestConstants.STORAGE_SIZE + 1, -1); + } + + /** */ + @Test(expected = IndexOutOfBoundsException.class) + public void setXNegative0() { + initVector(); + + testVector.setX(-1, -1); + } + + /** */ + @Test(expected = IndexOutOfBoundsException.class) + public void setXNegative1() { + initVector(); + + testVector.setX(MathTestConstants.STORAGE_SIZE + 1, -1); + } + + /** */ + @Test(expected = NullPointerException.class) + public void setXNegative2() { + testVector.setX(-1, -1); + } + + /** */ + @Test + public void isZero() { + assertTrue(MathTestConstants.UNEXPECTED_VAL, testVector.isZero(0d)); + + assertFalse(MathTestConstants.UNEXPECTED_VAL, testVector.isZero(1d)); + } + + /** */ + @Test + public void guid() { + assertNotNull(MathTestConstants.NULL_GUID, testVector.guid()); + + assertEquals(MathTestConstants.UNEXPECTED_GUID_VAL, testVector.guid(), testVector.guid()); + + assertFalse(MathTestConstants.EMPTY_GUID, testVector.guid().toString().isEmpty()); + + testVector = getAbstractVector(createStorage()); + + assertNotNull(MathTestConstants.NULL_GUID, testVector.guid()); + + assertEquals(MathTestConstants.UNEXPECTED_GUID_VAL, testVector.guid(), testVector.guid()); + + assertFalse(MathTestConstants.EMPTY_GUID, testVector.guid().toString().isEmpty()); + } + + /** */ + @Test + public void equalsTest() { + VectorStorage storage = createStorage(); + + AbstractVector testVector1 = getAbstractVector(); + + testVector1.setStorage(storage); + + AbstractVector testVector2 = getAbstractVector(); + + assertEquals(MathTestConstants.VAL_NOT_EQUALS, testVector, testVector); + + testVector2.setStorage(storage); + + assertTrue(MathTestConstants.VAL_NOT_EQUALS, testVector1.equals(testVector2)); + + assertFalse(MathTestConstants.VALUES_SHOULD_BE_NOT_EQUALS, testVector1.equals(testVector)); + } + + /** */ + @Test(expected = NullPointerException.class) + public void all() { + assertNotNull(MathTestConstants.NULL_VAL, testVector.all()); + + assertNotNull(MathTestConstants.NULL_VAL, getAbstractVector(createStorage()).all()); + + getAbstractVector().all().iterator().next(); + } + + /** */ + @Test + public void nonZeroElements() { + VectorStorage storage = createStorage(); + + double[] data = storage.data(); + + testVector = getAbstractVector(storage); + + assertEquals(MathTestConstants.VAL_NOT_EQUALS, testVector.nonZeroElements(), Arrays.stream(data).filter(x -> x != 0d).count()); + + addNilValues(data); + + assertEquals(MathTestConstants.VAL_NOT_EQUALS, testVector.nonZeroElements(), Arrays.stream(data).filter(x -> x != 0d).count()); + } + + /** */ + @Test + public void foldMapWithSecondVector() { + double[] data0 = initVector(); + + VectorStorage storage1 = createStorage(); + + double[] data1 = storage1.data().clone(); + + AbstractVector testVector1 = getAbstractVector(storage1); + + StringBuilder testVal = new StringBuilder(); + + for (int i = 0; i < data0.length; i++) + testVal.append(data0[i] + data1[i]); + + assertEquals(MathTestConstants.VAL_NOT_EQUALS, testVector.foldMap(testVector1, (string, xi) -> string.concat(xi.toString()), Functions.PLUS, ""), testVal.toString()); + } + + /** */ + @Test + public void nonZeroes() { + assertNotNull(MathTestConstants.NULL_VAL, testVector.nonZeroes()); + + double[] data = initVector(); + + assertNotNull(MathTestConstants.NULL_VAL, testVector.nonZeroes()); + + Assert.assertEquals(MathTestConstants.VAL_NOT_EQUALS, StreamSupport.stream(testVector.nonZeroes().spliterator(), false).count(), Arrays.stream(data).filter(x -> x != 0d).count()); + + addNilValues(data); + + Assert.assertEquals(MathTestConstants.VAL_NOT_EQUALS, StreamSupport.stream(testVector.nonZeroes().spliterator(), false).count(), Arrays.stream(data).filter(x -> x != 0d).count()); + } + + /** */ + @Test(expected = NullPointerException.class) + public void nonZeroesEmpty() { + testVector.nonZeroes().iterator().next(); + } + + /** */ + @Test(expected = NullPointerException.class) + public void assign() { + testVector.assign(MathTestConstants.TEST_VAL); + } + + /** */ + @Test(expected = NullPointerException.class) + public void assignArr() { + testVector.assign(new double[1]); + } + + /** */ + @Test(expected = NullPointerException.class) + public void assignArrEmpty() { + testVector.assign(new double[0]); + } + + /** */ + @Test(expected = NullPointerException.class) + public void dotNegative() { + testVector.dot(getAbstractVector(createEmptyStorage())); + } + + /** */ + @Test + public void dotSelf() { + double[] data = initVector(); + + assertEquals(MathTestConstants.VAL_NOT_EQUALS, testVector.dotSelf(), Arrays.stream(data).reduce(0, (x, y) -> x + y * y), MathTestConstants.NIL_DELTA); + } + + /** */ + @Test + public void getStorage() { + assertNotNull(MathTestConstants.NULL_VAL, getAbstractVector(createEmptyStorage())); + assertNotNull(MathTestConstants.NULL_VAL, getAbstractVector(createStorage())); + testVector.setStorage(createStorage()); + assertNotNull(MathTestConstants.NULL_VAL, testVector.getStorage()); + } + + /** */ + @Test + public void getElement() { + double[] data = initVector(); + + for (int i = 0; i < data.length; i++) { + assertNotNull(MathTestConstants.NULL_VAL, testVector.getElement(i)); + + assertEquals(MathTestConstants.UNEXPECTED_VAL, testVector.getElement(i).get(), data[i], MathTestConstants.NIL_DELTA); + + testVector.getElement(i).set(++data[i]); + + assertEquals(MathTestConstants.UNEXPECTED_VAL, testVector.getElement(i).get(), data[i], MathTestConstants.NIL_DELTA); + } + } + + /** + * Create {@link AbstractVector} with storage for tests. + * + * @param storage {@link VectorStorage} + * @return AbstractVector. + */ + @SuppressWarnings("ClassWithoutNoArgConstructor") + private AbstractVector getAbstractVector(VectorStorage storage) { + return new AbstractVector(storage) { // TODO: IGNTIE-5723, find out how to fix warning about missing constructor + /** {@inheritDoc} */ + @Override public boolean isDense() { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean isSequentialAccess() { + return false; + } + + /** {@inheritDoc} */ + @Override public Matrix likeMatrix(int rows, int cols) { + return null; + } + + /** {@inheritDoc} */ + @Override public Vector copy() { + return getAbstractVector(this.getStorage()); + } + + /** {@inheritDoc} */ + @Override public Vector like(int crd) { + return null; + } + + /** {@inheritDoc} */ + @Override public Vector normalize() { + return null; + } + + /** {@inheritDoc} */ + @Override public Vector normalize(double power) { + return null; + } + + /** {@inheritDoc} */ + @Override public Vector logNormalize() { + return null; + } + + /** {@inheritDoc} */ + @Override public Vector logNormalize(double power) { + return null; + } + + /** {@inheritDoc} */ + @Override public Vector viewPart(int off, int len) { + return null; + } + + /** {@inheritDoc} */ + @Override public boolean isRandomAccess() { + return true; + } + + /** {@inheritDoc} */ + @Override public boolean isDistributed() { + return false; + } + }; + } + + /** + * Create empty {@link AbstractVector} for tests. + * + * @return AbstractVector. + */ + private AbstractVector getAbstractVector() { + return new AbstractVector() { // TODO: IGNTIE-5723, find out how to fix warning about missing constructor + /** {@inheritDoc} */ + @Override public boolean isDense() { + return false; + } + + /** {@inheritDoc} */ + @Override public Matrix likeMatrix(int rows, int cols) { + return null; + } + + /** {@inheritDoc} */ + @Override public boolean isSequentialAccess() { + return false; + } + + /** {@inheritDoc} */ + @Override public Vector copy() { + return getAbstractVector(this.getStorage()); + } + + /** {@inheritDoc} */ + @Override public Vector like(int crd) { + return null; + } + + /** {@inheritDoc} */ + @Override public Vector normalize() { + return null; + } + + /** {@inheritDoc} */ + @Override public Vector normalize(double power) { + return null; + } + + /** {@inheritDoc} */ + @Override public Vector logNormalize() { + return null; + } + + /** {@inheritDoc} */ + @Override public Vector logNormalize(double power) { + return null; + } + + /** {@inheritDoc} */ + @Override public Vector viewPart(int off, int len) { + return null; + } + + /** {@inheritDoc} */ + @Override public boolean isRandomAccess() { + return true; + } + + /** {@inheritDoc} */ + @Override public boolean isDistributed() { + return false; + } + }; + } + + /** + * Create {@link VectorStorage} for tests. + * + * @return VectorStorage + */ + private VectorStorage createEmptyStorage() { + return new DenseVectorStorage(MathTestConstants.STORAGE_SIZE); + } + + /** + * Create filled {@link VectorStorage} for tests. + * + * @return VectorStorage. + */ + private VectorStorage createStorage() { + DenseVectorStorage storage = new DenseVectorStorage(MathTestConstants.STORAGE_SIZE); + + for (int i = 0; i < MathTestConstants.STORAGE_SIZE; i++) + storage.set(i, Math.random()); + + return storage; + } + + /** + * Init vector and return initialized values. + * + * @return Initial values. + */ + private double[] initVector() { + VectorStorage storage = createStorage(); + double[] data = storage.data().clone(); + + testVector = getAbstractVector(storage); + return data; + } + + /** + * Add some zeroes to vector elements. + */ + private void addNilValues() { + testVector.set(10, 0); + testVector.set(50, 0); + } + + /** + * Add some zeroes to vector elements. Also set zeroes to the same elements in reference array data + */ + private void addNilValues(double[] testRef) { + addNilValues(); + testRef[10] = 0; + testRef[50] = 0; + } +}
http://git-wip-us.apache.org/repos/asf/ignite/blob/26e40528/modules/ml/src/test/java/org/apache/ignite/ml/math/primitives/vector/DelegatingVectorConstructorTest.java ---------------------------------------------------------------------- diff --git a/modules/ml/src/test/java/org/apache/ignite/ml/math/primitives/vector/DelegatingVectorConstructorTest.java b/modules/ml/src/test/java/org/apache/ignite/ml/math/primitives/vector/DelegatingVectorConstructorTest.java new file mode 100644 index 0000000..f5033e3 --- /dev/null +++ b/modules/ml/src/test/java/org/apache/ignite/ml/math/primitives/vector/DelegatingVectorConstructorTest.java @@ -0,0 +1,63 @@ +/* + * 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.primitives.vector; + +import org.apache.ignite.ml.math.primitives.vector.impl.DenseVector; +import org.apache.ignite.ml.math.primitives.vector.impl.DelegatingVector; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** */ +public class DelegatingVectorConstructorTest { + /** */ + private static final int IMPOSSIBLE_SIZE = -1; + + /** */ + @Test + public void basicTest() { + final Vector parent = new DenseVector(new double[] {0, 1}); + + final Vector delegate = new DelegatingVector(parent); + + final int size = parent.size(); + + assertEquals("Delegate size differs from expected.", size, delegate.size()); + + for (int idx = 0; idx < size; idx++) + assertDelegate(parent, delegate, idx); + } + + /** */ + private void assertDelegate(Vector parent, Vector delegate, int idx) { + assertValue(parent, delegate, idx); + + parent.set(idx, parent.get(idx) + 1); + + assertValue(parent, delegate, idx); + + delegate.set(idx, delegate.get(idx) + 2); + + assertValue(parent, delegate, idx); + } + + /** */ + private void assertValue(Vector parent, Vector delegate, int idx) { + assertEquals("Unexpected value at index " + idx, parent.get(idx), delegate.get(idx), 0d); + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/26e40528/modules/ml/src/test/java/org/apache/ignite/ml/math/primitives/vector/DenseVectorConstructorTest.java ---------------------------------------------------------------------- diff --git a/modules/ml/src/test/java/org/apache/ignite/ml/math/primitives/vector/DenseVectorConstructorTest.java b/modules/ml/src/test/java/org/apache/ignite/ml/math/primitives/vector/DenseVectorConstructorTest.java new file mode 100644 index 0000000..24e4460 --- /dev/null +++ b/modules/ml/src/test/java/org/apache/ignite/ml/math/primitives/vector/DenseVectorConstructorTest.java @@ -0,0 +1,164 @@ +/* + * 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.primitives.vector; + +import java.util.HashMap; +import java.util.Map; +import org.apache.ignite.ml.math.exceptions.UnsupportedOperationException; +import org.apache.ignite.ml.math.primitives.vector.impl.DenseVector; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** */ +public class DenseVectorConstructorTest { + /** */ + private static final int IMPOSSIBLE_SIZE = -1; + + /** */ + @Test(expected = UnsupportedOperationException.class) + public void mapInvalidArgsTest() { + assertEquals("Expect exception due to invalid args.", IMPOSSIBLE_SIZE, + new DenseVector(new HashMap<String, Object>() {{ + put("invalid", 99); + }}).size()); + } + + /** */ + @Test(expected = UnsupportedOperationException.class) + public void mapMissingArgsTest() { + final Map<String, Object> test = new HashMap<String, Object>() {{ + put("arr", new double[0]); + put("shallowCopyMissing", "whatever"); + }}; + + assertEquals("Expect exception due to missing args.", IMPOSSIBLE_SIZE, + new DenseVector(test).size()); + } + + /** */ + @Test(expected = ClassCastException.class) + public void mapInvalidArrTypeTest() { + final Map<String, Object> test = new HashMap<String, Object>() {{ + put("size", "whatever"); + }}; + + assertEquals("Expect exception due to invalid arr type.", IMPOSSIBLE_SIZE, + new DenseVector(test).size()); + } + + /** */ + @Test(expected = UnsupportedOperationException.class) + public void mapInvalidCopyTypeTest() { + final Map<String, Object> test = new HashMap<String, Object>() {{ + put("arr", new double[0]); + put("shallowCopy", 0); + }}; + + assertEquals("Expect exception due to invalid copy type.", IMPOSSIBLE_SIZE, + new DenseVector(test).size()); + } + + /** */ + @Test(expected = AssertionError.class) + public void mapNullTest() { + //noinspection ConstantConditions + assertEquals("Null map args.", IMPOSSIBLE_SIZE, + new DenseVector((Map<String, Object>)null).size()); + } + + /** */ + @Test + public void mapTest() { + assertEquals("Size from args.", 99, + new DenseVector(new HashMap<String, Object>() {{ + put("size", 99); + }}).size()); + + final double[] test = new double[99]; + + assertEquals("Size from array in args.", test.length, + new DenseVector(new HashMap<String, Object>() {{ + put("arr", test); + put("copy", false); + }}).size()); + + assertEquals("Size from array in args, shallow copy.", test.length, + new DenseVector(new HashMap<String, Object>() {{ + put("arr", test); + put("copy", true); + }}).size()); + } + + /** */ + @Test(expected = AssertionError.class) + public void negativeSizeTest() { + assertEquals("Negative size.", IMPOSSIBLE_SIZE, + new DenseVector(-1).size()); + } + + /** */ + @Test(expected = AssertionError.class) + public void nullCopyTest() { + assertEquals("Null array to non-shallow copy.", IMPOSSIBLE_SIZE, + new DenseVector(null, false).size()); + } + + /** */ + @Test(expected = AssertionError.class) + public void nullDefaultCopyTest() { + assertEquals("Null array default copy.", IMPOSSIBLE_SIZE, + new DenseVector((double[])null).size()); + } + + /** */ + @Test(expected = NullPointerException.class) + public void defaultConstructorTest() { + assertEquals("Default constructor.", IMPOSSIBLE_SIZE, + new DenseVector().size()); + } + + /** */ + @Test(expected = AssertionError.class) + public void nullArrShallowCopyTest() { + assertEquals("Null array shallow copy.", IMPOSSIBLE_SIZE, + new DenseVector(null, true).size()); + } + + /** */ + @Test + public void primitiveTest() { + assertEquals("0 size shallow copy.", 0, + new DenseVector(new double[0], true).size()); + + assertEquals("0 size.", 0, + new DenseVector(new double[0], false).size()); + + assertEquals("1 size shallow copy.", 1, + new DenseVector(new double[1], true).size()); + + assertEquals("1 size.", 1, + new DenseVector(new double[1], false).size()); + + assertEquals("0 size default copy.", 0, + new DenseVector(new double[0]).size()); + + assertEquals("1 size default copy.", 1, + new DenseVector(new double[1]).size()); + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/26e40528/modules/ml/src/test/java/org/apache/ignite/ml/math/primitives/vector/MatrixVectorViewTest.java ---------------------------------------------------------------------- diff --git a/modules/ml/src/test/java/org/apache/ignite/ml/math/primitives/vector/MatrixVectorViewTest.java b/modules/ml/src/test/java/org/apache/ignite/ml/math/primitives/vector/MatrixVectorViewTest.java new file mode 100644 index 0000000..b978729 --- /dev/null +++ b/modules/ml/src/test/java/org/apache/ignite/ml/math/primitives/vector/MatrixVectorViewTest.java @@ -0,0 +1,226 @@ +/* + * 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.primitives.vector; + +import org.apache.ignite.ml.math.primitives.matrix.Matrix; +import org.apache.ignite.ml.math.exceptions.IndexException; +import org.apache.ignite.ml.math.primitives.matrix.impl.DenseMatrix; +import org.apache.ignite.ml.math.primitives.vector.impl.VectorizedViewMatrix; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * Tests for {@link VectorizedViewMatrix}. + */ +public class MatrixVectorViewTest { + /** */ + private static final String UNEXPECTED_VALUE = "Unexpected value"; + /** */ + private static final int SMALL_SIZE = 3; + /** */ + private static final int IMPOSSIBLE_SIZE = -1; + + /** */ + private Matrix parent; + + /** */ + @Before + public void setup() { + parent = newMatrix(SMALL_SIZE, SMALL_SIZE); + } + + /** */ + @Test + public void testDiagonal() { + Vector vector = parent.viewDiagonal(); + + for (int i = 0; i < SMALL_SIZE; i++) + assertView(i, i, vector, i); + } + + /** */ + @Test + public void testRow() { + for (int i = 0; i < SMALL_SIZE; i++) { + Vector viewRow = parent.viewRow(i); + + for (int j = 0; j < SMALL_SIZE; j++) + assertView(i, j, viewRow, j); + } + } + + /** */ + @Test + public void testCols() { + for (int i = 0; i < SMALL_SIZE; i++) { + Vector viewCol = parent.viewColumn(i); + + for (int j = 0; j < SMALL_SIZE; j++) + assertView(j, i, viewCol, j); + } + } + + /** */ + @Test + public void basicTest() { + for (int rowSize : new int[] {1, 2, 3, 4}) + for (int colSize : new int[] {1, 2, 3, 4}) + for (int row = 0; row < rowSize; row++) + for (int col = 0; col < colSize; col++) + for (int rowStride = 0; rowStride < rowSize; rowStride++) + for (int colStride = 0; colStride < colSize; colStride++) + if (rowStride != 0 || colStride != 0) + assertMatrixVectorView(newMatrix(rowSize, colSize), row, col, rowStride, colStride); + } + + /** */ + @Test(expected = AssertionError.class) + public void parentNullTest() { + //noinspection ConstantConditions + assertEquals(IMPOSSIBLE_SIZE, + new VectorizedViewMatrix(null, 1, 1, 1, 1).size()); + } + + /** */ + @Test(expected = IndexException.class) + public void rowNegativeTest() { + //noinspection ConstantConditions + assertEquals(IMPOSSIBLE_SIZE, + new VectorizedViewMatrix(parent, -1, 1, 1, 1).size()); + } + + /** */ + @Test(expected = IndexException.class) + public void colNegativeTest() { + //noinspection ConstantConditions + assertEquals(IMPOSSIBLE_SIZE, + new VectorizedViewMatrix(parent, 1, -1, 1, 1).size()); + } + + /** */ + @Test(expected = IndexException.class) + public void rowTooLargeTest() { + //noinspection ConstantConditions + assertEquals(IMPOSSIBLE_SIZE, + new VectorizedViewMatrix(parent, parent.rowSize() + 1, 1, 1, 1).size()); + } + + /** */ + @Test(expected = IndexException.class) + public void colTooLargeTest() { + //noinspection ConstantConditions + assertEquals(IMPOSSIBLE_SIZE, + new VectorizedViewMatrix(parent, 1, parent.columnSize() + 1, 1, 1).size()); + } + + /** */ + @Test(expected = AssertionError.class) + public void rowStrideNegativeTest() { + //noinspection ConstantConditions + assertEquals(IMPOSSIBLE_SIZE, + new VectorizedViewMatrix(parent, 1, 1, -1, 1).size()); + } + + /** */ + @Test(expected = AssertionError.class) + public void colStrideNegativeTest() { + //noinspection ConstantConditions + assertEquals(IMPOSSIBLE_SIZE, + new VectorizedViewMatrix(parent, 1, 1, 1, -1).size()); + } + + /** */ + @Test(expected = AssertionError.class) + public void rowStrideTooLargeTest() { + //noinspection ConstantConditions + assertEquals(IMPOSSIBLE_SIZE, + new VectorizedViewMatrix(parent, 1, 1, parent.rowSize() + 1, 1).size()); + } + + /** */ + @Test(expected = AssertionError.class) + public void colStrideTooLargeTest() { + //noinspection ConstantConditions + assertEquals(IMPOSSIBLE_SIZE, + new VectorizedViewMatrix(parent, 1, 1, 1, parent.columnSize() + 1).size()); + } + + /** */ + @Test(expected = AssertionError.class) + public void bothStridesZeroTest() { + //noinspection ConstantConditions + assertEquals(IMPOSSIBLE_SIZE, + new VectorizedViewMatrix(parent, 1, 1, 0, 0).size()); + } + + /** */ + private void assertMatrixVectorView(Matrix parent, int row, int col, int rowStride, int colStride) { + VectorizedViewMatrix view = new VectorizedViewMatrix(parent, row, col, rowStride, colStride); + + String desc = "parent [" + parent.rowSize() + "x" + parent.columnSize() + "], view [" + + row + "x" + col + "], strides [" + rowStride + ", " + colStride + "]"; + + final int size = view.size(); + + final int sizeByRows = rowStride == 0 ? IMPOSSIBLE_SIZE : (parent.rowSize() - row) / rowStride; + final int sizeByCols = colStride == 0 ? IMPOSSIBLE_SIZE : (parent.columnSize() - col) / colStride; + + assertTrue("Size " + size + " differs from expected for " + desc, + size == sizeByRows || size == sizeByCols); + + for (int idx = 0; idx < size; idx++) { + final int rowIdx = row + idx * rowStride; + final int colIdx = col + idx * colStride; + + assertEquals(UNEXPECTED_VALUE + " at view index " + idx + desc, + parent.get(rowIdx, colIdx), view.get(idx), 0d); + } + } + + /** */ + private Matrix newMatrix(int rowSize, int colSize) { + Matrix res = new DenseMatrix(rowSize, colSize); + + for (int i = 0; i < res.rowSize(); i++) + for (int j = 0; j < res.columnSize(); j++) + res.set(i, j, i * res.rowSize() + j); + + return res; + } + + /** */ + private void assertView(int row, int col, Vector view, int viewIdx) { + assertValue(row, col, view, viewIdx); + + parent.set(row, col, parent.get(row, col) + 1); + + assertValue(row, col, view, viewIdx); + + view.set(viewIdx, view.get(viewIdx) + 2); + + assertValue(row, col, view, viewIdx); + } + + /** */ + private void assertValue(int row, int col, Vector view, int viewIdx) { + assertEquals(UNEXPECTED_VALUE + " at row " + row + " col " + col, parent.get(row, col), view.get(viewIdx), 0d); + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/26e40528/modules/ml/src/test/java/org/apache/ignite/ml/math/primitives/vector/SparseVectorConstructorTest.java ---------------------------------------------------------------------- diff --git a/modules/ml/src/test/java/org/apache/ignite/ml/math/primitives/vector/SparseVectorConstructorTest.java b/modules/ml/src/test/java/org/apache/ignite/ml/math/primitives/vector/SparseVectorConstructorTest.java new file mode 100644 index 0000000..b53a952 --- /dev/null +++ b/modules/ml/src/test/java/org/apache/ignite/ml/math/primitives/vector/SparseVectorConstructorTest.java @@ -0,0 +1,55 @@ +/* + * 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.primitives.vector; + +import org.apache.ignite.ml.math.StorageConstants; +import org.apache.ignite.ml.math.primitives.vector.impl.SparseVector; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** */ +public class SparseVectorConstructorTest { + /** */ + private static final int IMPOSSIBLE_SIZE = -1; + + /** */ + @Test(expected = AssertionError.class) + public void negativeSizeTest() { + assertEquals("Negative size.", IMPOSSIBLE_SIZE, + new SparseVector(-1, 1).size()); + } + + /** */ + @Test(expected = AssertionError.class) + public void zeroSizeTest() { + assertEquals("0 size.", IMPOSSIBLE_SIZE, + new SparseVector(0, 1).size()); + } + + /** */ + @Test + public void primitiveTest() { + assertEquals("1 size, random access.", 1, + new SparseVector(1, StorageConstants.RANDOM_ACCESS_MODE).size()); + + assertEquals("1 size, sequential access.", 1, + new SparseVector(1, StorageConstants.SEQUENTIAL_ACCESS_MODE).size()); + + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/26e40528/modules/ml/src/test/java/org/apache/ignite/ml/math/primitives/vector/VectorArrayStorageTest.java ---------------------------------------------------------------------- diff --git a/modules/ml/src/test/java/org/apache/ignite/ml/math/primitives/vector/VectorArrayStorageTest.java b/modules/ml/src/test/java/org/apache/ignite/ml/math/primitives/vector/VectorArrayStorageTest.java new file mode 100644 index 0000000..f0a847a --- /dev/null +++ b/modules/ml/src/test/java/org/apache/ignite/ml/math/primitives/vector/VectorArrayStorageTest.java @@ -0,0 +1,59 @@ +/* + * 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.primitives.vector; + +import java.util.Arrays; +import org.apache.ignite.ml.math.primitives.MathTestConstants; +import org.apache.ignite.ml.math.primitives.vector.storage.DenseVectorStorage; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +/** + * Unit test for {@link DenseVectorStorage}. + */ +public class VectorArrayStorageTest extends VectorBaseStorageTest<DenseVectorStorage> { + /** */ + @Override public void setUp() { + storage = new DenseVectorStorage(MathTestConstants.STORAGE_SIZE); + } + + /** */ + @Test + public void isArrayBased() throws Exception { + assertTrue(MathTestConstants.WRONG_ATTRIBUTE_VAL, storage.isArrayBased()); + + assertTrue(MathTestConstants.WRONG_ATTRIBUTE_VAL, new DenseVectorStorage().isArrayBased()); + } + + /** */ + @Test + public void data() throws Exception { + assertNotNull(MathTestConstants.NULL_DATA_STORAGE, storage.data()); + + assertEquals(MathTestConstants.WRONG_DATA_SIZE, storage.data().length, MathTestConstants.STORAGE_SIZE); + + assertTrue(MathTestConstants.UNEXPECTED_DATA_VAL, Arrays.equals(storage.data(), new double[MathTestConstants.STORAGE_SIZE])); + + assertNull(MathTestConstants.UNEXPECTED_DATA_VAL, new DenseVectorStorage().data()); + } + +} http://git-wip-us.apache.org/repos/asf/ignite/blob/26e40528/modules/ml/src/test/java/org/apache/ignite/ml/math/primitives/vector/VectorAttributesTest.java ---------------------------------------------------------------------- diff --git a/modules/ml/src/test/java/org/apache/ignite/ml/math/primitives/vector/VectorAttributesTest.java b/modules/ml/src/test/java/org/apache/ignite/ml/math/primitives/vector/VectorAttributesTest.java new file mode 100644 index 0000000..691eeb1 --- /dev/null +++ b/modules/ml/src/test/java/org/apache/ignite/ml/math/primitives/vector/VectorAttributesTest.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.primitives.vector; + +import java.util.Arrays; +import java.util.List; +import java.util.function.Function; +import org.apache.ignite.ml.math.primitives.matrix.impl.DenseMatrix; +import org.apache.ignite.ml.math.primitives.vector.impl.DenseVector; +import org.apache.ignite.ml.math.primitives.vector.impl.SparseVector; +import org.apache.ignite.ml.math.primitives.vector.impl.DelegatingVector; +import org.apache.ignite.ml.math.primitives.vector.impl.VectorizedViewMatrix; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** */ +public class VectorAttributesTest { + /** */ + private final List<AttrCfg> attrCfgs = Arrays.asList( + new AttrCfg("isDense", Vector::isDense, + DenseVector.class), + new AttrCfg("isArrayBased", Vector::isArrayBased, + DenseVector.class), + new AttrCfg("isSequentialAccess", Vector::isSequentialAccess, + DenseVector.class, SparseLocalVectorSequentialAccess.class), + new AttrCfg("guidNotNull", v -> v.guid() == null), // IMPL NOTE this is somewhat artificial + new AttrCfg("isRandomAccess", Vector::isRandomAccess, + DenseVector.class, SparseLocalVectorSequentialAccess.class, SparseLocalVectorRandomAccess.class), + new AttrCfg("isDistributed", Vector::isDistributed)); + + /** */ + private final List<Specification> specFixture = Arrays.asList( + new Specification(new DenseVector(1)), + new Specification(new DelegatingVector(new DenseVector(1)), + DenseVector.class, "isDense", "isArrayBased", "isSequentialAccess", + "isRandomAccess", "isDistributed"), + new Specification(new SparseLocalVectorSequentialAccess(1)), + new Specification(new SparseLocalVectorRandomAccess(1)), + new Specification(new VectorizedViewMatrix(new DenseMatrix(1, 1), 0, 0, 1, 1), + DenseVector.class, "isDense", + "isRandomAccess", "isDistributed")); // TODO: IGNTIE-5723, find out why "isSequentialAccess" fails here + + /** */ + @Test + public void isDenseTest() { + assertAttribute("isDense"); + } + + /** */ + @Test + public void isArrayBasedTest() { + assertAttribute("isArrayBased"); + } + + /** */ + @Test + public void isSequentialAccessTest() { + assertAttribute("isSequentialAccess"); + } + + /** */ + @Test + public void guidTest() { + assertAttribute("guidNotNull"); + } + + /** */ + @Test + public void isRandomAccessTest() { + assertAttribute("isRandomAccess"); + } + + /** */ + @Test + public void isDistributedTest() { + assertAttribute("isDistributed"); + } + + /** */ + private void assertAttribute(String name) { + final AttrCfg attr = attrCfg(name); + + for (Specification spec : specFixture) + spec.verify(attr); + } + + /** */ + private AttrCfg attrCfg(String name) { + for (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 Vector v; + /** */ + private final Class<? extends Vector> underlyingType; + /** */ + private final List<String> attrsFromUnderlying; + /** */ + final String desc; + + /** */ + Specification(Vector v, Class<? extends Vector> underlyingType, String... attrsFromUnderlying) { + this.v = v; + this.underlyingType = underlyingType; + this.attrsFromUnderlying = Arrays.asList(attrsFromUnderlying); + final Class<? extends Vector> clazz = v.getClass(); + desc = clazz.getSimpleName() + (clazz.equals(underlyingType) + ? "" : " (underlying type " + underlyingType.getSimpleName() + ")"); + } + + /** */ + Specification(Vector v) { + this(v, v.getClass()); + } + + /** */ + void verify(AttrCfg attr) { + final boolean obtained = attr.obtain.apply(v); + + final Class<? extends Vector> typeToCheck + = attrsFromUnderlying.contains(attr.name) ? underlyingType : v.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<Vector, Boolean> obtain; + /** */ + final List<Class> trueInTypes; + + /** */ + AttrCfg(String name, Function<Vector, Boolean> obtain, Class... trueInTypes) { + this.name = name; + this.obtain = obtain; + this.trueInTypes = Arrays.asList(trueInTypes); + } + } + + /** */ + private static class SparseLocalVectorSequentialAccess extends SparseVector { + /** */ + public SparseLocalVectorSequentialAccess() { + // No-op. + } + + /** */ + SparseLocalVectorSequentialAccess(int size) { + super(size, SEQUENTIAL_ACCESS_MODE); + } + } + + /** */ + private static class SparseLocalVectorRandomAccess extends SparseVector { + /** */ + public SparseLocalVectorRandomAccess() { + // No-op. + } + + /** */ + SparseLocalVectorRandomAccess(int size) { + super(size, RANDOM_ACCESS_MODE); + } + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/26e40528/modules/ml/src/test/java/org/apache/ignite/ml/math/primitives/vector/VectorBaseStorageTest.java ---------------------------------------------------------------------- diff --git a/modules/ml/src/test/java/org/apache/ignite/ml/math/primitives/vector/VectorBaseStorageTest.java b/modules/ml/src/test/java/org/apache/ignite/ml/math/primitives/vector/VectorBaseStorageTest.java new file mode 100644 index 0000000..b6c9721 --- /dev/null +++ b/modules/ml/src/test/java/org/apache/ignite/ml/math/primitives/vector/VectorBaseStorageTest.java @@ -0,0 +1,69 @@ +/* + * 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.primitives.vector; + +import org.apache.ignite.ml.math.ExternalizeTest; +import org.apache.ignite.ml.math.primitives.vector.VectorStorage; +import org.apache.ignite.ml.math.primitives.MathTestConstants; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * Abstract class with base tests for each vector storage. + */ +public abstract class VectorBaseStorageTest<T extends VectorStorage> extends ExternalizeTest<T> { + /** */ + protected T storage; + + /** */ + @Before + public abstract void setUp(); + + /** */ + @After + public void tearDown() throws Exception { + storage.destroy(); + } + + /** */ + @Test + public void getSet() throws Exception { + for (int i = 0; i < MathTestConstants.STORAGE_SIZE; i++) { + double random = Math.random(); + + storage.set(i, random); + + assertEquals(MathTestConstants.WRONG_DATA_ELEMENT, storage.get(i), random, MathTestConstants.NIL_DELTA); + } + } + + /** */ + @Test + public void size() { + assertTrue(MathTestConstants.UNEXPECTED_VAL, storage.size() == MathTestConstants.STORAGE_SIZE); + } + + /** */ + @Override public void externalizeTest() { + super.externalizeTest(storage); + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/26e40528/modules/ml/src/test/java/org/apache/ignite/ml/math/primitives/vector/VectorFoldMapTest.java ---------------------------------------------------------------------- diff --git a/modules/ml/src/test/java/org/apache/ignite/ml/math/primitives/vector/VectorFoldMapTest.java b/modules/ml/src/test/java/org/apache/ignite/ml/math/primitives/vector/VectorFoldMapTest.java new file mode 100644 index 0000000..770340a --- /dev/null +++ b/modules/ml/src/test/java/org/apache/ignite/ml/math/primitives/vector/VectorFoldMapTest.java @@ -0,0 +1,121 @@ +/* + * 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.primitives.vector; + +import java.util.Arrays; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Function; +import org.apache.ignite.ml.math.functions.Functions; +import org.junit.Test; + +import static java.util.function.DoubleUnaryOperator.identity; +import static org.junit.Assert.assertTrue; + +/** See also: {@link AbstractVectorTest} and {@link VectorToMatrixTest}. */ +public class VectorFoldMapTest { + /** */ + @Test + public void mapVectorTest() { + operationVectorTest((operand1, operand2) -> operand1 + operand2, (Vector v1, Vector v2) -> v1.map(v2, Functions.PLUS)); + } + + /** */ + @Test + public void mapDoubleFunctionTest() { + consumeSampleVectors((v, desc) -> operatorTest(v, desc, + (vec) -> vec.map(Functions.INV), (val) -> 1.0 / val)); + } + + /** */ + @Test + public void mapBiFunctionTest() { + consumeSampleVectors((v, desc) -> operatorTest(v, desc, + (vec) -> vec.map(Functions.PLUS, 1.0), (val) -> 1.0 + val)); + } + + /** */ + @Test + public void foldMapTest() { + toDoubleTest( + ref -> Arrays.stream(ref).map(identity()).sum(), + (v) -> v.foldMap(Functions.PLUS, Functions.IDENTITY, 0.0)); + } + + /** */ + @Test + public void foldMapVectorTest() { + toDoubleTest( + ref -> 2.0 * Arrays.stream(ref).sum(), + (v) -> v.foldMap(v, Functions.PLUS, Functions.PLUS, 0.0)); + + } + + /** */ + private void operatorTest(Vector v, String desc, Function<Vector, Vector> op, Function<Double, Double> refOp) { + final int size = v.size(); + final double[] ref = new double[size]; + + VectorImplementationsTest.ElementsChecker checker = new VectorImplementationsTest.ElementsChecker(v, ref, desc); + + Vector actual = op.apply(v); + + for (int idx = 0; idx < size; idx++) + ref[idx] = refOp.apply(ref[idx]); + + checker.assertCloseEnough(actual, ref); + } + + /** */ + private void toDoubleTest(Function<double[], Double> calcRef, Function<Vector, Double> calcVec) { + consumeSampleVectors((v, desc) -> { + final int size = v.size(); + final double[] ref = new double[size]; + + new VectorImplementationsTest.ElementsChecker(v, ref, desc); // IMPL NOTE this initialises vector and reference array + + final VectorImplementationsTest.Metric metric = new VectorImplementationsTest.Metric(calcRef.apply(ref), calcVec.apply(v)); + + assertTrue("Not close enough at " + desc + + ", " + metric, metric.closeEnough()); + }); + } + + /** */ + private void operationVectorTest(BiFunction<Double, Double, Double> operation, + BiFunction<Vector, Vector, Vector> vecOperation) { + consumeSampleVectors((v, desc) -> { + // TODO: IGNTIE-5723, find out if more elaborate testing scenario is needed or it's okay as is. + final int size = v.size(); + final double[] ref = new double[size]; + + final VectorImplementationsTest.ElementsChecker checker = new VectorImplementationsTest.ElementsChecker(v, ref, desc); + final Vector operand = v.copy(); + + for (int idx = 0; idx < size; idx++) + ref[idx] = operation.apply(ref[idx], ref[idx]); + + checker.assertCloseEnough(vecOperation.apply(v, operand), ref); + }); + } + + /** */ + private void consumeSampleVectors(BiConsumer<Vector, String> consumer) { + new VectorImplementationsFixtures().consumeSampleVectors(null, consumer); + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/26e40528/modules/ml/src/test/java/org/apache/ignite/ml/math/primitives/vector/VectorImplementationsFixtures.java ---------------------------------------------------------------------- diff --git a/modules/ml/src/test/java/org/apache/ignite/ml/math/primitives/vector/VectorImplementationsFixtures.java b/modules/ml/src/test/java/org/apache/ignite/ml/math/primitives/vector/VectorImplementationsFixtures.java new file mode 100644 index 0000000..549a5f8 --- /dev/null +++ b/modules/ml/src/test/java/org/apache/ignite/ml/math/primitives/vector/VectorImplementationsFixtures.java @@ -0,0 +1,424 @@ +/* + * 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.primitives.vector; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; +import org.apache.ignite.ml.math.primitives.matrix.Matrix; +import org.apache.ignite.ml.math.StorageConstants; +import org.apache.ignite.ml.math.primitives.matrix.impl.DenseMatrix; +import org.apache.ignite.ml.math.primitives.vector.impl.DenseVector; +import org.apache.ignite.ml.math.primitives.vector.impl.SparseVector; +import org.apache.ignite.ml.math.primitives.vector.impl.DelegatingVector; +import org.apache.ignite.ml.math.primitives.vector.impl.VectorizedViewMatrix; +import org.jetbrains.annotations.NotNull; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** */ +class VectorImplementationsFixtures { + /** */ + private static final List<Supplier<Iterable<Vector>>> suppliers = Arrays.asList( + (Supplier<Iterable<Vector>>)DenseLocalOnHeapVectorFixture::new, + (Supplier<Iterable<Vector>>)SparseLocalVectorFixture::new, + (Supplier<Iterable<Vector>>)DelegatingVectorFixture::new, + (Supplier<Iterable<Vector>>)MatrixVectorViewFixture::new + ); + + /** */ + void consumeSampleVectors(Consumer<Integer> paramsConsumer, BiConsumer<Vector, String> consumer) { + for (Supplier<Iterable<Vector>> fixtureSupplier : VectorImplementationsFixtures.suppliers) { + final Iterable<Vector> fixture = fixtureSupplier.get(); + + for (Vector v : fixture) { + if (paramsConsumer != null) + paramsConsumer.accept(v.size()); + + consumer.accept(v, fixture.toString()); + } + } + } + + /** */ + private static class DenseLocalOnHeapVectorFixture extends VectorSizesExtraFixture<Boolean> { + /** */ + DenseLocalOnHeapVectorFixture() { + super("DenseLocalOnHeapVector", + (size, shallowCp) -> new DenseVector(new double[size], shallowCp), + "shallow copy", new Boolean[] {false, true, null}); + } + } + + /** */ + private static class SparseLocalVectorFixture extends VectorSizesExtraFixture<Integer> { + /** */ + SparseLocalVectorFixture() { + super("SparseLocalVector", SparseVector::new, "access mode", + new Integer[] {StorageConstants.SEQUENTIAL_ACCESS_MODE, StorageConstants.RANDOM_ACCESS_MODE, null}); + } + } + + /** */ + private static class MatrixVectorViewFixture extends VectorSizesExtraFixture<Integer> { + /** */ + MatrixVectorViewFixture() { + super("MatrixVectorView", + MatrixVectorViewFixture::newView, + "stride kind", new Integer[] {0, 1, 2, null}); + } + + /** */ + private static Vector newView(int size, int strideKind) { + final Matrix parent = new DenseMatrix(size, size); + + return new VectorizedViewMatrix(parent, 0, 0, strideKind != 1 ? 1 : 0, strideKind != 0 ? 1 : 0); + } + } + + /** */ + private static class VectorSizesExtraFixture<T> implements Iterable<Vector> { + /** */ + private final Supplier<VectorSizesExtraIterator<T>> iter; + + /** */ + private final AtomicReference<String> ctxDescrHolder = new AtomicReference<>("Iterator not started."); + + /** */ + VectorSizesExtraFixture(String vectorKind, BiFunction<Integer, T, Vector> ctor, String extraParamName, + T[] extras) { + iter = () -> new VectorSizesExtraIterator<>(vectorKind, ctor, ctxDescrHolder::set, extraParamName, extras); + } + + /** {@inheritDoc} */ + @NotNull + @Override public Iterator<Vector> iterator() { + return iter.get(); + } + + /** {@inheritDoc} */ + @Override public String toString() { + // IMPL NOTE index within bounds is expected to be guaranteed by proper code in this class + return ctxDescrHolder.get(); + } + } + + /** */ + private static abstract class VectorSizesFixture implements Iterable<Vector> { + /** */ + private final Supplier<VectorSizesIterator> iter; + + /** */ + private final AtomicReference<String> ctxDescrHolder = new AtomicReference<>("Iterator not started."); + + /** */ + VectorSizesFixture(String vectorKind, Function<Integer, Vector> ctor) { + iter = () -> new VectorSizesIterator(vectorKind, ctor, ctxDescrHolder::set); + } + + /** {@inheritDoc} */ + @NotNull + @Override public Iterator<Vector> iterator() { + return iter.get(); + } + + /** {@inheritDoc} */ + @Override public String toString() { + // IMPL NOTE index within bounds is expected to be guaranteed by proper code in this class + return ctxDescrHolder.get(); + } + } + + /** */ + private static class VectorSizesExtraIterator<T> extends VectorSizesIterator { + /** */ + private final T[] extras; + /** */ + private int extraIdx = 0; + /** */ + private final BiFunction<Integer, T, Vector> ctor; + /** */ + private final String extraParamName; + + /** + * @param vectorKind Descriptive name to use for context logging. + * @param ctor Constructor for objects to iterate over. + * @param ctxDescrConsumer Context logging consumer. + * @param extraParamName Name of extra parameter to iterate over. + * @param extras Array of extra parameter values to iterate over. + */ + VectorSizesExtraIterator(String vectorKind, BiFunction<Integer, T, Vector> ctor, + Consumer<String> ctxDescrConsumer, String extraParamName, T[] extras) { + super(vectorKind, null, ctxDescrConsumer); + + this.ctor = ctor; + this.extraParamName = extraParamName; + this.extras = extras; + } + + /** {@inheritDoc} */ + @Override public boolean hasNext() { + return super.hasNext() && hasNextExtra(extraIdx); + } + + /** {@inheritDoc} */ + @Override void nextIdx() { + assert extras[extraIdx] != null + : "Index(es) out of bound at " + VectorSizesExtraIterator.this; + + if (hasNextExtra(extraIdx + 1)) { + extraIdx++; + + return; + } + + extraIdx = 0; + + super.nextIdx(); + } + + /** {@inheritDoc} */ + @Override public String toString() { + // IMPL NOTE index within bounds is expected to be guaranteed by proper code in this class + return "{" + super.toString() + + ", " + extraParamName + "=" + extras[extraIdx] + + '}'; + } + + /** {@inheritDoc} */ + @Override BiFunction<Integer, Integer, Vector> ctor() { + return (size, delta) -> ctor.apply(size + delta, extras[extraIdx]); + } + + /** */ + void selfTest() { + final Set<Integer> extraIdxs = new HashSet<>(); + + int cnt = 0; + + while (hasNext()) { + assertNotNull("Expect not null vector at " + this, next()); + + if (extras[extraIdx] != null) + extraIdxs.add(extraIdx); + + cnt++; + } + + assertEquals("Extra param tested", extraIdxs.size(), extras.length - 1); + + assertEquals("Combinations tested mismatch.", + 7 * 3 * (extras.length - 1), cnt); + } + + /** */ + private boolean hasNextExtra(int idx) { + return extras[idx] != null; + } + } + + /** */ + private static class VectorSizesIterator extends TwoParamsIterator<Integer, Integer> { + /** */ + private final Function<Integer, Vector> ctor; + + /** */ + VectorSizesIterator(String vectorKind, Function<Integer, Vector> ctor, Consumer<String> ctxDescrConsumer) { + super(vectorKind, null, ctxDescrConsumer, + "size", new Integer[] {2, 4, 8, 16, 32, 64, 128, null}, + "size delta", new Integer[] {-1, 0, 1, null}); + + this.ctor = ctor; + } + + /** {@inheritDoc} */ + @Override BiFunction<Integer, Integer, Vector> ctor() { + return (size, delta) -> ctor.apply(size + delta); + } + } + + /** */ + private static class TwoParamsIterator<T, U> implements Iterator<Vector> { + /** */ + private final T params1[]; + + /** */ + private final U params2[]; + + /** */ + private final String vectorKind; + + /** */ + private final String param1Name; + + /** */ + private final String param2Name; + + /** */ + private final BiFunction<T, U, Vector> ctor; + + /** */ + private final Consumer<String> ctxDescrConsumer; + + /** */ + private int param1Idx = 0; + + /** */ + private int param2Idx = 0; + + /** */ + TwoParamsIterator(String vectorKind, BiFunction<T, U, Vector> ctor, + Consumer<String> ctxDescrConsumer, String param1Name, T[] params1, String param2Name, U[] params2) { + this.param1Name = param1Name; + this.params1 = params1; + + this.param2Name = param2Name; + this.params2 = params2; + + this.vectorKind = vectorKind; + + this.ctor = ctor; + + this.ctxDescrConsumer = ctxDescrConsumer; + } + + /** {@inheritDoc} */ + @Override public boolean hasNext() { + return hasNextParam1(param1Idx) && hasNextParam2(param2Idx); + } + + /** {@inheritDoc} */ + @Override public Vector next() { + if (!hasNext()) + throw new NoSuchElementException(TwoParamsIterator.this.toString()); + + if (ctxDescrConsumer != null) + ctxDescrConsumer.accept(toString()); + + Vector res = ctor().apply(params1[param1Idx], params2[param2Idx]); + + nextIdx(); + + return res; + } + + /** */ + void selfTest() { + final Set<Integer> sizeIdxs = new HashSet<>(), deltaIdxs = new HashSet<>(); + + int cnt = 0; + + while (hasNext()) { + assertNotNull("Expect not null vector at " + this, next()); + + if (params1[param1Idx] != null) + sizeIdxs.add(param1Idx); + + if (params2[param2Idx] != null) + deltaIdxs.add(param2Idx); + + cnt++; + } + + assertEquals("Sizes tested mismatch.", sizeIdxs.size(), params1.length - 1); + + assertEquals("Deltas tested", deltaIdxs.size(), params2.length - 1); + + assertEquals("Combinations tested mismatch.", + (params1.length - 1) * (params2.length - 1), cnt); + } + + /** IMPL NOTE override in subclasses if needed */ + void nextIdx() { + assert params1[param1Idx] != null && params2[param2Idx] != null + : "Index(es) out of bound at " + TwoParamsIterator.this; + + if (hasNextParam2(param2Idx + 1)) { + param2Idx++; + + return; + } + + param2Idx = 0; + + param1Idx++; + } + + /** {@inheritDoc} */ + @Override public String toString() { + // IMPL NOTE index within bounds is expected to be guaranteed by proper code in this class + return vectorKind + "{" + param1Name + "=" + params1[param1Idx] + + ", " + param2Name + "=" + params2[param2Idx] + + '}'; + } + + /** IMPL NOTE override in subclasses if needed */ + BiFunction<T, U, Vector> ctor() { + return ctor; + } + + /** */ + private boolean hasNextParam1(int idx) { + return params1[idx] != null; + } + + /** */ + private boolean hasNextParam2(int idx) { + return params2[idx] != null; + } + } + + /** Delegating vector with dense local onheap vector */ + private static class DelegatingVectorFixture implements Iterable<Vector> { + + /** */ + private final Supplier<VectorSizesExtraIterator<Boolean>> iter; + + /** */ + private final AtomicReference<String> ctxDescrHolder = new AtomicReference<>("Iterator not started."); + + /** */ + DelegatingVectorFixture() { + iter = () -> new VectorSizesExtraIterator<>("DelegatingVector with DenseLocalOnHeapVector", + (size, shallowCp) -> new DelegatingVector(new DenseVector(new double[size], shallowCp)), + ctxDescrHolder::set, "shallow copy", new Boolean[] {false, true, null}); + } + + /** {@inheritDoc} */ + @NotNull + @Override public Iterator<Vector> iterator() { + return iter.get(); + } + + /** {@inheritDoc} */ + @Override public String toString() { + // IMPL NOTE index within bounds is expected to be guaranteed by proper code in this class + return ctxDescrHolder.get(); + } + } +}
