chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:47:05 +08:00
commit 4f3b7da785
7394 changed files with 2005594 additions and 0 deletions
@@ -0,0 +1,97 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ /* ******************************************************************************
~ *
~ *
~ * This program and the accompanying materials are made available under the
~ * terms of the Apache License, Version 2.0 which is available at
~ * https://www.apache.org/licenses/LICENSE-2.0.
~ *
~ * See the NOTICE file distributed with this work for additional
~ * information regarding copyright ownership.
~ * 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.
~ *
~ * SPDX-License-Identifier: Apache-2.0
~ ******************************************************************************/
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.eclipse.deeplearning4j</groupId>
<artifactId>deeplearning4j-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>deeplearning4j-graph</artifactId>
<properties>
<module.name>deeplearning4j.graph</module.name>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.moditect</groupId>
<artifactId>moditect-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.eclipse.deeplearning4j</groupId>
<artifactId>deeplearning4j-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.threadly</groupId>
<artifactId>threadly</artifactId>
<version>${threadly.version}</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<version>${junit.platform.launcher.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.deeplearning4j</groupId>
<artifactId>deeplearning4j-common-tests</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,257 @@
/*
* ******************************************************************************
* *
* *
* * This program and the accompanying materials are made available under the
* * terms of the Apache License, Version 2.0 which is available at
* * https://www.apache.org/licenses/LICENSE-2.0.
* *
* * See the NOTICE file distributed with this work for additional
* * information regarding copyright ownership.
* * 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.
* *
* * SPDX-License-Identifier: Apache-2.0
* *****************************************************************************
*/
package org.deeplearning4j.graph;
import org.deeplearning4j.graph.api.BaseGraph;
import org.deeplearning4j.graph.api.Edge;
import org.deeplearning4j.graph.api.Vertex;
import org.deeplearning4j.graph.exception.NoEdgesException;
import org.deeplearning4j.graph.vertexfactory.VertexFactory;
import java.lang.reflect.Array;
import java.util.*;
public class Graph<V, E> extends BaseGraph<V, E> {
private boolean allowMultipleEdges;
private List<Edge<E>>[] edges; //edge[i].get(j).to = k, then edge from i -> k
private List<Vertex<V>> vertices;
public Graph(int numVertices, VertexFactory<V> vertexFactory) {
this(numVertices, false, vertexFactory);
}
@SuppressWarnings("unchecked")
public Graph(int numVertices, boolean allowMultipleEdges, VertexFactory<V> vertexFactory) {
if (numVertices <= 0)
throw new IllegalArgumentException();
this.allowMultipleEdges = allowMultipleEdges;
vertices = new ArrayList<>(numVertices);
for (int i = 0; i < numVertices; i++)
vertices.add(vertexFactory.create(i));
edges = (List<Edge<E>>[]) Array.newInstance(List.class, numVertices);
}
@SuppressWarnings("unchecked")
public Graph(List<Vertex<V>> vertices, boolean allowMultipleEdges) {
this.vertices = new ArrayList<>(vertices);
this.allowMultipleEdges = allowMultipleEdges;
edges = (List<Edge<E>>[]) Array.newInstance(List.class, vertices.size());
}
public Graph(List<Vertex<V>> vertices) {
this(vertices, false);
}
@Override
public int numVertices() {
return vertices.size();
}
@Override
public Vertex<V> getVertex(int idx) {
if (idx < 0 || idx >= vertices.size())
throw new IllegalArgumentException("Invalid index: " + idx);
return vertices.get(idx);
}
@Override
public List<Vertex<V>> getVertices(int[] indexes) {
List<Vertex<V>> out = new ArrayList<>(indexes.length);
for (int i : indexes)
out.add(getVertex(i));
return out;
}
@Override
public List<Vertex<V>> getVertices(int from, int to) {
if (to < from || from < 0 || to >= vertices.size())
throw new IllegalArgumentException("Invalid range: from=" + from + ", to=" + to);
List<Vertex<V>> out = new ArrayList<>(to - from + 1);
for (int i = from; i <= to; i++)
out.add(getVertex(i));
return out;
}
@Override
public void addEdge(Edge<E> edge) {
if (edge.getFrom() < 0 || edge.getTo() >= vertices.size())
throw new IllegalArgumentException("Invalid edge: " + edge + ", from/to indexes out of range");
List<Edge<E>> fromList = edges[edge.getFrom()];
if (fromList == null) {
fromList = new ArrayList<>();
edges[edge.getFrom()] = fromList;
}
addEdgeHelper(edge, fromList);
if (edge.isDirected())
return;
//Add other way too (to allow easy lookup for undirected edges)
List<Edge<E>> toList = edges[edge.getTo()];
if (toList == null) {
toList = new ArrayList<>();
edges[edge.getTo()] = toList;
}
addEdgeHelper(edge, toList);
}
@Override
@SuppressWarnings("unchecked")
public List<Edge<E>> getEdgesOut(int vertex) {
if (edges[vertex] == null)
return Collections.emptyList();
return new ArrayList<>(edges[vertex]);
}
@Override
public int getVertexDegree(int vertex) {
if (edges[vertex] == null)
return 0;
return edges[vertex].size();
}
@Override
public Vertex<V> getRandomConnectedVertex(int vertex, Random rng) throws NoEdgesException {
if (vertex < 0 || vertex >= vertices.size())
throw new IllegalArgumentException("Invalid vertex index: " + vertex);
if (edges[vertex] == null || edges[vertex].isEmpty())
throw new NoEdgesException("Cannot generate random connected vertex: vertex " + vertex
+ " has no outgoing/undirected edges");
int connectedVertexNum = rng.nextInt(edges[vertex].size());
Edge<E> edge = edges[vertex].get(connectedVertexNum);
if (edge.getFrom() == vertex)
return vertices.get(edge.getTo()); //directed or undirected, vertex -> x
else
return vertices.get(edge.getFrom()); //Undirected edge, x -> vertex
}
@Override
public List<Vertex<V>> getConnectedVertices(int vertex) {
if (vertex < 0 || vertex >= vertices.size())
throw new IllegalArgumentException("Invalid vertex index: " + vertex);
if (edges[vertex] == null)
return Collections.emptyList();
List<Vertex<V>> list = new ArrayList<>(edges[vertex].size());
for (Edge<E> edge : edges[vertex]) {
list.add(vertices.get(edge.getTo()));
}
return list;
}
@Override
public int[] getConnectedVertexIndices(int vertex) {
int[] out = new int[(edges[vertex] == null ? 0 : edges[vertex].size())];
if (out.length == 0)
return out;
for (int i = 0; i < out.length; i++) {
Edge<E> e = edges[vertex].get(i);
out[i] = (e.getFrom() == vertex ? e.getTo() : e.getFrom());
}
return out;
}
private void addEdgeHelper(Edge<E> edge, List<Edge<E>> list) {
if (!allowMultipleEdges) {
//Check to avoid multiple edges
boolean duplicate = false;
if (edge.isDirected()) {
for (Edge<E> e : list) {
if (e.getTo() == edge.getTo()) {
duplicate = true;
break;
}
}
} else {
for (Edge<E> e : list) {
if ((e.getFrom() == edge.getFrom() && e.getTo() == edge.getTo())
|| (e.getTo() == edge.getFrom() && e.getFrom() == edge.getTo())) {
duplicate = true;
break;
}
}
}
if (!duplicate) {
list.add(edge);
}
} else {
//allow multiple/duplicate edges
list.add(edge);
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Graph {");
sb.append("\nVertices {");
for (Vertex<V> v : vertices) {
sb.append("\n\t").append(v);
}
sb.append("\n}");
sb.append("\nEdges {");
for (int i = 0; i < edges.length; i++) {
sb.append("\n\t");
if (edges[i] == null)
continue;
sb.append(i).append(":");
for (Edge<E> e : edges[i]) {
sb.append(" ").append(e);
}
}
sb.append("\n}");
sb.append("\n}");
return sb.toString();
}
@Override
public boolean equals(Object o) {
if (!(o instanceof Graph))
return false;
Graph g = (Graph) o;
if (allowMultipleEdges != g.allowMultipleEdges)
return false;
if (edges.length != g.edges.length)
return false;
if (vertices.size() != g.vertices.size())
return false;
for (int i = 0; i < edges.length; i++) {
if (!edges[i].equals(g.edges[i]))
return false;
}
return vertices.equals(g.vertices);
}
@Override
public int hashCode() {
int result = 23;
result = 31 * result + (allowMultipleEdges ? 1 : 0);
result = 31 * result + Arrays.hashCode(edges);
result = 31 * result + vertices.hashCode();
return result;
}
}
@@ -0,0 +1,60 @@
/*
* ******************************************************************************
* *
* *
* * This program and the accompanying materials are made available under the
* * terms of the Apache License, Version 2.0 which is available at
* * https://www.apache.org/licenses/LICENSE-2.0.
* *
* * See the NOTICE file distributed with this work for additional
* * information regarding copyright ownership.
* * 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.
* *
* * SPDX-License-Identifier: Apache-2.0
* *****************************************************************************
*/
package org.deeplearning4j.graph;
import org.deeplearning4j.graph.api.IGraph;
import org.deeplearning4j.graph.api.IVertexSequence;
import org.deeplearning4j.graph.api.Vertex;
import java.util.NoSuchElementException;
public class VertexSequence<V> implements IVertexSequence<V> {
private final IGraph<V, ?> graph;
private int[] indices;
private int currIdx = 0;
public VertexSequence(IGraph<V, ?> graph, int[] indices) {
this.graph = graph;
this.indices = indices;
}
@Override
public int sequenceLength() {
return indices.length;
}
@Override
public boolean hasNext() {
return currIdx < indices.length;
}
@Override
public Vertex<V> next() {
if (!hasNext())
throw new NoSuchElementException();
return graph.getVertex(indices[currIdx++]);
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
@@ -0,0 +1,30 @@
/*
* ******************************************************************************
* *
* *
* * This program and the accompanying materials are made available under the
* * terms of the Apache License, Version 2.0 which is available at
* * https://www.apache.org/licenses/LICENSE-2.0.
* *
* * See the NOTICE file distributed with this work for additional
* * information regarding copyright ownership.
* * 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.
* *
* * SPDX-License-Identifier: Apache-2.0
* *****************************************************************************
*/
package org.deeplearning4j.graph.api;
public abstract class BaseGraph<V, E> implements IGraph<V, E> {
public void addEdge(int from, int to, E value, boolean directed) {
addEdge(new Edge<>(from, to, value, directed));
}
}
@@ -0,0 +1,83 @@
/*
* ******************************************************************************
* *
* *
* * This program and the accompanying materials are made available under the
* * terms of the Apache License, Version 2.0 which is available at
* * https://www.apache.org/licenses/LICENSE-2.0.
* *
* * See the NOTICE file distributed with this work for additional
* * information regarding copyright ownership.
* * 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.
* *
* * SPDX-License-Identifier: Apache-2.0
* *****************************************************************************
*/
package org.deeplearning4j.graph.api;
import lombok.Data;
@Data
public class Edge<T> {
private final int from;
private final int to;
private final T value;
private final boolean directed;
public Edge(int from, int to, T value, boolean directed) {
this.from = from;
this.to = to;
this.value = value;
this.directed = directed;
}
@Override
public String toString() {
return "edge(" + (directed ? "directed" : "undirected") + "," + from + (directed ? "->" : "--") + to + ","
+ (value != null ? value : "") + ")";
}
@Override
public boolean equals(Object o) {
if (!(o instanceof Edge))
return false;
Edge<?> e = (Edge<?>) o;
if (directed != e.directed)
return false;
if (directed) {
if (from != e.from)
return false;
if (to != e.to)
return false;
} else {
if (from == e.from) {
if (to != e.to)
return false;
} else {
if (from != e.to)
return false;
if (to != e.from)
return false;
}
}
if ((value != null && e.value == null) || (value == null && e.value != null))
return false;
return value == null || value.equals(e.value);
}
@Override
public int hashCode() {
int result = 17;
result = 31 * result + (directed ? 1 : 0);
result = 31 * result + from;
result = 31 * result + to;
result = 31 * result + (value == null ? 0 : value.hashCode());
return result;
}
}
@@ -0,0 +1,103 @@
/*
* ******************************************************************************
* *
* *
* * This program and the accompanying materials are made available under the
* * terms of the Apache License, Version 2.0 which is available at
* * https://www.apache.org/licenses/LICENSE-2.0.
* *
* * See the NOTICE file distributed with this work for additional
* * information regarding copyright ownership.
* * 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.
* *
* * SPDX-License-Identifier: Apache-2.0
* *****************************************************************************
*/
package org.deeplearning4j.graph.api;
import org.deeplearning4j.graph.exception.NoEdgesException;
import java.util.List;
import java.util.Random;
public interface IGraph<V, E> {
/** Number of vertices in the graph */
public int numVertices();
/**Get a vertex in the graph for a given index
* @param idx integer index of the vertex to get. must be in range 0 to numVertices()
* @return vertex
*/
public Vertex<V> getVertex(int idx);
/** Get multiple vertices in the graph
* @param indexes the indexes of the vertices to retrieve
* @return list of vertices
*/
public List<Vertex<V>> getVertices(int[] indexes);
/** Get multiple vertices in the graph, with secified indices
* @param from first vertex to get, inclusive
* @param to last vertex to get, inclusive
* @return list of vertices
*/
public List<Vertex<V>> getVertices(int from, int to);
/** Add an edge to the graph.
*/
public void addEdge(Edge<E> edge);
/** Convenience method for adding an edge (directed or undirected) to graph */
public void addEdge(int from, int to, E value, boolean directed);
/** Returns a list of edges for a vertex with a given index
* For undirected graphs, returns all edges incident on the vertex
* For directed graphs, only returns outward directed edges
* @param vertex index of the vertex to
* @return list of edges for this vertex
*/
public List<Edge<E>> getEdgesOut(int vertex);
/** Returns the degree of the vertex.<br>
* For undirected graphs, this is just the degree.<br>
* For directed graphs, this returns the outdegree
* @param vertex vertex to get degree for
* @return vertex degree
*/
public int getVertexDegree(int vertex);
/** Randomly sample a vertex connected to a given vertex. Sampling is done uniformly at random.
* Specifically, returns a random X such that either a directed edge (vertex -> X) exists,
* or an undirected edge (vertex -- X) exists<br>
* Can be used for example to implement a random walk on the graph (specifically: a unweighted random walk)
* @param vertex vertex to randomly sample from
* @param rng Random number generator to use
* @return A vertex connected to the specified vertex,
* @throws NoEdgesException thrown if the specified vertex has no edges, or no outgoing edges (in the case
* of a directed graph).
*/
public Vertex<V> getRandomConnectedVertex(int vertex, Random rng) throws NoEdgesException;
/**Get a list of all of the vertices that the specified vertex is connected to<br>
* Specifically, for undirected graphs return list of all X such that (vertex -- X) exists<br>
* For directed graphs, return list of all X such that (vertex -> X) exists
* @param vertex Index of the vertex
* @return list of vertices that the specified vertex is connected to
*/
public List<Vertex<V>> getConnectedVertices(int vertex);
/**Return an array of indexes of vertices that the specified vertex is connected to.<br>
* Specifically, for undirected graphs return int[] of all X.vertexID() such that (vertex -- X) exists<br>
* For directed graphs, return int[] of all X.vertexID() such that (vertex -> X) exists
* @param vertex index of the vertex
* @return list of vertices that the specified vertex is connected to
* @see #getConnectedVertices(int)
*/
public int[] getConnectedVertexIndices(int vertex);
}
@@ -0,0 +1,30 @@
/*
* ******************************************************************************
* *
* *
* * This program and the accompanying materials are made available under the
* * terms of the Apache License, Version 2.0 which is available at
* * https://www.apache.org/licenses/LICENSE-2.0.
* *
* * See the NOTICE file distributed with this work for additional
* * information regarding copyright ownership.
* * 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.
* *
* * SPDX-License-Identifier: Apache-2.0
* *****************************************************************************
*/
package org.deeplearning4j.graph.api;
import java.util.Iterator;
public interface IVertexSequence<T> extends Iterator<Vertex<T>> {
/** Length of the vertex sequence */
int sequenceLength();
}
@@ -0,0 +1,26 @@
/*
* ******************************************************************************
* *
* *
* * This program and the accompanying materials are made available under the
* * terms of the Apache License, Version 2.0 which is available at
* * https://www.apache.org/licenses/LICENSE-2.0.
* *
* * See the NOTICE file distributed with this work for additional
* * information regarding copyright ownership.
* * 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.
* *
* * SPDX-License-Identifier: Apache-2.0
* *****************************************************************************
*/
package org.deeplearning4j.graph.api;
public enum NoEdgeHandling {
SELF_LOOP_ON_DISCONNECTED, EXCEPTION_ON_DISCONNECTED
}
@@ -0,0 +1,63 @@
/*
* ******************************************************************************
* *
* *
* * This program and the accompanying materials are made available under the
* * terms of the Apache License, Version 2.0 which is available at
* * https://www.apache.org/licenses/LICENSE-2.0.
* *
* * See the NOTICE file distributed with this work for additional
* * information regarding copyright ownership.
* * 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.
* *
* * SPDX-License-Identifier: Apache-2.0
* *****************************************************************************
*/
package org.deeplearning4j.graph.api;
import lombok.AllArgsConstructor;
@AllArgsConstructor
public class Vertex<T> {
private final int idx;
private final T value;
public int vertexID() {
return idx;
}
public T getValue() {
return value;
}
@Override
public String toString() {
return "vertex(" + idx + "," + (value != null ? value : "") + ")";
}
@Override
public boolean equals(Object o) {
if (!(o instanceof Vertex))
return false;
Vertex<?> v = (Vertex<?>) o;
if (idx != v.idx)
return false;
if ((value == null && v.value != null) || (value != null && v.value == null))
return false;
return value == null || value.equals(v.value);
}
@Override
public int hashCode() {
int result = 17;
result = 31 * result + idx;
result = 31 * result + (value == null ? 0 : value.hashCode());
return result;
}
}
@@ -0,0 +1,32 @@
/*
* ******************************************************************************
* *
* *
* * This program and the accompanying materials are made available under the
* * terms of the Apache License, Version 2.0 which is available at
* * https://www.apache.org/licenses/LICENSE-2.0.
* *
* * See the NOTICE file distributed with this work for additional
* * information regarding copyright ownership.
* * 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.
* *
* * SPDX-License-Identifier: Apache-2.0
* *****************************************************************************
*/
package org.deeplearning4j.graph.data;
import org.deeplearning4j.graph.api.Edge;
public interface EdgeLineProcessor<E> {
/** Process a line of text into an edge.
* May return null if line is not a valid edge (i.e., comment line etc)
*/
Edge<E> processLine(String line);
}
@@ -0,0 +1,195 @@
/*
* ******************************************************************************
* *
* *
* * This program and the accompanying materials are made available under the
* * terms of the Apache License, Version 2.0 which is available at
* * https://www.apache.org/licenses/LICENSE-2.0.
* *
* * See the NOTICE file distributed with this work for additional
* * information regarding copyright ownership.
* * 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.
* *
* * SPDX-License-Identifier: Apache-2.0
* *****************************************************************************
*/
package org.deeplearning4j.graph.data;
import org.deeplearning4j.graph.api.Edge;
import org.deeplearning4j.graph.api.Vertex;
import org.deeplearning4j.graph.data.impl.DelimitedEdgeLineProcessor;
import org.deeplearning4j.graph.data.impl.WeightedEdgeLineProcessor;
import org.deeplearning4j.graph.Graph;
import org.deeplearning4j.graph.vertexfactory.StringVertexFactory;
import org.deeplearning4j.graph.vertexfactory.VertexFactory;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.List;
/** Utility methods for loading graphs
*
*/
public class GraphLoader {
private GraphLoader() {}
/** Simple method for loading an undirected graph, where the graph is represented by a edge list with one edge
* per line with a delimiter in between<br>
* This method assumes that all lines in the file are of the form {@code i<delim>j} where i and j are integers
* in range 0 to numVertices inclusive, and "<delim>" is the user-provided delimiter
* <b>Note</b>: this method calls {@link #loadUndirectedGraphEdgeListFile(String, int, String, boolean)} with allowMultipleEdges = true.
* @param path Path to the edge list file
* @param numVertices number of vertices in the graph
* @return graph
* @throws IOException if file cannot be read
*/
public static Graph<String, String> loadUndirectedGraphEdgeListFile(String path, int numVertices, String delim)
throws IOException {
return loadUndirectedGraphEdgeListFile(path, numVertices, delim, true);
}
/** Simple method for loading an undirected graph, where the graph is represented by a edge list with one edge
* per line with a delimiter in between<br>
* This method assumes that all lines in the file are of the form {@code i<delim>j} where i and j are integers
* in range 0 to numVertices inclusive, and "<delim>" is the user-provided delimiter
* @param path Path to the edge list file
* @param numVertices number of vertices in the graph
* @param allowMultipleEdges If set to false, the graph will not allow multiple edges between any two vertices to exist. However,
* checking for duplicates during graph loading can be costly, so use allowMultipleEdges=true when
* possible.
* @return graph
* @throws IOException if file cannot be read
*/
public static Graph<String, String> loadUndirectedGraphEdgeListFile(String path, int numVertices, String delim,
boolean allowMultipleEdges) throws IOException {
Graph<String, String> graph = new Graph<>(numVertices, allowMultipleEdges, new StringVertexFactory());
EdgeLineProcessor<String> lineProcessor = new DelimitedEdgeLineProcessor(delim, false);
try (BufferedReader br = new BufferedReader(new FileReader(new File(path)))) {
String line;
while ((line = br.readLine()) != null) {
Edge<String> edge = lineProcessor.processLine(line);
if (edge != null) {
graph.addEdge(edge);
}
}
}
return graph;
}
/**Method for loading a weighted graph from an edge list file, where each edge (inc. weight) is represented by a
* single line. Graph may be directed or undirected<br>
* This method assumes that edges are of the format: {@code fromIndex<delim>toIndex<delim>edgeWeight} where {@code <delim>}
* is the delimiter.
* <b>Note</b>: this method calls {@link #loadWeightedEdgeListFile(String, int, String, boolean, boolean, String...)} with allowMultipleEdges = true.
* @param path Path to the edge list file
* @param numVertices The number of vertices in the graph
* @param delim The delimiter used in the file (typically: "," or " " etc)
* @param directed whether the edges should be treated as directed (true) or undirected (false)
* @param ignoreLinesStartingWith Starting characters for comment lines. May be null. For example: "//" or "#"
* @return The graph
* @throws IOException
*/
public static Graph<String, Double> loadWeightedEdgeListFile(String path, int numVertices, String delim,
boolean directed, String... ignoreLinesStartingWith) throws IOException {
return loadWeightedEdgeListFile(path, numVertices, delim, directed, true, ignoreLinesStartingWith);
}
/**Method for loading a weighted graph from an edge list file, where each edge (inc. weight) is represented by a
* single line. Graph may be directed or undirected<br>
* This method assumes that edges are of the format: {@code fromIndex<delim>toIndex<delim>edgeWeight} where {@code <delim>}
* is the delimiter.
* @param path Path to the edge list file
* @param numVertices The number of vertices in the graph
* @param delim The delimiter used in the file (typically: "," or " " etc)
* @param directed whether the edges should be treated as directed (true) or undirected (false)
* @param allowMultipleEdges If set to false, the graph will not allow multiple edges between any two vertices to exist. However,
* checking for duplicates during graph loading can be costly, so use allowMultipleEdges=true when
* possible.
* @param ignoreLinesStartingWith Starting characters for comment lines. May be null. For example: "//" or "#"
* @return The graph
* @throws IOException
*/
public static Graph<String, Double> loadWeightedEdgeListFile(String path, int numVertices, String delim,
boolean directed, boolean allowMultipleEdges, String... ignoreLinesStartingWith)
throws IOException {
Graph<String, Double> graph = new Graph<>(numVertices, allowMultipleEdges, new StringVertexFactory());
EdgeLineProcessor<Double> lineProcessor =
new WeightedEdgeLineProcessor(delim, directed, ignoreLinesStartingWith);
try (BufferedReader br = new BufferedReader(new FileReader(new File(path)))) {
String line;
while ((line = br.readLine()) != null) {
Edge<Double> edge = lineProcessor.processLine(line);
if (edge != null) {
graph.addEdge(edge);
}
}
}
return graph;
}
/** Load a graph into memory, using a given EdgeLineProcessor.
* Assume one edge per line
* @param path Path to the file containing the edges, one per line
* @param lineProcessor EdgeLineProcessor used to convert lines of text into a graph (or null for comment lines etc)
* @param vertexFactory Used to create vertices
* @param numVertices number of vertices in the graph
* @param allowMultipleEdges whether the graph should allow multiple edges between a given pair of vertices or not
* @return IGraph
*/
public static <V, E> Graph<V, E> loadGraph(String path, EdgeLineProcessor<E> lineProcessor,
VertexFactory<V> vertexFactory, int numVertices, boolean allowMultipleEdges) throws IOException {
Graph<V, E> graph = new Graph<>(numVertices, allowMultipleEdges, vertexFactory);
try (BufferedReader br = new BufferedReader(new FileReader(new File(path)))) {
String line;
while ((line = br.readLine()) != null) {
Edge<E> edge = lineProcessor.processLine(line);
if (edge != null) {
graph.addEdge(edge);
}
}
}
return graph;
}
/** Load graph, assuming vertices are in one file and edges are in another file.
*
* @param vertexFilePath Path to file containing vertices, one per line
* @param edgeFilePath Path to the file containing edges, one per line
* @param vertexLoader VertexLoader, for loading vertices from the file
* @param edgeLineProcessor EdgeLineProcessor, converts text lines into edges
* @param allowMultipleEdges whether the graph should allow (or filter out) multiple edges
* @return IGraph loaded from files
*/
public static <V, E> Graph<V, E> loadGraph(String vertexFilePath, String edgeFilePath, VertexLoader<V> vertexLoader,
EdgeLineProcessor<E> edgeLineProcessor, boolean allowMultipleEdges) throws IOException {
//Assume vertices are in one file
//And edges are in another file
List<Vertex<V>> vertices = vertexLoader.loadVertices(vertexFilePath);
Graph<V, E> graph = new Graph<>(vertices, allowMultipleEdges);
try (BufferedReader br = new BufferedReader(new FileReader(new File(edgeFilePath)))) {
String line;
while ((line = br.readLine()) != null) {
Edge<E> edge = edgeLineProcessor.processLine(line);
if (edge != null) {
graph.addEdge(edge);
}
}
}
return graph;
}
}
@@ -0,0 +1,32 @@
/*
* ******************************************************************************
* *
* *
* * This program and the accompanying materials are made available under the
* * terms of the Apache License, Version 2.0 which is available at
* * https://www.apache.org/licenses/LICENSE-2.0.
* *
* * See the NOTICE file distributed with this work for additional
* * information regarding copyright ownership.
* * 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.
* *
* * SPDX-License-Identifier: Apache-2.0
* *****************************************************************************
*/
package org.deeplearning4j.graph.data;
import org.deeplearning4j.graph.api.Vertex;
import java.io.IOException;
import java.util.List;
public interface VertexLoader<V> {
List<Vertex<V>> loadVertices(String path) throws IOException;
}
@@ -0,0 +1,60 @@
/*
* ******************************************************************************
* *
* *
* * This program and the accompanying materials are made available under the
* * terms of the Apache License, Version 2.0 which is available at
* * https://www.apache.org/licenses/LICENSE-2.0.
* *
* * See the NOTICE file distributed with this work for additional
* * information regarding copyright ownership.
* * 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.
* *
* * SPDX-License-Identifier: Apache-2.0
* *****************************************************************************
*/
package org.deeplearning4j.graph.data.impl;
import org.deeplearning4j.graph.api.Edge;
import org.deeplearning4j.graph.data.EdgeLineProcessor;
public class DelimitedEdgeLineProcessor implements EdgeLineProcessor<String> {
private final String delimiter;
private final String[] skipLinesStartingWith;
private final boolean directed;
public DelimitedEdgeLineProcessor(String delimiter, boolean directed) {
this(delimiter, directed, null);
}
public DelimitedEdgeLineProcessor(String delimiter, boolean directed, String... skipLinesStartingWith) {
this.delimiter = delimiter;
this.skipLinesStartingWith = skipLinesStartingWith;
this.directed = directed;
}
@Override
public Edge<String> processLine(String line) {
if (skipLinesStartingWith != null) {
for (String s : skipLinesStartingWith) {
if (line.startsWith(s))
return null;
}
}
String[] split = line.split(delimiter);
if (split.length != 2)
throw new IllegalArgumentException(
"Invalid line: expected format \"" + 0 + delimiter + 1 + "\"; received \"" + line + "\"");
int from = Integer.parseInt(split[0]);
int to = Integer.parseInt(split[1]);
String edgeName = from + (directed ? "->" : "--") + to;
return new Edge<>(from, to, edgeName, directed);
}
}
@@ -0,0 +1,84 @@
/*
* ******************************************************************************
* *
* *
* * This program and the accompanying materials are made available under the
* * terms of the Apache License, Version 2.0 which is available at
* * https://www.apache.org/licenses/LICENSE-2.0.
* *
* * See the NOTICE file distributed with this work for additional
* * information regarding copyright ownership.
* * 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.
* *
* * SPDX-License-Identifier: Apache-2.0
* *****************************************************************************
*/
package org.deeplearning4j.graph.data.impl;
import org.deeplearning4j.graph.api.Vertex;
import org.deeplearning4j.graph.data.VertexLoader;
import org.deeplearning4j.graph.exception.ParseException;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**Load vertex information, one per line of form "0<delim>Some text attribute/label"
*/
public class DelimitedVertexLoader implements VertexLoader<String> {
private final String delimiter;
private final String[] ignoreLinesPrefix;
public DelimitedVertexLoader(String delimiter) {
this(delimiter, null);
}
public DelimitedVertexLoader(String delimiter, String... ignoreLinesPrefix) {
this.delimiter = delimiter;
this.ignoreLinesPrefix = ignoreLinesPrefix;
}
@Override
public List<Vertex<String>> loadVertices(String path) throws IOException {
List<Vertex<String>> vertices = new ArrayList<>();
int lineCount = 0;
try (BufferedReader br = new BufferedReader(new FileReader(new File(path)))) {
String line;
while ((line = br.readLine()) != null) {
lineCount++;
if (ignoreLinesPrefix != null) {
boolean skipLine = false;
for (String s : ignoreLinesPrefix) {
if (line.startsWith(s)) {
skipLine = true;
break;
}
}
if (skipLine)
continue;
}
int idx = line.indexOf(delimiter);
if (idx == -1)
throw new ParseException("Error parsing line (could not find delimiter): " + line);
String first = line.substring(0, idx);
String second = line.substring(idx + 1);
vertices.add(new Vertex<>(Integer.parseInt(first), second));
}
}
return vertices;
}
}
@@ -0,0 +1,60 @@
/*
* ******************************************************************************
* *
* *
* * This program and the accompanying materials are made available under the
* * terms of the Apache License, Version 2.0 which is available at
* * https://www.apache.org/licenses/LICENSE-2.0.
* *
* * See the NOTICE file distributed with this work for additional
* * information regarding copyright ownership.
* * 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.
* *
* * SPDX-License-Identifier: Apache-2.0
* *****************************************************************************
*/
package org.deeplearning4j.graph.data.impl;
import org.deeplearning4j.graph.api.Edge;
import org.deeplearning4j.graph.data.EdgeLineProcessor;
public class WeightedEdgeLineProcessor implements EdgeLineProcessor<Double> {
private final String delimiter;
private final String[] skipLinesStartingWith;
private final boolean directed;
public WeightedEdgeLineProcessor(String delimiter, boolean directed) {
this(delimiter, directed, null);
}
public WeightedEdgeLineProcessor(String delimiter, boolean directed, String... skipLinesStartingWith) {
this.delimiter = delimiter;
this.skipLinesStartingWith = skipLinesStartingWith;
this.directed = directed;
}
@Override
public Edge<Double> processLine(String line) {
if (skipLinesStartingWith != null) {
for (String s : skipLinesStartingWith) {
if (line.startsWith(s))
return null;
}
}
String[] split = line.split(delimiter);
if (split.length != 3)
throw new IllegalArgumentException("Invalid line: expected format \"" + 0 + delimiter + 1 + delimiter
+ "weight\"; received \"" + line + "\"");
int from = Integer.parseInt(split[0]);
int to = Integer.parseInt(split[1]);
double weight = Double.parseDouble(split[2]);
return new Edge<>(from, to, weight, directed);
}
}
@@ -0,0 +1,37 @@
/*
* ******************************************************************************
* *
* *
* * This program and the accompanying materials are made available under the
* * terms of the Apache License, Version 2.0 which is available at
* * https://www.apache.org/licenses/LICENSE-2.0.
* *
* * See the NOTICE file distributed with this work for additional
* * information regarding copyright ownership.
* * 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.
* *
* * SPDX-License-Identifier: Apache-2.0
* *****************************************************************************
*/
package org.deeplearning4j.graph.exception;
public class NoEdgesException extends RuntimeException {
public NoEdgesException() {
super();
}
public NoEdgesException(String s) {
super(s);
}
public NoEdgesException(String s, Exception e) {
super(s, e);
}
}
@@ -0,0 +1,35 @@
/*
* ******************************************************************************
* *
* *
* * This program and the accompanying materials are made available under the
* * terms of the Apache License, Version 2.0 which is available at
* * https://www.apache.org/licenses/LICENSE-2.0.
* *
* * See the NOTICE file distributed with this work for additional
* * information regarding copyright ownership.
* * 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.
* *
* * SPDX-License-Identifier: Apache-2.0
* *****************************************************************************
*/
package org.deeplearning4j.graph.exception;
public class ParseException extends RuntimeException {
public ParseException() {
super();
}
public ParseException(String s) {
super(s);
}
public ParseException(String s, Exception e) {
super(s, e);
}
}
@@ -0,0 +1,41 @@
/*
* ******************************************************************************
* *
* *
* * This program and the accompanying materials are made available under the
* * terms of the Apache License, Version 2.0 which is available at
* * https://www.apache.org/licenses/LICENSE-2.0.
* *
* * See the NOTICE file distributed with this work for additional
* * information regarding copyright ownership.
* * 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.
* *
* * SPDX-License-Identifier: Apache-2.0
* *****************************************************************************
*/
package org.deeplearning4j.graph.iterator;
import org.deeplearning4j.graph.api.IVertexSequence;
public interface GraphWalkIterator<T> {
/** Length of the walks returned by next()
* Note that a walk of length {@code i} contains {@code i+1} vertices
*/
int walkLength();
/**Get the next vertex sequence.
*/
IVertexSequence<T> next();
/** Whether the iterator has any more vertex sequences. */
boolean hasNext();
/** Reset the graph walk iterator. */
void reset();
}
@@ -0,0 +1,151 @@
/*
* ******************************************************************************
* *
* *
* * This program and the accompanying materials are made available under the
* * terms of the Apache License, Version 2.0 which is available at
* * https://www.apache.org/licenses/LICENSE-2.0.
* *
* * See the NOTICE file distributed with this work for additional
* * information regarding copyright ownership.
* * 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.
* *
* * SPDX-License-Identifier: Apache-2.0
* *****************************************************************************
*/
package org.deeplearning4j.graph.iterator;
import org.deeplearning4j.graph.api.IGraph;
import org.deeplearning4j.graph.api.IVertexSequence;
import org.deeplearning4j.graph.api.NoEdgeHandling;
import org.deeplearning4j.graph.api.Vertex;
import org.deeplearning4j.graph.exception.NoEdgesException;
import org.deeplearning4j.graph.VertexSequence;
import java.util.NoSuchElementException;
import java.util.Random;
public class RandomWalkIterator<V> implements GraphWalkIterator<V> {
private final IGraph<V, ?> graph;
private final int walkLength;
private final NoEdgeHandling mode;
private final int firstVertex;
private final int lastVertex;
private int position;
private Random rng;
private int[] order;
public RandomWalkIterator(IGraph<V, ?> graph, int walkLength) {
this(graph, walkLength, System.currentTimeMillis(), NoEdgeHandling.EXCEPTION_ON_DISCONNECTED);
}
/**Construct a RandomWalkIterator for a given graph, with a specified walk length and random number generator seed.<br>
* Uses {@code NoEdgeHandling.EXCEPTION_ON_DISCONNECTED} - hence exception will be thrown when generating random
* walks on graphs with vertices containing having no edges, or no outgoing edges (for directed graphs)
* @see #RandomWalkIterator(IGraph, int, long, NoEdgeHandling)
*/
public RandomWalkIterator(IGraph<V, ?> graph, int walkLength, long rngSeed) {
this(graph, walkLength, rngSeed, NoEdgeHandling.EXCEPTION_ON_DISCONNECTED);
}
/**
* @param graph IGraph to conduct walks on
* @param walkLength length of each walk. Walk of length 0 includes 1 vertex, walk of 1 includes 2 vertices etc
* @param rngSeed seed for randomization
* @param mode mode for handling random walks from vertices with either no edges, or no outgoing edges (for directed graphs)
*/
public RandomWalkIterator(IGraph<V, ?> graph, int walkLength, long rngSeed, NoEdgeHandling mode) {
this(graph, walkLength, rngSeed, mode, 0, graph.numVertices());
}
/**Constructor used to generate random walks starting at a subset of the vertices in the graph. Order of starting
* vertices is randomized within this subset
* @param graph IGraph to conduct walks on
* @param walkLength length of each walk. Walk of length 0 includes 1 vertex, walk of 1 includes 2 vertices etc
* @param rngSeed seed for randomization
* @param mode mode for handling random walks from vertices with either no edges, or no outgoing edges (for directed graphs)
* @param firstVertex first vertex index (inclusive) to start random walks from
* @param lastVertex last vertex index (exclusive) to start random walks from
*/
public RandomWalkIterator(IGraph<V, ?> graph, int walkLength, long rngSeed, NoEdgeHandling mode, int firstVertex,
int lastVertex) {
this.graph = graph;
this.walkLength = walkLength;
this.rng = new Random(rngSeed);
this.mode = mode;
this.firstVertex = firstVertex;
this.lastVertex = lastVertex;
order = new int[lastVertex - firstVertex];
for (int i = 0; i < order.length; i++)
order[i] = firstVertex + i;
reset();
}
@Override
public IVertexSequence<V> next() {
if (!hasNext())
throw new NoSuchElementException();
//Generate a random walk starting at vertex order[current]
int currVertexIdx = order[position++];
int[] indices = new int[walkLength + 1];
indices[0] = currVertexIdx;
if (walkLength == 0)
return new VertexSequence<>(graph, indices);
Vertex<V> next;
try {
next = graph.getRandomConnectedVertex(currVertexIdx, rng);
} catch (NoEdgesException e) {
switch (mode) {
case SELF_LOOP_ON_DISCONNECTED:
for (int i = 1; i < walkLength; i++)
indices[i] = currVertexIdx;
return new VertexSequence<>(graph, indices);
case EXCEPTION_ON_DISCONNECTED:
throw e;
default:
throw new RuntimeException("Unknown/not implemented NoEdgeHandling mode: " + mode);
}
}
indices[1] = next.vertexID();
currVertexIdx = indices[1];
for (int i = 2; i <= walkLength; i++) { //<= walk length: i.e., if walk length = 2, it contains 3 vertices etc
next = graph.getRandomConnectedVertex(currVertexIdx, rng);
currVertexIdx = next.vertexID();
indices[i] = currVertexIdx;
}
return new VertexSequence<>(graph, indices);
}
@Override
public boolean hasNext() {
return position < order.length;
}
@Override
public void reset() {
position = 0;
//https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm
for (int i = order.length - 1; i > 0; i--) {
int j = rng.nextInt(i + 1);
int temp = order[j];
order[j] = order[i];
order[i] = temp;
}
}
@Override
public int walkLength() {
return walkLength;
}
}
@@ -0,0 +1,176 @@
/*
* ******************************************************************************
* *
* *
* * This program and the accompanying materials are made available under the
* * terms of the Apache License, Version 2.0 which is available at
* * https://www.apache.org/licenses/LICENSE-2.0.
* *
* * See the NOTICE file distributed with this work for additional
* * information regarding copyright ownership.
* * 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.
* *
* * SPDX-License-Identifier: Apache-2.0
* *****************************************************************************
*/
package org.deeplearning4j.graph.iterator;
import org.deeplearning4j.graph.api.Edge;
import org.deeplearning4j.graph.api.IGraph;
import org.deeplearning4j.graph.api.IVertexSequence;
import org.deeplearning4j.graph.api.NoEdgeHandling;
import org.deeplearning4j.graph.exception.NoEdgesException;
import org.deeplearning4j.graph.VertexSequence;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Random;
public class WeightedRandomWalkIterator<V> implements GraphWalkIterator<V> {
private final IGraph<V, ? extends Number> graph;
private final int walkLength;
private final NoEdgeHandling mode;
private final int firstVertex;
private final int lastVertex;
private int position;
private Random rng;
private int[] order;
public WeightedRandomWalkIterator(IGraph<V, ? extends Number> graph, int walkLength) {
this(graph, walkLength, System.currentTimeMillis(), NoEdgeHandling.EXCEPTION_ON_DISCONNECTED);
}
/**Construct a RandomWalkIterator for a given graph, with a specified walk length and random number generator seed.<br>
* Uses {@code NoEdgeHandling.EXCEPTION_ON_DISCONNECTED} - hence exception will be thrown when generating random
* walks on graphs with vertices containing having no edges, or no outgoing edges (for directed graphs)
* @see #WeightedRandomWalkIterator(IGraph, int, long, NoEdgeHandling)
*/
public WeightedRandomWalkIterator(IGraph<V, ? extends Number> graph, int walkLength, long rngSeed) {
this(graph, walkLength, rngSeed, NoEdgeHandling.EXCEPTION_ON_DISCONNECTED);
}
/**
* @param graph IGraph to conduct walks on
* @param walkLength length of each walk. Walk of length 0 includes 1 vertex, walk of 1 includes 2 vertices etc
* @param rngSeed seed for randomization
* @param mode mode for handling random walks from vertices with either no edges, or no outgoing edges (for directed graphs)
*/
public WeightedRandomWalkIterator(IGraph<V, ? extends Number> graph, int walkLength, long rngSeed,
NoEdgeHandling mode) {
this(graph, walkLength, rngSeed, mode, 0, graph.numVertices());
}
/**Constructor used to generate random walks starting at a subset of the vertices in the graph. Order of starting
* vertices is randomized within this subset
* @param graph IGraph to conduct walks on
* @param walkLength length of each walk. Walk of length 0 includes 1 vertex, walk of 1 includes 2 vertices etc
* @param rngSeed seed for randomization
* @param mode mode for handling random walks from vertices with either no edges, or no outgoing edges (for directed graphs)
* @param firstVertex first vertex index (inclusive) to start random walks from
* @param lastVertex last vertex index (exclusive) to start random walks from
*/
public WeightedRandomWalkIterator(IGraph<V, ? extends Number> graph, int walkLength, long rngSeed,
NoEdgeHandling mode, int firstVertex, int lastVertex) {
this.graph = graph;
this.walkLength = walkLength;
this.rng = new Random(rngSeed);
this.mode = mode;
this.firstVertex = firstVertex;
this.lastVertex = lastVertex;
order = new int[lastVertex - firstVertex];
for (int i = 0; i < order.length; i++)
order[i] = firstVertex + i;
reset();
}
@Override
public IVertexSequence<V> next() {
if (!hasNext())
throw new NoSuchElementException();
//Generate a weighted random walk starting at vertex order[current]
int currVertexIdx = order[position++];
int[] indices = new int[walkLength + 1];
indices[0] = currVertexIdx;
if (walkLength == 0)
return new VertexSequence<>(graph, indices);
for (int i = 1; i <= walkLength; i++) {
List<? extends Edge<? extends Number>> edgeList = graph.getEdgesOut(currVertexIdx);
//First: check if there are any outgoing edges from this vertex. If not: handle the situation
if (edgeList == null || edgeList.isEmpty()) {
switch (mode) {
case SELF_LOOP_ON_DISCONNECTED:
for (int j = i; j < walkLength; j++)
indices[j] = currVertexIdx;
return new VertexSequence<>(graph, indices);
case EXCEPTION_ON_DISCONNECTED:
throw new NoEdgesException("Cannot conduct random walk: vertex " + currVertexIdx
+ " has no outgoing edges. "
+ " Set NoEdgeHandling mode to NoEdgeHandlingMode.SELF_LOOP_ON_DISCONNECTED to self loop instead of "
+ "throwing an exception in this situation.");
default:
throw new RuntimeException("Unknown/not implemented NoEdgeHandling mode: " + mode);
}
}
//To do a weighted random walk: we need to know total weight of all outgoing edges
double totalWeight = 0.0;
for (Edge<? extends Number> edge : edgeList) {
totalWeight += edge.getValue().doubleValue();
}
double d = rng.nextDouble();
double threshold = d * totalWeight;
double sumWeight = 0.0;
for (Edge<? extends Number> edge : edgeList) {
sumWeight += edge.getValue().doubleValue();
if (sumWeight >= threshold) {
if (edge.isDirected()) {
currVertexIdx = edge.getTo();
} else {
if (edge.getFrom() == currVertexIdx) {
currVertexIdx = edge.getTo();
} else {
currVertexIdx = edge.getFrom(); //Undirected edge: might be next--currVertexIdx instead of currVertexIdx--next
}
}
indices[i] = currVertexIdx;
break;
}
}
}
return new VertexSequence<>(graph, indices);
}
@Override
public boolean hasNext() {
return position < order.length;
}
@Override
public void reset() {
position = 0;
//https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm
for (int i = order.length - 1; i > 0; i--) {
int j = rng.nextInt(i + 1);
int temp = order[j];
order[j] = order[i];
order[i] = temp;
}
}
@Override
public int walkLength() {
return walkLength;
}
}
@@ -0,0 +1,35 @@
/*
* ******************************************************************************
* *
* *
* * This program and the accompanying materials are made available under the
* * terms of the Apache License, Version 2.0 which is available at
* * https://www.apache.org/licenses/LICENSE-2.0.
* *
* * See the NOTICE file distributed with this work for additional
* * information regarding copyright ownership.
* * 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.
* *
* * SPDX-License-Identifier: Apache-2.0
* *****************************************************************************
*/
package org.deeplearning4j.graph.iterator.parallel;
import org.deeplearning4j.graph.iterator.GraphWalkIterator;
import java.util.List;
public interface GraphWalkIteratorProvider<V> {
/**Get a list of GraphWalkIterators. In general: may return less than the specified number of iterators,
* (for example, for small networks) but never more than it
* @param numIterators Number of iterators to return
*/
List<GraphWalkIterator<V>> getGraphWalkIterators(int numIterators);
}
@@ -0,0 +1,74 @@
/*
* ******************************************************************************
* *
* *
* * This program and the accompanying materials are made available under the
* * terms of the Apache License, Version 2.0 which is available at
* * https://www.apache.org/licenses/LICENSE-2.0.
* *
* * See the NOTICE file distributed with this work for additional
* * information regarding copyright ownership.
* * 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.
* *
* * SPDX-License-Identifier: Apache-2.0
* *****************************************************************************
*/
package org.deeplearning4j.graph.iterator.parallel;
import org.deeplearning4j.graph.api.IGraph;
import org.deeplearning4j.graph.api.NoEdgeHandling;
import org.deeplearning4j.graph.iterator.GraphWalkIterator;
import org.deeplearning4j.graph.iterator.RandomWalkIterator;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class RandomWalkGraphIteratorProvider<V> implements GraphWalkIteratorProvider<V> {
private IGraph<V, ?> graph;
private int walkLength;
private Random rng;
private NoEdgeHandling mode;
public RandomWalkGraphIteratorProvider(IGraph<V, ?> graph, int walkLength) {
this(graph, walkLength, System.currentTimeMillis(), NoEdgeHandling.EXCEPTION_ON_DISCONNECTED);
}
public RandomWalkGraphIteratorProvider(IGraph<V, ?> graph, int walkLength, long seed, NoEdgeHandling mode) {
this.graph = graph;
this.walkLength = walkLength;
this.rng = new Random(seed);
this.mode = mode;
}
@Override
public List<GraphWalkIterator<V>> getGraphWalkIterators(int numIterators) {
int nVertices = graph.numVertices();
if (numIterators > nVertices)
numIterators = nVertices;
int verticesPerIter = nVertices / numIterators;
List<GraphWalkIterator<V>> list = new ArrayList<>(numIterators);
int last = 0;
for (int i = 0; i < numIterators; i++) {
int from = last;
int to = Math.min(nVertices, from + verticesPerIter);
if (i == numIterators - 1)
to = nVertices;
GraphWalkIterator<V> iter = new RandomWalkIterator<>(graph, walkLength, rng.nextLong(), mode, from, to);
list.add(iter);
last = to;
}
return list;
}
}
@@ -0,0 +1,76 @@
/*
* ******************************************************************************
* *
* *
* * This program and the accompanying materials are made available under the
* * terms of the Apache License, Version 2.0 which is available at
* * https://www.apache.org/licenses/LICENSE-2.0.
* *
* * See the NOTICE file distributed with this work for additional
* * information regarding copyright ownership.
* * 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.
* *
* * SPDX-License-Identifier: Apache-2.0
* *****************************************************************************
*/
package org.deeplearning4j.graph.iterator.parallel;
import org.deeplearning4j.graph.api.IGraph;
import org.deeplearning4j.graph.api.NoEdgeHandling;
import org.deeplearning4j.graph.iterator.GraphWalkIterator;
import org.deeplearning4j.graph.iterator.WeightedRandomWalkIterator;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class WeightedRandomWalkGraphIteratorProvider<V> implements GraphWalkIteratorProvider<V> {
private IGraph<V, ? extends Number> graph;
private int walkLength;
private Random rng;
private NoEdgeHandling mode;
public WeightedRandomWalkGraphIteratorProvider(IGraph<V, ? extends Number> graph, int walkLength) {
this(graph, walkLength, System.currentTimeMillis(), NoEdgeHandling.EXCEPTION_ON_DISCONNECTED);
}
public WeightedRandomWalkGraphIteratorProvider(IGraph<V, ? extends Number> graph, int walkLength, long seed,
NoEdgeHandling mode) {
this.graph = graph;
this.walkLength = walkLength;
this.rng = new Random(seed);
this.mode = mode;
}
@Override
public List<GraphWalkIterator<V>> getGraphWalkIterators(int numIterators) {
int nVertices = graph.numVertices();
if (numIterators > nVertices)
numIterators = nVertices;
int verticesPerIter = nVertices / numIterators;
List<GraphWalkIterator<V>> list = new ArrayList<>(numIterators);
int last = 0;
for (int i = 0; i < numIterators; i++) {
int from = last;
int to = Math.min(nVertices, from + verticesPerIter);
if (i == numIterators - 1)
to = nVertices;
GraphWalkIterator<V> iter =
new WeightedRandomWalkIterator<>(graph, walkLength, rng.nextLong(), mode, from, to);
list.add(iter);
last = to;
}
return list;
}
}
@@ -0,0 +1,32 @@
/*
* ******************************************************************************
* *
* *
* * This program and the accompanying materials are made available under the
* * terms of the Apache License, Version 2.0 which is available at
* * https://www.apache.org/licenses/LICENSE-2.0.
* *
* * See the NOTICE file distributed with this work for additional
* * information regarding copyright ownership.
* * 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.
* *
* * SPDX-License-Identifier: Apache-2.0
* *****************************************************************************
*/
package org.deeplearning4j.graph.models;
public interface BinaryTree {
long getCode(int element);
int getCodeLength(int element);
String getCodeString(int element);
int[] getPathInnerNodes(int element);
}
@@ -0,0 +1,47 @@
/*
* ******************************************************************************
* *
* *
* * This program and the accompanying materials are made available under the
* * terms of the Apache License, Version 2.0 which is available at
* * https://www.apache.org/licenses/LICENSE-2.0.
* *
* * See the NOTICE file distributed with this work for additional
* * information regarding copyright ownership.
* * 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.
* *
* * SPDX-License-Identifier: Apache-2.0
* *****************************************************************************
*/
package org.deeplearning4j.graph.models;
import org.deeplearning4j.graph.api.IGraph;
import org.deeplearning4j.graph.api.Vertex;
import org.nd4j.linalg.api.ndarray.INDArray;
import java.io.Serializable;
public interface GraphVectors<V, E> extends Serializable {
public IGraph<V, E> getGraph();
public int numVertices();
public int getVectorSize();
public INDArray getVertexVector(Vertex<V> vertex);
public INDArray getVertexVector(int vertexIdx);
public int[] verticesNearest(int vertexIdx, int top);
double similarity(Vertex<V> vertex1, Vertex<V> vertex2);
double similarity(int vertexIdx1, int vertexIdx2);
}
@@ -0,0 +1,254 @@
/*
* ******************************************************************************
* *
* *
* * This program and the accompanying materials are made available under the
* * terms of the Apache License, Version 2.0 which is available at
* * https://www.apache.org/licenses/LICENSE-2.0.
* *
* * See the NOTICE file distributed with this work for additional
* * information regarding copyright ownership.
* * 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.
* *
* * SPDX-License-Identifier: Apache-2.0
* *****************************************************************************
*/
package org.deeplearning4j.graph.models.deepwalk;
import lombok.AllArgsConstructor;
import org.deeplearning4j.graph.api.IGraph;
import org.deeplearning4j.graph.api.IVertexSequence;
import org.deeplearning4j.graph.api.NoEdgeHandling;
import org.deeplearning4j.graph.iterator.GraphWalkIterator;
import org.deeplearning4j.graph.iterator.parallel.GraphWalkIteratorProvider;
import org.deeplearning4j.graph.iterator.parallel.RandomWalkGraphIteratorProvider;
import org.deeplearning4j.graph.models.embeddings.GraphVectorLookupTable;
import org.deeplearning4j.graph.models.embeddings.GraphVectorsImpl;
import org.deeplearning4j.graph.models.embeddings.InMemoryGraphLookupTable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.threadly.concurrent.PriorityScheduler;
import org.threadly.concurrent.future.FutureUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
public class DeepWalk<V, E> extends GraphVectorsImpl<V, E> {
public static final int STATUS_UPDATE_FREQUENCY = 1000;
private Logger log = LoggerFactory.getLogger(DeepWalk.class);
private int vectorSize;
private int windowSize;
private double learningRate;
private boolean initCalled = false;
private long seed;
private int nThreads = Runtime.getRuntime().availableProcessors();
private transient AtomicLong walkCounter = new AtomicLong(0);
public DeepWalk() {
}
public int getVectorSize() {
return vectorSize;
}
public int getWindowSize() {
return windowSize;
}
public double getLearningRate() {
return learningRate;
}
public void setLearningRate(double learningRate) {
this.learningRate = learningRate;
if (lookupTable != null)
lookupTable.setLearningRate(learningRate);
}
/** Initialize the DeepWalk model with a given graph. */
public void initialize(IGraph<V, E> graph) {
int nVertices = graph.numVertices();
int[] degrees = new int[nVertices];
for (int i = 0; i < nVertices; i++)
degrees[i] = graph.getVertexDegree(i);
initialize(degrees);
}
/** Initialize the DeepWalk model with a list of vertex degrees for a graph.<br>
* Specifically, graphVertexDegrees[i] represents the vertex degree of the ith vertex<br>
* vertex degrees are used to construct a binary (Huffman) tree, which is in turn used in
* the hierarchical softmax implementation
* @param graphVertexDegrees degrees of each vertex
*/
public void initialize(int[] graphVertexDegrees) {
log.info("Initializing: Creating Huffman tree and lookup table...");
GraphHuffman gh = new GraphHuffman(graphVertexDegrees.length);
gh.buildTree(graphVertexDegrees);
lookupTable = new InMemoryGraphLookupTable(graphVertexDegrees.length, vectorSize, gh, learningRate);
initCalled = true;
log.info("Initialization complete");
}
/** Fit the model, in parallel.
* This creates a set of GraphWalkIterators, which are then distributed one to each thread
* @param graph Graph to fit
* @param walkLength Length of rangom walks to generate
*/
public void fit(IGraph<V, E> graph, int walkLength) {
if (!initCalled)
initialize(graph);
//First: create iterators, one for each thread
GraphWalkIteratorProvider<V> iteratorProvider = new RandomWalkGraphIteratorProvider<>(graph, walkLength, seed,
NoEdgeHandling.SELF_LOOP_ON_DISCONNECTED);
fit(iteratorProvider);
}
/** Fit the model, in parallel, using a given GraphWalkIteratorProvider.<br>
* This object is used to generate multiple GraphWalkIterators, which can then be distributed to each thread
* to do in parallel<br>
* Note that {@link #fit(IGraph, int)} will be more convenient in many cases<br>
* Note that {@link #initialize(IGraph)} or {@link #initialize(int[])} <em>must</em> be called first.
* @param iteratorProvider GraphWalkIteratorProvider
* @see #fit(IGraph, int)
*/
public void fit(GraphWalkIteratorProvider<V> iteratorProvider) {
if (!initCalled)
throw new UnsupportedOperationException("DeepWalk not initialized (call initialize before fit)");
List<GraphWalkIterator<V>> iteratorList = iteratorProvider.getGraphWalkIterators(nThreads);
PriorityScheduler scheduler = new PriorityScheduler(nThreads);
List<Future<Void>> list = new ArrayList<>(iteratorList.size());
//log.info("Fitting Graph with {} threads", Math.max(nThreads,iteratorList.size()));
for (GraphWalkIterator<V> iter : iteratorList) {
LearningCallable c = new LearningCallable(iter);
list.add(scheduler.submit(c));
}
scheduler.shutdown(); // wont shutdown till complete
try {
FutureUtils.blockTillAllCompleteOrFirstError(list);
} catch (InterruptedException e) {
// should not be possible with blocking till scheduler terminates
Thread.currentThread().interrupt();
throw new RuntimeException(e);
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
}
/**Fit the DeepWalk model <b>using a single thread</b> using a given GraphWalkIterator. If parallel fitting is required,
* {@link #fit(IGraph, int)} or {@link #fit(GraphWalkIteratorProvider)} should be used.<br>
* Note that {@link #initialize(IGraph)} or {@link #initialize(int[])} <em>must</em> be called first.
*
* @param iterator iterator for graph walks
*/
public void fit(GraphWalkIterator<V> iterator) {
if (!initCalled)
throw new UnsupportedOperationException("DeepWalk not initialized (call initialize before fit)");
int walkLength = iterator.walkLength();
while (iterator.hasNext()) {
IVertexSequence<V> sequence = iterator.next();
//Skipgram model:
int[] walk = new int[walkLength + 1];
int i = 0;
while (sequence.hasNext())
walk[i++] = sequence.next().vertexID();
skipGram(walk);
long iter = walkCounter.incrementAndGet();
if (iter % STATUS_UPDATE_FREQUENCY == 0) {
log.info("Processed {} random walks on graph", iter);
}
}
}
private void skipGram(int[] walk) {
for (int mid = windowSize; mid < walk.length - windowSize; mid++) {
for (int pos = mid - windowSize; pos <= mid + windowSize; pos++) {
if (pos == mid)
continue;
//pair of vertices: walk[mid] -> walk[pos]
lookupTable.iterate(walk[mid], walk[pos]);
}
}
}
public GraphVectorLookupTable lookupTable() {
return lookupTable;
}
public static class Builder<V, E> {
private int vectorSize = 100;
private long seed = System.currentTimeMillis();
private double learningRate = 0.01;
private int windowSize = 2;
/** Sets the size of the vectors to be learned for each vertex in the graph */
public Builder<V, E> vectorSize(int vectorSize) {
this.vectorSize = vectorSize;
return this;
}
/** Set the learning rate */
public Builder<V, E> learningRate(double learningRate) {
this.learningRate = learningRate;
return this;
}
/** Sets the window size used in skipgram model */
public Builder<V, E> windowSize(int windowSize) {
this.windowSize = windowSize;
return this;
}
/** Seed for random number generation (used for repeatability).
* Note however that parallel/async gradient descent might result in behaviour that
* is not repeatable, in spite of setting seed
*/
public Builder<V, E> seed(long seed) {
this.seed = seed;
return this;
}
public DeepWalk<V, E> build() {
DeepWalk<V, E> dw = new DeepWalk<>();
dw.vectorSize = vectorSize;
dw.windowSize = windowSize;
dw.learningRate = learningRate;
dw.seed = seed;
return dw;
}
}
@AllArgsConstructor
private class LearningCallable implements Callable<Void> {
private final GraphWalkIterator<V> iterator;
@Override
public Void call() throws Exception {
fit(iterator);
return null;
}
}
}
@@ -0,0 +1,152 @@
/*
* ******************************************************************************
* *
* *
* * This program and the accompanying materials are made available under the
* * terms of the Apache License, Version 2.0 which is available at
* * https://www.apache.org/licenses/LICENSE-2.0.
* *
* * See the NOTICE file distributed with this work for additional
* * information regarding copyright ownership.
* * 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.
* *
* * SPDX-License-Identifier: Apache-2.0
* *****************************************************************************
*/
package org.deeplearning4j.graph.models.deepwalk;
import lombok.AllArgsConstructor;
import org.deeplearning4j.graph.models.BinaryTree;
import java.util.Arrays;
import java.util.PriorityQueue;
public class GraphHuffman implements BinaryTree {
private final int MAX_CODE_LENGTH;
private final long[] codes;
private final byte[] codeLength;
private final int[][] innerNodePathToLeaf;
/**
* @param nVertices number of vertices in the graph that this Huffman tree is being built for
*/
public GraphHuffman(int nVertices) {
this(nVertices, 64);
}
/**
* @param nVertices nVertices number of vertices in the graph that this Huffman tree is being built for
* @param maxCodeLength MAX_CODE_LENGTH for Huffman tree
*/
public GraphHuffman(int nVertices, int maxCodeLength) {
this.codes = new long[nVertices];
this.codeLength = new byte[nVertices];
this.innerNodePathToLeaf = new int[nVertices][0];
this.MAX_CODE_LENGTH = maxCodeLength;
}
/** Build the Huffman tree given an array of vertex degrees
* @param vertexDegree vertexDegree[i] = degree of ith vertex
*/
public void buildTree(int[] vertexDegree) {
PriorityQueue<Node> pq = new PriorityQueue<>();
for (int i = 0; i < vertexDegree.length; i++)
pq.add(new Node(i, vertexDegree[i], null, null));
while (pq.size() > 1) {
Node left = pq.remove();
Node right = pq.remove();
Node newNode = new Node(-1, left.count + right.count, left, right);
pq.add(newNode);
}
//Eventually: only one node left -> full tree
Node tree = pq.remove();
//Now: convert tree into binary codes. Traverse tree (preorder traversal) -> record path (left/right) -> code
int[] innerNodePath = new int[MAX_CODE_LENGTH];
traverse(tree, 0L, (byte) 0, -1, innerNodePath, 0);
}
@AllArgsConstructor
private static class Node implements Comparable<Node> {
private final int vertexIdx;
private final long count;
private Node left;
private Node right;
@Override
public int compareTo(Node o) {
return Long.compare(count, o.count);
}
}
private int traverse(Node node, long codeSoFar, byte codeLengthSoFar, int innerNodeCount, int[] innerNodePath,
int currDepth) {
if (codeLengthSoFar >= MAX_CODE_LENGTH)
throw new RuntimeException("Cannot generate code: code length exceeds " + MAX_CODE_LENGTH + " bits");
if (node.left == null && node.right == null) {
//Leaf node
codes[node.vertexIdx] = codeSoFar;
codeLength[node.vertexIdx] = codeLengthSoFar;
innerNodePathToLeaf[node.vertexIdx] = Arrays.copyOf(innerNodePath, currDepth);
return innerNodeCount;
}
//This is an inner node. It's index is 'innerNodeCount'
innerNodeCount++;
innerNodePath[currDepth] = innerNodeCount;
long codeLeft = setBit(codeSoFar, codeLengthSoFar, false);
innerNodeCount = traverse(node.left, codeLeft, (byte) (codeLengthSoFar + 1), innerNodeCount, innerNodePath,
currDepth + 1);
long codeRight = setBit(codeSoFar, codeLengthSoFar, true);
innerNodeCount = traverse(node.right, codeRight, (byte) (codeLengthSoFar + 1), innerNodeCount, innerNodePath,
currDepth + 1);
return innerNodeCount;
}
private static long setBit(long in, int bitNum, boolean value) {
if (value)
return (in | 1L << bitNum); //Bit mask |: 00010000
else
return (in & ~(1 << bitNum)); //Bit mask &: 11101111
}
private static boolean getBit(long in, int bitNum) {
long mask = 1L << bitNum;
return (in & mask) != 0L;
}
@Override
public long getCode(int vertexNum) {
return codes[vertexNum];
}
@Override
public int getCodeLength(int vertexNum) {
return codeLength[vertexNum];
}
@Override
public String getCodeString(int vertexNum) {
long code = codes[vertexNum];
int len = codeLength[vertexNum];
StringBuilder sb = new StringBuilder();
for (int i = 0; i < len; i++)
sb.append(getBit(code, i) ? "1" : "0");
return sb.toString();
}
@Override
public int[] getPathInnerNodes(int vertexNum) {
return innerNodePathToLeaf[vertexNum];
}
}
@@ -0,0 +1,46 @@
/*
* ******************************************************************************
* *
* *
* * This program and the accompanying materials are made available under the
* * terms of the Apache License, Version 2.0 which is available at
* * https://www.apache.org/licenses/LICENSE-2.0.
* *
* * See the NOTICE file distributed with this work for additional
* * information regarding copyright ownership.
* * 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.
* *
* * SPDX-License-Identifier: Apache-2.0
* *****************************************************************************
*/
package org.deeplearning4j.graph.models.embeddings;
import org.nd4j.linalg.api.ndarray.INDArray;
public interface GraphVectorLookupTable {
/**The size of the vector representations
*/
int vectorSize();
/** Reset (randomize) the weights. */
void resetWeights();
/** Conduct learning given a pair of vertices (in and out) */
void iterate(int first, int second);
/** Get the vector for the vertex with index idx */
public INDArray getVector(int idx);
/** Set the learning rate */
void setLearningRate(double learningRate);
/** Returns the number of vertices in the graph */
int getNumVertices();
}
@@ -0,0 +1,127 @@
/*
* ******************************************************************************
* *
* *
* * This program and the accompanying materials are made available under the
* * terms of the Apache License, Version 2.0 which is available at
* * https://www.apache.org/licenses/LICENSE-2.0.
* *
* * See the NOTICE file distributed with this work for additional
* * information regarding copyright ownership.
* * 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.
* *
* * SPDX-License-Identifier: Apache-2.0
* *****************************************************************************
*/
package org.deeplearning4j.graph.models.embeddings;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import org.deeplearning4j.graph.api.IGraph;
import org.deeplearning4j.graph.api.Vertex;
import org.deeplearning4j.graph.models.GraphVectors;
import org.nd4j.linalg.api.blas.Level1;
import org.nd4j.linalg.api.ndarray.INDArray;
import org.nd4j.linalg.factory.Nd4j;
import org.nd4j.linalg.ops.transforms.Transforms;
import org.nd4j.common.primitives.Pair;
import java.util.Comparator;
import java.util.PriorityQueue;
@AllArgsConstructor
@NoArgsConstructor
public class GraphVectorsImpl<V, E> implements GraphVectors<V, E> {
protected IGraph<V, E> graph;
protected GraphVectorLookupTable lookupTable;
@Override
public IGraph<V, E> getGraph() {
return graph;
}
@Override
public int numVertices() {
return lookupTable.getNumVertices();
}
@Override
public int getVectorSize() {
return lookupTable.vectorSize();
}
@Override
public INDArray getVertexVector(Vertex<V> vertex) {
return lookupTable.getVector(vertex.vertexID());
}
@Override
public INDArray getVertexVector(int vertexIdx) {
return lookupTable.getVector(vertexIdx);
}
@Override
public int[] verticesNearest(int vertexIdx, int top) {
INDArray vec = lookupTable.getVector(vertexIdx).dup();
double norm2 = vec.norm2Number().doubleValue();
PriorityQueue<Pair<Double, Integer>> pq =
new PriorityQueue<>(lookupTable.getNumVertices(), new PairComparator());
Level1 l1 = Nd4j.getBlasWrapper().level1();
for (int i = 0; i < numVertices(); i++) {
if (i == vertexIdx)
continue;
INDArray other = lookupTable.getVector(i);
double cosineSim = l1.dot(vec.length(), 1.0, vec, other) / (norm2 * other.norm2Number().doubleValue());
pq.add(new Pair<>(cosineSim, i));
}
int[] out = new int[top];
for (int i = 0; i < top; i++) {
out[i] = pq.remove().getSecond();
}
return out;
}
private static class PairComparator implements Comparator<Pair<Double, Integer>> {
@Override
public int compare(Pair<Double, Integer> o1, Pair<Double, Integer> o2) {
return -Double.compare(o1.getFirst(), o2.getFirst());
}
}
/**Returns the cosine similarity of the vector representations of two vertices in the graph
* @return Cosine similarity of two vertices
*/
@Override
public double similarity(Vertex<V> vertex1, Vertex<V> vertex2) {
return similarity(vertex1.vertexID(), vertex2.vertexID());
}
/**Returns the cosine similarity of the vector representations of two vertices in the graph,
* given the indices of these verticies
* @return Cosine similarity of two vertices
*/
@Override
public double similarity(int vertexIdx1, int vertexIdx2) {
if (vertexIdx1 == vertexIdx2)
return 1.0;
INDArray vector = Transforms.unitVec(getVertexVector(vertexIdx1));
INDArray vector2 = Transforms.unitVec(getVertexVector(vertexIdx2));
return Nd4j.getBlasWrapper().dot(vector, vector2);
}
}
@@ -0,0 +1,209 @@
/*
* ******************************************************************************
* *
* *
* * This program and the accompanying materials are made available under the
* * terms of the Apache License, Version 2.0 which is available at
* * https://www.apache.org/licenses/LICENSE-2.0.
* *
* * See the NOTICE file distributed with this work for additional
* * information regarding copyright ownership.
* * 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.
* *
* * SPDX-License-Identifier: Apache-2.0
* *****************************************************************************
*/
package org.deeplearning4j.graph.models.embeddings;
import org.apache.commons.math3.util.FastMath;
import org.deeplearning4j.graph.models.BinaryTree;
import org.nd4j.linalg.api.blas.Level1;
import org.nd4j.linalg.api.ndarray.INDArray;
import org.nd4j.linalg.factory.Nd4j;
public class InMemoryGraphLookupTable implements GraphVectorLookupTable {
protected int nVertices;
protected int vectorSize;
protected BinaryTree tree;
protected INDArray vertexVectors; //'input' vectors
protected INDArray outWeights; //'output' vectors. Specifically vectors for inner nodes in binary tree
protected double learningRate;
protected double[] expTable;
protected static double MAX_EXP = 6;
public InMemoryGraphLookupTable(int nVertices, int vectorSize, BinaryTree tree, double learningRate) {
this.nVertices = nVertices;
this.vectorSize = vectorSize;
this.tree = tree;
this.learningRate = learningRate;
resetWeights();
expTable = new double[1000];
for (int i = 0; i < expTable.length; i++) {
double tmp = FastMath.exp((i / (double) expTable.length * 2 - 1) * MAX_EXP);
expTable[i] = tmp / (tmp + 1.0);
}
}
public INDArray getVertexVectors() {
return vertexVectors;
}
public INDArray getOutWeights() {
return outWeights;
}
@Override
public int vectorSize() {
return vectorSize;
}
@Override
public void resetWeights() {
this.vertexVectors = Nd4j.rand(nVertices, vectorSize).subi(0.5).divi(vectorSize);
this.outWeights = Nd4j.rand(nVertices - 1, vectorSize).subi(0.5).divi(vectorSize); //Full binary tree with L leaves has L-1 inner nodes
}
@Override
public void iterate(int first, int second) {
//Get vectors and gradients
//vecAndGrads[0][0] is vector of vertex(first); vecAndGrads[1][0] is corresponding gradient
INDArray[][] vecAndGrads = vectorsAndGradients(first, second);
Level1 l1 = Nd4j.getBlasWrapper().level1();
for (int i = 0; i < vecAndGrads[0].length; i++) {
//Update: v = v - lr * gradient
l1.axpy(vecAndGrads[0][i].length(), -learningRate, vecAndGrads[1][i], vecAndGrads[0][i]);
}
}
/** Returns vertex vector and vector gradients, plus inner node vectors and inner node gradients<br>
* Specifically, out[0] are vectors, out[1] are gradients for the corresponding vectors<br>
* out[0][0] is vector for first vertex; out[0][1] is gradient for this vertex vector<br>
* out[0][i] (i>0) is the inner node vector along path to second vertex; out[1][i] is gradient for inner node vertex<br>
* This design is used primarily to aid in testing (numerical gradient checks)
* @param first first (input) vertex index
* @param second second (output) vertex index
*/
public INDArray[][] vectorsAndGradients(int first, int second) {
//Input vertex vector gradients are composed of the inner node gradients
//Get vector for first vertex, as well as code for second:
INDArray vec = vertexVectors.getRow(first);
int codeLength = tree.getCodeLength(second);
long code = tree.getCode(second);
int[] innerNodesForVertex = tree.getPathInnerNodes(second);
INDArray[][] out = new INDArray[2][innerNodesForVertex.length + 1];
Level1 l1 = Nd4j.getBlasWrapper().level1();
INDArray accumError = Nd4j.create(vec.shape());
for (int i = 0; i < codeLength; i++) {
//Inner node:
int innerNodeIdx = innerNodesForVertex[i];
boolean path = getBit(code, i); //left or right?
INDArray innerNodeVector = outWeights.getRow(innerNodeIdx);
double sigmoidDot = sigmoid(Nd4j.getBlasWrapper().dot(innerNodeVector, vec));
//Calculate gradient for inner node + accumulate error:
INDArray innerNodeGrad;
if (path) {
innerNodeGrad = vec.mul(sigmoidDot - 1);
l1.axpy(vec.length(), sigmoidDot - 1, innerNodeVector, accumError);
} else {
innerNodeGrad = vec.mul(sigmoidDot);
l1.axpy(vec.length(), sigmoidDot, innerNodeVector, accumError);
}
out[0][i + 1] = innerNodeVector;
out[1][i + 1] = innerNodeGrad;
}
out[0][0] = vec;
out[1][0] = accumError;
return out;
}
/** Calculate the probability of the second vertex given the first vertex
* i.e., P(v_second | v_first)
* @param first index of the first vertex
* @param second index of the second vertex
* @return probability, P(v_second | v_first)
*/
public double calculateProb(int first, int second) {
//Get vector for first vertex, as well as code for second:
INDArray vec = vertexVectors.getRow(first);
int codeLength = tree.getCodeLength(second);
long code = tree.getCode(second);
int[] innerNodesForVertex = tree.getPathInnerNodes(second);
double prob = 1.0;
for (int i = 0; i < codeLength; i++) {
boolean path = getBit(code, i); //left or right?
//Inner node:
int innerNodeIdx = innerNodesForVertex[i];
INDArray nwi = outWeights.getRow(innerNodeIdx);
double dot = Nd4j.getBlasWrapper().dot(nwi, vec);
//double sigmoidDot = sigmoid(dot);
double innerProb = (path ? sigmoid(dot) : sigmoid(-dot)); //prob of going left or right at inner node
prob *= innerProb;
}
return prob;
}
/** Calculate score. -log P(v_second | v_first) */
public double calculateScore(int first, int second) {
//Score is -log P(out|in)
double prob = calculateProb(first, second);
return -FastMath.log(prob);
}
public BinaryTree getTree() {
return tree;
}
public INDArray getInnerNodeVector(int innerNode) {
return outWeights.getRow(innerNode);
}
@Override
public INDArray getVector(int idx) {
return vertexVectors.getRow(idx);
}
@Override
public void setLearningRate(double learningRate) {
this.learningRate = learningRate;
}
@Override
public int getNumVertices() {
return nVertices;
}
private static double sigmoid(double in) {
return 1.0 / (1.0 + FastMath.exp(-in));
}
private boolean getBit(long in, int bitNum) {
long mask = 1L << bitNum;
return (in & mask) != 0L;
}
public void setVertexVectors(INDArray vertexVectors) {
this.vertexVectors = vertexVectors;
}
}
@@ -0,0 +1,101 @@
/*
* ******************************************************************************
* *
* *
* * This program and the accompanying materials are made available under the
* * terms of the Apache License, Version 2.0 which is available at
* * https://www.apache.org/licenses/LICENSE-2.0.
* *
* * See the NOTICE file distributed with this work for additional
* * information regarding copyright ownership.
* * 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.
* *
* * SPDX-License-Identifier: Apache-2.0
* *****************************************************************************
*/
package org.deeplearning4j.graph.models.loader;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.LineIterator;
import org.deeplearning4j.graph.models.GraphVectors;
import org.deeplearning4j.graph.models.deepwalk.DeepWalk;
import org.deeplearning4j.graph.models.embeddings.GraphVectorsImpl;
import org.deeplearning4j.graph.models.embeddings.InMemoryGraphLookupTable;
import org.nd4j.linalg.api.ndarray.INDArray;
import org.nd4j.linalg.factory.Nd4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
public class GraphVectorSerializer {
private static final Logger log = LoggerFactory.getLogger(GraphVectorSerializer.class);
private static final String DELIM = "\t";
private GraphVectorSerializer() {}
public static void writeGraphVectors(DeepWalk deepWalk, String path) throws IOException {
int nVertices = deepWalk.numVertices();
int vectorSize = deepWalk.getVectorSize();
try (BufferedWriter write = new BufferedWriter(new FileWriter(new File(path), false))) {
for (int i = 0; i < nVertices; i++) {
StringBuilder sb = new StringBuilder();
sb.append(i);
INDArray vec = deepWalk.getVertexVector(i);
for (int j = 0; j < vectorSize; j++) {
double d = vec.getDouble(j);
sb.append(DELIM).append(d);
}
sb.append("\n");
write.write(sb.toString());
}
}
log.info("Wrote {} vectors of length {} to: {}", nVertices, vectorSize, path);
}
public static GraphVectors loadTxtVectors(File file) throws IOException {
List<double[]> vectorList = new ArrayList<>();
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
LineIterator iter = IOUtils.lineIterator(reader);
while (iter.hasNext()) {
String line = iter.next();
String[] split = line.split(DELIM);
double[] vec = new double[split.length - 1];
for (int i = 1; i < split.length; i++) {
vec[i - 1] = Double.parseDouble(split[i]);
}
vectorList.add(vec);
}
}
int vecSize = vectorList.get(0).length;
int nVertices = vectorList.size();
INDArray vectors = Nd4j.create(nVertices, vecSize);
for (int i = 0; i < vectorList.size(); i++) {
double[] vec = vectorList.get(i);
for (int j = 0; j < vec.length; j++) {
vectors.put(i, j, vec[j]);
}
}
InMemoryGraphLookupTable table = new InMemoryGraphLookupTable(nVertices, vecSize, null, 0.01);
table.setVertexVectors(vectors);
return new GraphVectorsImpl<>(null, table);
}
}
@@ -0,0 +1,30 @@
/*
* ******************************************************************************
* *
* *
* * This program and the accompanying materials are made available under the
* * terms of the Apache License, Version 2.0 which is available at
* * https://www.apache.org/licenses/LICENSE-2.0.
* *
* * See the NOTICE file distributed with this work for additional
* * information regarding copyright ownership.
* * 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.
* *
* * SPDX-License-Identifier: Apache-2.0
* *****************************************************************************
*/
package org.deeplearning4j.graph.vertexfactory;
import org.deeplearning4j.graph.api.Vertex;
public class IntegerVertexFactory implements VertexFactory<Integer> {
@Override
public Vertex<Integer> create(int vertexIdx) {
return new Vertex<>(vertexIdx, vertexIdx);
}
}
@@ -0,0 +1,44 @@
/*
* ******************************************************************************
* *
* *
* * This program and the accompanying materials are made available under the
* * terms of the Apache License, Version 2.0 which is available at
* * https://www.apache.org/licenses/LICENSE-2.0.
* *
* * See the NOTICE file distributed with this work for additional
* * information regarding copyright ownership.
* * 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.
* *
* * SPDX-License-Identifier: Apache-2.0
* *****************************************************************************
*/
package org.deeplearning4j.graph.vertexfactory;
import org.deeplearning4j.graph.api.Vertex;
public class StringVertexFactory implements VertexFactory<String> {
private final String format;
public StringVertexFactory() {
this(null);
}
public StringVertexFactory(String format) {
this.format = format;
}
@Override
public Vertex<String> create(int vertexIdx) {
if (format != null)
return new Vertex<>(vertexIdx, String.format(format, vertexIdx));
else
return new Vertex<>(vertexIdx, String.valueOf(vertexIdx));
}
}
@@ -0,0 +1,29 @@
/*
* ******************************************************************************
* *
* *
* * This program and the accompanying materials are made available under the
* * terms of the Apache License, Version 2.0 which is available at
* * https://www.apache.org/licenses/LICENSE-2.0.
* *
* * See the NOTICE file distributed with this work for additional
* * information regarding copyright ownership.
* * 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.
* *
* * SPDX-License-Identifier: Apache-2.0
* *****************************************************************************
*/
package org.deeplearning4j.graph.vertexfactory;
import org.deeplearning4j.graph.api.Vertex;
public interface VertexFactory<T> {
Vertex<T> create(int vertexIdx);
}
@@ -0,0 +1,30 @@
/*
* ******************************************************************************
* *
* *
* * This program and the accompanying materials are made available under the
* * terms of the Apache License, Version 2.0 which is available at
* * https://www.apache.org/licenses/LICENSE-2.0.
* *
* * See the NOTICE file distributed with this work for additional
* * information regarding copyright ownership.
* * 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.
* *
* * SPDX-License-Identifier: Apache-2.0
* *****************************************************************************
*/
package org.deeplearning4j.graph.vertexfactory;
import org.deeplearning4j.graph.api.Vertex;
public class VoidVertexFactory implements VertexFactory<Void> {
@Override
public Vertex<Void> create(int vertexIdx) {
return new Vertex<>(vertexIdx, null);
}
}
@@ -0,0 +1,20 @@
open module deeplearning4j.graph {
requires commons.io;
requires commons.math3;
requires nd4j.common;
requires slf4j.api;
requires threadly;
requires nd4j.api;
exports org.deeplearning4j.graph.api;
exports org.deeplearning4j.graph.data;
exports org.deeplearning4j.graph.data.impl;
exports org.deeplearning4j.graph.exception;
exports org.deeplearning4j.graph.graph;
exports org.deeplearning4j.graph.iterator;
exports org.deeplearning4j.graph.iterator.parallel;
exports org.deeplearning4j.graph.models;
exports org.deeplearning4j.graph.models.deepwalk;
exports org.deeplearning4j.graph.models.embeddings;
exports org.deeplearning4j.graph.models.loader;
exports org.deeplearning4j.graph.vertexfactory;
}