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

org.jgrapht.generate.DirectedScaleFreeGraphGenerator Maven / Gradle / Ivy

/*
 * (C) Copyright 2019-2021, by Amr ALHOSSARY 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.generate;

import org.jgrapht.*;

import java.util.*;

/**
 * A generator for directed scale-free graphs.
 * 

* This generator creates a directed scale-free graph according to a power law, as described in * Bollobás et al. The paper can be cited as * Béla Bollobás, Christian Borgs, Jennifer Chayes, and Oliver Riordan. "Directed scale-free * graphs." Proceedings of the fourteenth annual ACM-SIAM symposium on Discrete algorithms. Society * for Industrial and Applied Mathematics, 2003. *

* In This generator, the graph continues to grow one edge per step, according to the probabilities * alpha, beta, and gamma (which sum up to 1).
*

    *
  • alpha is the probability that the new edge is from a new vertex v to an existing * vertex w, where w is chosen according to d_in + delta_in. *
  • beta is the probability that the new edge is from an existing vertex v to an existing * vertex w, where v and w are chosen independently, v according to d_out + delta_out, and w * according to d_in + delta_in. *
  • gamma is the probability that the new edge is from an existing vertex v to a new * vertex w, where v is chosen according to d_out + delta_out. *
* *

* In their original paper, the graph continues to grow according to a certain power law until a * certain number of edges is reached irrespective to the number of nodes.
* However, because the target number of edges is not known beforehand, in this implementation, we * added another feature that enables the user to grow the curve according to that power law until * the target number of edges or target number of nodes is reached. * * @author Amr ALHOSSARY * * @param the graph vertex type * @param the graph edge type */ public class DirectedScaleFreeGraphGenerator implements GraphGenerator { private final Random rng; /** * probability that the new edge is from a new vertex v to an existing vertex w, where w is * chosen according to d_in + delta_in criterion. */ private final float alpha; /** * The probability that the new edge is (from a new vertex v to an existing vertex w) plus the * probability that the new edge is (from an existing vertex v to an existing vertex w), where v * and w are chosen independently, v according to d_out + delta_out, and w according to d_in + * delta_in criteria. This equals 1 - gamma. Gamma refers to the probability that the new edge * is from an existing vertex v to a new vertex w. */ private final float alphaPlusBeta; /** In-degree bias used for Alpha and Beta */ private final float deltaIn; /** Out-degree bias used for Beta and Gamma */ private final float deltaOut; /** * Target total number of edges to reach. It has a higher priority than {@link #targetNodes}. * Zero is a valid value.
* If negative number, the user does not care about the total number of edges and is interested * only in the number of nodes, therefore, {@link #targetNodes} will be considered instead. * Otherwise, {@link #targetEdges} will be considered and {@link #targetEdges} will be ignored. */ private final int targetEdges; /** * Target total number of targetNodes to reach.
* This has lower priority than {@link #targetEdges}. It will not be used unless * {@link #targetEdges} given is a negative number. */ private final int targetNodes; /** * An enum to indicate the vertex selection using its inDegree or outDegree */ private enum Direction { IN, OUT } /** * Maximum number of consecutive failed attempts to add an edge. */ private int maxFailures = 1000; /** * Control whether the generated graph may contain loops. */ private boolean allowingMultipleEdges = true; /** * Control whether the generated graph many contain multiple (parallel) edges between the same * two vertices */ private boolean allowingSelfLoops = true; /** * Constructs a Generator. * * @param alpha The probability that the new edge is from a new vertex v to an existing vertex * w, where w is chosen according to d_in + delta_in. * @param gamma The probability that the new edge is from an existing vertex v to a new vertex * w, where v is chosen according to d_out + delta_out. * @param deltaIn The in-degree bias used for Alpha and Beta. * @param deltaOut The out-degree bias used for Beta and Gamma. * @param targetEdges Target total number of edges to reach. It has a higher priority than * {@link #targetNodes}. Zero is a valid value.
* If negative number, the user does not care about the total number of edges and is * interested only in the number of nodes, therefore, {@link #targetNodes} will be * considered instead. Otherwise, {@link #targetEdges} will be considered and * {@link #targetEdges} will be ignored. * @param targetNodes Target number of nodes to reach. Zero is a valid value.
* This parameter has lower priority than {@link #targetEdges} and will be used only if * {@link #targetEdges} given is a negative number. */ public DirectedScaleFreeGraphGenerator( float alpha, float gamma, float deltaIn, float deltaOut, int targetEdges, int targetNodes) { this(alpha, gamma, deltaIn, deltaOut, targetEdges, targetNodes, new Random()); } /** * Constructs a Generator using a seed for the random number generator. * * @param alpha The probability that the new edge is from a new vertex v to an existing vertex * w, where w is chosen according to d_in + delta_in. * @param gamma The probability that the new edge is from an existing vertex v to a new vertex * w, where v is chosen according to d_out + delta_out. * @param deltaIn The in-degree bias used for Alpha and Beta. * @param deltaOut The out-degree bias used for Beta and Gamma. * @param targetEdges Target total number of edges to reach. It has a higher priority than * {@link #targetNodes}. Zero is a valid value.
* If negative number, the user does not care about the total number of edges and is * interested only in the number of nodes, therefore, {@link #targetNodes} will be * considered instead. Otherwise, {@link #targetEdges} will be considered and * {@link #targetEdges} will be ignored. * @param targetNodes Target number of nodes to reach. Zero is a valid value.
* This parameter has lower priority than {@link #targetEdges} and will be used only if * {@link #targetEdges} given is a negative number. * @param seed The seed to feed to the random number generator. */ public DirectedScaleFreeGraphGenerator( float alpha, float gamma, float deltaIn, float deltaOut, int targetEdges, int targetNodes, long seed) { this(alpha, gamma, deltaIn, deltaOut, targetEdges, targetNodes, new Random(seed)); } /** * Constructs a Generator using a seed for the random number generator and sets the two * relaxation options allowingMultipleEdges and allowingSelfLoops. * * @param alpha The probability that the new edge is from a new vertex v to an existing vertex * w, where w is chosen according to d_in + delta_in. * @param gamma The probability that the new edge is from an existing vertex v to a new vertex * w, where v is chosen according to d_out + delta_out. * @param deltaIn The in-degree bias used for Alpha and Beta. * @param deltaOut The out-degree bias used for Beta and Gamma. * @param targetEdges Target total number of edges to reach. It has a higher priority than * {@link #targetNodes}. Zero is a valid value.
* If negative number, the user does not care about the total number of edges and is * interested only in the number of nodes, therefore, {@link #targetNodes} will be * considered instead. Otherwise, {@link #targetEdges} will be considered and * {@link #targetEdges} will be ignored. * @param targetNodes Target number of nodes to reach. Zero is a valid value.
* This parameter has lower priority than {@link #targetEdges} and will be used only if * {@link #targetEdges} given is a negative number. * @param seed The seed to feed to the random number generator. * @param allowingMultipleEdges whether the generator allows multiple parallel edges between the * same two vertices (v, w). * @param allowingSelfLoops whether the generator allows self loops from the a vertex to itself. */ public DirectedScaleFreeGraphGenerator( float alpha, float gamma, float deltaIn, float deltaOut, int targetEdges, int targetNodes, long seed, boolean allowingMultipleEdges, boolean allowingSelfLoops) { this(alpha, gamma, deltaIn, deltaOut, targetEdges, targetNodes, seed); this.allowingMultipleEdges = allowingMultipleEdges; this.allowingSelfLoops = allowingSelfLoops; } /** * Construct a new generator using the provided random number generator. * * @param alpha The probability that the new edge is from a new vertex v to an existing vertex * w, where w is chosen according to d_in + delta_in. * @param gamma The probability that the new edge is from an existing vertex v to a new vertex * w, where v is chosen according to d_out + delta_out. * @param deltaIn The in-degree bias used for Alpha and Beta. * @param deltaOut The out-degree bias used for Beta and Gamma. * @param targetEdges Target total number of edges to reach. It has a higher priority than * {@link #targetNodes}. Zero is a valid value.
* If negative number, the user does not care about the total number of edges and is * interested only in the number of nodes, therefore, {@link #targetNodes} will be * considered instead. Otherwise, {@link #targetEdges} will be considered and * {@link #targetEdges} will be ignored. * @param targetNodes Target number of nodes to reach. Zero is a valid value.
* This parameter has lower priority than {@link #targetEdges} and will be used only if * {@link #targetEdges} given is a negative number. * @param rng The {@link Random} object to use. */ public DirectedScaleFreeGraphGenerator( float alpha, float gamma, float deltaIn, float deltaOut, int targetEdges, int targetNodes, Random rng) { this.alpha = alpha; this.alphaPlusBeta = 1.0f - gamma; this.deltaIn = deltaIn; this.deltaOut = deltaOut; this.targetEdges = targetEdges; this.targetNodes = targetNodes; this.rng = Objects.requireNonNull(rng, "Random number generator cannot be null"); // Do several checks on the parameters if (alpha < 0 || gamma < 0 || alpha + gamma > 1) { throw new IllegalArgumentException( String.format("alpha and gamma values of (%f, %f) are invalid", alpha, gamma)); } if (deltaIn < 0 || deltaOut < 0) { throw new IllegalArgumentException( String .format( "deltaIn and deltaOut values of (%f, %f) are invalid", deltaIn, deltaOut)); } if (targetEdges < 0 && targetNodes < 0) { throw new IllegalArgumentException( "can not have both targetEdges and targetNodes not set."); } } /** * Construct a new generator using the provided random number generator and sets the two * relaxation options allowingMultipleEdges and allowingSelfLoops. * * @param alpha The probability that the new edge is from a new vertex v to an existing vertex * w, where w is chosen according to d_in + delta_in. * @param gamma The probability that the new edge is from an existing vertex v to a new vertex * w, where v is chosen according to d_out + delta_out. * @param deltaIn The in-degree bias used for Alpha and Beta. * @param deltaOut The out-degree bias used for Beta and Gamma. * @param targetEdges Target total number of edges to reach. It has a higher priority than * {@link #targetNodes}. Zero is a valid value.
* If negative number, the user does not care about the total number of edges and is * interested only in the number of nodes, therefore, {@link #targetNodes} will be * considered instead. Otherwise, {@link #targetEdges} will be considered and * {@link #targetEdges} will be ignored. * @param targetNodes Target number of nodes to reach. Zero is a valid value.
* This parameter has lower priority than {@link #targetEdges} and will be used only if * {@link #targetEdges} given is a negative number. * @param rng The {@link Random} object to use. * @param allowingMultipleEdges whether the generator allows multiple parallel edges between the * same two vertices (v, w). * @param allowingSelfLoops whether the generator allows self loops from the a vertex to itself. */ public DirectedScaleFreeGraphGenerator( float alpha, float gamma, float deltaIn, float deltaOut, int targetEdges, int targetNodes, Random rng, boolean allowingMultipleEdges, boolean allowingSelfLoops) { this(alpha, gamma, deltaIn, deltaOut, targetEdges, targetNodes, rng); this.allowingMultipleEdges = allowingMultipleEdges; this.allowingSelfLoops = allowingSelfLoops; } /** * Generates an instance of the {@link Graph}. * * @param target the target graph * @param resultMap not used by this generator, can be null * * @throws TooManyFailuresException When the method fails {@link #maxFailures} times to add a * new edge to the growing graph. * @throws IllegalArgumentException When the graph does not support Multiple edges or self loop * while the generator does. */ @Override public void generateGraph(Graph target, Map resultMap) { if (this.allowingMultipleEdges && !target.getType().isAllowingMultipleEdges()) { throw new IllegalArgumentException( "Generator allows Multiple Edges while graph does not. Consider changing this generator parameters or the target graph type."); } if (this.allowingSelfLoops && !target.getType().isAllowingSelfLoops()) { throw new IllegalArgumentException( "Generator allows Self loops while graph does not. Consider changing this generator parameters or the target graph type."); } Set newNodesSet = new HashSet<>(); Set newEdgesSet = new HashSet<>(); if (targetEdges == 0 || (targetEdges < 0 && targetNodes == 0)) return; V initV = target.addVertex(); newNodesSet.add(initV); int failuresCounter = 0; // grow network now, edge by edge. If the number of edges is unlocked, continue growing not // only until targetNodes is reached, but until adding any more edges would add another // node. i.e. allow more beta growth but not alpha nor gamma. while (targetEdges >= 0 ? targetEdges > newEdgesSet.size() : targetNodes >= newNodesSet.size()) { if (failuresCounter >= maxFailures) { throw new TooManyFailuresException( failuresCounter + " consecutive failures is more than maximum allowed number (" + maxFailures + ")."); } V v = null, w = null; boolean newV = false, newW = false; E e; float tributaries = rng.nextFloat(); if (tributaries <= alpha) { // stop adding nodes if you will exceed the target if (targetEdges < 0 && newNodesSet.size() == targetNodes) break; newV = true; w = pickAVertex(target, newNodesSet, newEdgesSet, Direction.IN, deltaIn); } else if (tributaries <= alphaPlusBeta) { v = pickAVertex(target, newNodesSet, newEdgesSet, Direction.OUT, deltaOut); w = pickAVertex(target, newNodesSet, newEdgesSet, Direction.IN, deltaIn); } else {// gamma // stop adding nodes if you will exceed the target if (targetEdges < 0 && newNodesSet.size() == targetNodes) break; v = pickAVertex(target, newNodesSet, newEdgesSet, Direction.OUT, deltaOut); newW = true; } if ((newV && w == null) || (newW && v == null)) { failuresCounter++; continue; } // check for self loops if (!allowingSelfLoops && v == w) { failuresCounter++; continue; } // check for multiple parallel targetEdges if (!allowingMultipleEdges && target.containsEdge(v, w)) { failuresCounter++; continue; } if (newV) { v = target.addVertex(); } if (newW) { w = target.addVertex(); } e = target.addEdge(v, w); failuresCounter = 0; newNodesSet.add(v); newNodesSet.add(w); newEdgesSet.add(e); } } /** * Select a vertex from the currently available vertices, using the passed bias. * * @param target The target graph * @param allNewNodes All (new) nodes in the target graph * @param allNewEdgesSet All (new) edges in the target graph * @param direction {@link Direction#IN} for inDegree or {@link Direction#IN} for outDegree * @param bias deltaIn or deltaOut value according to #directioIn * @return the selected node. */ private V pickAVertex( Graph target, Set allNewNodes, Set allNewEdgesSet, Direction direction, float bias) { final int allNewNodesSize = allNewNodes.size(); if (allNewNodesSize == 0) { return null; } else if (allNewNodesSize == 1) { return allNewNodes.iterator().next(); } float indicatorAccumulator = 0; V ret; float denominator = allNewEdgesSet.size() + allNewNodesSize * bias; float numerator; float r = rng.nextFloat(); // multiply r by denominator instead of dividing all individual values by it. r *= denominator; Iterator verticesIterator = allNewNodes.iterator(); do { ret = verticesIterator.next(); numerator = (direction == Direction.IN) ? (target.inDegreeOf(ret) + bias) : (target.outDegreeOf(ret) + bias); indicatorAccumulator += numerator; } while (verticesIterator.hasNext() && indicatorAccumulator < r); return ret; } /** * Returns the maximum allowed number of consecutive failed attempts to add an edge. * * @return maxFailure field. */ public int getMaxFailures() { return maxFailures; } /** * Sets the maximum allowed number of consecutive failed attempts to add an edge (must be non * negative). * * @param maxFailures Maximum allowed (non negative) number of consecutive failed attempts to * add an edge. */ public void setMaxFailures(int maxFailures) { if (maxFailures < 0) { throw new IllegalArgumentException("value must be non negative"); } this.maxFailures = maxFailures; } /** * Returns whether the generated graph may contain multiple (parallel) edges between the same * two vertices. * * @return whether the generated graph may contain multiple (parallel) edges between the same * two vertices */ public boolean isAllowingMultipleEdges() { return allowingMultipleEdges; } /** * Sets whether the generated graph may contain multiple (parallel) edges between the same two * vertices * * @param allowingMultipleEdges whether the generated graph may contain multiple (parallel) * edges between the same two vertices */ public void setAllowingMultipleEdges(boolean allowingMultipleEdges) { this.allowingMultipleEdges = allowingMultipleEdges; } /** * Returns whether the generated graph may contain multiple (parallel) edges between the same * two vertices * * @return whether the generated graph many contain multiple (parallel) edges between the same * two vertices */ public boolean isAllowingSelfLoops() { return allowingSelfLoops; } /** * Sets whether the generated graph may contain multiple (parallel) edges between the same two * vertices * * @param allowingSelfLoops whether the generated graph many contain multiple (parallel) edges * between the same two vertices */ public void setAllowingSelfLoops(boolean allowingSelfLoops) { this.allowingSelfLoops = allowingSelfLoops; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy