Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id AE78A200C56 for ; Fri, 14 Apr 2017 17:51:03 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id AD068160BB7; Fri, 14 Apr 2017 15:51:03 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id AC6C2160B80 for ; Fri, 14 Apr 2017 17:51:01 +0200 (CEST) Received: (qmail 46288 invoked by uid 500); 14 Apr 2017 15:51:00 -0000 Mailing-List: contact commits-help@ignite.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@ignite.apache.org Delivered-To: mailing list commits@ignite.apache.org Received: (qmail 46278 invoked by uid 99); 14 Apr 2017 15:51:00 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 14 Apr 2017 15:51:00 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 0BDABDFC31; Fri, 14 Apr 2017 15:51:00 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: av@apache.org To: commits@ignite.apache.org Date: Fri, 14 Apr 2017 15:51:00 -0000 Message-Id: <0b1063d4bce14dfe90e596512f497ab6@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [01/13] ignite git commit: IGNITE-4572 Machine Learning: Develop distributed algebra support for dense and sparse data sets. archived-at: Fri, 14 Apr 2017 15:51:03 -0000 Repository: ignite Updated Branches: refs/heads/master 1f867c603 -> acd21fb8b http://git-wip-us.apache.org/repos/asf/ignite/blob/acd21fb8/modules/math/src/test/java/org/apache/ignite/math/impls/vector/VectorImplementationsTest.java ---------------------------------------------------------------------- diff --git a/modules/math/src/test/java/org/apache/ignite/math/impls/vector/VectorImplementationsTest.java b/modules/math/src/test/java/org/apache/ignite/math/impls/vector/VectorImplementationsTest.java new file mode 100644 index 0000000..0e61513 --- /dev/null +++ b/modules/math/src/test/java/org/apache/ignite/math/impls/vector/VectorImplementationsTest.java @@ -0,0 +1,860 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.math.impls.vector; + +import java.util.Arrays; +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 org.apache.ignite.IgniteException; +import org.apache.ignite.math.ExternalizeTest; +import org.apache.ignite.math.Vector; +import org.apache.ignite.math.exceptions.CardinalityException; +import org.apache.ignite.math.exceptions.UnsupportedOperationException; +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.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +/** See also: {@link AbstractVectorTest} and {@link VectorToMatrixTest}. */ +public class VectorImplementationsTest { // todo split this to smaller cohesive test classes + /** */ + @Test + public void vectorImplementationsFixturesTest() { + new VectorImplementationsFixtures().selfTest(); + } + + /** */ + @Test + public void setGetTest() { + consumeSampleVectors((v, desc) -> mutateAtIdxTest(v, desc, (vec, idx, val) -> { + vec.set(idx, val); + + return val; + })); + } + + /** */ + @Test + public void setXTest() { + consumeSampleVectors((v, desc) -> mutateAtIdxTest(v, desc, (vec, idx, val) -> { + vec.setX(idx, val); + + return val; + })); + } + + /** */ + @Test + public void incrementTest() { + consumeSampleVectors((v, desc) -> mutateAtIdxTest(v, desc, (vec, idx, val) -> { + double old = vec.get(idx); + + vec.increment(idx, val); + + return old + val; + })); + } + + /** */ + @Test + public void incrementXTest() { + consumeSampleVectors((v, desc) -> mutateAtIdxTest(v, desc, (vec, idx, val) -> { + double old = vec.getX(idx); + + vec.incrementX(idx, val); + + return old + val; + })); + } + + /** */ + @Test + public void operateXOutOfBoundsTest() { + consumeSampleVectors((v, desc) -> { + if (v instanceof DenseLocalOffHeapVector || v instanceof SparseLocalVector || v instanceof SparseLocalOffHeapVector) + return; // todo find out if it's OK to skip by instances here + + boolean expECaught = false; + + try { + v.getX(-1); + } + catch (ArrayIndexOutOfBoundsException | IgniteException e) { + expECaught = true; + } + + if (!getXOutOfBoundsOK(v)) + assertTrue("Expect exception at negative index getX in " + desc, expECaught); + + expECaught = false; + + try { + v.setX(-1, 0); + } + catch (ArrayIndexOutOfBoundsException | IgniteException e) { + expECaught = true; + } + + assertTrue("Expect exception at negative index setX in " + desc, expECaught); + + expECaught = false; + + try { + v.incrementX(-1, 1); + } + catch (ArrayIndexOutOfBoundsException | IgniteException e) { + expECaught = true; + } + + assertTrue("Expect exception at negative index incrementX in " + desc, expECaught); + + expECaught = false; + + try { + v.getX(v.size()); + } + catch (ArrayIndexOutOfBoundsException | IgniteException e) { + expECaught = true; + } + + if (!getXOutOfBoundsOK(v)) + assertTrue("Expect exception at too large index getX in " + desc, expECaught); + + expECaught = false; + + try { + v.setX(v.size(), 1); + } + catch (ArrayIndexOutOfBoundsException | IgniteException e) { + expECaught = true; + } + + assertTrue("Expect exception at too large index setX in " + desc, expECaught); + + expECaught = false; + + try { + v.incrementX(v.size(), 1); + } + catch (ArrayIndexOutOfBoundsException | IgniteException e) { + expECaught = true; + } + + assertTrue("Expect exception at too large index incrementX in " + desc, expECaught); + }); + } + + /** */ + @Test + public void sizeTest() { + final AtomicReference expSize = new AtomicReference<>(0); + + consumeSampleVectors( + expSize::set, + (v, desc) -> assertEquals("Expected size for " + desc, + (int)expSize.get(), v.size()) + ); + } + + /** */ + @Test + public void getElementTest() { + consumeSampleVectors((v, desc) -> new ElementsChecker(v, desc).assertCloseEnough(v)); + } + + /** */ + @Test + public void copyTest() { + consumeSampleVectors((v, desc) -> new ElementsChecker(v, desc).assertCloseEnough(v.copy())); + } + + /** */ + @Test + public void divideTest() { + operationTest((val, operand) -> val / operand, Vector::divide); + } + + /** */ + @Test + public void likeTest() { + for (int card : new int[] {1, 2, 4, 8, 16, 32, 64, 128}) + consumeSampleVectors((v, desc) -> { + Class expType = expLikeType(v); + + if (expType == null) { + try { + v.like(card); + } + catch (UnsupportedOperationException uoe) { + return; + } + + fail("Expected exception wasn't caught for " + desc); + + return; + } + + Vector vLike = v.like(card); + + assertNotNull("Expect non-null like vector for " + expType.getSimpleName() + " in " + desc, vLike); + assertEquals("Expect size equal to cardinality at " + desc, card, vLike.size()); + + Class actualType = vLike.getClass(); + + assertTrue("Actual vector type " + actualType.getSimpleName() + + " should be assignable from expected type " + expType.getSimpleName() + " in " + desc, + actualType.isAssignableFrom(expType)); + }); + } + + /** */ + @Test + public void minusTest() { + operationVectorTest((operand1, operand2) -> operand1 - operand2, Vector::minus); + } + + /** */ + @Test + public void plusVectorTest() { + operationVectorTest((operand1, operand2) -> operand1 + operand2, Vector::plus); + } + + /** */ + @Test + public void plusDoubleTest() { + operationTest((val, operand) -> val + operand, Vector::plus); + } + + /** */ + @Test + public void timesVectorTest() { + operationVectorTest((operand1, operand2) -> operand1 * operand2, Vector::times); + } + + /** */ + @Test + public void timesDoubleTest() { + operationTest((val, operand) -> val * operand, Vector::times); + } + + /** */ + @Test + public void viewPartTest() { + consumeSampleVectors((v, desc) -> { + final int size = v.size(); + final double[] ref = new double[size]; + final int delta = size > 32 ? 3 : 1; // IMPL NOTE this is for faster test execution + + final ElementsChecker checker = new ElementsChecker(v, ref, desc); + + for (int off = 0; off < size; off += delta) + for (int len = 1; len < size - off; len += delta) + checker.assertCloseEnough(v.viewPart(off, len), Arrays.copyOfRange(ref, off, off + len)); + }); + } + + /** */ + @Test + public void sumTest() { + toDoubleTest( + ref -> Arrays.stream(ref).sum(), + Vector::sum); + } + + /** */ + @Test + public void minValueTest() { + toDoubleTest( + ref -> Arrays.stream(ref).min().getAsDouble(), + Vector::minValue); + } + + /** */ + @Test + public void maxValueTest() { + toDoubleTest( + ref -> Arrays.stream(ref).max().getAsDouble(), + Vector::maxValue); + } + + /** */ + @Test + public void sortTest() { + consumeSampleVectors((v, desc) -> { + if (readOnly(v) || !v.isArrayBased()) { + boolean expECaught = false; + + try { + v.sort(); + } + catch (UnsupportedOperationException uoe) { + expECaught = true; + } + + assertTrue("Expected exception was not caught for sort in " + desc, expECaught); + + return; + } + + final int size = v.size(); + final double[] ref = new double[size]; + + new ElementsChecker(v, ref, desc).assertCloseEnough(v.sort(), Arrays.stream(ref).sorted().toArray()); + }); + } + + /** */ + @Test + public void metaAttributesTest() { + consumeSampleVectors((v, desc) -> { + assertNotNull("Null meta storage in " + desc, v.getMetaStorage()); + + final String key = "test key"; + final String val = "test value"; + final String details = "key [" + key + "] for " + desc; + + v.setAttribute(key, val); + assertTrue("Expect to have meta attribute for " + details, v.hasAttribute(key)); + assertEquals("Unexpected meta attribute value for " + details, val, v.getAttribute(key)); + + v.removeAttribute(key); + assertFalse("Expect not to have meta attribute for " + details, v.hasAttribute(key)); + assertNull("Unexpected meta attribute value for " + details, v.getAttribute(key)); + }); + } + + /** */ + @Test + public void assignDoubleTest() { + consumeSampleVectors((v, desc) -> { + if (readOnly(v)) + return; + + for (double val : new double[] {0, -1, 0, 1}) { + v.assign(val); + + for (int idx = 0; idx < v.size(); idx++) { + final Metric metric = new Metric(val, v.get(idx)); + + assertTrue("Not close enough at index " + idx + ", val " + val + ", " + metric + + ", " + desc, metric.closeEnough()); + } + } + }); + } + + /** */ + @Test + public void assignDoubleArrTest() { + consumeSampleVectors((v, desc) -> { + if (readOnly(v)) + return; + + final int size = v.size(); + final double[] ref = new double[size]; + + final ElementsChecker checker = new ElementsChecker(v, ref, desc); + + for (int idx = 0; idx < size; idx++) + ref[idx] = -ref[idx]; + + v.assign(ref); + + checker.assertCloseEnough(v, ref); + + assignDoubleArrWrongCardinality(v, desc); + }); + } + + /** */ + @Test + public void assignVectorTest() { + consumeSampleVectors((v, desc) -> { + if (readOnly(v)) + return; + + final int size = v.size(); + final double[] ref = new double[size]; + + final ElementsChecker checker = new ElementsChecker(v, ref, desc); + + for (int idx = 0; idx < size; idx++) + ref[idx] = -ref[idx]; + + v.assign(new DenseLocalOnHeapVector(ref)); + + checker.assertCloseEnough(v, ref); + + assignVectorWrongCardinality(v, desc); + }); + } + + /** */ + @Test + public void assignFunctionTest() { + consumeSampleVectors((v, desc) -> { + if (readOnly(v)) + return; + + final int size = v.size(); + final double[] ref = new double[size]; + + final ElementsChecker checker = new ElementsChecker(v, ref, desc); + + for (int idx = 0; idx < size; idx++) + ref[idx] = -ref[idx]; + + v.assign((idx) -> ref[idx]); + + checker.assertCloseEnough(v, ref); + }); + } + + /** */ + @Test + public void minElementTest() { + consumeSampleVectors((v, desc) -> { + final ElementsChecker checker = new ElementsChecker(v, desc); + + final Vector.Element minE = v.minElement(); + + final int minEIdx = minE.index(); + + assertTrue("Unexpected index from minElement " + minEIdx + ", " + desc, + minEIdx >= 0 && minEIdx < v.size()); + + final Metric metric = new Metric(minE.get(), v.minValue()); + + assertTrue("Not close enough minElement at index " + minEIdx + ", " + metric + + ", " + desc, metric.closeEnough()); + + checker.assertNewMinElement(v); + }); + } + + /** */ + @Test + public void maxElementTest() { + consumeSampleVectors((v, desc) -> { + final ElementsChecker checker = new ElementsChecker(v, desc); + + final Vector.Element maxE = v.maxElement(); + + final int minEIdx = maxE.index(); + + assertTrue("Unexpected index from minElement " + minEIdx + ", " + desc, + minEIdx >= 0 && minEIdx < v.size()); + + final Metric metric = new Metric(maxE.get(), v.maxValue()); + + assertTrue("Not close enough maxElement at index " + minEIdx + ", " + metric + + ", " + desc, metric.closeEnough()); + + checker.assertNewMaxElement(v); + }); + } + + /** */ + @Test + public void externalizeTest() { + (new ExternalizeTest() { + /** {@inheritDoc} */ + @Override public void externalizeTest() { + consumeSampleVectors((v, desc) -> { + if (v instanceof SparseLocalOffHeapVector) + return; //TODO: wait till SparseLocalOffHeapVector externalization support. + + externalizeTest(v); + }); + } + }).externalizeTest(); + } + + /** */ + @Test + public void hashCodeTest() { + consumeSampleVectors((v, desc) -> assertTrue("Zero hash code for " + desc, v.hashCode() != 0)); + } + + /** */ + private boolean getXOutOfBoundsOK(Vector v) { + // todo find out if this is indeed OK + return v instanceof RandomVector || v instanceof ConstantVector + || v instanceof SingleElementVector || v instanceof SingleElementVectorView; + } + + /** */ + private void mutateAtIdxTest(Vector v, String desc, MutateAtIdx operation) { + if (readOnly(v)) { + if (v.size() < 1) + return; + + boolean expECaught = false; + + try { + operation.apply(v, 0, 1); + } + catch (UnsupportedOperationException uoe) { + expECaught = true; + } + + assertTrue("Expect exception at attempt to mutate element in " + desc, expECaught); + + return; + } + + for (double val : new double[] {0, -1, 0, 1}) + for (int idx = 0; idx < v.size(); idx++) { + double exp = operation.apply(v, idx, val); + + final Metric metric = new Metric(exp, v.get(idx)); + + assertTrue("Not close enough at index " + idx + ", val " + val + ", " + metric + + ", " + desc, metric.closeEnough()); + } + } + + /** */ + private Class expLikeType(Vector v) { + Class clazz = v.getClass(); + + if (clazz.isAssignableFrom(PivotedVectorView.class) || clazz.isAssignableFrom(SingleElementVectorView.class)) + return null; + + if (clazz.isAssignableFrom(MatrixVectorView.class) || clazz.isAssignableFrom(DelegatingVector.class)) + return DenseLocalOnHeapVector.class; // IMPL NOTE per fixture + + return clazz; + } + + /** */ + private void toDoubleTest(Function calcRef, Function calcVec) { + consumeSampleVectors((v, desc) -> { + final int size = v.size(); + final double[] ref = new double[size]; + + new ElementsChecker(v, ref, desc); // IMPL NOTE this initialises vector and reference array + + final Metric metric = new Metric(calcRef.apply(ref), calcVec.apply(v)); + + assertTrue("Not close enough at " + desc + + ", " + metric, metric.closeEnough()); + }); + } + + /** */ + private void operationVectorTest(BiFunction operation, + BiFunction vecOperation) { + consumeSampleVectors((v, desc) -> { + // TODO 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 ElementsChecker checker = new 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); + + assertWrongCardinality(v, desc, vecOperation); + }); + } + + /** */ + private void assignDoubleArrWrongCardinality(Vector v, String desc) { + boolean expECaught = false; + + try { + v.assign(new double[v.size() + 1]); + } + catch (CardinalityException ce) { + expECaught = true; + } + + assertTrue("Expect exception at too large size in " + desc, expECaught); + + if (v.size() < 2) + return; + + expECaught = false; + + try { + v.assign(new double[v.size() - 1]); + } + catch (CardinalityException ce) { + expECaught = true; + } + + assertTrue("Expect exception at too small size in " + desc, expECaught); + } + + /** */ + private void assignVectorWrongCardinality(Vector v, String desc) { + boolean expECaught = false; + + try { + v.assign(new DenseLocalOnHeapVector(v.size() + 1)); + } + catch (CardinalityException ce) { + expECaught = true; + } + + assertTrue("Expect exception at too large size in " + desc, expECaught); + + if (v.size() < 2) + return; + + expECaught = false; + + try { + v.assign(new DenseLocalOnHeapVector(v.size() - 1)); + } + catch (CardinalityException ce) { + expECaught = true; + } + + assertTrue("Expect exception at too small size in " + desc, expECaught); + } + + /** */ + private void assertWrongCardinality( + Vector v, String desc, BiFunction vecOperation) { + boolean expECaught = false; + + try { + vecOperation.apply(v, new DenseLocalOnHeapVector(v.size() + 1)); + } + catch (CardinalityException ce) { + expECaught = true; + } + + assertTrue("Expect exception at too large size in " + desc, expECaught); + + if (v.size() < 2) + return; + + expECaught = false; + + try { + vecOperation.apply(v, new DenseLocalOnHeapVector(v.size() - 1)); + } + catch (CardinalityException ce) { + expECaught = true; + } + + assertTrue("Expect exception at too small size in " + desc, expECaught); + } + + /** */ + private void operationTest(BiFunction operation, + BiFunction vecOperation) { + for (double val : new double[] {0, 0.1, 1, 2, 10}) + consumeSampleVectors((v, desc) -> { + final int size = v.size(); + final double[] ref = new double[size]; + + final ElementsChecker checker = new ElementsChecker(v, ref, "val " + val + ", " + desc); + + for (int idx = 0; idx < size; idx++) + ref[idx] = operation.apply(ref[idx], val); + + checker.assertCloseEnough(vecOperation.apply(v, val), ref); + }); + } + + /** */ + private void consumeSampleVectors(BiConsumer consumer) { + consumeSampleVectors(null, consumer); + } + + /** */ + private void consumeSampleVectors(Consumer paramsConsumer, BiConsumer consumer) { + new VectorImplementationsFixtures().consumeSampleVectors(paramsConsumer, consumer); + } + + /** */ + private static boolean readOnly(Vector v) { + return v instanceof RandomVector || v instanceof ConstantVector; + } + + /** */ + private interface MutateAtIdx { + /** */ + double apply(Vector v, int idx, double val); + } + + /** */ + static class ElementsChecker { + /** */ + private final String fixtureDesc; + + /** */ + private final double[] refReadOnly; + + /** */ + private final boolean nonNegative; + + /** */ + ElementsChecker(Vector v, double[] ref, String fixtureDesc, boolean nonNegative) { + this.fixtureDesc = fixtureDesc; + + this.nonNegative = nonNegative; + + refReadOnly = readOnly(v) && ref == null ? new double[v.size()] : null; + + init(v, ref); + } + + /** */ + ElementsChecker(Vector v, double[] ref, String fixtureDesc) { + this(v, ref, fixtureDesc, false); + } + + /** */ + ElementsChecker(Vector v, String fixtureDesc) { + this(v, null, fixtureDesc); + } + + /** */ + void assertCloseEnough(Vector obtained, double[] exp) { + final int size = obtained.size(); + + for (int i = 0; i < size; i++) { + final Vector.Element e = obtained.getElement(i); + + if (refReadOnly != null && exp == null) + exp = refReadOnly; + + final Metric metric = new Metric(exp == null ? generated(i) : exp[i], e.get()); + + assertEquals("Unexpected vector index at " + fixtureDesc, i, e.index()); + assertTrue("Not close enough at index " + i + ", size " + size + ", " + metric + + ", " + fixtureDesc, metric.closeEnough()); + } + } + + /** */ + void assertCloseEnough(Vector obtained) { + assertCloseEnough(obtained, null); + } + + /** */ + void assertNewMinElement(Vector v) { + if (readOnly(v)) + return; + + int exp = v.size() / 2; + + v.set(exp, -(v.size() * 2 + 1)); + + assertEquals("Unexpected minElement index at " + fixtureDesc, exp, v.minElement().index()); + } + + /** */ + void assertNewMaxElement(Vector v) { + if (readOnly(v)) + return; + + int exp = v.size() / 2; + + v.set(exp, v.size() * 2 + 1); + + assertEquals("Unexpected minElement index at " + fixtureDesc, exp, v.maxElement().index()); + } + + /** */ + private void init(Vector v, double[] ref) { + if (readOnly(v)) { + initReadonly(v, ref); + + return; + } + + for (Vector.Element e : v.all()) { + int idx = e.index(); + + // IMPL NOTE introduce negative values because their absence + // blocked catching an ugly bug in AbstractVector#kNorm + int val = generated(idx); + + e.set(val); + + if (ref != null) + ref[idx] = val; + } + } + + /** */ + private void initReadonly(Vector v, double[] ref) { + if (refReadOnly != null) + for (Vector.Element e : v.all()) + refReadOnly[e.index()] = e.get(); + + if (ref != null) + for (Vector.Element e : v.all()) + ref[e.index()] = e.get(); + } + + /** */ + private int generated(int idx) { + return nonNegative || (idx & 1) == 0 ? idx : -idx; + } + } + + /** */ + static class Metric { // todo consider if softer tolerance (like say 0.1 or 0.01) would make sense here + /** */ + private final double exp; + + /** */ + private final double obtained; + + /** **/ + Metric(double exp, double obtained) { + this.exp = exp; + this.obtained = obtained; + } + + /** */ + boolean closeEnough() { + return new Double(exp).equals(obtained) || closeEnoughToZero(); + } + + /** {@inheritDoc} */ + @Override public String toString() { + return "Metric{" + "expected=" + exp + + ", obtained=" + obtained + + '}'; + } + + /** */ + private boolean closeEnoughToZero() { + return (new Double(exp).equals(0.0) && new Double(obtained).equals(-0.0)) + || (new Double(exp).equals(-0.0) && new Double(obtained).equals(0.0)); + } + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/acd21fb8/modules/math/src/test/java/org/apache/ignite/math/impls/vector/VectorIterableTest.java ---------------------------------------------------------------------- diff --git a/modules/math/src/test/java/org/apache/ignite/math/impls/vector/VectorIterableTest.java b/modules/math/src/test/java/org/apache/ignite/math/impls/vector/VectorIterableTest.java new file mode 100644 index 0000000..7a64c85 --- /dev/null +++ b/modules/math/src/test/java/org/apache/ignite/math/impls/vector/VectorIterableTest.java @@ -0,0 +1,376 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.math.impls.vector; + +import java.util.Arrays; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Spliterator; +import java.util.function.BiConsumer; +import org.apache.ignite.math.Vector; +import org.apache.ignite.math.impls.MathTestConstants; +import org.junit.Test; + +import static java.util.Spliterator.ORDERED; +import static java.util.Spliterator.SIZED; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +/** */ +public class VectorIterableTest { + /** */ + @Test + public void allTest() { + consumeSampleVectors( + (v, desc) -> { + int expIdx = 0; + + for (Vector.Element e : v.all()) { + int actualIdx = e.index(); + + assertEquals("Unexpected index for " + desc, + expIdx, actualIdx); + + expIdx++; + } + + assertEquals("Unexpected amount of elements for " + desc, + expIdx, v.size()); + } + ); + } + + /** */ + @Test + public void allTestBound() { + consumeSampleVectors( + (v, desc) -> iteratorTestBound(v.all().iterator(), desc) + ); + } + + /** */ + @Test + public void nonZeroesTestBasic() { + final int size = 5; + + final double[] nonZeroesOddData = new double[size], nonZeroesEvenData = new double[size]; + + for (int idx = 0; idx < size; idx++) { + final boolean odd = (idx & 1) == 1; + + nonZeroesOddData[idx] = odd ? 1 : 0; + + nonZeroesEvenData[idx] = odd ? 0 : 1; + } + + assertTrue("Arrays failed to initialize.", + !isZero(nonZeroesEvenData[0]) + && isZero(nonZeroesEvenData[1]) + && isZero(nonZeroesOddData[0]) + && !isZero(nonZeroesOddData[1])); + + final Vector nonZeroesEvenVec = new DenseLocalOnHeapVector(nonZeroesEvenData), + nonZeroesOddVec = new DenseLocalOnHeapVector(nonZeroesOddData); + + assertTrue("Vectors failed to initialize.", + !isZero(nonZeroesEvenVec.getElement(0).get()) + && isZero(nonZeroesEvenVec.getElement(1).get()) + && isZero(nonZeroesOddVec.getElement(0).get()) + && !isZero(nonZeroesOddVec.getElement(1).get())); + + assertTrue("Iterator(s) failed to start.", + nonZeroesEvenVec.nonZeroes().iterator().next() != null + && nonZeroesOddVec.nonZeroes().iterator().next() != null); + + int nonZeroesActual = 0; + + for (Vector.Element e : nonZeroesEvenVec.nonZeroes()) { + final int idx = e.index(); + + final boolean odd = (idx & 1) == 1; + + final double val = e.get(); + + assertTrue("Not an even index " + idx + ", for value " + val, !odd); + + assertTrue("Zero value " + val + " at even index " + idx, !isZero(val)); + + nonZeroesActual++; + } + + final int nonZeroesOddExp = (size + 1) / 2; + + assertEquals("Unexpected num of iterated odd non-zeroes.", nonZeroesOddExp, nonZeroesActual); + + assertEquals("Unexpected nonZeroElements of odd.", nonZeroesOddExp, nonZeroesEvenVec.nonZeroElements()); + + nonZeroesActual = 0; + + for (Vector.Element e : nonZeroesOddVec.nonZeroes()) { + final int idx = e.index(); + + final boolean odd = (idx & 1) == 1; + + final double val = e.get(); + + assertTrue("Not an odd index " + idx + ", for value " + val, odd); + + assertTrue("Zero value " + val + " at even index " + idx, !isZero(val)); + + nonZeroesActual++; + } + + final int nonZeroesEvenExp = size / 2; + + assertEquals("Unexpected num of iterated even non-zeroes", nonZeroesEvenExp, nonZeroesActual); + + assertEquals("Unexpected nonZeroElements of even", nonZeroesEvenExp, nonZeroesOddVec.nonZeroElements()); + } + + /** */ + @Test + public void nonZeroesTest() { + // todo make RandomVector constructor that accepts a function and use it here + // in order to *reliably* test non-zeroes in there + consumeSampleVectors( + (v, desc) -> consumeSampleVectorsWithZeroes(v, (vec, numZeroes) + -> { + int numZeroesActual = vec.size(); + + for (Vector.Element e : vec.nonZeroes()) { + numZeroesActual--; + + assertTrue("Unexpected zero at " + desc + ", index " + e.index(), !isZero(e.get())); + } + + assertEquals("Unexpected num zeroes at " + desc, (int)numZeroes, numZeroesActual); + })); + } + + /** */ + @Test + public void nonZeroesTestBound() { + consumeSampleVectors( + (v, desc) -> consumeSampleVectorsWithZeroes(v, (vec, numZeroes) + -> iteratorTestBound(vec.nonZeroes().iterator(), desc))); + } + + /** */ + @Test + public void nonZeroElementsTest() { + consumeSampleVectors( + (v, desc) -> consumeSampleVectorsWithZeroes(v, (vec, numZeroes) + -> assertEquals("Unexpected num zeroes at " + desc, + (int)numZeroes, vec.size() - vec.nonZeroElements()))); + } + + /** */ + @Test + public void allSpliteratorTest() { + consumeSampleVectors( + (v, desc) -> { + final String desc1 = " " + desc; + + Spliterator spliterator = v.allSpliterator(); + + assertNotNull(MathTestConstants.NULL_VAL + desc1, spliterator); + + assertNull(MathTestConstants.NOT_NULL_VAL + desc1, spliterator.trySplit()); + + assertTrue(MathTestConstants.UNEXPECTED_VAL + desc1, spliterator.hasCharacteristics(ORDERED | SIZED)); + + if (!readOnly(v)) + fillWithNonZeroes(v); + + spliterator = v.allSpliterator(); + + assertNotNull(MathTestConstants.NULL_VAL + desc1, spliterator); + + assertEquals(MathTestConstants.VAL_NOT_EQUALS + desc1, spliterator.estimateSize(), v.size()); + + assertEquals(MathTestConstants.VAL_NOT_EQUALS + desc1, spliterator.getExactSizeIfKnown(), v.size()); + + assertTrue(MathTestConstants.UNEXPECTED_VAL + desc1, spliterator.hasCharacteristics(ORDERED | SIZED)); + + Spliterator secondHalf = spliterator.trySplit(); + + assertNull(MathTestConstants.NOT_NULL_VAL + desc1, secondHalf); + + spliterator.tryAdvance(x -> { + }); + } + ); + } + + /** */ + @Test + public void nonZeroSpliteratorTest() { + consumeSampleVectors( + (v, desc) -> consumeSampleVectorsWithZeroes(v, (vec, numZeroes) + -> { + final String desc1 = " Num zeroes " + numZeroes + " " + desc; + + Spliterator spliterator = vec.nonZeroSpliterator(); + + assertNotNull(MathTestConstants.NULL_VAL + desc1, spliterator); + + assertNull(MathTestConstants.NOT_NULL_VAL + desc1, spliterator.trySplit()); + + assertTrue(MathTestConstants.UNEXPECTED_VAL + desc1, spliterator.hasCharacteristics(ORDERED | SIZED)); + + spliterator = vec.nonZeroSpliterator(); + + assertNotNull(MathTestConstants.NULL_VAL + desc1, spliterator); + + assertEquals(MathTestConstants.VAL_NOT_EQUALS + desc1, spliterator.estimateSize(), vec.size() - numZeroes); + + assertEquals(MathTestConstants.VAL_NOT_EQUALS + desc1, spliterator.getExactSizeIfKnown(), vec.size() - numZeroes); + + assertTrue(MathTestConstants.UNEXPECTED_VAL + desc1, spliterator.hasCharacteristics(ORDERED | SIZED)); + + Spliterator secondHalf = spliterator.trySplit(); + + assertNull(MathTestConstants.NOT_NULL_VAL + desc1, secondHalf); + + double[] data = new double[vec.size()]; + + for (Vector.Element e : vec.all()) + data[e.index()] = e.get(); + + spliterator = vec.nonZeroSpliterator(); + + assertNotNull(MathTestConstants.NULL_VAL + desc1, spliterator); + + assertEquals(MathTestConstants.VAL_NOT_EQUALS + desc1, spliterator.estimateSize(), + Arrays.stream(data).filter(x -> x != 0d).count()); + + assertEquals(MathTestConstants.VAL_NOT_EQUALS + desc1, spliterator.getExactSizeIfKnown(), + Arrays.stream(data).filter(x -> x != 0d).count()); + + assertTrue(MathTestConstants.UNEXPECTED_VAL + desc1, spliterator.hasCharacteristics(ORDERED | SIZED)); + + secondHalf = spliterator.trySplit(); + + assertNull(MathTestConstants.NOT_NULL_VAL + desc1, secondHalf); + + if (!spliterator.tryAdvance(x -> { + })) + fail(MathTestConstants.NO_NEXT_ELEMENT + desc1); + })); + } + + /** */ + private void iteratorTestBound(Iterator it, String desc) { + while (it.hasNext()) + assertNotNull(it.next()); + + boolean expECaught = false; + + try { + it.next(); + } + catch (NoSuchElementException e) { + expECaught = true; + } + + assertTrue("Expected exception missed for " + desc, + expECaught); + } + + /** */ + private void consumeSampleVectorsWithZeroes(Vector sample, + BiConsumer consumer) { + if (readOnly(sample)) { + int numZeroes = 0; + + for (Vector.Element e : sample.all()) + if (isZero(e.get())) + numZeroes++; + + consumer.accept(sample, numZeroes); + + return; + } + + fillWithNonZeroes(sample); + + consumer.accept(sample, 0); + + final int sampleSize = sample.size(); + + if (sampleSize == 0) + return; + + for (Vector.Element e : sample.all()) + e.set(0); + + consumer.accept(sample, sampleSize); + + fillWithNonZeroes(sample); + + for (int testIdx : new int[] {0, sampleSize / 2, sampleSize - 1}) { + final Vector.Element e = sample.getElement(testIdx); + + final double backup = e.get(); + + e.set(0); + + consumer.accept(sample, 1); + + e.set(backup); + } + + if (sampleSize < 3) + return; + + sample.getElement(sampleSize / 3).set(0); + + sample.getElement((2 * sampleSize) / 3).set(0); + + consumer.accept(sample, 2); + } + + /** */ + private void fillWithNonZeroes(Vector sample) { + int idx = 0; + + for (Vector.Element e : sample.all()) + e.set(1 + idx++); + + assertEquals("Not all filled with non-zeroes", idx, sample.size()); + } + + /** */ + private void consumeSampleVectors(BiConsumer consumer) { + new VectorImplementationsFixtures().consumeSampleVectors(null, consumer); + } + + /** */ + private boolean isZero(double val) { + return val == 0.0; + } + + /** */ + private boolean readOnly(Vector v) { + return v instanceof RandomVector || v instanceof ConstantVector; + } +} + http://git-wip-us.apache.org/repos/asf/ignite/blob/acd21fb8/modules/math/src/test/java/org/apache/ignite/math/impls/vector/VectorNormTest.java ---------------------------------------------------------------------- diff --git a/modules/math/src/test/java/org/apache/ignite/math/impls/vector/VectorNormTest.java b/modules/math/src/test/java/org/apache/ignite/math/impls/vector/VectorNormTest.java new file mode 100644 index 0000000..f1c2928 --- /dev/null +++ b/modules/math/src/test/java/org/apache/ignite/math/impls/vector/VectorNormTest.java @@ -0,0 +1,247 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.math.impls.vector; + +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Function; +import org.apache.ignite.math.Vector; +import org.junit.Test; + +import static org.junit.Assert.assertTrue; + +/** */ +public class VectorNormTest { + /** */ + @Test + public void normalizeTest() { + normalizeTest(2, (val, len) -> val / len, Vector::normalize); + } + + /** */ + @Test + public void normalizePowerTest() { + for (double pow : new double[] {0, 0.5, 1, 2, 2.5, Double.POSITIVE_INFINITY}) + normalizeTest(pow, (val, norm) -> val / norm, (v) -> v.normalize(pow)); + } + + /** */ + @Test + public void logNormalizeTest() { + normalizeTest(2, (val, len) -> Math.log1p(val) / (len * Math.log(2)), Vector::logNormalize); + } + + /** */ + @Test + public void logNormalizePowerTest() { + for (double pow : new double[] {1.1, 2, 2.5}) + normalizeTest(pow, (val, norm) -> Math.log1p(val) / (norm * Math.log(pow)), (v) -> v.logNormalize(pow)); + } + + /** */ + @Test + public void kNormTest() { + for (double pow : new double[] {0, 0.5, 1, 2, 2.5, Double.POSITIVE_INFINITY}) + toDoubleTest(pow, ref -> new Norm(ref, pow).calculate(), v -> v.kNorm(pow)); + } + + /** */ + @Test + public void getLengthSquaredTest() { + toDoubleTest(2.0, ref -> new Norm(ref, 2).sumPowers(), Vector::getLengthSquared); + } + + /** */ + @Test + public void getDistanceSquaredTest() { + consumeSampleVectors((v, desc) -> { + new VectorImplementationsTest.ElementsChecker(v, desc); // IMPL NOTE this initialises vector + + final int size = v.size(); + final Vector vOnHeap = new DenseLocalOnHeapVector(size); + final Vector vOffHeap = new DenseLocalOffHeapVector(size); + + invertValues(v, vOnHeap); + invertValues(v, vOffHeap); + + for (int idx = 0; idx < size; idx++) { + final double exp = v.get(idx); + final int idxMirror = size - 1 - idx; + + assertTrue("On heap vector difference at " + desc + ", idx " + idx, + exp - vOnHeap.get(idxMirror) == 0); + assertTrue("Off heap vector difference at " + desc + ", idx " + idx, + exp - vOffHeap.get(idxMirror) == 0); + } + + final double exp = vOnHeap.minus(v).getLengthSquared(); // IMPL NOTE this won't mutate vOnHeap + final VectorImplementationsTest.Metric metric = new VectorImplementationsTest.Metric(exp, v.getDistanceSquared(vOnHeap)); + + assertTrue("On heap vector not close enough at " + desc + ", " + metric, + metric.closeEnough()); + + final VectorImplementationsTest.Metric metric1 = new VectorImplementationsTest.Metric(exp, v.getDistanceSquared(vOffHeap)); + + assertTrue("Off heap vector not close enough at " + desc + ", " + metric1, + metric1.closeEnough()); + }); + } + + /** */ + @Test + public void dotTest() { + consumeSampleVectors((v, desc) -> { + new VectorImplementationsTest.ElementsChecker(v, desc); // IMPL NOTE this initialises vector + + final int size = v.size(); + final Vector v1 = new DenseLocalOnHeapVector(size); + + invertValues(v, v1); + + final double actual = v.dot(v1); + + double exp = 0; + + for (Vector.Element e : v.all()) + exp += e.get() * v1.get(e.index()); + + final VectorImplementationsTest.Metric metric = new VectorImplementationsTest.Metric(exp, actual); + + assertTrue("Dot product not close enough at " + desc + ", " + metric, + metric.closeEnough()); + }); + } + + /** */ + private void invertValues(Vector src, Vector dst) { + final int size = src.size(); + + for (Vector.Element e : src.all()) { + final int idx = size - 1 - e.index(); + final double val = e.get(); + + dst.set(idx, val); + } + } + + /** */ + private void toDoubleTest(Double val, Function calcRef, Function 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 double exp = calcRef.apply(ref); + final double obtained = calcVec.apply(v); + final VectorImplementationsTest.Metric metric = new VectorImplementationsTest.Metric(exp, obtained); + + assertTrue("Not close enough at " + desc + + (val == null ? "" : ", value " + val) + ", " + metric, metric.closeEnough()); + }); + } + + /** */ + private void normalizeTest(double pow, BiFunction operation, + Function vecOperation) { + consumeSampleVectors((v, desc) -> { + final int size = v.size(); + final double[] ref = new double[size]; + final boolean nonNegative = pow != (int)pow; + + final VectorImplementationsTest.ElementsChecker checker = new VectorImplementationsTest.ElementsChecker(v, ref, desc + ", pow = " + pow, nonNegative); + final double norm = new Norm(ref, pow).calculate(); + + for (int idx = 0; idx < size; idx++) + ref[idx] = operation.apply(ref[idx], norm); + + checker.assertCloseEnough(vecOperation.apply(v), ref); + }); + } + + /** */ + private void consumeSampleVectors(BiConsumer consumer) { + new VectorImplementationsFixtures().consumeSampleVectors(null, consumer); + } + + /** */ + private static class Norm { + /** */ + private final double[] arr; + + /** */ + private final Double pow; + + /** */ + Norm(double[] arr, double pow) { + this.arr = arr; + this.pow = pow; + } + + /** */ + double calculate() { + if (pow.equals(0.0)) + return countNonZeroes(); // IMPL NOTE this is beautiful if you think of it + + if (pow.equals(Double.POSITIVE_INFINITY)) + return maxAbs(); + + return Math.pow(sumPowers(), 1 / pow); + } + + /** */ + double sumPowers() { + if (pow.equals(0.0)) + return countNonZeroes(); + + double norm = 0; + + for (double val : arr) + norm += pow == 1 ? Math.abs(val) : Math.pow(val, pow); + + return norm; + } + + /** */ + private int countNonZeroes() { + int cnt = 0; + + final Double zero = 0.0; + + for (double val : arr) + if (!zero.equals(val)) + cnt++; + + return cnt; + } + + /** */ + private double maxAbs() { + double res = 0; + + for (double val : arr) { + final double abs = Math.abs(val); + + if (abs > res) + res = abs; + } + + return res; + } + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/acd21fb8/modules/math/src/test/java/org/apache/ignite/math/impls/vector/VectorToMatrixTest.java ---------------------------------------------------------------------- diff --git a/modules/math/src/test/java/org/apache/ignite/math/impls/vector/VectorToMatrixTest.java b/modules/math/src/test/java/org/apache/ignite/math/impls/vector/VectorToMatrixTest.java new file mode 100644 index 0000000..adcb2cc --- /dev/null +++ b/modules/math/src/test/java/org/apache/ignite/math/impls/vector/VectorToMatrixTest.java @@ -0,0 +1,291 @@ +package org.apache.ignite.math.impls.vector; + +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; +import org.apache.ignite.math.Matrix; +import org.apache.ignite.math.Vector; +import org.apache.ignite.math.exceptions.UnsupportedOperationException; +import org.apache.ignite.math.impls.matrix.DenseLocalOffHeapMatrix; +import org.apache.ignite.math.impls.matrix.DenseLocalOnHeapMatrix; +import org.apache.ignite.math.impls.matrix.RandomMatrix; +import org.apache.ignite.math.impls.matrix.SparseLocalOnHeapMatrix; +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; + +/** Tests for methods of Vector that involve Matrix. */ +public class VectorToMatrixTest { + /** */ + private static final Map, Class> typesMap = typesMap(); + + /** */ + private static final List> likeMatrixUnsupported = Arrays.asList(FunctionVector.class, + SingleElementVector.class, SingleElementVectorView.class, ConstantVector.class); + + /** */ + @Test + public void testHaveLikeMatrix() throws InstantiationException, IllegalAccessException { + for (Class key : typesMap.keySet()) { + Class val = typesMap.get(key); + + if (val == null && likeMatrixSupported(key)) + System.out.println("Missing test for implementation of likeMatrix for " + key.getSimpleName()); + } + } + + /** */ + @Test + public void testLikeMatrixUnsupported() throws Exception { + consumeSampleVectors((v, desc) -> { + if (likeMatrixSupported(v.getClass())) + return; + + boolean expECaught = false; + + try { + assertNull("Null view instead of exception in " + desc, v.likeMatrix(1, 1)); + } + catch (UnsupportedOperationException uoe) { + expECaught = true; + } + + assertTrue("Expected exception was not caught in " + desc, expECaught); + }); + } + + /** */ + @Test + public void testLikeMatrix() { + consumeSampleVectors((v, desc) -> { + if (!availableForTesting(v)) + return; + + final Matrix matrix = v.likeMatrix(1, 1); + + Class key = v.getClass(); + + Class expMatrixType = typesMap.get(key); + + assertNotNull("Expect non-null matrix for " + key.getSimpleName() + " in " + desc, matrix); + + Class actualMatrixType = matrix.getClass(); + + assertTrue("Expected matrix type " + expMatrixType.getSimpleName() + + " should be assignable from actual type " + actualMatrixType.getSimpleName() + " in " + desc, + expMatrixType.isAssignableFrom(actualMatrixType)); + + for (int rows : new int[] {1, 2}) + for (int cols : new int[] {1, 2}) { + final Matrix actualMatrix = v.likeMatrix(rows, cols); + + String details = "rows " + rows + " cols " + cols; + + assertNotNull("Expect non-null matrix for " + details + " in " + desc, + actualMatrix); + + assertEquals("Unexpected number of rows in " + desc, rows, actualMatrix.rowSize()); + + assertEquals("Unexpected number of cols in " + desc, cols, actualMatrix.columnSize()); + } + }); + } + + /** */ + @Test + public void testToMatrix() { + consumeSampleVectors((v, desc) -> { + if (!availableForTesting(v)) + return; + + fillWithNonZeroes(v); + + final Matrix matrixRow = v.toMatrix(true); + + final Matrix matrixCol = v.toMatrix(false); + + for (Vector.Element e : v.all()) + assertToMatrixValue(desc, matrixRow, matrixCol, e.get(), e.index()); + }); + } + + /** */ + @Test + public void testToMatrixPlusOne() { + consumeSampleVectors((v, desc) -> { + if (!availableForTesting(v)) + return; + + fillWithNonZeroes(v); + + for (double zeroVal : new double[] {-1, 0, 1, 2}) { + final Matrix matrixRow = v.toMatrixPlusOne(true, zeroVal); + + final Matrix matrixCol = v.toMatrixPlusOne(false, zeroVal); + + final Metric metricRow0 = new Metric(zeroVal, matrixRow.get(0, 0)); + + assertTrue("Not close enough row like " + metricRow0 + " at index 0 in " + desc, + metricRow0.closeEnough()); + + final Metric metricCol0 = new Metric(zeroVal, matrixCol.get(0, 0)); + + assertTrue("Not close enough cols like " + metricCol0 + " at index 0 in " + desc, + metricCol0.closeEnough()); + + for (Vector.Element e : v.all()) + assertToMatrixValue(desc, matrixRow, matrixCol, e.get(), e.index() + 1); + } + }); + } + + /** */ + @Test + public void testCross() { + consumeSampleVectors((v, desc) -> { + if (!availableForTesting(v)) + return; + + fillWithNonZeroes(v); + + for (int delta : new int[] {-1, 0, 1}) { + final int size2 = v.size() + delta; + + if (size2 < 1) + return; + + final Vector v2 = new DenseLocalOnHeapVector(size2); + + for (Vector.Element e : v2.all()) + e.set(size2 - e.index()); + + assertCross(v, v2, desc); + } + }); + } + + /** */ + private void assertCross(Vector v1, Vector v2, String desc) { + assertNotNull(v1); + assertNotNull(v2); + + final Matrix res = v1.cross(v2); + + assertNotNull("Cross matrix is expected to be not null in " + desc, res); + + assertEquals("Unexpected number of rows in cross Matrix in " + desc, v1.size(), res.rowSize()); + + assertEquals("Unexpected number of cols in cross Matrix in " + desc, v2.size(), res.columnSize()); + + for (int row = 0; row < v1.size(); row++) + for (int col = 0; col < v2.size(); col++) { + final Metric metric = new Metric(v1.get(row) * v2.get(col), res.get(row, col)); + + assertTrue("Not close enough cross " + metric + " at row " + row + " at col " + col + + " in " + desc, metric.closeEnough()); + } + } + + /** */ + private void assertToMatrixValue(String desc, Matrix matrixRow, Matrix matrixCol, double exp, int idx) { + final Metric metricRow = new Metric(exp, matrixRow.get(0, idx)); + + assertTrue("Not close enough row like " + metricRow + " at index " + idx + " in " + desc, + metricRow.closeEnough()); + + final Metric metricCol = new Metric(exp, matrixCol.get(idx, 0)); + + assertTrue("Not close enough cols like " + matrixCol + " at index " + idx + " in " + desc, + metricCol.closeEnough()); + } + + /** */ + private void fillWithNonZeroes(Vector sample) { + if (sample instanceof RandomVector) + return; + + for (Vector.Element e : sample.all()) + e.set(1 + e.index()); + } + + /** */ + private boolean availableForTesting(Vector v) { + assertNotNull("Error in test: vector is null", v); + + if (!likeMatrixSupported(v.getClass())) + return false; + + final boolean availableForTesting = typesMap.get(v.getClass()) != null; + + final Matrix actualLikeMatrix = v.likeMatrix(1, 1); + + assertTrue("Need to enable matrix testing for vector type " + v.getClass().getSimpleName(), + availableForTesting || actualLikeMatrix == null); + + return availableForTesting; + } + + /** Ignore test for given vector type. */ + private boolean likeMatrixSupported(Class clazz) { + for (Class ignoredClass : likeMatrixUnsupported) + if (ignoredClass.isAssignableFrom(clazz)) + return false; + + return true; + } + + /** */ + private void consumeSampleVectors(BiConsumer consumer) { + new VectorImplementationsFixtures().consumeSampleVectors(null, consumer); + } + + /** */ + private static Map, Class> typesMap() { + return new LinkedHashMap, Class>() {{ + put(DenseLocalOnHeapVector.class, DenseLocalOnHeapMatrix.class); + put(DenseLocalOffHeapVector.class, DenseLocalOffHeapMatrix.class); + put(RandomVector.class, RandomMatrix.class); + put(SparseLocalVector.class, SparseLocalOnHeapMatrix.class); + put(SingleElementVector.class, null); // todo find out if we need SingleElementMatrix to match, or skip it + put(ConstantVector.class, null); + put(FunctionVector.class, null); + put(PivotedVectorView.class, DenseLocalOnHeapMatrix.class); // IMPL NOTE per fixture + put(SingleElementVectorView.class, null); + put(MatrixVectorView.class, DenseLocalOnHeapMatrix.class); // IMPL NOTE per fixture + put(DelegatingVector.class, DenseLocalOnHeapMatrix.class); // IMPL NOTE per fixture + // IMPL NOTE check for presence of all implementations here will be done in testHaveLikeMatrix via Fixture + }}; + } + + /** */ + private static class Metric { // todo consider if softer tolerance (like say 0.1 or 0.01) would make sense here + /** */ + private final double exp; + + /** */ + private final double obtained; + + /** **/ + Metric(double exp, double obtained) { + this.exp = exp; + this.obtained = obtained; + } + + /** */ + boolean closeEnough() { + return new Double(exp).equals(obtained); + } + + /** {@inheritDoc} */ + @Override public String toString() { + return "Metric{" + "expected=" + exp + + ", obtained=" + obtained + + '}'; + } + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/acd21fb8/modules/math/src/test/java/org/apache/ignite/math/impls/vector/VectorViewTest.java ---------------------------------------------------------------------- diff --git a/modules/math/src/test/java/org/apache/ignite/math/impls/vector/VectorViewTest.java b/modules/math/src/test/java/org/apache/ignite/math/impls/vector/VectorViewTest.java new file mode 100644 index 0000000..55893d0 --- /dev/null +++ b/modules/math/src/test/java/org/apache/ignite/math/impls/vector/VectorViewTest.java @@ -0,0 +1,162 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.math.impls.vector; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.function.BiConsumer; +import java.util.stream.IntStream; +import org.apache.ignite.math.Vector; +import org.apache.ignite.math.exceptions.UnsupportedOperationException; +import org.apache.ignite.math.impls.MathTestConstants; +import org.junit.AfterClass; +import org.junit.Before; +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; +import static org.junit.Assert.fail; + +/** + * Unit tests for {@link VectorView}. + */ +public class VectorViewTest { + /** */ + private static final int OFFSET = 10; + + /** */ + private static final int VIEW_LENGTH = 80; + + /** */ + private static final String EXTERNALIZE_TEST_FILE_NAME = "externalizeTest"; + + /** */ + private VectorView testVector; + + /** */ + private DenseLocalOnHeapVector parentVector; + + /** */ + private double[] parentData; + + /** */ + @Before + public void setup() { + parentVector = new DenseLocalOnHeapVector(MathTestConstants.STORAGE_SIZE); + + IntStream.range(0, MathTestConstants.STORAGE_SIZE).forEach(idx -> parentVector.set(idx, Math.random())); + + parentData = parentVector.getStorage().data().clone(); + + testVector = new VectorView(parentVector, OFFSET, VIEW_LENGTH); + } + + /** */ + @AfterClass + public static void cleanup() throws IOException { + Files.deleteIfExists(Paths.get(EXTERNALIZE_TEST_FILE_NAME)); + } + + /** */ + @Test + public void testCopy() throws Exception { + Vector cp = testVector.copy(); + + assertTrue(MathTestConstants.VAL_NOT_EQUALS, cp.equals(testVector)); + } + + /** */ + @Test(expected = org.apache.ignite.math.exceptions.UnsupportedOperationException.class) + public void testLike() throws Exception { + for (int card : new int[] {1, 2, 4, 8, 16, 32, 64, 128}) + consumeSampleVectors((v, desc) -> { + Vector vLike = new VectorView(v, 0, 1).like(card); + + Class expType = v.getClass(); + + assertNotNull("Expect non-null like vector for " + expType.getSimpleName() + " in " + desc, vLike); + + assertEquals("Expect size equal to cardinality at " + desc, card, vLike.size()); + + Class actualType = vLike.getClass(); + + assertTrue("Expected matrix type " + expType.getSimpleName() + + " should be assignable from actual type " + actualType.getSimpleName() + " in " + desc, + expType.isAssignableFrom(actualType)); + + }); + } + + /** See also {@link VectorToMatrixTest#testLikeMatrix()}. */ + @Test + public void testLikeMatrix() { + consumeSampleVectors((v, desc) -> { + boolean expECaught = false; + + try { + assertNull("Null view instead of exception in " + desc, new VectorView(v, 0, 1).likeMatrix(1, 1)); + } + catch (UnsupportedOperationException uoe) { + expECaught = true; + } + + assertTrue("Expected exception was not caught in " + desc, expECaught); + }); + } + + /** */ + @Test + public void testWriteReadExternal() throws Exception { + assertNotNull("Unexpected null parent data", parentData); + + File f = new File(EXTERNALIZE_TEST_FILE_NAME); + + try { + ObjectOutputStream objOutputStream = new ObjectOutputStream(new FileOutputStream(f)); + + objOutputStream.writeObject(testVector); + + objOutputStream.close(); + + ObjectInputStream objInputStream = new ObjectInputStream(new FileInputStream(f)); + + VectorView readVector = (VectorView)objInputStream.readObject(); + + objInputStream.close(); + + assertTrue(MathTestConstants.VAL_NOT_EQUALS, testVector.equals(readVector)); + } + catch (ClassNotFoundException | IOException e) { + fail(e.getMessage()); + } + } + + /** */ + private void consumeSampleVectors(BiConsumer consumer) { + new VectorImplementationsFixtures().consumeSampleVectors(null, consumer); + } + +} http://git-wip-us.apache.org/repos/asf/ignite/blob/acd21fb8/pom.xml ---------------------------------------------------------------------- diff --git a/pom.xml b/pom.xml index 5f2cb7c..9e9d877 100644 --- a/pom.xml +++ b/pom.xml @@ -933,6 +933,13 @@ + + + math + + modules/math + +