neo4j / graph-data-science

Source code for the Neo4j Graph Data Science library of graph algorithms.
https://neo4j.com/docs/graph-data-science/current/
Other
596 stars 157 forks source link

"Unknown subtask:", need help for developing my own algorithm and procedure base on Open GDS 2.5.5 #295

Closed icejean closed 5 months ago

icejean commented 5 months ago

Hi,all, I'm trying to develop my own algorithm and procedure base on Open GDS 2.5.5, just setup my Eclipse Maven project by addressing the test-utils issue#294 with the help from @vnickolov, thanks a lot! Now that I've developed an algorithm PrimK,successfully have a Junit test on it, and create a procedure MyKSpanningTreeWriteProc and it's Junit test case, but the Junit test failed, I know little about the internal mechanism of Open GDS, so any help is appreciated, thanks in advance! Here's the source of my Eclipse Maven project, and here're the steps I take:

Stage 1 Create and test the algorithm.

  1. Develop an algorithm org.neo4j.gds.kspanningtree.PrimK.java, to write a K spanning tree to node relationships, instead of the algorithm org.neo4j.gds.kspanningtree.KSpanningTree.java, which is writing to node properties, by replacing the growApproach() method of it. I need a K spanning tree which root is the given starting node, and writing to node relationships. Just apply the Prim algorithm on the whole spanning tree a second time to generate the K spanning tree, and give it a subtask name “MyKSpanningTree”. It references 2 more classes: A. org.neo4j.gds.spanningtree.Arc.java, represents an arc in graph. B. org.neo4j.gds.spanningtree.OrderArcs.java, helps to order arc array by weight. The arc array just can be referenced by int index, not long, it is a TODO issue.
  2. Create a Junit test org.neo4j.gds.kspanningtree.PrimKTest.java to test the PrimK algorithm.

Stage 2 Create a procedure.

  1. Create MyKSpanningTreeWriteConfig.java, add a configuration parameter k, based on org.neo4j.gds.spanningtree.SpanningTreeBaseConfig.java
    
    package org.neo4j.gds.kspanningtree;

import org.neo4j.gds.annotation.Configuration; import org.neo4j.gds.annotation.ValueClass; import org.neo4j.gds.config.WritePropertyConfig; import org.neo4j.gds.config.WriteRelationshipConfig; import org.neo4j.gds.core.CypherMapWrapper; import org.neo4j.gds.spanningtree.SpanningTreeBaseConfig;

@ValueClass @Configuration @SuppressWarnings("immutables:subtype") public interface MyKSpanningTreeWriteConfig extends SpanningTreeBaseConfig, WritePropertyConfig, WriteRelationshipConfig {

// Add a parameter k for k spanning tree.
long k();

static MyKSpanningTreeWriteConfig of(CypherMapWrapper userInput) {
    return new MyKSpanningTreeWriteConfigImpl(userInput);
}

}

4.  Generate MyKSpanningTreeWriteConfigImpl.java
A. Copy MyKSpanningTreeWriteConfig.java to ~/ algo/src/main/java/org/neo4j/gds/kspanningtree
B. Run `gradle :algo:assemble -Pneo4jVersion=5.10.0` to generate MyKSpanningTreeWriteConfigImpl.java
5.  Import MyKSpanningTreeWriteConfigImpl.java into my Eclipse project.
6.  Create an algorithm factory for my procedure: org.neo4j.gds.kspanningtree.MyKSpanningTreeAlgorithmFactory.java, it’s copied from org.neo4j.gds.spanningtree.SpanningTreeAlgorithmFactory.java, as it uses a different algorithm PrimK other than Prim, and a different configuration MyKSpanningTreeWriteConfig other than SpanningTreeBaseConfig, give it a task name “MyKSpanningTree”, just same as the subtask name of PrimK algorithm.
7.  Create a procedure to call the PrimK algorithm:
A. org.neo4j.gds.paths.kspanningtree.MyKSpanningTreeWriteProc.java is modified from org.neo4j.gds.paths.spanningtree.SpanningTreeWriteProc.java, give it a procedure name "gds.MyKSpanningTree.write".
B. org.neo4j.gds.paths.kspanningtree.MyKSpanningTreeWriteSpec.java is modified from org.neo4j.gds.paths.spanningtree.SpanningTreeWriteSpec.java, give it a procedure name "gds.MyKSpanningTree.write" and a name “MyKSpanningTreeWrite”.
C. org.neo4j.gds.paths.kspanningtree.MyKSpanningTreeWriteResult.java is just a copy of org.neo4j.gds.paths.spanningtree.WriteResult.java.

