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

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

There is a newer version: 1.5.2
Show newest version
/*
 * (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.
 *
 * 

* Two subclasses, {@link CumulativeSuccessors} and {@link CumulativeDegrees}, generate the monotone * lists that will be encoded using the Elias–Fano representation. * *

* First, we store the monotone lists of cumulative outdegrees and indegrees. * *

* Then, we store the outgoing edges x → y as a monotone * sequence using the encoding x2⌈log n + * y. At that point the k-th edge can be obtained by retrieving the * k-th element of the sequence and some bit shifting (the encoding * xn + y 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 * x → y is an edge we just look for * x2⌈log n + y in the sequence. * *

* Finally, we store incoming edges y → x again as a monotone * sequence using the encoding xn + y - e, where * e 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. * *

* 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 the graph edge type */ public abstract class AbstractSuccinctDirectedGraph extends AbstractSuccinctGraph { private static final long serialVersionUID = 0L; public AbstractSuccinctDirectedGraph(final int n, final int m) { super(n, m); } /** * Turns all edges x → y into a monotone sequence using the * encoding x2⌈log n + y, or the * encoding xn + y - e, where e is the * index of the edge in lexicographical order, depending on the value of the {@code strict} * parameter. * * @param the graph edge type */ protected final static class CumulativeSuccessors implements LongIterator { private final Graph graph; private final long n; private final int sourceShift; private final Function> 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 graph, final Function> 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 degreeOf; private final int n; private int i = -1; private long cumul = 0; protected CumulativeDegrees(final int n, final Function 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(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy