Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ public enum AlgorithmLabel implements Label {
MCMF("MinCostMaxFlow"),
Modularity("Modularity"),
ModularityOptimization("ModularityOptimization"),
NodeCount("NodeCount"),
NodeSimilarity("Node Similarity"),
Node2Vec("Node2Vec"),
PageRank("PageRank"),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of 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.nodecount;

import org.neo4j.gds.AlgorithmParameters;
import org.neo4j.gds.core.concurrency.Concurrency;

/**
* The immutable, user-input-free parameters the algorithm actually runs on.
* Node counting needs nothing but the concurrency that the surrounding machinery expects; a real algorithm
* would carry its tuning knobs here, derived from the configuration via {@code toParameters()}.
*/
public record NodeCountParameters(Concurrency concurrency) implements AlgorithmParameters {
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,18 @@
import org.neo4j.gds.core.utils.progress.tasks.Task;
import org.neo4j.gds.indexInverse.InverseRelationshipsTask;
import org.neo4j.gds.indexinverse.InverseRelationshipsParameters;
import org.neo4j.gds.nodecount.NodeCountTask;
import org.neo4j.gds.scaleproperties.ScalePropertiesParameters;
import org.neo4j.gds.scaleproperties.ScalePropertiesTask;
import org.neo4j.gds.undirected.ToUndirectedTask;

public final class MiscellaneousAlgorithmsTasks {
private MiscellaneousAlgorithmsTasks() {}

public static Task nodeCount(Graph graph){
return NodeCountTask.create(graph);
}

public static Task scaleProperties(Graph graph, ScalePropertiesParameters parameters){
return ScalePropertiesTask.create(graph, parameters);
}
Expand Down
57 changes: 57 additions & 0 deletions algo/src/main/java/org/neo4j/gds/nodecount/NodeCount.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of 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.nodecount;

import org.neo4j.gds.Algorithm;
import org.neo4j.gds.api.Graph;
import org.neo4j.gds.core.utils.progress.tasks.ProgressTracker;
import org.neo4j.gds.termination.TerminationFlag;

/**
* A deliberately trivial example algorithm: it counts the number of nodes in the graph and returns it.
* <p>
* Its only purpose is to act as a template that shows the full set of modules and wiring required to add a
* new algorithm to GDS, without the distraction of any real algorithmic complexity. To implement a real
* algorithm, replace the body of {@link #compute()} with the actual computation; everything around it
* (config, parameters, facades, procedures, progress tracking) follows the same shape demonstrated here.
*/
public class NodeCount extends Algorithm<NodeCountResult> {

private final Graph graph;

public NodeCount(Graph graph, ProgressTracker progressTracker, TerminationFlag terminationFlag) {
super(progressTracker);
this.graph = graph;
this.terminationFlag = terminationFlag;
}

@Override
public NodeCountResult compute() {
progressTracker.beginSubTask();

terminationFlag.assertRunning();
var nodeCount = graph.nodeCount();
progressTracker.onProgress(nodeCount);

progressTracker.endSubTask();

return new NodeCountResult(nodeCount);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of 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.nodecount;

import org.neo4j.gds.mem.MemoryEstimateDefinition;
import org.neo4j.gds.mem.MemoryEstimation;
import org.neo4j.gds.mem.MemoryEstimations;

public class NodeCountMemoryEstimateDefinition implements MemoryEstimateDefinition {

@Override
public MemoryEstimation memoryEstimation() {
// Counting nodes only requires holding on to a single long, independent of the graph size.
return MemoryEstimations.builder(NodeCount.class)
.fixed("node count", Long.BYTES)
.build();
}
}
23 changes: 23 additions & 0 deletions algo/src/main/java/org/neo4j/gds/nodecount/NodeCountResult.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of 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.nodecount;

public record NodeCountResult(long nodeCount) {
}
33 changes: 33 additions & 0 deletions algo/src/main/java/org/neo4j/gds/nodecount/NodeCountTask.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of 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.nodecount;

import org.neo4j.gds.api.Graph;
import org.neo4j.gds.applications.algorithms.machinery.AlgorithmLabel;
import org.neo4j.gds.core.utils.progress.tasks.Task;
import org.neo4j.gds.core.utils.progress.tasks.Tasks;

public final class NodeCountTask {
private NodeCountTask() {}

public static Task create(Graph graph) {
return Tasks.leaf(AlgorithmLabel.NodeCount.asString(), graph.nodeCount());
}
}
80 changes: 80 additions & 0 deletions algo/src/test/java/org/neo4j/gds/nodecount/NodeCountTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of 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.nodecount;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.neo4j.gds.MiscellaneousAlgorithmsTasks;
import org.neo4j.gds.TestGraph;
import org.neo4j.gds.TestProgressTrackerHelper;
import org.neo4j.gds.core.concurrency.Concurrency;
import org.neo4j.gds.core.utils.progress.tasks.ProgressTracker;
import org.neo4j.gds.extension.GdlExtension;
import org.neo4j.gds.extension.GdlGraph;
import org.neo4j.gds.extension.Inject;
import org.neo4j.gds.termination.TerminationFlag;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.neo4j.gds.assertj.Extractors.removingThreadId;
import static org.neo4j.gds.compat.TestLog.INFO;

@GdlExtension
class NodeCountTest {

@GdlGraph
static String GDL =
"(a)-->(b)" +
",(b)-->(c)" +
",(c)-->(a)";

@Inject
private TestGraph graph;

@Test
void countsNodes() {
var algorithm = new NodeCount(graph, ProgressTracker.NULL_TRACKER, TerminationFlag.RUNNING_TRUE);

var result = algorithm.compute();

assertEquals(graph.nodeCount(), result.nodeCount());
assertEquals(3L, result.nodeCount());
}

@Test
void progressLogging() {
var progressTrackerWithLog = TestProgressTrackerHelper.create(
MiscellaneousAlgorithmsTasks.nodeCount(graph),
new Concurrency(1)
);

var progressTracker = progressTrackerWithLog.progressTracker();
var log = progressTrackerWithLog.log();

new NodeCount(graph, progressTracker, TerminationFlag.RUNNING_TRUE).compute();

Assertions.assertThat(log.getMessages(INFO))
// avoid asserting on the thread id
.extracting(removingThreadId())
.contains(
"NodeCount :: Start",
"NodeCount :: Finished"
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
import org.neo4j.gds.core.utils.progress.tasks.ProgressTracker;
import org.neo4j.gds.indexInverse.InverseRelationships;
import org.neo4j.gds.indexinverse.InverseRelationshipsParameters;
import org.neo4j.gds.nodecount.NodeCount;
import org.neo4j.gds.nodecount.NodeCountResult;
import org.neo4j.gds.scaleproperties.ScaleProperties;
import org.neo4j.gds.scaleproperties.ScalePropertiesParameters;
import org.neo4j.gds.scaleproperties.ScalePropertiesResult;
Expand All @@ -46,6 +48,12 @@ public MiscellaneousAlgorithms( TerminationFlag terminationFlag) {
this.terminationFlag = terminationFlag;
}

public NodeCountResult nodeCount(Graph graph, ProgressTracker progressTracker) {
var algorithm = new NodeCount(graph, progressTracker, terminationFlag);

return algorithm.compute();
}

public SingleTypeRelationships collapsePath(GraphStore graphStore, CollapsePathParameters parameters) {

var algorithm = CollapsePath.create(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
import org.neo4j.gds.core.loading.SingleTypeRelationships;
import org.neo4j.gds.indexInverse.InverseRelationshipsConfig;
import org.neo4j.gds.indexInverse.InverseRelationshipsParamsTransformer;
import org.neo4j.gds.nodecount.NodeCountBaseConfig;
import org.neo4j.gds.nodecount.NodeCountResult;
import org.neo4j.gds.scaleproperties.ScalePropertiesBaseConfig;
import org.neo4j.gds.scaleproperties.ScalePropertiesResult;
import org.neo4j.gds.undirected.ToUndirectedConfig;
Expand All @@ -48,6 +50,18 @@ public MiscellaneousAlgorithmsBusinessFacade(MiscellaneousAlgorithms miscellaneo
this.miscellaneousAlgorithms = miscellaneousAlgorithms;
}

NodeCountResult nodeCount(Graph graph, NodeCountBaseConfig configuration) {
var params = configuration.toParameters();
var task = MiscellaneousAlgorithmsTasks.nodeCount(graph);
var progressTracker = progressTrackerCreator.createProgressTracker(task, configuration);

return algorithmMachinery.getResult(
() -> miscellaneousAlgorithms.nodeCount(graph, progressTracker),
progressTracker,
params.concurrency()
);
}

public SingleTypeRelationships collapsePath(GraphStore graphStore, CollapsePathConfig configuration) {
var params = CollapsePathParamsTransformer.create(configuration,graphStore);
return miscellaneousAlgorithms.collapsePath(graphStore, params);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
import org.neo4j.gds.exceptions.MemoryEstimationNotImplementedException;
import org.neo4j.gds.indexInverse.InverseRelationshipsMemoryEstimateDefinition;
import org.neo4j.gds.mem.MemoryEstimation;
import org.neo4j.gds.nodecount.NodeCountBaseConfig;
import org.neo4j.gds.nodecount.NodeCountMemoryEstimateDefinition;
import org.neo4j.gds.scaleproperties.ScalePropertiesBaseConfig;
import org.neo4j.gds.scaleproperties.ScalePropertiesMemoryEstimateDefinition;
import org.neo4j.gds.undirected.ToUndirectedConfig;
Expand All @@ -42,6 +44,21 @@ public MemoryEstimation collapsePath() {
throw new MemoryEstimationNotImplementedException();
}

public MemoryEstimation nodeCount() {
return new NodeCountMemoryEstimateDefinition().memoryEstimation();
}

public MemoryEstimateResult nodeCount(
NodeCountBaseConfig configuration,
Object graphNameOrConfiguration
) {
return algorithmEstimationTemplate.estimate(
configuration,
graphNameOrConfiguration,
nodeCount()
);
}

public MemoryEstimation indexInverse(AlgoBaseConfig configuration) {
return new InverseRelationshipsMemoryEstimateDefinition(configuration.relationshipTypes()).memoryEstimation();
}
Expand Down
Loading
Loading