utils.RandomTopo.java Source code

Java tutorial

Introduction

Here is the source code for utils.RandomTopo.java

Source

package utils;

import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Random;
import java.util.TreeMap;

import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.Namespace;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;

import utils.DOTrenderer;
import utils.RandGraphFactory;
import utils.RandGraphFactory.Edge;
import utils.RandGraphFactory.Graph;

public class RandomTopo {

    /*
    FRODO: a FRamework for Open/Distributed Optimization
    Copyright (C) 2008-2013  Thomas Leaute, Brammert Ottens & Radoslaw Szymanek
        
    FRODO is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
        
    FRODO is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.
        
    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
        
        
    How to contact the authors: 
    <http://frodo2.sourceforge.net/>
    */

    /** Generates a random smart grid problem with random topology and writes it to a file
     * @param args    [-soft] nbrNodes density nbrColors [stochNodeRatio]
     * @throws IOException    if an error occurs while attempting to write the output file
     * @todo Add support for various graph topologies
     */
    public static void main(String[] args) throws IOException {

        // The GNU GPL copyright notice
        System.out.println("FRODO  Copyright (C) 2008-2013  Thomas Leaute, Brammert Ottens & Radoslaw Szymanek\n"
                + "This program comes with ABSOLUTELY NO WARRANTY.\n"
                + "This is free software, and you are welcome to redistribute it\n"
                + "under certain conditions. \n");

        ArrayList<String> args2 = new ArrayList<String>(Arrays.asList(args));

        boolean intensional = args2.remove("-i");
        boolean soft = args2.remove("-soft");
        boolean mpc = args2.remove("-mpc");
        boolean nbrLinks = args2.remove("-nbrLinks");

        if (args2.size() < 6 || args2.size() > 7) {
            System.err.println("Usage: " + RandomTopo.class.getSimpleName()
                    + " [-i] [-soft] [-mpc] [-nbrLinks] nbrNodes density tightness nbrColors gaussian [stochNodeRatio]\n"
                    + "\t -i [optional]               if present, the output problem is expressed in intensional form\n"
                    + "\t -soft [optional]            if present, the output is a Max-DisCSP instead of a DisCSP with hard constraints (default is DisCSP)\n"
                    + "\t -mpc [optional]             if present, also outputs an alternative problem formulation in which all constraints are public\n"
                    + "\t -nbrLinks [optional]      if present, the density parameter states that there must be density*nbrNodes links in the graph\n"
                    + "\t nbrNodes                    the number of nodes\n"
                    + "\t density                     the fraction of pairs of nodes that are neighbors of each other\n"
                    + "\t tightness                   if >0, the output problem contains unary constraints of expected tightness equal to this input parameter\n"
                    + "\t nbrColors                   the number of colors\n"
                    + "\t gaussian                    Whether to use a gaussian or uniform distribution for volatility\n"
                    + "\t nbrGraphs               The number of graphs to generate.\n"
                    + "\t stochNodeRatio [optional]   the fraction of nodes whose color is uncontrollable; the output is a StochDCOP (default is 0)");
            System.exit(1);
        }

        final int nbrNodes = Integer.parseInt(args2.get(0));
        assert nbrNodes >= 2 : "To few nodes (" + nbrNodes + ")";
        final double density = Double.parseDouble(args2.get(1));
        assert nbrLinks || (density >= 0 && density <= 1) : "The input density is not between 0 and 1: " + density;
        final double tightness = Double.parseDouble(args2.get(2));
        assert tightness >= 0 && tightness <= 1 : "The input tightness is not between 0 and 1: " + tightness;
        final int nbrColors = Integer.parseInt(args2.get(3));
        assert nbrColors > 0 : "The number of colors must be positive (" + nbrColors + ")";
        final boolean gaussian = Boolean.parseBoolean(args2.get(4));
        final int nbrGraphs = Integer.parseInt(args2.get(5));
        // Look for the ratio of stochastic nodes
        int nbrStochNodes = 0;
        if (args2.size() >= 7)
            nbrStochNodes = (int) (nbrNodes * Double.parseDouble(args2.get(6)));
        assert nbrStochNodes >= 0 && nbrStochNodes <= nbrNodes : "Incorrect ratio of stochastic nodes";

        for (int i = 0; i < nbrGraphs; i++) {
            // Generate the random instance
            RandomTopo instance = new RandomTopo(nbrNodes, density, tightness, nbrColors, nbrStochNodes, nbrLinks,
                    gaussian);
            new DOTrenderer("graph", instance.graph.toString(), "twopi");

            // Generate the XCSP representation and write it to a file
            Document problem = instance.toXCSP(false, soft, intensional);
            new XMLOutputter(Format.getPrettyFormat()).output(problem, new FileWriter(
                    "randomTopo" + (gaussian ? "gaussian" : "uniform") + (i + 1) + "_" + nbrNodes + "nodes.xml"));
            System.out.println("Wrote randomTopo" + (gaussian ? "gaussian" : "uniform") + (i + 1) + "_" + nbrNodes
                    + "nodes.xml");

            if (mpc) {
                problem = instance.toXCSP(true, soft, intensional);
                new XMLOutputter(Format.getPrettyFormat()).output(problem,
                        new FileWriter("randomTopo_MPC" + i + ".xml"));
                System.out.println("Wrote randomTopo_MPC" + i + ".xml");
            }
        }
    }

    /** Generates a problem instance
     * @param soft             whether to make it a DisCSP (\c false) or a Max-DisCSP (\c true)
     * @param nbrNodes          total number of nodes
     * @param density          graph density
     * @param tightness       the tightness of the unary constraints
     * @param nbrColors       number of colors
     * @param nbrStochNodes    number of uncontrollable nodes
     * @param intensional       whether the output should be intensional
     * @param gaussian          whether to generate volatilities using gaussian or uniform distribution
     * @return a problem instance
     */
    public static Document generateProblem(boolean soft, int nbrNodes, double density, double tightness,
            int nbrColors, int nbrStochNodes, boolean intensional, boolean gaussian) {

        RandomTopo instance = new RandomTopo(nbrNodes, density, tightness, nbrColors, nbrStochNodes, false,
                gaussian);
        return instance.toXCSP(false, soft, intensional);
    }

    /** The underlying graph */
    final public Graph graph;

    /** For each node, its list of forbidden colors */
    final public TreeMap<String, ArrayList<Integer>> unaryCons;

    /** The number of colors */
    final public int nbrColors;

    /** The set of uncontrollable nodes */
    final private HashSet<String> stochNodes;

    /** The name of the problem instance */
    private final String instanceName;

    /** The desired density */
    private double targetDensity = -1;

    /** The desired tightness of the unary constraints */
    private final double targetTightness;

    private boolean gaussian = false;

    /** Constructor
     * @param nbrNodes          total number of nodes
     * @param density          graph density
     * @param tightness       the tightness of the unary constraints
     * @param nbrColors       number of colors
     * @param nbrStochNodes    number of uncontrollable nodes
     * @param nbrLinks          if true, the density parameter states that there must be density*nbrNodes links in the graph
     */
    public RandomTopo(int nbrNodes, double density, final double tightness, final int nbrColors, int nbrStochNodes,
            boolean nbrLinks, boolean gaussian) {
        this(nbrLinks ? RandGraphFactory.getSizedRandGraph(nbrNodes, (int) (density * nbrNodes), 0)
                : RandGraphFactory.getSizedRandGraph(nbrNodes, (int) (density * nbrNodes * (nbrNodes - 1) / 2.0),
                        0),
                tightness, nbrColors, nbrStochNodes, gaussian);
        this.targetDensity = density;
    }

    /** Constructor
     * @param graph          the underlying graph
     * @param tightness       the tightness of the unary constraints
     * @param nbrColors       number of colors
     * @param nbrStochNodes    number of uncontrollable nodes
     */
    public RandomTopo(Graph graph, final double tightness, final int nbrColors, final int nbrStochNodes,
            final boolean gaussian) {
        this.graph = graph;
        this.targetTightness = tightness;
        this.nbrColors = nbrColors;
        this.instanceName = "graphColoring_" + System.currentTimeMillis();
        this.gaussian = gaussian;

        // The first nbrStochNodes nodes are selected as the uncontrollable ones
        this.stochNodes = new HashSet<String>();
        for (int i = 0; i < nbrStochNodes; i++)
            this.stochNodes.add(graph.nodes.get(i));

        this.unaryCons = new TreeMap<String, ArrayList<Integer>>();
        if (tightness > 0) { // generate the unary constraints

            for (String n : this.graph.nodes) {
                if (this.stochNodes.contains(n)) // skip uncontrollable nodes
                    continue;

                ArrayList<Integer> colors = new ArrayList<Integer>(nbrColors);
                for (int i = 0; i < nbrColors; i++)
                    if (Math.random() <= tightness)
                        colors.add(i);

                if (!colors.isEmpty())
                    this.unaryCons.put(n, colors);
            }
        }
    }

    /** Creates a "stats" element
     * @param name       the value of the "name" attribute
     * @param value    the text
     * @return a new "stats" element
     */
    public static Element createStats(String name, String value) {

        Element stats = new Element("stats");
        stats.setAttribute("name", name);
        stats.setText(value);

        return stats;
    }

    /** Generates an XCSP representation of a graph coloring problem
     * @param publicInteragentConstraints    whether inter-agent constraints should be public
     * @param soft                      whether the output should be a Max-DisCSP
     * @param intensional                whether the output should be intensional
     * @return An XCSP-formatted Document
     */
    public Document toXCSP(final boolean publicInteragentConstraints, final boolean soft,
            final boolean intensional) {

        // Create the root element
        Element probElement = new Element("instance");
        probElement.setAttribute("noNamespaceSchemaLocation",
                "src/frodo2/algorithms/XCSPschema" + (soft || !intensional ? "" : "JaCoP") + ".xsd",
                Namespace.getNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance"));

        // Create the "presentation" element
        Element presElmt = new Element("presentation");
        probElement.addContent(presElmt);
        presElmt.setAttribute("name", this.instanceName);
        presElmt.setAttribute("maxConstraintArity", "2");
        presElmt.setAttribute("maximize", "false");
        presElmt.setAttribute("format", "XCSP 2.1_FRODO");

        // Create the "agents" element
        Element elmt = new Element("agents");
        probElement.addContent(elmt);
        final int nbrNodes = graph.nodes.size();
        elmt.setAttribute("nbAgents", Integer.toString(nbrNodes - this.stochNodes.size()));
        for (String varID : this.graph.nodes) {

            // Skip the stoch nodes
            if (this.stochNodes.contains(varID))
                continue;

            Element subElmt = new Element("agent");
            elmt.addContent(subElmt);
            subElmt.setAttribute("name", "a" + varID);
        }

        // Create the "domains" element
        elmt = new Element("domains");
        probElement.addContent(elmt);
        elmt.setAttribute("nbDomains", "1");
        Element subElmt = new Element("domain");
        elmt.addContent(subElmt);
        subElmt.setAttribute("name", "colors");
        subElmt.setAttribute("nbValues", Integer.toString(nbrColors));
        subElmt.addContent("1.." + Integer.toString(nbrColors));

        // Create the "variables" element
        elmt = new Element("variables");
        probElement.addContent(elmt);
        elmt.setAttribute("nbVariables", Integer.toString(graph.nodes.size()));
        for (String varID : this.graph.nodes) {
            subElmt = new Element("variable");
            elmt.addContent(subElmt);
            subElmt.setAttribute("name", "n" + varID);
            subElmt.setAttribute("domain", "colors");
            if (this.stochNodes.contains(varID))
                subElmt.setAttribute("type", "random");
            else
                subElmt.setAttribute("agent", "a" + varID);
        }

        // Create the "relations" element
        Element relElmt = new Element("relations");
        if (soft || !intensional || !this.unaryCons.isEmpty()) {
            probElement.addContent(relElmt);
        }

        if (soft || !intensional) { // extensional

            subElmt = new Element("relation");
            relElmt.addContent(subElmt);
            subElmt.setAttribute("name", "neq");
            subElmt.setAttribute("semantics", "soft");
            subElmt.setAttribute("arity", "2");
            subElmt.setAttribute("defaultCost", "0");
            subElmt.setAttribute("nbTuples", Integer.toString(nbrColors));

            StringBuilder builder = new StringBuilder(soft ? "1: " : "infinity: ");
            for (int i = 1; i < nbrColors; i++)
                builder.append(Integer.toString(i) + " " + i + " | ");
            builder.append(Integer.toString(nbrColors) + " " + nbrColors);
            subElmt.setText(builder.toString());

        } else { // pure satisfaction, intensional

            // Create the "predicates" element
            elmt = new Element("predicates");
            probElement.addContent(elmt);
            elmt.setAttribute("nbPredicates", "1");

            subElmt = new Element("predicate");
            elmt.addContent(subElmt);
            subElmt.setAttribute("name", "neq");

            elmt = new Element("parameters");
            subElmt.addContent(elmt);
            elmt.setText("int X int Y");

            elmt = new Element("expression");
            subElmt.addContent(elmt);

            subElmt = new Element("functional");
            elmt.addContent(subElmt);
            subElmt.setText("ne(X, Y)");
        }

        if (!this.stochNodes.isEmpty()) {

            // Create the "probabilities" element
            elmt = new Element("probabilities");
            probElement.addContent(elmt);
            elmt.setAttribute("nbProbabilities", Integer.toString(this.stochNodes.size()));

            for (String varID : this.stochNodes) {

                subElmt = new Element("probability");
                elmt.addContent(subElmt);
                subElmt.setAttribute("name", "n" + varID + "proba");
                subElmt.setAttribute("semantics", "soft");
                subElmt.setAttribute("arity", "1");
                subElmt.setAttribute("nbTuples", Integer.toString(nbrColors));

                // Choose a random probability distribution
                StringBuilder builder = new StringBuilder();
                double[] probas = new double[nbrColors];
                double sum = 0.0;
                for (int i = 0; i < nbrColors; i++) {
                    probas[i] = Math.random();
                    sum += probas[i];
                }
                for (int i = 0; i < nbrColors - 1; i++)
                    builder.append(Double.toString(probas[i] / sum) + ": " + (i + 1) + " | ");
                builder.append(Double.toString(probas[nbrColors - 1] / sum) + ": " + nbrColors);
                subElmt.setText(builder.toString());
            }
        }

        // Create the "constraints" element
        Element conElmt = new Element("constraints");
        probElement.addContent(conElmt);

        // Create the unary constraints
        double tightness = 0.0;
        final double weight = 1.0 / (this.nbrColors * (this.graph.nodes.size() - this.stochNodes.size()));
        for (Map.Entry<String, ArrayList<Integer>> entry : this.unaryCons.entrySet()) {
            String n = entry.getKey();
            ArrayList<Integer> colors = entry.getValue();
            tightness += colors.size() * weight;

            // Create the relation
            subElmt = new Element("relation");
            relElmt.addContent(subElmt);
            subElmt.setAttribute("name", "unaryRel_" + n);
            subElmt.setAttribute("semantics", soft || !intensional ? "soft" : "conflicts");
            subElmt.setAttribute("arity", "1");
            if (soft || !intensional)
                subElmt.setAttribute("defaultCost", "0");
            subElmt.setAttribute("nbTuples", Integer.toString(colors.size()));

            StringBuilder builder = new StringBuilder(soft ? "1: " : !intensional ? "infinity: " : "");
            for (int i = colors.size() - 1; i > 0; i--)
                builder.append(colors.get(i)).append("|");
            builder.append(colors.get(0));
            subElmt.setText(builder.toString());

            // Create the constraint
            elmt = new Element("constraint");
            conElmt.addContent(elmt);
            elmt.setAttribute("name", "unaryCons_" + n);
            elmt.setAttribute("scope", "n" + n);
            elmt.setAttribute("arity", "1");
            elmt.setAttribute("reference", "unaryRel_" + n);
            elmt.setAttribute("agent", "a" + n);
            //random volatility
            int vol;
            do {
                vol = (int) Math.round(gaussian ? ((new Random().nextGaussian() + 50)) : ((Math.random() * 100)));
            } while (vol < 0 || vol > 100);
            elmt.setAttribute("volatility", String.valueOf(vol));
        }

        // Go through all edges in the graph
        for (Edge edge : graph.edges) {

            // Skip this constraint if it involves two random variables
            if (this.stochNodes.contains(edge.source) && this.stochNodes.contains(edge.dest))
                continue;

            elmt = new Element("constraint");
            conElmt.addContent(elmt);

            final String n1 = "n" + edge.source;
            final String n2 = "n" + edge.dest;

            elmt.setAttribute("name", n1 + "_neq_" + n2);
            elmt.setAttribute("scope", n1 + " " + n2);
            elmt.setAttribute("arity", "2");
            elmt.setAttribute("reference", "neq");
            if (publicInteragentConstraints)
                elmt.setAttribute("agent", "PUBLIC");

            if (!soft && intensional) {
                subElmt = new Element("parameters");
                elmt.addContent(subElmt);
                subElmt.setText(n1 + " " + n2);
            }
            //random volatility
            int vol;
            do {
                vol = (int) Math
                        .round(gaussian ? ((new Random().nextGaussian() * 100 + 50)) : ((Math.random() * 100)));
            } while (vol < 0 || vol > 100);
            elmt.setAttribute("volatility", String.valueOf(vol));
        }

        // Add the probability distributions
        for (String varID : this.stochNodes) {
            final String varName = "n" + varID;

            elmt = new Element("constraint");
            conElmt.addContent(elmt);
            elmt.setAttribute("name", varName + "dist");
            elmt.setAttribute("scope", varName);
            elmt.setAttribute("arity", "1");
            elmt.setAttribute("reference", varName + "proba");
        }

        relElmt.setAttribute("nbRelations", Integer.toString(relElmt.getContentSize()));
        conElmt.setAttribute("nbConstraints", Integer.toString(conElmt.getContentSize()));

        // Write the stats
        presElmt.addContent(createStats("number of nodes", Integer.toString(this.graph.nodes.size())));
        presElmt.addContent(createStats("target density", Double.toString(this.targetDensity)));
        presElmt.addContent(createStats("true average density", Double.toString(this.graph.computeDensity())));
        presElmt.addContent(createStats("target unary tightness", Double.toString(this.targetTightness)));
        presElmt.addContent(createStats("true average unary tightness", Double.toString(tightness)));
        presElmt.addContent(createStats("number of colors", Integer.toString(this.nbrColors)));
        presElmt.addContent(
                createStats("number of uncontrollable nodes", Integer.toString(this.stochNodes.size())));
        presElmt.addContent(
                createStats("number of disconnected components", Integer.toString(graph.components.size())));
        presElmt.addContent(createStats("max degree", Integer.toString(graph.computeMaxDeg())));

        return new Document(probElement);
    }

}