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

org.jgrapht.webgraph.ImmutableDirectedGraphAdapter Maven / Gradle / Ivy

There is a newer version: 1.5.2
Show newest version
/*
 * (C) Copyright 2020-2020, 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.webgraph;

import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Set;

import org.jgrapht.GraphIterables;
import org.jgrapht.GraphType;
import org.jgrapht.graph.DefaultGraphType;

import com.google.common.collect.Iterables;

import it.unimi.dsi.fastutil.ints.IntIntPair;
import it.unimi.dsi.fastutil.ints.IntIntSortedPair;
import it.unimi.dsi.fastutil.objects.ObjectIterables;
import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashBigSet;
import it.unimi.dsi.lang.FlyweightPrototype;
import it.unimi.dsi.webgraph.EFGraph;
import it.unimi.dsi.webgraph.ImmutableGraph;
import it.unimi.dsi.webgraph.LazyIntIterator;
import it.unimi.dsi.webgraph.LazyIntIterators;
import it.unimi.dsi.webgraph.LazyIntSkippableIterator;
import it.unimi.dsi.webgraph.NodeIterator;
import it.unimi.dsi.webgraph.Transform;

/**
 * An adapter class for directed graphs using WebGraph's
 * {@link ImmutableGraph}.
 *
 * 

* Nodes are instances of {@link Integer} corresponding to the index of a node in WebGraph. Edges * are represented by an {@link IntIntPair}. The left and right element are the source and the * target of the edge. Since the underlying graph is immutable, the resulting graph is unmodifiable. * Edges are immutable and can be tested for equality (e.g., stored in a dictionary). * *

* WebGraph provides methods for successors only, so to adapt a directed graph you must provide both * a graph and its transpose (methods to compute the transpose are available in {@link Transform}). * * You need to load an {@link ImmutableGraph} and its transpose using one of the available load * methods, and then build an adapter: * *

 * immutableGraph = ImmutableGraph.loadMapped("mygraph");
 * immutableTranspose = ImmutableGraph.loadMapped("mygraph-t");
 * adapter = new ImmutableDirectedGraphAdapter(immutableGraph, immutableTranspose);
 * 
* *

* The first graph will be used to implement {@link #outgoingEdgesOf(Integer)}, and the second graph * to implement {@link #incomingEdgesOf(Integer)}. It is your responsibility that the two provided * graphs are one the transpose of the other (for each arc * x → y in a graph there must be an arc * y → x in the other, and vice versa). No check will be * performed. Note that {@linkplain GraphIterables#edgeCount() computing the number of edges of a * graph} requires a full scan of the edge set if {@link ImmutableGraph#numArcs()} is not supported * (the first time—then it will be cached). * *

* If you use a load method that does not provide random access, most methods will throw an * {@link UnsupportedOperationException}. * *

* If you know that you will never used methods based on incoming edges * ({@link #incomingEdgesOf(Integer)}, {@link #inDegreeOf(Integer)}, {@link #edgesOf(Integer)}, * {@link #degreeOf(Integer)}), you can also use the constructor using just a graph, but all such * methods will throw a {@link NullPointerException}: * *

 * immutableGraph = ImmutableGraph.loadMapped("mygraph");
 * adapter = new ImmutableDirectedGraphAdapter(immutableGraph);
 * 
* *

* If necessary, you can adapt a {@linkplain it.unimi.dsi.big.webgraph.ImmutableGraph big WebGraph * graph} with at most {@link Integer#MAX_VALUE} vertices using the suitable * {@linkplain it.unimi.dsi.big.webgraph.ImmutableGraph#wrap(ImmutableGraph) wrapper}. * *

Thread safety

* *

* This class is not thread safe: following the {@link FlyweightPrototype} pattern, users can access * concurrently the graph {@linkplain #copy() by getting lightweight copies}. * *

Fast adjacency check

* *

* As it happens for the sparse representation of JGraphT, usually a WebGraph compressed * representation requires scanning the adjacency list of a node to * {@linkplain #getEdge(Integer, Integer) test whether a specific arc exists}. However, if you adapt * a WebGraph class (such as {@link EFGraph}) which provides {@linkplain LazyIntSkippableIterator * skippable iterators} with fast skipping, adjacency can be tested more quickly (e.g., essentially * in constant time in the case of {@link EFGraph}). * * @see AbstractImmutableBigGraphAdapter * @author Sebastiano Vigna */ public class ImmutableDirectedGraphAdapter extends AbstractImmutableGraphAdapter implements FlyweightPrototype { /** * The transpose of {@link #immutableGraph}, for a directed graph with full support; * {@code null}, for a directed graph with access to outgoing edges, only. */ private final ImmutableGraph immutableTranspose; /** * Creates an adapter for a directed immutable graph. * *

* It is responsibility of the caller that the two provided graphs are one the transpose of the * other (for each arc x → y in a graph there must be an * arc y → x in the other). If this property is not true, * results will be unpredictable. * * @param immutableGraph an immutable graph. * @param immutableTranspose its transpose. */ public ImmutableDirectedGraphAdapter( final ImmutableGraph immutableGraph, final ImmutableGraph immutableTranspose) { super(immutableGraph); this.immutableTranspose = immutableTranspose; if (immutableTranspose != null && n != immutableTranspose.numNodes()) throw new IllegalArgumentException( "The graph has " + n + " nodes, but the transpose has " + immutableTranspose.numNodes()); } /** * Creates an adapter for a directed immutable graph implementing only methods based on outgoing * edges. * * @param immutableGraph an immutable graph. */ public ImmutableDirectedGraphAdapter(final ImmutableGraph immutableGraph) { this(immutableGraph, null); } @Override protected IntIntPair makeEdge(final int x, final int y) { return IntIntPair.of(x, y); } @Override public boolean containsEdge(final IntIntPair e) { if (e == null) return false; if (e instanceof IntIntSortedPair) return false; return containsEdgeFast(e.leftInt(), e.rightInt()); } @Override public Set edgeSet() { final NodeIterator nodeIterator = immutableGraph.nodeIterator(); final long m = iterables().edgeCount(); final ObjectOpenHashBigSet edges = new ObjectOpenHashBigSet<>(m); for (int i = 0; i < n; i++) { final int x = nodeIterator.nextInt(); final LazyIntIterator successors = nodeIterator.successors(); for (int y; (y = successors.nextInt()) != -1;) edges.add(IntIntPair.of(x, y)); } return edges; } @Override public int degreeOf(final Integer vertex) { final long d = (long) inDegreeOf(vertex) + outDegreeOf(vertex); if (d > Integer.MAX_VALUE) throw new ArithmeticException(); return (int) d; } @Override public Set edgesOf(final Integer vertex) { final ObjectLinkedOpenHashSet set = new ObjectLinkedOpenHashSet<>(); final int source = vertex; final LazyIntIterator successors = immutableGraph.successors(source); for (int target; (target = successors.nextInt()) != -1;) set.add(IntIntPair.of(source, target)); final LazyIntIterator predecessors = immutableTranspose.successors(source); for (int target; (target = predecessors.nextInt()) != -1;) if (source != target) set.add(IntIntPair.of(target, source)); return set; } @Override public int inDegreeOf(final Integer vertex) { return immutableTranspose.outdegree(vertex); } @Override public Set incomingEdgesOf(final Integer vertex) { final ObjectLinkedOpenHashSet set = new ObjectLinkedOpenHashSet<>(); final int source = vertex; final LazyIntIterator predecessors = immutableTranspose.successors(source); for (int target; (target = predecessors.nextInt()) != -1;) set.add(IntIntPair.of(target, source)); return set; } @Override public int outDegreeOf(final Integer vertex) { return immutableGraph.outdegree(vertex); } @Override public Set outgoingEdgesOf(final Integer vertex) { final ObjectLinkedOpenHashSet set = new ObjectLinkedOpenHashSet<>(); final int source = vertex; final LazyIntIterator successors = immutableGraph.successors(source); for (int target; (target = successors.nextInt()) != -1;) set.add(IntIntPair.of(source, target)); return set; } @Override public GraphType getType() { return new DefaultGraphType.Builder() .weighted(false).modifiable(false).allowMultipleEdges(false).allowSelfLoops(true) .directed().build(); } @Override public ImmutableDirectedGraphAdapter copy() { return new ImmutableDirectedGraphAdapter( immutableGraph.copy(), immutableTranspose != null ? immutableTranspose.copy() : null); } private final GraphIterables ITERABLES = new GraphIterables<>() { @Override public ImmutableDirectedGraphAdapter getGraph() { return ImmutableDirectedGraphAdapter.this; } @Override public long vertexCount() { return n; } @Override public long edgeCount() { if (m != -1) return m; try { return m = immutableGraph.numArcs(); } catch (final UnsupportedOperationException e) { } return m = ObjectIterables.size(edges()); } @Override public long degreeOf(final Integer vertex) { return inDegreeOf(vertex) + outDegreeOf(vertex); } @Override public Iterable edgesOf(final Integer source) { return Iterables.concat(outgoingEdgesOf(source), incomingEdgesOf(source, true)); } @Override public long inDegreeOf(final Integer vertex) { return immutableTranspose.outdegree(vertex); } private Iterable incomingEdgesOf(final int x, final boolean skipLoops) { return () -> new Iterator<>() { final LazyIntIterator successors = immutableTranspose.successors(x); int y = successors.nextInt(); @Override public boolean hasNext() { if (y == -1) { y = successors.nextInt(); if (skipLoops && x == y) y = successors.nextInt(); } return y != -1; } @Override public IntIntPair next() { final IntIntPair edge = IntIntPair.of(y, x); y = -1; return edge; } }; } @Override public Iterable incomingEdgesOf(final Integer vertex) { return incomingEdgesOf(vertex, false); } @Override public long outDegreeOf(final Integer vertex) { return immutableGraph.outdegree(vertex); } @Override public Iterable outgoingEdgesOf(final Integer vertex) { return () -> new Iterator<>() { final int x = vertex; final LazyIntIterator successors = immutableGraph.successors(x); int y = successors.nextInt(); @Override public boolean hasNext() { if (y == -1) y = successors.nextInt(); return y != -1; } @Override public IntIntPair next() { final IntIntPair edge = IntIntPair.of(x, y); y = -1; return edge; } }; } @Override public Iterable edges() { return () -> new Iterator<>() { final NodeIterator nodeIterator = immutableGraph.nodeIterator(); LazyIntIterator successors = LazyIntIterators.EMPTY_ITERATOR; int x, y = -1; @Override public boolean hasNext() { if (y != -1) return true; while ((y = successors.nextInt()) == -1) { if (!nodeIterator.hasNext()) return false; x = nodeIterator.nextInt(); successors = nodeIterator.successors(); } return true; } @Override public IntIntPair next() { if (!hasNext()) throw new NoSuchElementException(); final IntIntPair edge = IntIntPair.of(x, y); y = -1; return edge; } }; } }; @Override public GraphIterables iterables() { return ITERABLES; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy