IGNITE-7332: Add test suite for ML tests this closes #3316
Project: http://git-wip-us.apache.org/repos/asf/ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/7994de48 Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/7994de48 Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/7994de48 Branch: refs/heads/ignite-zk Commit: 7994de48b8157d095b72dc075e7976aa08b36071 Parents: fe87476 Author: dmitrievanthony <[email protected]> Authored: Fri Jan 12 12:17:22 2018 +0300 Committer: YuriBabak <[email protected]> Committed: Fri Jan 12 12:17:22 2018 +0300 ---------------------------------------------------------------------- examples/pom.xml | 11 ++ .../examples/ml/math/tracer/TracerExample.java | 6 +- .../testsuites/IgniteExamplesMLTestSuite.java | 140 +++++++++++++++++++ .../java/org/apache/ignite/ml/math/Tracer.java | 118 ++++++++++++---- .../org/apache/ignite/ml/math/TracerTest.java | 50 +++++-- 5 files changed, 282 insertions(+), 43 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ignite/blob/7994de48/examples/pom.xml ---------------------------------------------------------------------- diff --git a/examples/pom.xml b/examples/pom.xml index 9c3c360..7ae0187 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -98,6 +98,7 @@ <lgpl.test.folder>src/test/java</lgpl.test.folder> <java8.test.folder>src/test/java</java8.test.folder> <ml.folder>src/main/java</ml.folder> + <ml.test.folder>src/test/java</ml.test.folder> </properties> <profiles> @@ -221,6 +222,7 @@ <properties> <ml.folder>src/main/ml</ml.folder> + <ml.test.folder>src/test/ml</ml.test.folder> <java.ver>1.8</java.ver> </properties> @@ -230,11 +232,19 @@ <artifactId>ignite-ml</artifactId> <version>${project.version}</version> </dependency> + <dependency> <groupId>commons-cli</groupId> <artifactId>commons-cli</artifactId> <version>1.2</version> </dependency> + + <dependency> + <groupId>org.javassist</groupId> + <artifactId>javassist</artifactId> + <version>${javassist.version}</version> + <scope>test</scope> + </dependency> </dependencies> </profile> @@ -313,6 +323,7 @@ <source>${lgpl.test.folder}</source> <source>${spark.test.folder}</source> <source>${java8.test.folder}</source> + <source>${ml.test.folder}</source> </sources> </configuration> </execution> http://git-wip-us.apache.org/repos/asf/ignite/blob/7994de48/examples/src/main/ml/org/apache/ignite/examples/ml/math/tracer/TracerExample.java ---------------------------------------------------------------------- diff --git a/examples/src/main/ml/org/apache/ignite/examples/ml/math/tracer/TracerExample.java b/examples/src/main/ml/org/apache/ignite/examples/ml/math/tracer/TracerExample.java index 305f4b9..c14335a 100644 --- a/examples/src/main/ml/org/apache/ignite/examples/ml/math/tracer/TracerExample.java +++ b/examples/src/main/ml/org/apache/ignite/examples/ml/math/tracer/TracerExample.java @@ -30,9 +30,9 @@ public class TracerExample { * Double to color mapper example. */ private static final Tracer.ColorMapper COLOR_MAPPER = d -> { - if (d <= 0.33) + if (d <= 1.5) return Color.RED; - else if (d <= 0.66) + else if (d <= 2.5) return Color.GREEN; else return Color.BLUE; @@ -56,7 +56,7 @@ public class TracerExample { Tracer.showAscii(m, "%.3g"); System.out.println("\n>>> Tracer output to browser in HTML."); - Tracer.showHtml(m, COLOR_MAPPER); + Tracer.showHtml(m, COLOR_MAPPER, true); System.out.println("\n>>> Tracer utility example completed."); } http://git-wip-us.apache.org/repos/asf/ignite/blob/7994de48/examples/src/test/ml/org/apache/ignite/ml/testsuites/IgniteExamplesMLTestSuite.java ---------------------------------------------------------------------- diff --git a/examples/src/test/ml/org/apache/ignite/ml/testsuites/IgniteExamplesMLTestSuite.java b/examples/src/test/ml/org/apache/ignite/ml/testsuites/IgniteExamplesMLTestSuite.java new file mode 100644 index 0000000..bc917b9 --- /dev/null +++ b/examples/src/test/ml/org/apache/ignite/ml/testsuites/IgniteExamplesMLTestSuite.java @@ -0,0 +1,140 @@ +/* + * 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.testsuites; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import javassist.CannotCompileException; +import javassist.ClassClassPath; +import javassist.ClassPool; +import javassist.CtClass; +import javassist.CtNewMethod; +import javassist.NotFoundException; +import junit.framework.TestSuite; +import org.apache.ignite.testframework.GridTestUtils; +import org.apache.ignite.testframework.junits.common.GridAbstractExamplesTest; + +import static org.apache.ignite.IgniteSystemProperties.IGNITE_OVERRIDE_MCAST_GRP; + +/** + * Examples test suite. + * <p> + * Contains only Spring ignite examples tests. + */ +public class IgniteExamplesMLTestSuite extends TestSuite { + /** + * @return Suite. + * @throws Exception If failed. + */ + public static TestSuite suite() throws Exception { + System.setProperty(IGNITE_OVERRIDE_MCAST_GRP, + GridTestUtils.getNextMulticastGroup(IgniteExamplesMLTestSuite.class)); + + TestSuite suite = new TestSuite("Ignite ML Examples Test Suite"); + + for (Class clazz : getClasses("org.apache.ignite.examples.ml", ".*Example$")) + suite.addTest(new TestSuite(makeTestClass(clazz, "org.apache.ignite.ml.examples"))); + + return suite; + } + + /** + * Creates test class for given example. + * + * @param exampleCls Class of the example to be tested + * @param basePkgForTests Base package to create test classes in + * @return Test class + * @throws NotFoundException if class not found + * @throws CannotCompileException if test class cannot be compiled + */ + private static Class makeTestClass(Class<?> exampleCls, String basePkgForTests) + throws NotFoundException, CannotCompileException { + ClassPool cp = ClassPool.getDefault(); + cp.insertClassPath(new ClassClassPath(IgniteExamplesMLTestSuite.class)); + + CtClass cl = cp.makeClass(basePkgForTests + "." + exampleCls.getSimpleName() + "SelfName"); + + cl.setSuperclass(cp.get(GridAbstractExamplesTest.class.getName())); + + cl.addMethod(CtNewMethod.make("public void testExample() { " + + exampleCls.getCanonicalName() + + ".main(" + + GridAbstractExamplesTest.class.getName() + + ".EMPTY_ARGS); }", cl)); + + return cl.toClass(); + } + + /** + * Scans all classes accessible from the context class loader which belong to the given package and subpackages. + * + * @param pkgName The base package + * @return The classes + * @throws ClassNotFoundException if some classes not found + * @throws IOException if some resources unavailable + */ + private static List<Class> getClasses(String pkgName, String clsNamePtrn) throws ClassNotFoundException, IOException { + String path = pkgName.replace('.', '/'); + + Enumeration<URL> resources = Thread.currentThread() + .getContextClassLoader() + .getResources(path); + + List<File> dirs = new ArrayList<>(); + while (resources.hasMoreElements()) + dirs.add(new File(resources.nextElement().getFile())); + + List<Class> classes = new ArrayList<>(); + for (File directory : dirs) + classes.addAll(findClasses(directory, pkgName, clsNamePtrn)); + + return classes; + } + + /** + * Recursive method used to find all classes in a given directory and subdirs. + * + * @param dir The base directory + * @param pkgName The package name for classes found inside the base directory + * @return The classes + * @throws ClassNotFoundException if class not found + */ + private static List<Class> findClasses(File dir, String pkgName, String clsNamePtrn) throws ClassNotFoundException { + List<Class> classes = new ArrayList<>(); + if (!dir.exists()) + return classes; + + File[] files = dir.listFiles(); + if (files != null) + for (File file : files) { + if (file.isDirectory()) + classes.addAll(findClasses(file, pkgName + "." + file.getName(), clsNamePtrn)); + else if (file.getName().endsWith(".class")) { + String clsName = pkgName + '.' + file.getName().substring(0, file.getName().length() - 6); + if (clsName.matches(clsNamePtrn)) + classes.add(Class.forName(clsName)); + } + } + + return classes; + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/7994de48/modules/ml/src/main/java/org/apache/ignite/ml/math/Tracer.java ---------------------------------------------------------------------- diff --git a/modules/ml/src/main/java/org/apache/ignite/ml/math/Tracer.java b/modules/ml/src/main/java/org/apache/ignite/ml/math/Tracer.java index 1928f46..b73ee3e 100644 --- a/modules/ml/src/main/java/org/apache/ignite/ml/math/Tracer.java +++ b/modules/ml/src/main/java/org/apache/ignite/ml/math/Tracer.java @@ -57,6 +57,7 @@ public class Tracer { return new ColorMapper() { /** {@inheritDoc} */ @Override public Color apply(Double d) { + d = (d - min) / range; int r = (int)Math.round(255 * d); int g = 0; int b = (int)Math.round(255 * (1 - d)); @@ -224,7 +225,18 @@ public class Tracer { * @throws IOException Thrown in case of any errors. */ public static void showHtml(Matrix mtx) throws IOException { - showHtml(mtx, mkMatrixColorMapper(mtx)); + showHtml(mtx, false); + } + + /** + * Shows given matrix in the browser with D3-based visualization. + * + * @param mtx Matrix to show. + * @param useAsciiFallback Use ascii fallback is desktop or browser is unavailable. + * @throws IOException Thrown in case of any errors. + */ + public static void showHtml(Matrix mtx, boolean useAsciiFallback) throws IOException { + showHtml(mtx, mkMatrixColorMapper(mtx), useAsciiFallback); } /** @@ -235,20 +247,36 @@ public class Tracer { * @throws IOException Thrown in case of any errors. */ public static void showHtml(Matrix mtx, ColorMapper cm) throws IOException { - // Read it every time so that we can change it at runtime. - String tmpl = fileToString("d3-matrix-template.html"); - - String cls = mtx.getClass().getSimpleName(); - - double min = mtx.minValue(); - double max = mtx.maxValue(); + showHtml(mtx, cm, false); + } - openHtmlFile(tmpl. - replaceAll("/\\*@NAME@\\*/.*\n", "var name = \"" + cls + "\";\n"). - replaceAll("/\\*@MIN@\\*/.*\n", "var min = " + dataColorJson(min, cm.apply(min)) + ";\n"). - replaceAll("/\\*@MAX@\\*/.*\n", "var max = " + dataColorJson(max, cm.apply(max)) + ";\n"). - replaceAll("/\\*@DATA@\\*/.*\n", "var data = " + mkJsArrayString(mtx, cm) + ";\n") - ); + /** + * Shows given matrix in the browser with D3-based visualization. + * + * @param mtx Matrix to show. + * @param cm Optional color mapper. If not provided - red-to-blue (R_B) mapper will be used. + * @param useAsciiFallback Use ascii fallback is desktop or browser is unavailable. + * @throws IOException Thrown in case of any errors. + */ + public static void showHtml(Matrix mtx, ColorMapper cm, boolean useAsciiFallback) throws IOException { + if (!isBrowseSupported() && useAsciiFallback) + showAscii(mtx); + else { + // Read it every time so that we can change it at runtime. + String tmpl = fileToString("d3-matrix-template.html"); + + String cls = mtx.getClass().getSimpleName(); + + double min = mtx.minValue(); + double max = mtx.maxValue(); + + openHtmlFile(tmpl. + replaceAll("/\\*@NAME@\\*/.*\n", "var name = \"" + cls + "\";\n"). + replaceAll("/\\*@MIN@\\*/.*\n", "var min = " + dataColorJson(min, cm.apply(min)) + ";\n"). + replaceAll("/\\*@MAX@\\*/.*\n", "var max = " + dataColorJson(max, cm.apply(max)) + ";\n"). + replaceAll("/\\*@DATA@\\*/.*\n", "var data = " + mkJsArrayString(mtx, cm) + ";\n") + ); + } } /** @@ -258,7 +286,18 @@ public class Tracer { * @throws IOException Thrown in case of any errors. */ public static void showHtml(Vector vec) throws IOException { - showHtml(vec, mkVectorColorMapper(vec)); + showHtml(vec, false); + } + + /** + * Shows given vector in the browser with D3-based visualization. + * + * @param vec Vector to show. + * @param useAsciiFallback Use ascii fallback is desktop or browser is unavailable. + * @throws IOException Thrown in case of any errors. + */ + public static void showHtml(Vector vec, boolean useAsciiFallback) throws IOException { + showHtml(vec, mkVectorColorMapper(vec), useAsciiFallback); } /** @@ -283,20 +322,45 @@ public class Tracer { * @throws IOException Thrown in case of any errors. */ public static void showHtml(Vector vec, ColorMapper cm) throws IOException { - // Read it every time so that we can change it at runtime. - String tmpl = fileToString("d3-vector-template.html"); - - String cls = vec.getClass().getSimpleName(); + showHtml(vec, cm, false); + } - double min = vec.minValue(); - double max = vec.maxValue(); + /** + * Shows given vector in the browser with D3-based visualization. + * + * @param vec Vector to show. + * @param cm Optional color mapper. If not provided - red-to-blue (R_B) mapper will be used. + * @param useAsciiFallback Use ascii fallback is desktop or browser is unavailable. + * @throws IOException Thrown in case of any errors. + */ + public static void showHtml(Vector vec, ColorMapper cm, boolean useAsciiFallback) throws IOException { + if (!isBrowseSupported() && useAsciiFallback) + showAscii(vec); + else { + // Read it every time so that we can change it at runtime. + String tmpl = fileToString("d3-vector-template.html"); + + String cls = vec.getClass().getSimpleName(); + + double min = vec.minValue(); + double max = vec.maxValue(); + + openHtmlFile(tmpl. + replaceAll("/\\*@NAME@\\*/.*\n", "var name = \"" + cls + "\";\n"). + replaceAll("/\\*@MIN@\\*/.*\n", "var min = " + dataColorJson(min, cm.apply(min)) + ";\n"). + replaceAll("/\\*@MAX@\\*/.*\n", "var max = " + dataColorJson(max, cm.apply(max)) + ";\n"). + replaceAll("/\\*@DATA@\\*/.*\n", "var data = " + mkJsArrayString(vec, cm) + ";\n") + ); + } + } - openHtmlFile(tmpl. - replaceAll("/\\*@NAME@\\*/.*\n", "var name = \"" + cls + "\";\n"). - replaceAll("/\\*@MIN@\\*/.*\n", "var min = " + dataColorJson(min, cm.apply(min)) + ";\n"). - replaceAll("/\\*@MAX@\\*/.*\n", "var max = " + dataColorJson(max, cm.apply(max)) + ";\n"). - replaceAll("/\\*@DATA@\\*/.*\n", "var data = " + mkJsArrayString(vec, cm) + ";\n") - ); + /** + * Returns {@code true} if browse can be used (to show HTML for example), otherwise returns {@code false}. + * + * @return {@code true} if browse can be used (to show HTML for example), otherwise returns {@code false} + */ + private static boolean isBrowseSupported() { + return Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE); } /** http://git-wip-us.apache.org/repos/asf/ignite/blob/7994de48/modules/ml/src/test/java/org/apache/ignite/ml/math/TracerTest.java ---------------------------------------------------------------------- diff --git a/modules/ml/src/test/java/org/apache/ignite/ml/math/TracerTest.java b/modules/ml/src/test/java/org/apache/ignite/ml/math/TracerTest.java index 407f3a0..1a8534e 100644 --- a/modules/ml/src/test/java/org/apache/ignite/ml/math/TracerTest.java +++ b/modules/ml/src/test/java/org/apache/ignite/ml/math/TracerTest.java @@ -81,9 +81,7 @@ public class TracerTest { return mtx; } - /** - * - */ + /** */ @Test public void testAsciiVectorTracer() { Vector vec = makeRandomVector(20); @@ -93,9 +91,7 @@ public class TracerTest { Tracer.showAscii(vec, "%.3g"); } - /** - * - */ + /** */ @Test public void testAsciiMatrixTracer() { Matrix mtx = makeRandomMatrix(10, 10); @@ -105,9 +101,7 @@ public class TracerTest { Tracer.showAscii(mtx, "%.3g"); } - /** - * - */ + /** */ @Test @Ignore("Can not run on TeamCity yet, see IGNITE-5725") public void testHtmlVectorTracer() throws IOException { @@ -120,12 +114,10 @@ public class TracerTest { Tracer.showHtml(vec1, COLOR_MAPPER); // Default color mapping with sorted vector. - Tracer.showHtml(vec1.sort()); + Tracer.showHtml(vec1.copy().sort()); } - /** - * - */ + /** */ @Test @Ignore("Can not run on TeamCity yet, see IGNITE-5725") public void testHtmlMatrixTracer() throws IOException { @@ -145,6 +137,38 @@ public class TracerTest { /** */ @Test + public void testHtmlVectorTracerWithAsciiFallback() throws IOException { + Vector vec1 = makeRandomVector(1000); + + // Default color mapping. + Tracer.showHtml(vec1, true); + + // Custom color mapping. + Tracer.showHtml(vec1, COLOR_MAPPER, true); + + // Default color mapping with sorted vector. + Tracer.showHtml(vec1.copy().sort(), true); + } + + /** */ + @Test + public void testHtmlMatrixTracerWithAsciiFallback() throws IOException { + Matrix mtx1 = makeRandomMatrix(100, 100); + + // Custom color mapping. + Tracer.showHtml(mtx1, COLOR_MAPPER, true); + + Matrix mtx2 = new DenseLocalOnHeapMatrix(100, 100); + + double MAX = (double)(mtx2.rowSize() * mtx2.columnSize()); + + mtx2.assign((x, y) -> (double)(x * y) / MAX); + + Tracer.showHtml(mtx2, true); + } + + /** */ + @Test public void testWriteVectorToCSVFile() throws IOException { DenseLocalOnHeapVector vector = new DenseLocalOnHeapVector(MathTestConstants.STORAGE_SIZE);
