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);
 

Reply via email to