All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jgrapht.sux4j.SuccinctIntDirectedGraph Maven / Gradle / Ivy

/*
 * (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.List;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Stream;

import org.jgrapht.Graph;
import org.jgrapht.GraphIterables;
import org.jgrapht.alg.util.Pair;
import org.jgrapht.opt.graph.sparse.IncomingEdgesSupport;
import org.jgrapht.opt.graph.sparse.SparseIntDirectedGraph;

import com.google.common.collect.Iterables;

import it.unimi.dsi.fastutil.ints.IntIntPair;
import it.unimi.dsi.fastutil.ints.IntIterator;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import it.unimi.dsi.fastutil.ints.IntSets;
import it.unimi.dsi.fastutil.longs.LongBigListIterator;
import it.unimi.dsi.sux4j.util.EliasFanoIndexedMonotoneLongBigList;
import it.unimi.dsi.sux4j.util.EliasFanoMonotoneLongBigList;

/**
 * An immutable directed graph with {@link Integer} edges represented using quasi-succinct data
 * structures.
 *
 * 

* The graph representation of this implementation is similar to that of * {@link SparseIntDirectedGraph}: nodes and edges are initial intervals of the natural numbers. * Under the hood, however, this class uses the {@linkplain EliasFanoMonotoneLongBigList * Elias–Fano representation of monotone sequences} to represent the positions of ones in the * (linearized) adjacency matrix of the graph. Instances are serializable and thread safe. * *

* If the vertex set is compact (i.e., vertices are numbered from 0 consecutively), space usage will * be close to twice the information-theoretical lower bound (typically, a few times smaller than a * {@link SparseIntDirectedGraph}). If you {@link #SuccinctIntDirectedGraph(Graph, boolean) drop * support for incoming edges} the space will close to the information-theoretical lower bound . * *

* {@linkplain org.jgrapht.GraphIterables#outgoingEdgesOf(Object) Enumeration of outgoing edges} is * quite fast, but {@linkplain org.jgrapht.GraphIterables#incomingEdgesOf(Object) enumeration of * incoming edges} is very slow. {@linkplain org.jgrapht.Graph#containsEdge(Object) Adjacency tests} * are very fast and happen in almost constant time. * *

* {@link SuccinctDirectedGraph} is a much faster implementation with a similar footprint using * {@link IntIntPair} as edge type. Please read the {@linkplain org.jgrapht.sux4j class * documentation} for more information. * * @author Sebastiano Vigna * @see SuccinctDirectedGraph */ public class SuccinctIntDirectedGraph extends AbstractSuccinctDirectedGraph implements Serializable { private static final long serialVersionUID = 0L; /** The cumulative list of outdegrees. */ private final EliasFanoIndexedMonotoneLongBigList cumulativeOutdegrees; /** The cumulative list of indegrees. */ private final EliasFanoMonotoneLongBigList cumulativeIndegrees; /** The cumulative list of successor lists. */ private final EliasFanoIndexedMonotoneLongBigList successors; /** The cumulative list of predecessor lists. */ private final EliasFanoMonotoneLongBigList predecessors; /** * Creates a new immutable succinct directed graph from a given directed graph, choosing whether * to support incoming edges. * * @param graph a directed graph: for good results, vertices should be numbered consecutively * starting from 0. * @param incomingEdgesSupport whether to support incoming edges or not. * @param the graph edge type */ public SuccinctIntDirectedGraph( final Graph graph, final boolean incomingEdgesSupport) { super((int) graph.iterables().vertexCount(), (int) graph.iterables().edgeCount()); if (graph.getType().isUndirected()) throw new IllegalArgumentException("This class supports directed graphs only"); assert graph.getType().isDirected(); final GraphIterables iterables = graph.iterables(); if (iterables.vertexCount() > Integer.MAX_VALUE) throw new IllegalArgumentException( "The number of nodes (" + iterables.vertexCount() + ") is greater than " + Integer.MAX_VALUE); if (iterables.edgeCount() > Integer.MAX_VALUE) throw new IllegalArgumentException( "The number of edges (" + iterables.edgeCount() + ") is greater than " + Integer.MAX_VALUE); cumulativeOutdegrees = new EliasFanoIndexedMonotoneLongBigList( n + 1, m, new CumulativeDegrees(n, graph::outDegreeOf)); assert cumulativeOutdegrees.getLong(cumulativeOutdegrees.size64() - 1) == m; successors = new EliasFanoIndexedMonotoneLongBigList( m, (long) n << sourceShift, new CumulativeSuccessors<>(graph, iterables::outgoingEdgesOf, true)); if (incomingEdgesSupport) { cumulativeIndegrees = new EliasFanoMonotoneLongBigList( n + 1, m, new CumulativeDegrees(n, graph::inDegreeOf)); assert cumulativeIndegrees.getLong(cumulativeIndegrees.size64() - 1) == m; predecessors = new EliasFanoIndexedMonotoneLongBigList( m, (long) n * n - m, new CumulativeSuccessors<>(graph, iterables::incomingEdgesOf, false)); } else { cumulativeIndegrees = predecessors = null; } } /** * Creates a new immutable succinct directed graph from a given directed graph, supporting both * outgoing and incoming edges. * * @param graph a directed graph: for good results, vertices should be numbered consecutively * starting from 0. * @param the graph edge type */ public SuccinctIntDirectedGraph(final Graph graph) { this(graph, true); } /** * Creates a new immutable succinct directed graph from an edge list, choosing whether to * support incoming edges. * *

* This constructor just builds a {@link SparseIntDirectedGraph} and delegates to the * {@linkplain #SuccinctIntDirectedGraph(Graph) main constructor}. * * @param numVertices the number of vertices. * @param edges the edge list. * @param incomingEdgesSupport whether to support incoming edges or not. * @see #SuccinctIntDirectedGraph(Graph) */ public SuccinctIntDirectedGraph( final int numVertices, final List> edges, final boolean incomingEdgesSupport) { this( new SparseIntDirectedGraph( numVertices, edges, incomingEdgesSupport ? IncomingEdgesSupport.FULL_INCOMING_EDGES : IncomingEdgesSupport.NO_INCOMING_EDGES)); } /** * Creates a new immutable succinct directed graph from an edge list, supporting both outgoing * and incoming edges. *

* This constructor just builds a {@link SparseIntDirectedGraph} and delegates to the * {@linkplain #SuccinctIntDirectedGraph(Graph) main constructor}. * * @param numVertices the number of vertices. * @param edges the edge list. * @see #SuccinctIntDirectedGraph(Graph) */ public SuccinctIntDirectedGraph(final int numVertices, final List> edges) { this(numVertices, edges, true); } /** * Creates a new immutable succinct directed graph from a supplier of streams of edges, choosing * whether to support incoming edges. * *

* This constructor just builds a {@link SparseIntDirectedGraph} and delegates to the * {@linkplain #SuccinctIntDirectedGraph(Graph) main constructor}. * * @param numVertices the number of vertices. * @param numEdges the number of edges. * @param edges a supplier of streams of edges. * @param incomingEdgesSupport whether to support incoming edges or not. * @see #SuccinctIntDirectedGraph(Graph) */ public SuccinctIntDirectedGraph( final int numVertices, final int numEdges, final Supplier>> edges, final boolean incomingEdgesSupport) { this( new SparseIntDirectedGraph( numVertices, numEdges, edges, incomingEdgesSupport ? IncomingEdgesSupport.FULL_INCOMING_EDGES : IncomingEdgesSupport.NO_INCOMING_EDGES)); } /** * Creates a new immutable succinct directed graph from a supplier of streams of edges, * supporting both outgoing and incoming edges. * *

* This constructor just builds a {@link SparseIntDirectedGraph} and delegates to the * {@linkplain #SuccinctIntDirectedGraph(Graph) main constructor}. * * @param numVertices the number of vertices. * @param numEdges the number of edges. * @param edges a supplier of streams of edges. * @see #SuccinctIntDirectedGraph(Graph) */ public SuccinctIntDirectedGraph( final int numVertices, final int numEdges, final Supplier>> edges) { this(numVertices, numEdges, edges, true); } @Override public boolean containsEdge(final Integer e) { return e >= 0 && e < m; } @Override public Set edgeSet() { return IntSets.fromTo(0, m); } @Override public IntSet edgesOf(final Integer vertex) { final IntSet result = outgoingEdgesOf(vertex); result.addAll(incomingEdgesOf(vertex)); return result; } @Override public int inDegreeOf(final Integer vertex) { assertVertexExist(vertex); return (int) cumulativeIndegrees.getDelta(vertex); } @Override public IntSet incomingEdgesOf(final Integer target) { assertVertexExist(target); final int t = target; final long[] result = new long[2]; cumulativeIndegrees.get(t, result); final int d = (int) (result[1] - result[0]); final LongBigListIterator iterator = predecessors.listIterator(result[0]); final IntOpenHashSet s = new IntOpenHashSet(); long base = (long) n * t - result[0]; for (int i = d; i-- != 0;) { final long source = iterator.nextLong() - base--; final int e = (int) (successors.successorIndexUnsafe((source << sourceShift) + t)); assert getEdgeSource(e).longValue() == source; assert getEdgeTarget(e).longValue() == target; s.add(e); } return s; } @Override public int outDegreeOf(final Integer vertex) { assertVertexExist(vertex); return (int) cumulativeOutdegrees.getDelta(vertex); } @Override public IntSet outgoingEdgesOf(final Integer vertex) { assertVertexExist(vertex); final long[] result = new long[2]; cumulativeOutdegrees.get(vertex, result); return IntSets.fromTo((int) result[0], (int) result[1]); } @Override public Integer getEdgeSource(final Integer e) { assertEdgeExist(e); return (int) (successors.getLong(e) >>> sourceShift); } @Override public Integer getEdgeTarget(final Integer e) { assertEdgeExist(e); return (int) (successors.getLong(e) & targetMask); } @Override public Integer getEdge(final Integer sourceVertex, final Integer targetVertex) { final long index = successors.indexOfUnsafe(((long) sourceVertex << sourceShift) + targetVertex); return index != -1 ? (int) index : null; } @Override public boolean containsEdge(final Integer sourceVertex, final Integer targetVertex) { return successors.indexOfUnsafe(((long) sourceVertex << sourceShift) + targetVertex) != -1; } /** * Ensures that the specified edge exists in this graph, or else throws exception. * * @param e edge * @return true if this assertion holds. * @throws IllegalArgumentException if specified edge does not exist in this graph. */ protected boolean assertEdgeExist(final Integer e) { if (e < 0 || e >= m) throw new IllegalArgumentException(); return true; } private final static class SuccinctGraphIterables implements GraphIterables, Serializable { private static final long serialVersionUID = 0L; private final SuccinctIntDirectedGraph graph; private SuccinctGraphIterables() { graph = null; } private SuccinctGraphIterables(final SuccinctIntDirectedGraph graph) { this.graph = graph; } @Override public Graph getGraph() { return graph; } @Override public long vertexCount() { return graph.n; } @Override public long edgeCount() { return graph.m; } @Override public Iterable edgesOf(final Integer source) { return Iterables.concat(outgoingEdgesOf(source), incomingEdgesOf(source, true)); } private Iterable incomingEdgesOf(final int target, final boolean skipLoops) { final SuccinctIntDirectedGraph graph = this.graph; final long[] result = new long[2]; graph.cumulativeIndegrees.get(target, result); final int d = (int) (result[1] - result[0]); final EliasFanoIndexedMonotoneLongBigList successors = graph.successors; final LongBigListIterator iterator = graph.predecessors.listIterator(result[0]); final int sourceShift = graph.sourceShift; return () -> new IntIterator() { int i = d; int edge = -1; long n = graph.n; long base = target * n - result[0]; @Override public boolean hasNext() { if (edge == -1 && i > 0) { i--; final long source = iterator.nextLong() - base--; if (skipLoops && source == target && i-- != 0) return false; final long v = (source << sourceShift) + target; assert v == successors.successor(v) : v + " != " + successors.successor(v); edge = (int) successors.successorIndexUnsafe(v); assert graph.getEdgeSource(edge).longValue() == source; assert graph.getEdgeTarget(edge).longValue() == target; } return edge != -1; } @Override public int nextInt() { if (!hasNext()) throw new NoSuchElementException(); final int result = edge; edge = -1; return result; } }; } @Override public Iterable incomingEdgesOf(final Integer vertex) { return incomingEdgesOf(vertex, false); } } private final GraphIterables ITERABLES = new SuccinctGraphIterables(this); @Override public GraphIterables iterables() { return ITERABLES; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy