Java tutorial
/* dsh-analysis Data analysis. Copyright (c) 2011-2014 held jointly by the individual authors. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; with out even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. > http://www.fsf.org/licensing/licenses/lgpl.html > http://www.opensource.org/licenses/lgpl-license.php */ package org.dishevelled.analysis; import java.util.List; import org.apache.commons.math.random.JDKRandomGenerator; import org.apache.commons.math.random.RandomGenerator; import org.dishevelled.collect.Lists; import org.dishevelled.functor.BinaryFunction; import org.dishevelled.graph.Edge; import org.dishevelled.graph.Graph; import org.dishevelled.graph.Node; import org.dishevelled.graph.impl.GraphUtils; import org.dishevelled.multimap.BinaryKeyMap; import org.dishevelled.multimap.impl.BinaryKeyMaps; import org.dishevelled.weighted.WeightedMap; import org.dishevelled.weighted.WeightedMaps; /** * Graph generators. * * @author Michael Heuer */ public final class GraphGenerators { /** Default unconnected node weight, <code>0.1d</code>. */ private static final double DEFAULT_UNCONNECTED_NODE_WEIGHT = 0.1d; /** * Private no-arg constructor. */ private GraphGenerators() { // empty } /** * Connect the specified graph completely with the specified value on all added edges. * * @param <N> graph node type * @param <E> graph edge type * @param graph graph to connect, must not be null * @param edgeValue edge value * @return the specified graph connected completely with the specified value on all added edges */ public static <N, E> Graph<N, E> connectCompletely(final Graph<N, E> graph, final E edgeValue) { return connectCompletely(graph, new Scalar<N, E>(edgeValue)); } /** * Connect the specified graph completely with values provided by the specified function on edges. * * @param <N> graph node type * @param <E> graph edge type * @param graph graph to connect, must not be null * @param edgeValues edge values, must not be null * @return the specified graph connected completely with values provided by the specified function on edges */ public static <N, E> Graph<N, E> connectCompletely(final Graph<N, E> graph, final BinaryFunction<N, N, E> edgeValues) { if (graph == null) { throw new IllegalArgumentException("graph must not be null"); } if (edgeValues == null) { throw new IllegalArgumentException("edgeValues must not be null"); } int n = graph.nodeCount(); List<Node<N, E>> nodes = Lists.asImmutableList(graph.nodes()); BinaryKeyMap<Node<N, E>, Node<N, E>, Edge<N, E>> edges = BinaryKeyMaps.createBinaryKeyMap(n * n); for (Edge<N, E> edge : graph.edges()) { edges.put(edge.source(), edge.target(), edge); } for (int i = 0; i < n; i++) { Node<N, E> source = nodes.get(i); for (int j = 0; j < n; j++) { Node<N, E> target = nodes.get(j); if (!source.equals(target) && !edges.containsKey(source, target)) { E edgeValue = edgeValues.evaluate(source.getValue(), target.getValue()); graph.createEdge(source, target, edgeValue); } } } return graph; } /** * Create and return a new completely connected graph with the specified nodes values * on nodes and the specified edge value on all added edges. * * @param <N> graph node type * @param <E> graph edge type * @param nodeValues list of node values, must not be null * @param edgeValue edge value * @return a new completely connected graph with the specified nodes values * on nodes and the specified edge value on all added edges */ public static <N, E> Graph<N, E> connectCompletely(final List<N> nodeValues, final E edgeValue) { return connectCompletely(nodeValues, new Scalar<N, E>(edgeValue)); } /** * Create and return a new completely connected graph with the specified nodes values * on nodes and values provided by the specified function on edges. * * @param <N> graph node type * @param <E> graph edge type * @param nodeValues list of node values, must not be null * @param edgeValues edge values, must not be null * @return a new completely connected graph with the specified nodes values * on nodes and the specified edge value on all added edges */ public static <N, E> Graph<N, E> connectCompletely(final List<N> nodeValues, final BinaryFunction<N, N, E> edgeValues) { if (nodeValues == null) { throw new IllegalArgumentException("nodeValues must not be null"); } if (edgeValues == null) { throw new IllegalArgumentException("edgeValues must not be null"); } int n = nodeValues.size(); Graph<N, E> graph = GraphUtils.createGraph(n, n * n); List<Node<N, E>> nodes = Lists.createList(n); for (N nodeValue : nodeValues) { nodes.add(graph.createNode(nodeValue)); } for (int i = 0; i < n; i++) { Node<N, E> source = nodes.get(i); for (int j = 0; j < n; j++) { Node<N, E> target = nodes.get(j); if (!source.equals(target)) { E edgeValue = edgeValues.evaluate(source.getValue(), target.getValue()); graph.createEdge(source, target, edgeValue); } } } return graph; } /** * Connect the specified graph randomly with the specified value on all added edges. * * @param <N> graph node type * @param <E> graph edge type * @param graph graph to connect, must not be null * @param edgeCount edge count, must be at least zero * @param edgeValue edge value * @return the specified graph connected randomly with the specified value on all added edges */ public static <N, E> Graph<N, E> connectRandomly(final Graph<N, E> graph, final int edgeCount, final E edgeValue) { return connectRandomly(graph, edgeCount, new Scalar<N, E>(edgeValue)); } /** * Connect the specified graph randomly with the specified value on all added edges. * * @param <N> graph node type * @param <E> graph edge type * @param graph graph to connect, must not be null * @param edgeCount edge count, must be at least zero * @param edgeValue edge value * @param random source of randomness, must not be null * @return the specified graph connected randomly with the specified value on all added edges */ public static <N, E> Graph<N, E> connectRandomly(final Graph<N, E> graph, final int edgeCount, final E edgeValue, final RandomGenerator random) { return connectRandomly(graph, edgeCount, new Scalar<N, E>(edgeValue), random); } /** * Connect the specified graph randomly with the specified value on all added edges. * * @param <N> graph node type * @param <E> graph edge type * @param graph graph to connect, must not be null * @param edgeCount edge count, must be at least zero * @param edgeValues edge values, must not be null * @return the specified graph connected randomly with the specified value on all added edges */ public static <N, E> Graph<N, E> connectRandomly(final Graph<N, E> graph, final int edgeCount, final BinaryFunction<N, N, E> edgeValues) { return connectRandomly(graph, edgeCount, edgeValues, new JDKRandomGenerator()); } /** * Connect the specified graph randomly with the specified value on all added edges. * * @param <N> graph node type * @param <E> graph edge type * @param graph graph to connect, must not be null * @param edgeCount edge count, must be at least zero * @param edgeValues edge values, must not be null * @param random source of randomness, must not be null * @return the specified graph connected randomly with the specified value on all added edges */ public static <N, E> Graph<N, E> connectRandomly(final Graph<N, E> graph, final int edgeCount, final BinaryFunction<N, N, E> edgeValues, final RandomGenerator random) { if (graph == null) { throw new IllegalArgumentException("graph must not be null"); } if (edgeCount < 0) { throw new IllegalArgumentException("edgeCount must be at least zero"); } if (edgeValues == null) { throw new IllegalArgumentException("edgeValues must not be null"); } if (random == null) { throw new IllegalArgumentException("random must not be null"); } List<Node<N, E>> nodes = Lists.asImmutableList(graph.nodes()); for (int i = graph.edgeCount(); i < edgeCount; i++) { Node<N, E> source = nodes.get(random.nextInt(nodes.size())); Node<N, E> target = nodes.get(random.nextInt(nodes.size())); E edgeValue = edgeValues.evaluate(source.getValue(), target.getValue()); graph.createEdge(source, target, edgeValue); } return graph; } /** * Create and return a new randomly connected graph with the specified nodes values * on nodes and the specified edge value on all added edges. * * @param <N> graph node type * @param <E> graph edge type * @param nodeValues list of node values, must not be null * @param edgeCount edge count, must be at least zero * @param edgeValue edge value * @return a new randomly connected graph with the specified nodes values * on nodes and the specified edge value on all added edges */ public static <N, E> Graph<N, E> connectRandomly(final List<N> nodeValues, final int edgeCount, final E edgeValue) { return connectRandomly(nodeValues, edgeCount, new Scalar<N, E>(edgeValue)); } /** * Create and return a new randomly connected graph with the specified nodes values * on nodes and the specified edge value on all added edges. * * @param <N> graph node type * @param <E> graph edge type * @param nodeValues list of node values, must not be null * @param edgeCount edge count, must be at least zero * @param edgeValue edge value * @param random source of randomness, must not be null * @return a new randomly connected graph with the specified nodes values * on nodes and the specified edge value on all added edges */ public static <N, E> Graph<N, E> connectRandomly(final List<N> nodeValues, final int edgeCount, final E edgeValue, final RandomGenerator random) { return connectRandomly(nodeValues, edgeCount, new Scalar<N, E>(edgeValue), random); } /** * Create and return a new randomly connected graph with the specified nodes values * on nodes and values provided by the specified function on edges. * * @param <N> graph node type * @param <E> graph edge type * @param nodeValues list of node values, must not be null * @param edgeCount edge count, must be at least zero * @param edgeValues edge values, must not be null * @return a new randomly connected graph with the specified nodes values * on nodes and the specified edge value on all added edges */ public static <N, E> Graph<N, E> connectRandomly(final List<N> nodeValues, final int edgeCount, final BinaryFunction<N, N, E> edgeValues) { return connectRandomly(nodeValues, edgeCount, edgeValues, new JDKRandomGenerator()); } /** * Create and return a new randomly connected graph with the specified nodes values * on nodes and values provided by the specified function on edges. * * @param <N> graph node type * @param <E> graph edge type * @param nodeValues list of node values, must not be null * @param edgeCount edge count, must be at least zero * @param edgeValues edge values, must not be null * @param random source of randomness, must not be null * @return a new randomly connected graph with the specified nodes values * on nodes and the specified edge value on all added edges */ public static <N, E> Graph<N, E> connectRandomly(final List<N> nodeValues, final int edgeCount, final BinaryFunction<N, N, E> edgeValues, final RandomGenerator random) { if (nodeValues == null) { throw new IllegalArgumentException("nodeValues must not be null"); } if (edgeCount < 0) { throw new IllegalArgumentException("edgeCount must be at least zero"); } if (edgeValues == null) { throw new IllegalArgumentException("edgeValues must not be null"); } if (random == null) { throw new IllegalArgumentException("random must not be null"); } int n = nodeValues.size(); Graph<N, E> graph = GraphUtils.createGraph(n, edgeCount); List<Node<N, E>> nodes = Lists.createList(n); for (N nodeValue : nodeValues) { nodes.add(graph.createNode(nodeValue)); } for (int i = 0; i < edgeCount; i++) { Node<N, E> source = nodes.get(random.nextInt(n)); Node<N, E> target = nodes.get(random.nextInt(n)); E edgeValue = edgeValues.evaluate(source.getValue(), target.getValue()); graph.createEdge(source, target, edgeValue); } return graph; } /** * Connect the specified graph preferentially with the specified value on all added edges. * * @param <N> graph node type * @param <E> graph edge type * @param graph graph to connect, must not be null * @param edgeCount edge count, must be at least zero * @param edgeValue edge value * @return the specified graph connected preferentially with the specified value on all added edges */ public static <N, E> Graph<N, E> connectPreferentially(final Graph<N, E> graph, final int edgeCount, final E edgeValue) { return connectPreferentially(graph, edgeCount, new Scalar<N, E>(edgeValue)); } /** * Connect the specified graph preferentially with the specified value on all added edges. * * @param <N> graph node type * @param <E> graph edge type * @param graph graph to connect, must not be null * @param edgeCount edge count, must be at least zero * @param edgeValues edge values, must not be null * @return the specified graph connected preferentially with the specified value on all added edges */ public static <N, E> Graph<N, E> connectPreferentially(final Graph<N, E> graph, final int edgeCount, final BinaryFunction<N, N, E> edgeValues) { if (graph == null) { throw new IllegalArgumentException("graph must not be null"); } if (edgeCount < 0) { throw new IllegalArgumentException("edgeCount must be at least zero"); } if (edgeValues == null) { throw new IllegalArgumentException("edgeValues must not be null"); } // weight nodes by degree WeightedMap<Node<N, E>> weightedNodes = WeightedMaps.createWeightedMap(graph.nodeCount()); for (Node<N, E> node : graph.nodes()) { weightedNodes.put(node, Double.valueOf(node.degree() + DEFAULT_UNCONNECTED_NODE_WEIGHT)); } // store existing edges to prevent creating duplicates BinaryKeyMap<Node<N, E>, Node<N, E>, Edge<N, E>> edges = BinaryKeyMaps.createBinaryKeyMap(edgeCount); for (Edge<N, E> edge : graph.edges()) { edges.put(edge.source(), edge.target(), edge); } // connect preferentially until edgeCount is reached while (edges.size() < edgeCount) { Node<N, E> source = weightedNodes.sample(); Node<N, E> target = weightedNodes.sample(); // avoid self-edges if (!source.equals(target) && !edges.containsKey(source, target)) { E edgeValue = edgeValues.evaluate(source.getValue(), target.getValue()); edges.put(source, target, graph.createEdge(source, target, edgeValue)); // update weights weightedNodes.put(source, Double.valueOf(source.degree())); weightedNodes.put(target, Double.valueOf(target.degree())); } } return graph; } /** * Create and return a new preferentially connected graph with the specified nodes values * on nodes and the specified edge value on all added edges. * * @param <N> graph node type * @param <E> graph edge type * @param nodeValues list of node values, must not be null * @param edgeCount edge count, must be at least zero * @param edgeValue edge value * @return a new randomly connected graph with the specified nodes values * on nodes and the specified edge value on all added edges */ public static <N, E> Graph<N, E> connectPreferentially(final List<N> nodeValues, final int edgeCount, final E edgeValue) { return connectPreferentially(nodeValues, edgeCount, new Scalar<N, E>(edgeValue)); } /** * Create and return a new preferentially connected graph with the specified nodes values * on nodes and values provided by the specified function on edges. * * @param <N> graph node type * @param <E> graph edge type * @param nodeValues list of node values, must not be null * @param edgeCount edge count, must be at least zero * @param edgeValues edge values, must not be null * @return a new randomly connected graph with the specified nodes values * on nodes and values provided by the specified function on edges */ public static <N, E> Graph<N, E> connectPreferentially(final List<N> nodeValues, final int edgeCount, final BinaryFunction<N, N, E> edgeValues) { if (nodeValues == null) { throw new IllegalArgumentException("nodeValues must not be null"); } if (edgeCount < 0) { throw new IllegalArgumentException("edgeCount must be at least zero"); } if (edgeValues == null) { throw new IllegalArgumentException("edgeValues must not be null"); } int n = nodeValues.size(); Graph<N, E> graph = GraphUtils.createGraph(n, edgeCount); List<Node<N, E>> nodes = Lists.createList(n); for (N nodeValue : nodeValues) { nodes.add(graph.createNode(nodeValue)); } // weight nodes by degree WeightedMap<Node<N, E>> weightedNodes = WeightedMaps.createWeightedMap(n); for (Node<N, E> node : graph.nodes()) { weightedNodes.put(node, Double.valueOf(node.degree() + DEFAULT_UNCONNECTED_NODE_WEIGHT)); } // store existing edges to prevent creating duplicates BinaryKeyMap<Node<N, E>, Node<N, E>, Edge<N, E>> edges = BinaryKeyMaps.createBinaryKeyMap(edgeCount); // connect preferentially until edgeCount is reached while (edges.size() < edgeCount) { Node<N, E> source = weightedNodes.sample(); Node<N, E> target = weightedNodes.sample(); if (!source.equals(target) && !edges.containsKey(source, target)) { E edgeValue = edgeValues.evaluate(source.getValue(), target.getValue()); edges.put(source, target, graph.createEdge(source, target, edgeValue)); weightedNodes.put(source, Double.valueOf(source.degree())); weightedNodes.put(target, Double.valueOf(target.degree())); } } return graph; } /** * Scalar function. * * @param <N> graph node type * @param <E> graph edge type */ private static final class Scalar<N, E> implements BinaryFunction<N, N, E> { /** Value. */ private final E value; /** * Create a new scalar function with the specified value. * * @param value value */ private Scalar(final E value) { this.value = value; } @Override public E evaluate(final N ignore0, final N ignore1) { return value; } } }