Stage 3 Test the procedure.
8.  Create a Junit test org.neo4j.gds.paths.kspanningtree.MyKSpanningTreeWriteProcTest.java for the procedure, applying [the fix of issue#294](https://github.com/neo4j/graph-data-science/issues/294), it’s modified from org.neo4j.gds.paths.spanningtree.SpanningTreeWriteProcTest.java.

Junit test failed prompting "Unknown subtask:":

org.neo4j.graphdb.QueryExecutionException: Failed to invoke procedure gds.MyKSpanningTree.write: Caused by: java.lang.IllegalArgumentException: Unknown subtask:

![MyKSpanningTreeProcWriteTest](https://github.com/neo4j/graph-data-science/assets/54383348/7cea85cc-c3fb-4d89-9fa5-e4f72d556a63)

Here're the detail exceptions:

D:\eclipse2022\workspace\Open-GDS-Extend-2.5.5>mvn test -Dtest=org.neo4j.gds.paths.kspanningtree.MyKSpanningTreeWriteProcTest.java [INFO] Scanning for projects... [INFO] [INFO] ----------------------< cn.jean.neo4j:gds-extend >---------------------- [INFO] Building Jean's Neo4j GDS Extend 1.0.0-SNAPSHOT [INFO] from pom.xml [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- enforcer:3.4.1:enforce (enforce) @ gds-extend --- [INFO] Rule 0: org.apache.maven.enforcer.rules.version.RequireJavaVersion passed [INFO] Rule 1: org.apache.maven.enforcer.rules.version.RequireMavenVersion passed [INFO] [INFO] --- resources:3.3.1:resources (default-resources) @ gds-extend --- [INFO] skip non existing resourceDirectory D:\eclipse2022\workspace\Open-GDS-Extend-2.5.5\src\main\resources [INFO] [INFO] --- compiler:3.11.0:compile (default-compile) @ gds-extend --- [INFO] Changes detected - recompiling the module! :source [INFO] Compiling 39 source files with javac [debug release 17] to target\classes [WARNING] 注释处理不适用于隐式编译的文件。 使用 -proc:none 禁用注释处理或使用 -implicit 指定用于隐式编译的策略。 [INFO] /D:/eclipse2022/workspace/Open-GDS-Extend-2.5.5/src/main/java/org/neo4j/gds/kspanningtree/MyKSpanningTreeWriteConfigImpl.java: D:\eclipse2022\workspace\Open-GDS-Extend-2.5.5\src\main\java\org\neo4j\gds\kspanningtree\MyKSpanningTreeWriteConfigImpl.java使用了未经检查或不安全的操作。 [INFO] /D:/eclipse2022/workspace/Open-GDS-Extend-2.5.5/src/main/java/org/neo4j/gds/kspanningtree/MyKSpanningTreeWriteConfigImpl.java: 有关详细信息, 请使用 -Xlint:unchecked 重新编译。 [INFO] [INFO] --- resources:3.3.1:testResources (default-testResources) @ gds-extend --- [INFO] Copying 1 resource from src\test\resources to target\test-classes [INFO] [INFO] --- compiler:3.11.0:testCompile (default-testCompile) @ gds-extend --- [INFO] Changes detected - recompiling the module! :dependency [INFO] Compiling 10 source files with javac [debug release 17] to target\test-classes [INFO] [INFO] --- surefire:3.1.2:test (default-test) @ gds-extend --- [INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider [INFO] [INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] Running org.neo4j.gds.paths.kspanningtree.MyKSpanningTreeWriteProcTest SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder". SLF4J: Defaulting to no-operation (NOP) logger implementation SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details. [ERROR] Tests run: 2, Failures: 0, Errors: 2, Skipped: 0, Time elapsed: 9.582 s <<< FAILURE! -- in org.neo4j.gds.paths.kspanningtree.MyKSpanningTreeWriteProcTest [ERROR] org.neo4j.gds.paths.kspanningtree.MyKSpanningTreeWriteProcTest.testYields(String)[1] -- Time elapsed: 9.127 s <<< ERROR! org.neo4j.graphdb.QueryExecutionException: Failed to invoke procedure gds.MyKSpanningTree.write: Caused by: java.lang.IllegalArgumentException: Unknown subtask: at org.neo4j.kernel.impl.query.QueryExecutionKernelException.asUserException(QueryExecutionKernelException.java:32) at org.neo4j.cypher.internal.javacompat.ResultSubscriber.converted(ResultSubscriber.java:361) at org.neo4j.cypher.internal.javacompat.ResultSubscriber.fetchResults(ResultSubscriber.java:321) at org.neo4j.cypher.internal.javacompat.ResultSubscriber.materialize(ResultSubscriber.java:87) at org.neo4j.cypher.internal.result.StandardInternalExecutionResult.initiate(StandardInternalExecutionResult.scala:65) at org.neo4j.cypher.internal.result.ClosingExecutionResult.$anonfun$initiate$1(ClosingExecutionResult.scala:61) at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.scala:18) at org.neo4j.cypher.internal.result.ClosingExecutionResult.safely(ClosingExecutionResult.scala:104) at org.neo4j.cypher.internal.result.ClosingExecutionResult.initiate(ClosingExecutionResult.scala:61) at org.neo4j.cypher.internal.result.ClosingExecutionResult$.wrapAndInitiate(ClosingExecutionResult.scala:198) at org.neo4j.cypher.internal.CypherCurrentCompiler$CypherExecutableQuery.innerExecute(CypherCurrentCompiler.scala:496) at org.neo4j.cypher.internal.CypherCurrentCompiler$CypherExecutableQuery.execute(CypherCurrentCompiler.scala:414) at org.neo4j.cypher.internal.ExecutionEngine.doExecute(ExecutionEngine.scala:235) at org.neo4j.cypher.internal.ExecutionEngine.$anonfun$executeSubquery$1(ExecutionEngine.scala:179) at org.neo4j.cypher.internal.ExecutionEngine.closing(ExecutionEngine.scala:185) at org.neo4j.cypher.internal.ExecutionEngine.executeSubquery(ExecutionEngine.scala:165) at org.neo4j.cypher.internal.ExecutionEngine.execute(ExecutionEngine.scala:99) at org.neo4j.cypher.internal.javacompat.ExecutionEngine.executeQuery(ExecutionEngine.java:121) at org.neo4j.cypher.internal.javacompat.ExecutionEngine.executeQuery(ExecutionEngine.java:106) at org.neo4j.kernel.impl.coreapi.TransactionImpl.execute(TransactionImpl.java:236) at org.neo4j.kernel.impl.coreapi.TransactionImpl.execute(TransactionImpl.java:226) at org.neo4j.gds.compat.GraphDatabaseApiProxy.runQueryWithoutClosingTheResult(GraphDatabaseApiProxy.java:162) at org.neo4j.gds.QueryRunner.lambda$runQueryWithRowConsumer$6(QueryRunner.java:105) at org.neo4j.gds.compat.GraphDatabaseApiProxy.runInFullAccessTransaction(GraphDatabaseApiProxy.java:175) at org.neo4j.gds.QueryRunner.runQueryWithRowConsumer(QueryRunner.java:104) at org.neo4j.gds.BaseTest5x.runQueryWithRowConsumer(BaseTest5x.java:143) at org.neo4j.gds.paths.kspanningtree.MyKSpanningTreeWriteProcTest.testYields(MyKSpanningTreeWriteProcTest.java:103) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:568) at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:728) at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60) at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131) at org.junit.jupiter.engine.extension.SameThreadTimeoutInvocation.proceed(SameThreadTimeoutInvocation.java:45) at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:156) at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:147) at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestTemplateMethod(TimeoutExtension.java:94) at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:103) at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:93) at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106) at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64) at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45) at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37) at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:92) at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:86) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:217) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:213) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:138) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:68) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35) at org.junit.platform.engine.support.hierarchical.NodeTestTask$DefaultDynamicTestExecutor.execute(NodeTestTask.java:226) at org.junit.platform.engine.support.hierarchical.NodeTestTask$DefaultDynamicTestExecutor.execute(NodeTestTask.java:204) at org.junit.jupiter.engine.descriptor.TestTemplateTestDescriptor.execute(TestTemplateTestDescriptor.java:142) at org.junit.jupiter.engine.descriptor.TestTemplateTestDescriptor.lambda$execute$2(TestTemplateTestDescriptor.java:110) at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183) at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179) at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183) at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183) at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:992) at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150) at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173) at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596) at java.base/java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:276) at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1625) at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150) at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173) at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596) at java.base/java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:276) at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1625) at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150) at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173) at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596) at org.junit.jupiter.engine.descriptor.TestTemplateTestDescriptor.execute(TestTemplateTestDescriptor.java:110) at org.junit.jupiter.engine.descriptor.TestTemplateTestDescriptor.execute(TestTemplateTestDescriptor.java:44) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) at java.base/java.util.ArrayList.forEach(ArrayList.java:1511) at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) at java.base/java.util.ArrayList.forEach(ArrayList.java:1511) at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35) at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57) at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54) at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:147) at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:127) at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:90) at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:55) at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:102) at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:54) at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114) at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86) at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86) at org.apache.maven.surefire.junitplatform.LazyLauncher.execute(LazyLauncher.java:56) at org.apache.maven.surefire.junitplatform.JUnitPlatformProvider.execute(JUnitPlatformProvider.java:184) at org.apache.maven.surefire.junitplatform.JUnitPlatformProvider.invokeAllTests(JUnitPlatformProvider.java:148) at org.apache.maven.surefire.junitplatform.JUnitPlatformProvider.invoke(JUnitPlatformProvider.java:122) at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:385) at org.apache.maven.surefire.booter.ForkedBooter.execute(ForkedBooter.java:162) at org.apache.maven.surefire.booter.ForkedBooter.run(ForkedBooter.java:507) at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:495) Caused by: org.neo4j.kernel.impl.query.QueryExecutionKernelException: Failed to invoke procedure gds.MyKSpanningTree.write: Caused by: java.lang.IllegalArgumentException: Unknown subtask: ... 149 more

icejean commented 5 months ago

@vnickolov O.K., I address it by myself after inspecting into those code lines of Neo4j 4.5+Open GDS 2.0, and the code lines of Open GDS 2.5.5, it's because of the code lines in org.neo4j.gds.kspanningtree.MyKSpanningTreeAlgorithmFactory.java, should add Tasks.leaf() to progressTask():

/*
 * Copyright (c) "Jean Ye"
 * ZhuHai Taxation Bureau, China [1793893070@qq.com]
 * Original algorithm implementation in Python by David Eisenstat, [https://stackoverflow.com/users/2144669/david-eisenstat]
 * Reference to: Chu-Liu Edmond's algorithm for Minimum Spanning Tree on Directed Graphs
 * [https://stackoverflow.com/questions/23988236/chu-liu-edmonds-algorithm-for-minimum-spanning-tree-on-directed-graphs]
 *
 * This file is an addition to Neo4j.
 *
 * Neo4j is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.neo4j.gds.kspanningtree;

import org.neo4j.gds.GraphAlgorithmFactory;
import org.neo4j.gds.api.Graph;
import org.neo4j.gds.collections.ha.HugeLongArray;
import org.neo4j.gds.core.utils.mem.MemoryEstimation;
import org.neo4j.gds.core.utils.mem.MemoryEstimations;
import org.neo4j.gds.core.utils.progress.tasks.ProgressTracker;
import org.neo4j.gds.core.utils.progress.tasks.Task;
import org.neo4j.gds.core.utils.progress.tasks.Tasks;
import org.neo4j.gds.core.utils.queue.HugeLongPriorityQueue;
import org.neo4j.gds.mem.MemoryUsage;

public class MyKSpanningTreeAlgorithmFactory<CONFIG extends MyKSpanningTreeWriteConfig> extends GraphAlgorithmFactory<PrimK, CONFIG> {

    @Override
    public PrimK build(Graph graphOrGraphStore, CONFIG configuration, ProgressTracker progressTracker) {
        if (!graphOrGraphStore.schema().isUndirected()) {
            throw new IllegalArgumentException(
                "The K Spanning Tree algorithm works only with undirected graphs. Please orient the edges properly");
        }
        return new PrimK(
            graphOrGraphStore,
            configuration.objective(),
            graphOrGraphStore.toMappedNodeId(configuration.sourceNode()),
            // Add a parameter k for spanning tree.
            configuration.k(),
            progressTracker
        );

    }

    @Override
    public String taskName() {
        return "MyKSpanningTree";
    }

    @Override
    public MemoryEstimation memoryEstimation(CONFIG config) {
        return MemoryEstimations.builder(PrimK.class)
            .perNode("Parent array", HugeLongArray::memoryEstimation)
            .add("Priority queue", HugeLongPriorityQueue.memoryEstimation())
            .perNode("visited", MemoryUsage::sizeOfBitset)
            .build();
    }
    public Task progressTask(Graph graph, CONFIG config) {
        return Tasks.task(
                taskName(),
                Tasks.leaf("MyKSpanningTree", graph.nodeCount()),
                Tasks.leaf("Add relationship weights")                
            );
    }

}

You can have a look at the corrected source of my Eclipse Maven project for more details. :)

icejean commented 5 months ago

Here're the snapshots of Junit test: Junit test for algorithm PrimK: MyKSpanningTreeProcWriteTest-3 MyKSpanningTreeProcWriteTest-4

Junit test for procedure: MyKSpanningTreeProcWriteTest-2

icejean commented 5 months ago

And here's the plugin test running on Neo4j community 5.10.0: MyKSpanningTreeProcWriteTest-1

And the Cypher codes to run the test:

// My K spanning tree algorithm test --------------------------------------

// Ensure the algorithm procedure is loaded
SHOW PROCEDURES yield name,signature,description,mode
where name contains "MyKSpanningTree"
return name,signature,description,mode
order by name;

// Create a graph for test ------------------------------------------------
match(n) detach delete n;

CREATE (a:Place {id: 'A'}),
       (b:Place {id: 'B'}),
       (c:Place {id: 'C'}),
       (d:Place {id: 'D'}),
       (e:Place {id: 'E'}),
       (f:Place {id: 'F'}),
       (g:Place {id: 'G'}),
       (d)-[:LINK {cost:4}]->(b),
       (d)-[:LINK {cost:6}]->(e),
       (b)-[:LINK {cost:1}]->(a),
       (b)-[:LINK {cost:3}]->(c),
       (a)-[:LINK {cost:2}]->(c),
       (c)-[:LINK {cost:5}]->(e),
       (f)-[:LINK {cost:1}]->(g);

match (n)-[r]-(m) return n,r,m;

// Create a graph projection for running the algorithm procedure
CALL gds.graph.drop("graph") YIELD graphName;

CALL gds.graph.project(
  'graph',
  'Place',
  {
    LINK: {
      properties: 'cost',
      orientation: 'UNDIRECTED'
    }
  }
)

// Minimum spanning tree ----------------------------------------------------
MATCH (n:Place {id: 'A'})
CALL gds.spanningTree.write('graph', {
  sourceNode: n,
  relationshipWeightProperty: 'cost',
  writeProperty: 'writeCost',
  writeRelationshipType: 'MINST'
})
YIELD preProcessingMillis, computeMillis, writeMillis, effectiveNodeCount
RETURN preProcessingMillis, computeMillis, writeMillis, effectiveNodeCount;

// My K minium spanning tree
MATCH (n:Place {id: 'A'})
CALL gds.MyKSpanningTree.write('graph', {
  sourceNode: n,
  relationshipWeightProperty: 'cost',
  writeProperty: 'writeCost',
  writeRelationshipType: 'KMINST',
  k:4
})
YIELD preProcessingMillis, computeMillis, writeMillis, effectiveNodeCount
RETURN preProcessingMillis, computeMillis, writeMillis, effectiveNodeCount;

// Maximum spanning tree-----------------------------------------------------
MATCH (n:Place {id: 'A'})
CALL gds.spanningTree.write('graph', {
  sourceNode: n,
  relationshipWeightProperty: 'cost',
  writeProperty: 'writeCost',
  writeRelationshipType: 'MAXST',
  objective: 'maximum'
})
YIELD preProcessingMillis, computeMillis, writeMillis, effectiveNodeCount
RETURN preProcessingMillis, computeMillis, writeMillis, effectiveNodeCount;

// My K maximum spanning tree
MATCH (n:Place {id: 'A'})
CALL gds.MyKSpanningTree.write('graph', {
  sourceNode: n,
  relationshipWeightProperty: 'cost',
  writeProperty: 'writeCost',
  writeRelationshipType: 'KMAXST',
  objective: 'maximum',
  k:4
})
YIELD preProcessingMillis, computeMillis, writeMillis, effectiveNodeCount
RETURN preProcessingMillis, computeMillis, writeMillis, effectiveNodeCount;

// Show and check the algorithms' result
match (n)-[r]-(m) return n,r,m;

Cheers!