diff --git a/.github/workflows/capymoa.yml b/.github/workflows/capymoa.yml index 58df5b7f6..337013759 100644 --- a/.github/workflows/capymoa.yml +++ b/.github/workflows/capymoa.yml @@ -1,4 +1,4 @@ -name: Package Jar for CapyMOA +name: CapyMOA Test and Package on: push: @@ -13,17 +13,32 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Set up JDK 17 + + # TODO: moa tests are sensitive to java versions https://github.com/Waikato/moa/issues/273 + - name: Set up JDK 21 uses: actions/setup-java@v4 with: - java-version: '17' - distribution: 'temurin' + java-version: '21' + distribution: 'zulu' cache: maven - - name: Build with Maven + - name: Version + working-directory: ./moa + run: mvn -v + + - name: Unit Tests + working-directory: ./moa + # -B: non-interactive (batch) mode + # -q: quiet output + run: mvn -B -q test + + - name: Package Jar working-directory: ./moa - # no tests - run: mvn -B package --file pom.xml -DskipTests + # -B: non-interactive (batch) mode + # -DskipTests: skip tests (they were run earlier) + # -Dmaven.javadoc.skip=true: skip javadoc generation + # -Dlatex.skipBuild=true: skip latex documentation build this needs extra dependencies + run: mvn -B package -DskipTests -Dmaven.javadoc.skip=true -Dlatex.skipBuild=true # Upload jar file as artifact - name: Upload artifact diff --git a/README.md b/README.md index 740d525de..090da1127 100755 --- a/README.md +++ b/README.md @@ -1,3 +1,46 @@ +# MOA (CapyMOA Edition) + +This is a fork of the [Waikato/moa](https://github.com/Waikato/moa) repository that is +packaged with CapyMOA providing additional functionality to MOA and better integration +with CapyMOA. + +A long term project goal is to merge all CapyMOA changes back into the upstream +`Waikato/moa:master` branch. This will be an iterative process and much slower than +CapyMOA development. For now, this fork exists to facilitate CapyMOA development without +blocking on upstream MOA changes. Changes from this branch shall be cherry-picked into +upstream MOA as appropriate. This fork shall then be rebased on-top of the latest MOA +upstream changes periodically. + +To create a pull request into MOA CapyMOA edition branch, create a PR from your feature +branch into `adaptive-machine-learning/moa:capymoa`: +![CapyMOA](img/capymoa-pr.png) + +To build MOA for use with CapyMOA run: +```bash +cd moa +mvn package -DskipTests -Dmaven.javadoc.skip=true -Dlatex.skipBuild=true +``` +This will create a `target/moa-*-jar-with-dependencies.jar` file that can be used by +CapyMOA. To let CapyMOA know where this file is, set the `CAPYMOA_MOA_JAR` environment +variable to the path of this file. + +You can do this temporarily in your terminal session with: +```bash +export CAPYMOA_MOA_JAR=/path/to/moa/target/moa-*-jar-with-dependencies.jar +``` +To check that CapyMOA can find MOA, run: +```bash +python -c "import capymoa; capymoa.about()" +# CapyMOA 0.10.0 +# CAPYMOA_DATASETS_DIR: .../datasets +# CAPYMOA_MOA_JAR: .../moa/moa/target/moa-2024.07.2-SNAPSHOT-jar-with-dependencies.jar +# CAPYMOA_JVM_ARGS: ['-Xmx8g', '-Xss10M'] +# JAVA_HOME: /usr/lib/jvm/java-21-openjdk +# MOA version: aa955ebbcbd99e9e1d19ab16582e3e5a6fca5801ba250e4d164c16a89cf798ea +# JAVA version: 21.0.7 +``` + + # MOA (Massive Online Analysis) [![Build Status](https://travis-ci.org/Waikato/moa.svg?branch=master)](https://travis-ci.org/Waikato/moa) [![Maven Central](https://img.shields.io/maven-central/v/nz.ac.waikato.cms.moa/moa-pom.svg)](https://mvnrepository.com/artifact/nz.ac.waikato.cms) diff --git a/img/capymoa-pr.png b/img/capymoa-pr.png new file mode 100644 index 000000000..af440a564 Binary files /dev/null and b/img/capymoa-pr.png differ diff --git a/moa/src/main/java/moa/clusterers/clustream/ClustreamKernel.java b/moa/src/main/java/moa/clusterers/clustream/ClustreamKernel.java index 609ad8fdb..84b72e048 100644 --- a/moa/src/main/java/moa/clusterers/clustream/ClustreamKernel.java +++ b/moa/src/main/java/moa/clusterers/clustream/ClustreamKernel.java @@ -46,12 +46,8 @@ public ClustreamKernel(Instance instance, int dimensions, long timestamp , doubl // Avoid situations where the instance header hasn't been defined and runtime errors. if(instance.dataset() != null) { this.classObserver = new double[instance.numClasses()]; -// instance.numAttributes() <= instance.classIndex() -> edge case where the class index is equal the -// number of attributes (i.e. there is no class value in the attributes array). - if (instance.numAttributes() > instance.classIndex() && - !instance.classIsMissing() && - instance.classValue() >= 0 && - instance.classValue() < instance.numClasses()) { +// + if (this.instanceHasClass(instance)) { this.classObserver[(int) instance.classValue()]++; } } @@ -72,12 +68,21 @@ public ClustreamKernel( ClustreamKernel cluster, double t, int m ) { this.classObserver = cluster.classObserver; } + private boolean instanceHasClass(Instance instance) { + // TODO: Why is this check necessary? Shouldn't classIsMissing() be enough? + // Edge case where the class index is out of bounds. number of attributes + // (i.e. there is no class value in the attributes array). + return instance.numAttributes() > instance.classIndex() && + !instance.classIsMissing() && // Also check for missing class. + instance.classValue() >= 0 && // Or invalid class values. + instance.classValue() < instance.numClasses(); + } + public void insert( Instance instance, long timestamp ) { if(this.classObserver == null) this.classObserver = new double[instance.numClasses()]; - if(!instance.classIsMissing() && - instance.classValue() >= 0 && - instance.classValue() < instance.numClasses()) { + + if(this.instanceHasClass(instance)) { this.classObserver[(int)instance.classValue()]++; } N++; @@ -314,4 +319,4 @@ protected void getClusterSpecificInfo(ArrayList infoTitle, ArrayList moa.DoTask.MAX_STATUS_STRING_LENGTH) { progressLine.setLength(moa.DoTask.MAX_STATUS_STRING_LENGTH); progressLine.setCharAt( - moa.DoTask.MAX_STATUS_STRING_LENGTH - 1, '~'); + moa.DoTask.MAX_STATUS_STRING_LENGTH - 1, '~'); } System.out.print(progressLine.toString()); System.out.print('\r'); @@ -146,7 +154,7 @@ public void doTask(String [] args) throws Exception { System.out.println(); } } - + if (result instanceof FailedTaskReport) { System.out.println("Task failed. Reason: "); ((FailedTaskReport) result).getFailureReason().printStackTrace(); @@ -166,6 +174,6 @@ public void doTask(String [] args) throws Exception { } } - } - -} + } + +} \ No newline at end of file diff --git a/moa/src/test/java/moa/test/MoaTestCase.java b/moa/src/test/java/moa/test/MoaTestCase.java index 061ec665b..5ff59639d 100644 --- a/moa/src/test/java/moa/test/MoaTestCase.java +++ b/moa/src/test/java/moa/test/MoaTestCase.java @@ -71,21 +71,11 @@ public MoaTestCase(String name) { * @return the class that is being tested or null if none could * be determined */ - protected Class getTestedClass() { - Class result; - - result = null; - - if (getClass().getName().endsWith("Test")) { - try { - result = Class.forName(getClass().getName().replaceAll("Test$", "")); - } - catch (Exception e) { - result = null; - } + protected Class getTestedClass() throws ClassNotFoundException { + if (!getClass().getName().endsWith("Test")) { + throw new IllegalStateException("Class name must end with 'Test': " + getClass().getName()); } - - return result; + return Class.forName(getClass().getName().replaceAll("Test$", "")); } /** @@ -105,14 +95,9 @@ protected boolean canHandleHeadless() { */ @Override protected void setUp() throws Exception { - Class cls; - super.setUp(); - - cls = getTestedClass(); - if (cls != null) - m_Regression = new Regression(cls); + m_Regression = new Regression(getTestedClass()); m_TestHelper = newTestHelper(); m_Headless = Boolean.getBoolean(PROPERTY_HEADLESS); m_NoRegressionTest = Boolean.getBoolean(PROPERTY_NOREGRESSION); diff --git a/moa/src/test/resources/moa/classifiers/trees/AdaHoeffdingOptionTree.ref b/moa/src/test/resources/moa/classifiers/trees/AdaHoeffdingOptionTree.ref index 37c4259c5..d5ff74078 100644 --- a/moa/src/test/resources/moa/classifiers/trees/AdaHoeffdingOptionTree.ref +++ b/moa/src/test/resources/moa/classifiers/trees/AdaHoeffdingOptionTree.ref @@ -49,7 +49,7 @@ Index 30000 Votes 0: 3.612203649887567E14 - 1: 1.24768970176524544E17 + 1: 1.2476897017652454E17 Measurements classified instances: 29999 classifications correct (percent): 85.57618587