Skip to content

Commit

Permalink
Succinct graph representations (#1041)
Browse files Browse the repository at this point in the history
* Moved Sux4J files to unimi-dsi

* Moved Sux4J files to unimi-dsi

* Fixed dependencies

* Fixed copyright

* Package info

* IntIntPair-based implementation

* Imported modifications from sux4j-test

* Imported modifications from sux4j-test

* Fixed javadoc

* Tweaking

* Faster iteration

* Improved iteration

* Improved iteration

* Fixed checkstyle violations

* Fixed checkstyle violations

* Now we implement serializable

* Improved docs and enumeration

* Improved docs

* Improved docs

* Improved docs

* Made indexing function taking and returning longs

* Fixed javadoc

* Made statement about size precise

* Added constructors with choice of support for incoming arcs and constructors acceping a supplier of streams of edges

* Fixed Javadoc

* Fixed Javadoc
  • Loading branch information
vigna committed Mar 8, 2021
1 parent 8f71fd4 commit c523396
Show file tree
Hide file tree
Showing 14 changed files with 3,583 additions and 1 deletion.
4 changes: 4 additions & 0 deletions jgrapht-unimi-dsi/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@
<groupId>${project.groupId}</groupId>
<artifactId>jgrapht-core</artifactId>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>jgrapht-opt</artifactId>
</dependency>
<dependency>
<groupId>it.unimi.dsi</groupId>
<artifactId>fastutil</artifactId>
Expand Down
4 changes: 3 additions & 1 deletion jgrapht-unimi-dsi/src/main/java/module-info.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
module org.jgrapht.unimi.dsi
{
exports org.jgrapht.webgraph;
exports org.jgrapht.sux4j;

requires transitive org.jgrapht.core;
requires transitive org.jgrapht.opt;
requires transitive it.unimi.dsi.fastutil;
requires transitive it.unimi.dsi.webgraph;
requires transitive it.unimi.dsi.big.webgraph;
requires transitive it.unimi.dsi.dsiutils;
requires transitive it.unimi.dsi.sux4j;
requires transitive com.google.common;
requires transitive com.google.common;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
/*
* (C) Copyright 2020-2021, by Sebastiano Vigna and Contributors.
*
* JGraphT : a free Java graph-theory library
*
* See the CONTRIBUTORS.md file distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the
* GNU Lesser General Public License v2.1 or later
* which is available at
* http://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html.
*
* SPDX-License-Identifier: EPL-2.0 OR LGPL-2.1-or-later
*/

package org.jgrapht.sux4j;

import java.util.Arrays;
import java.util.NoSuchElementException;
import java.util.function.Function;

import org.jgrapht.Graph;
import org.jgrapht.GraphType;
import org.jgrapht.Graphs;
import org.jgrapht.graph.DefaultGraphType;

import it.unimi.dsi.bits.Fast;
import it.unimi.dsi.fastutil.ints.IntArrays;
import it.unimi.dsi.fastutil.longs.LongIterator;

/**
* An abstract base class for all succinct directed implementations.
*
* <p>
* Two subclasses, {@link CumulativeSuccessors} and {@link CumulativeDegrees}, generate the monotone
* lists that will be encoded using the Elias&ndash;Fano representation.
*
* <p>
* First, we store the monotone lists of cumulative outdegrees and indegrees.
*
* <p>
* Then, we store the outgoing edges <var>x</var>&nbsp;&rarr;&nbsp;<var>y</var> as a monotone
* sequence using the encoding <var>x</var>2<sup>&lceil;log&nbsp;<var>n</var>&rceil;</sup> +
* <var>y</var>. At that point the <var>k</var>-th edge can be obtained by retrieving the
* <var>k</var>-th element of the sequence and some bit shifting (the encoding
* <var>x</var><var>n</var> + <var>y</var> would be slightly more compact, but much slower to
* decode). Since we know the list of cumulative outdegrees, we know which range of indices
* corresponds to the edges outgoing from each vertex. If we need to know whether
* <var>x</var>&nbsp;&rarr;&nbsp;<var>y</var> is an edge we just look for
* <var>x</var>2<sup>&lceil;log&nbsp;<var>n</var>&rceil;</sup> + <var>y</var> in the sequence.
*
* <p>
* Finally, we store incoming edges <var>y</var>&nbsp;&rarr;&nbsp;<var>x</var> again as a monotone
* sequence using the encoding <var>x</var><var>n</var> + <var>y</var> - <var>e</var>, where
* <var>e</var> is the index of the edge in lexicographical order. In this case we just need to be
* able to recover the edges associated with a vertex, so we can use a more compact format.
*
* <p>
* However, in the case of a {@link SuccinctIntDirectedGraph} after retrieving the source and target
* of an incoming edge we need to index it. The slow indexing of the incoming edges is the reason
* why a {@link SuccinctIntDirectedGraph} enumerates incoming edges very slowly, whereas a
* {@link SuccinctDirectedGraph} does not.
*
* @param <E> the graph edge type
*/

public abstract class AbstractSuccinctDirectedGraph<E>
extends
AbstractSuccinctGraph<E>
{
private static final long serialVersionUID = 0L;

public AbstractSuccinctDirectedGraph(final int n, final int m)
{
super(n, m);
}

/**
* Turns all edges <var>x</var>&nbsp;&rarr;&nbsp;<var>y</var> into a monotone sequence using the
* encoding <var>x</var>2<sup>&lceil;log&nbsp;<var>n</var>&rceil;</sup> + <var>y</var>, or the
* encoding <var>x</var><var>n</var> + <var>y</var> - <var>e</var>, where <var>e</var> is the
* index of the edge in lexicographical order, depending on the value of the {@code strict}
* parameter.
*
* @param <E> the graph edge type
*/
protected final static class CumulativeSuccessors<E>
implements
LongIterator
{
private final Graph<Integer, E> graph;
private final long n;
private final int sourceShift;
private final Function<Integer, Iterable<E>> succ;
private final boolean strict;

private int x = -1, d, i, e;
private long next = -1;
private int[] s = IntArrays.EMPTY_ARRAY;

protected CumulativeSuccessors(
final Graph<Integer, E> graph, final Function<Integer, Iterable<E>> succ,
final boolean strict)
{
this.n = graph.iterables().vertexCount();
this.sourceShift = Fast.ceilLog2(n);
this.graph = graph;
this.succ = succ;
this.strict = strict;
}

@Override
public boolean hasNext()
{
if (next != -1)
return true;
if (x == n)
return false;
while (i == d) {
if (++x == n)
return false;
int d = 0;
for (final E e : succ.apply(x)) {
s = IntArrays.grow(s, d + 1);
s[d++] = Graphs.getOppositeVertex(graph, e, x);
}
Arrays.sort(s, 0, d);
this.d = d;
i = 0;
}
// The predecessor list will not be indexed, so we can gain a few bits of space by
// subtracting the edge position in the list
next = strict ? s[i] + ((long) x << sourceShift) : s[i] + x * n - e++;
i++;
return true;
}

@Override
public long nextLong()
{
if (!hasNext())
throw new NoSuchElementException();
final long result = next;
next = -1;
return result;
}
}

/**
* Iterates over the cumulative degrees (starts with a zero).
*/
protected final static class CumulativeDegrees
implements
LongIterator
{
private final Function<Integer, Integer> degreeOf;
private final int n;
private int i = -1;
private long cumul = 0;

protected CumulativeDegrees(final int n, final Function<Integer, Integer> degreeOf)
{
this.n = n;
this.degreeOf = degreeOf;
}

@Override
public boolean hasNext()
{
return i < n;
}

@Override
public long nextLong()
{
if (!hasNext())
throw new NoSuchElementException();
if (i == -1)
return ++i;
return cumul += degreeOf.apply(i++);
}
}

@Override
public int degreeOf(final Integer vertex)
{
return inDegreeOf(vertex) + outDegreeOf(vertex);
}

@Override
public GraphType getType()
{
return new DefaultGraphType.Builder()
.directed().weighted(false).modifiable(false).allowMultipleEdges(false)
.allowSelfLoops(true).build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
/*
* (C) Copyright 2020-2021, by Sebastiano Vigna and Contributors.
*
* JGraphT : a free Java graph-theory library
*
* See the CONTRIBUTORS.md file distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the
* GNU Lesser General Public License v2.1 or later
* which is available at
* http://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html.
*
* SPDX-License-Identifier: EPL-2.0 OR LGPL-2.1-or-later
*/

package org.jgrapht.sux4j;

import java.io.Serializable;
import java.util.Set;
import java.util.function.Supplier;

import org.jgrapht.graph.AbstractGraph;

import it.unimi.dsi.bits.Fast;
import it.unimi.dsi.fastutil.ints.IntSets;
import it.unimi.dsi.fastutil.objects.ObjectSets;

/**
* An abstract base class for all succinct implementations.
*
* <p>
* This class provides mutators throwing {@link UnsupportedOperationException} and operations
* depending only on the number of vertices and edges.
*
* @param <E> the graph edge type
*/

public abstract class AbstractSuccinctGraph<E>
extends
AbstractGraph<Integer, E>
implements
Serializable
{
private static final long serialVersionUID = 0L;

protected static final String UNMODIFIABLE = "this graph is unmodifiable";

/** The number of vertices in the graph. */
protected final int n;
/** The number of edges in the graph. */
protected final int m;
/** The shift used to read sources in the successor list. */
protected final int sourceShift;
/** The mask used to read targets in the successor list (lowest {@link #sourceShift} bits). */
protected final long targetMask;

public AbstractSuccinctGraph(final int n, final int m)
{
super();
this.n = n;
this.m = m;
sourceShift = Fast.ceilLog2(n);
targetMask = (1L << sourceShift) - 1;
}

@Override
public Set<Integer> vertexSet()
{
return IntSets.fromTo(0, n);
}

/**
* Ensures that the specified vertex exists in this graph, or else throws exception.
*
* @param v vertex
* @return <code>true</code> if this assertion holds.
* @throws IllegalArgumentException if specified vertex does not exist in this graph.
*/
@Override
protected boolean assertVertexExist(final Integer v)
{
if (v < 0 || v >= n)
throw new IllegalArgumentException();
return true;
}

@Override
public boolean containsVertex(final Integer v)
{
return v >= 0 && v < n;
}

@Override
public Set<E> getAllEdges(final Integer sourceVertex, final Integer targetVertex)
{
final E edge = getEdge(sourceVertex, targetVertex);
return edge == null ? ObjectSets.emptySet() : ObjectSets.singleton(edge);
}

@Override
public Supplier<Integer> getVertexSupplier()
{
return null;
}

@Override
public Supplier<E> getEdgeSupplier()
{
return null;
}

@Override
public E addEdge(final Integer sourceVertex, final Integer targetVertex)
{
throw new UnsupportedOperationException(UNMODIFIABLE);
}

@Override
public boolean addEdge(final Integer sourceVertex, final Integer targetVertex, final E e)
{
throw new UnsupportedOperationException(UNMODIFIABLE);
}

@Override
public Integer addVertex()
{
throw new UnsupportedOperationException(UNMODIFIABLE);
}

@Override
public boolean addVertex(final Integer v)
{
throw new UnsupportedOperationException(UNMODIFIABLE);
}

@Override
public E removeEdge(final Integer sourceVertex, final Integer targetVertex)
{
throw new UnsupportedOperationException(UNMODIFIABLE);
}

@Override
public boolean removeEdge(final E e)
{
throw new UnsupportedOperationException(UNMODIFIABLE);
}

@Override
public boolean removeVertex(final Integer v)
{
throw new UnsupportedOperationException(UNMODIFIABLE);
}

@Override
public double getEdgeWeight(final E e)
{
return 1.0;
}

@Override
public void setEdgeWeight(final E e, final double weight)
{
throw new UnsupportedOperationException(UNMODIFIABLE);
}

}
Loading

0 comments on commit c523396

Please sign in to comment.