com.googlecode.blaisemath.graph.modules.suppliers.DegreeDistributionGraphSupplier Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of blaise-graphtheory Show documentation
Show all versions of blaise-graphtheory Show documentation
Link graph definitions, algorithms, and visualization.
The newest version!
/*
* DegreeDistributionGraphSupplier.java
* Created Aug 6, 2010
*/
package com.googlecode.blaisemath.graph.modules.suppliers;
/*
* #%L
* BlaiseGraphTheory
* --
* Copyright (C) 2009 - 2016 Elisha Peterson
* --
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import com.google.common.base.Supplier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.googlecode.blaisemath.graph.Graph;
import com.googlecode.blaisemath.graph.SparseGraph;
/**
* Provides a random-graph model based on a degree sequence. If the
* graph is directed, the degree sequence is for the outdegrees. If the
* graph is undirected, the degree sequence is as usual (and must sum to an
* even number).
*
* @author elisha
*/
public final class DegreeDistributionGraphSupplier implements Supplier> {
private boolean directed = false;
private int[] degSequence;
public DegreeDistributionGraphSupplier() {
}
/**
* Returns graph with random number of connections between vertices. If the
* graph is directed, the degree sequence is for the outdegrees. If the
* graph is undirected, the degree sequence is as usual (and must sum to an
* even number).
*
* @param directed whether graph is directed
* @param degs degree sequence, where deg[i] is the # of nodes of degree i
* (should sum to an even number)
*/
public DegreeDistributionGraphSupplier(boolean directed, int[] degs) {
setDirected(directed);
setDegreeSequence(degs);
}
public boolean isDirected() {
return directed;
}
public void setDirected(boolean directed) {
this.directed = directed;
}
public int[] getDegreeSequence() {
return degSequence;
}
public void setDegreeSequence(int[] degs) {
this.degSequence = Arrays.copyOf(degs, degs.length);
}
public Graph get() {
return directed ? getDirectedInstance(degSequence) : getUndirectedInstance(degSequence);
}
/**
* Generate a random (directed) graph with specified outdegrees.
* @param deg the outdegree distribution of the graph
* @return directed instance of this type of random graph
*/
public static Graph getDirectedInstance(int[] deg) {
int n = sum(deg);
if (deg.length > n) {
throw new IllegalArgumentException("Maximum degree of sequence " + Arrays.toString(deg) + " is too large!");
}
Set edges = new TreeSet(EdgeCountGraphSupplier.PAIR_COMPARE);
int i = 0;
for (int iDeg = 0; iDeg < deg.length; iDeg++) {
for (int nDegI = 0; nDegI < deg[iDeg]; nDegI++) {
// each iteration here is a separate vertex of degree iDeg
// need to generate this many edges at random
int[] subset = randomSubset(n, iDeg, i);
for (int i2 : subset) {
edges.add(new Integer[]{i, i2});
}
i++;
}
}
return SparseGraph.createFromArrayEdges(true, GraphSuppliers.intList(n), edges);
}
/**
* @param deg a specified degree sequence
* @return undirected instance of this type of random graph; not guaranteed
* to have the actual degree sum
* @throws IllegalArgumentException if the degree sequence is not good (i.e.
* sums to an odd total degree, or the maximum degree is too large)
*/
public static Graph getUndirectedInstance(int[] deg) {
int n = sum(deg);
if (deg.length > n) {
throw new IllegalArgumentException("Maximum degree of sequence " + Arrays.toString(deg) + " is too large!");
}
int degSum = 0;
for (int i = 0; i < deg.length; i++) {
degSum += i * deg[i];
}
if (degSum % 2 != 0) {
throw new IllegalArgumentException("Degree sequence " + Arrays.toString(deg) + " has odd total degree!");
}
// stores the mapping of nodes to desired degrees
Map vxLeft = new TreeMap();
int i = 0;
for (int iDeg = 1; iDeg < deg.length; iDeg++) // ignore the degree 0 vertices
{
for (int nDegI = 0; nDegI < deg[iDeg]; nDegI++) {
vxLeft.put(i++, iDeg);
}
}
// stores the edges in the resulting graph
Set edges = new TreeSet(EdgeCountGraphSupplier.PAIR_COMPARE_UNDIRECTED);
while (vxLeft.size() > 1) {
Set vv = vxLeft.keySet();
Integer[] edge = new Integer[]{0, 0};
while (edge[0] == edge[1]) {
edge = new Integer[]{random(vv), random(vv)};
}
int attempts = 1;
// attempt to find new edge at random
while ((edges.contains(edge) || edge[0] == edge[1]) && attempts < 20) {
edge = new Integer[]{random(vv), random(vv)};
attempts++;
}
// if it takes too long, brute-force check to ensure edges are not there
if (edges.contains(edge) || edge[0] == edge[1]) {
Set edgesLeft = new TreeSet(EdgeCountGraphSupplier.PAIR_COMPARE_UNDIRECTED);
for (Integer i1 : vv) {
for (Integer i2 : vv) {
if (i1 != i2) {
Integer[] e = new Integer[]{i1, i2};
if (vxLeft.containsKey(i1) && vxLeft.containsKey(i2) && !edges.contains(e)) {
edgesLeft.add(e);
}
}
}
}
if (edgesLeft.isEmpty()) {
break;
} else {
edge = random(edgesLeft);
}
}
if (edges.contains(edge)) {
throw new IllegalStateException();
} else {
edges.add(edge);
if (vxLeft.get(edge[0]) == 1) {
vxLeft.remove(edge[0]);
} else {
vxLeft.put(edge[0], vxLeft.get(edge[0]) - 1);
}
if (vxLeft.get(edge[1]) == 1) {
vxLeft.remove(edge[1]);
} else {
vxLeft.put(edge[1], vxLeft.get(edge[1]) - 1);
}
}
}
if (vxLeft.size() > 0) {
Logger.getLogger(DegreeDistributionGraphSupplier.class.getName()).log(Level.WARNING,
"Unable to find edges for all vertices. Remaining list=" + vxLeft);
}
return SparseGraph.createFromArrayEdges(false, GraphSuppliers.intList(n), edges);
}
/**
* @return random value in given set
*/
private static V random(Set set) {
List list = new ArrayList(set);
int n = list.size();
int i = (int) Math.floor(n * Math.random());
return list.get(i);
}
/**
* Generate a random subset of the integers 0,...,n-1, possibly with the
* exclusion of a specific value.
*
* @param n the overall set size
* @param k the subset size
* @param omit an integer value to omit from the sequence; if outside the
* range 0,...,n-1, it is ignored
* @return random subset of integers 0,...,n-1 of given size
* @throw IllegalArgumentException if k is not in the range 0,...,n (if omit
* is in the sequence), or the range 0,...,n-1 (if omit is not in the
* sequence)
*/
private static int[] randomSubset(int n, int k, int omit) {
if (k < 0 || k > n || (k == n && omit >= 0 && omit <= n - 1)) {
throw new IllegalArgumentException("Cannot construct subset of size " + k + " from " + n + " values omitting " + omit);
}
int[] result = new int[k];
Set left = new TreeSet();
for (int i = 0; i < n; i++) {
left.add(i);
}
left.remove(omit); // will be ignored if omit is outside of range
for (int i = 0; i < k; i++) {
Integer value = random(left);
result[i] = value;
left.remove(value);
}
return result;
}
/**
* @return sum of array
*/
private static int sum(int[] arr) {
int sum = 0;
for (int i : arr) {
sum += i;
}
return sum;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy