Package bgu.bio.algorithms.graphs

Source Code of bgu.bio.algorithms.graphs.MaximalWeightedClique$NodeInformationComparator

package bgu.bio.algorithms.graphs;

import gnu.trove.list.array.TIntArrayList;

import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Random;

import bgu.bio.adt.graphs.FlexibleUndirectedGraph;
import bgu.bio.adt.tuples.IntDoublePair;

/**
* This class finds the maximum weighted clique in the current graph according
* to the algorithm presented in: <i>Improving the maximum-weight clique
* algorithm for the dense graphs</i> by <i>Kumlander Deniss</i> from 2006. In
* this implementation the case where you don't select a node from a color class
* is implemented using a dummy node. <br/>
* <br/>
* <b>This class is not Thread safe.</b>
*
* @author milon
*/
public class MaximalWeightedClique {

  private static NumberFormat numberFormat = new DecimalFormat("0.00");

  /**
   * Used in the coloring process for random coloring
   */
  private Random rand;
  /**
   * Helper list, helps in keeping the ordering of the nodes in the graph
   */
  private TIntArrayList nodeOrdering;
  /**
   * used to color the graph
   */
  private GraphColoring coloring;
  /**
   * Holds the current graph
   */
  private FlexibleUndirectedGraph graph;
  /**
   * Holds the temporary graph
   */
  private FlexibleUndirectedGraph tmpGraph;

  private FlexibleUndirectedGraph subgraph1, subgraph2;
  /**
   * A {@link Comparator} used to sort the colors by decreasing node weights
   */
  private Comparator<ArrayList<IntDoublePair>> classesComparator;
  /**
   * A {@link Comparator} used to sort each color class according to the
   * weights
   */
  private Comparator<IntDoublePair> nodeComparator;
  /**
   * Contains the maximal number of milliseconds the code should run
   */
  private long timeout;

  /**
   * true if and only if the last call to
   * {@link #findMaximumWeightedClique(double[])} or
   * {@link #findMaximumWeightedClique(double[], TIntArrayList)} timed out
   */
  private boolean timedout;

  /**
   * if given is used as the initial score value of the "best" clique
   */
  private double pruneStart;

  /**
   * The dummy node in the graph, this is used to represent the case where you
   * don't choose a node
   */
  private final IntDoublePair dummy;

  /**
   * Holds statistical information
   */
  private double[] stats;
  /**
   * Holds statistical information header texts
   */
  private String[] statsNames;
  private long type1, type2;

  public MaximalWeightedClique() {
    this.dummy = new IntDoublePair(-1, 0.0);
    this.classesComparator = new ClassesComparatorByDegree(false);
    this.nodeComparator = new NodeInformationComparator();
    coloring = new GraphColoring();
    nodeOrdering = new TIntArrayList();
    rand = new Random();
    statsNames = new String[] { "Amount", "Nodes", "Density", "MaxDegree",
        "Colors", "NewNodes/Nodes", "Color by Degeneracy",
        "Color by Degree", "Color by Random", "Timeout" };
    stats = new double[statsNames.length];
    this.graph = new FlexibleUndirectedGraph();
    this.tmpGraph = new FlexibleUndirectedGraph();
    this.subgraph1 = new FlexibleUndirectedGraph();
    this.subgraph2 = new FlexibleUndirectedGraph();
  }

  /**
   * Calculate the maximal weighted clique in the current graph (set by
   * {@link #setGraph(FlexibleUndirectedGraph)}). if {@link #setTimeout(long)}
   * or {@link #setPruneScore(double)(double)} were given, then they may be a
   * case where not the optimal result is calculated but a local optimal.
   *
   * @param weights
   *            the weights of the nodes in the graph
   * @return the maximal weighted clique in the graph, if exist
   */
  public TIntArrayList findMaximumWeightedClique(double[] weights) {
    return findMaximumWeightedClique(weights, null);
  }

  /**
   * Same as {@link #findMaximumWeightedClique(double[])} only with the
   * additional constraint that we want the clique to contain <b>at least
   * one</b> of the nodes given in the additional list
   *
   * @see {@link #findMaximumWeightedClique(double[])}
   * @param weights
   *            the weights of the nodes in the graph
   * @param mandatory
   *            the list of mandatory nodes. if the given list is null it is
   *            equivalent to the case where all the nodes are mandatory
   * @return The maximal clique containing at least one of the nodes in the
   *         mandatory list
   */
  public TIntArrayList findMaximumWeightedClique(double[] weights,
      TIntArrayList mandatory) {
    type1 = 0;
    type2 = 0;
    FlexibleUndirectedGraph tmp = graph;
    this.graph.copyTo(tmpGraph);
    graph = tmpGraph;

    detachExtendableNodes(weights, mandatory);

    // split the graph to two subgraphs for the coloring
    subgraph1.clear();
    subgraph2.clear();

    graph.split(mandatory, subgraph1, subgraph2);
    int[] color1 = new int[subgraph1.getNodeNum()];
    int amountOfColors1 = colorGraph(subgraph1, color1, weights);
    int[] color2 = new int[subgraph2.getNodeNum()];
    int amountOfColors2 = colorGraph(subgraph2, color2, weights);
    int amountOfColors = Math.max(amountOfColors1, amountOfColors2);

    // step 0
    this.updateStats(mandatory, amountOfColors);

    ArrayList<ArrayList<IntDoublePair>> colorClasses = new ArrayList<ArrayList<IntDoublePair>>();

    int mandatoryClasses = fillColorClasses(weights, mandatory, color1,
        amountOfColors1, color2, amountOfColors2, colorClasses);

    WeightedClique clique = new WeightedCliqueTree();
    clique.setGraph(graph);

    ArrayList<IntDoublePair> bestClique = new ArrayList<IntDoublePair>(
        colorClasses.size());

    // calculate the maximum gain from each position for the pruning
    // including the current

    double[] maximas = new double[colorClasses.size()];
    for (int i = 0; i < maximas.length; i++) {
      double max = Double.NEGATIVE_INFINITY;
      ArrayList<IntDoublePair> list = colorClasses.get(i);
      for (int j = 0; j < list.size(); j++) {
        if (max < list.get(j).getSecond()) {
          max = list.get(j).getSecond();
        }
      }
      maximas[i] = max;
    }
    double[] maxGainInFuture = new double[maximas.length];
    double[][] maxGainSorted = new double[maximas.length][maximas.length];

    fillFutureGainEstimations(maximas, maxGainInFuture, maxGainSorted);

    int[] positions = new int[colorClasses.size()];
    Arrays.fill(positions, -1);

    double bestWeight = pruneStart;

    final boolean checkTimeout = !(timeout == 0);
    long startTime = System.currentTimeMillis();
    timedout = false;

    int currentClass = 0;
    while (currentClass >= 0) {
      positions[currentClass]++;
      int currentPos = positions[currentClass];
      final ArrayList<IntDoublePair> currentColorClass = colorClasses
          .get(currentClass);
      if (currentPos > 0 && currentPos <= currentColorClass.size() - 1) {
        clique.removeLast();
      }
      // we can stop after trying all the mandatory nodes as start
      // positions
      if (currentClass == mandatoryClasses && clique.size() == 0) {
        break;
      }
      if (currentPos >= currentColorClass.size()
          || (shouldPrune(clique, currentClass, maxGainInFuture,
              maxGainSorted, bestWeight))) {
        positions[currentClass] = -1;
        currentClass--;
        if (checkTimeout
            && System.currentTimeMillis() - startTime > timeout) {
          positions[currentClass] = -1;
          currentClass = -1000;
          stats[stats.length - 1]++;
          timedout = true;
        }
      } else if (clique.potential() == 0) {
        if (clique.weight() > bestWeight) {
          bestWeight = clique.weight();
          bestClique.clear();
          clique.copyTo(bestClique);
        }
        positions[currentClass] = -1;
        currentClass--;
      } else {
        boolean notFound = true;
        while (notFound && currentPos < currentColorClass.size()) {
          final IntDoublePair candidate = currentColorClass
              .get(currentPos);

          if (!canJoin(clique, candidate)) {
            currentPos++;
          } else {
            if (candidate != dummy) {
              clique.add(candidate);
            }
            notFound = false;
          }
        }
        positions[currentClass] = currentPos;
        currentClass++;
        if (currentClass == positions.length) {
          /*
           * found a clique, check if it is better than the current
           * best clique so far
           */
          if (clique.weight() > bestWeight) {
            bestWeight = clique.weight();
            bestClique.clear();
            clique.copyTo(bestClique);
          }
          currentClass--;
        }
      }
    }
    TIntArrayList ans = new TIntArrayList();
    for (int i = 0; i < bestClique.size(); i++) {
      if (bestClique.get(i) != dummy) {
        ans.add(bestClique.get(i).getFirst());
      }
    }
    graph = tmp;
    return ans;
  }

  /**
   * @param weights
   * @param mandatory
   * @param color1
   * @param amountOfColors1
   * @param color2
   * @param amountOfColors2
   * @param colorClasses
   * @return
   */
  public int fillColorClasses(double[] weights, TIntArrayList mandatory,
      int[] color1, int amountOfColors1, int[] color2,
      int amountOfColors2,
      ArrayList<ArrayList<IntDoublePair>> colorClasses) {
    ArrayList<ArrayList<IntDoublePair>> colorClassesRegular = new ArrayList<ArrayList<IntDoublePair>>();
    ArrayList<ArrayList<IntDoublePair>> colorClassesMandatory = new ArrayList<ArrayList<IntDoublePair>>();

    // build the colorClasses
    fillColorClasses(amountOfColors1, amountOfColors2, color1, color2,
        weights, mandatory, colorClassesRegular, colorClassesMandatory);

    sortEachClassByWeight(colorClassesMandatory);
    sortEachClassByWeight(colorClassesRegular);

    Collections.sort(colorClassesRegular, this.classesComparator);
    Collections.sort(colorClassesMandatory, this.classesComparator);

    // merge the colorClasses

    int mandatoryClasses = 0;
    for (int i = 0; i < colorClassesMandatory.size(); i++) {
      if (colorClassesMandatory.get(i).size() > 0) {
        colorClasses.add(colorClassesMandatory.get(i));
        mandatoryClasses++;
      }
    }

    for (int i = 0; i < colorClassesRegular.size(); i++) {
      if (colorClassesRegular.get(i).size() > 0) {
        colorClasses.add(colorClassesRegular.get(i));
      }
    }

    // add dummy to every list
    for (int i = 0; i < colorClasses.size(); i++) {
      colorClasses.get(i).add(dummy);
    }
    return mandatoryClasses;
  }

  /**
   * @param maximas
   * @param maxGainInFuture
   * @param maxGainSorted
   */
  public void fillFutureGainEstimations(double[] maximas,
      double[] maxGainInFuture, double[][] maxGainSorted) {
    maxGainInFuture[maxGainInFuture.length - 1] = maximas[maxGainInFuture.length - 1];

    for (int i = maxGainInFuture.length - 2; i >= 0; i--) {
      maxGainInFuture[i] = maxGainInFuture[i + 1] + maximas[i];
    }

    for (int i = 0; i < maxGainSorted.length; i++) {
      for (int j = i; j < maxGainSorted.length; j++) {
        maxGainSorted[i][j] = maximas[j];
      }
      Arrays.sort(maxGainSorted[i]);
      reverse(maxGainSorted[i]);
    }
  }

  /**
   * @return The list of nodes that can be extended in the graph
   */
  private TIntArrayList detachExtendableNodes(double[] weights,
      TIntArrayList mandatory) {

    TIntArrayList list = new TIntArrayList();
    TIntArrayList list1 = new TIntArrayList();
    TIntArrayList list2 = new TIntArrayList();
    for (int u = 0; u < graph.getNodeNum(); u++) {
      boolean isFirstMandatory = mandatory != null ? mandatory
          .binarySearch(u) >= 0 : false;
      list1.resetQuick();
      for (int d = 0; d < graph.deg(u); d++) {
        list1.add(graph.getNeighbor(u, d));
      }
      list1.sort();
      for (int v = 0; v < graph.getNodeNum(); v++) {
        boolean isSecondMandatory = mandatory != null ? mandatory
            .binarySearch(v) >= 0 : false;
        if (u != v
            && graph.deg(u) <= graph.deg(v)
            && graph.deg(u) > 0
            && ((isFirstMandatory && isSecondMandatory) || (!isFirstMandatory && !isSecondMandatory))
            && !graph.isNeighbours(u, v)
            && weights[u] <= weights[v]) {
          // scan the lists

          list2.resetQuick();
          for (int d = 0; d < graph.deg(v); d++) {
            list2.add(graph.getNeighbor(v, d));
          }
          list2.sort();
          boolean contains = true;
          for (int x = 0; contains && x < list1.size(); x++) {
            if (list2.binarySearch(list1.get(x)) < 0) {
              contains = false;
            }
          }
          if (contains) {
            list.add(u);
            graph.detachNode(u);
            v = 2 * graph.getNodeNum();
          }
        }
      }
    }
    return list;
  }

  private void reverse(double[] arr) {
    int top = arr.length - 1;
    int bottom = 0;
    double tmp;
    while (top > bottom) {
      tmp = arr[top];
      arr[top] = arr[bottom];
      arr[bottom] = tmp;
      bottom++;
      top--;
    }

  }

  /**
   * Update the statistics array
   *
   * @param mandatory
   *            the mandatory list
   * @param amountOfColors
   */
  private void updateStats(TIntArrayList mandatory, int amountOfColors) {
    this.stats[0]++;
    this.stats[1] += graph.getNodeNum();
    this.stats[2] += (2 * (double) graph.getEdgeNum())
        / (graph.getNodeNum() * (graph.getNodeNum() - 1));
    this.stats[3] += graph.getMaxDeg();
    this.stats[4] += amountOfColors;
    this.stats[5] += mandatory == null ? 1.0 : mandatory.size()
        / ((double) graph.getNodeNum());
  }

  /**
   * Calculate the best way to color the graph. the method tries several
   * methods of coloring: degeneracy, weight, degree and random.
   *
   * @param color
   *            the array that will contain the coloring of the nodes
   * @param weights
   *            the weights of the nodes
   * @return the best found coloring of the graph
   */
  private int colorGraph(FlexibleUndirectedGraph theGraph, int[] color,
      double[] weights) {
    nodeOrdering.clear();
    theGraph.degeneracy(nodeOrdering);
    coloring.setGraph(theGraph);
    // color by degeneracy
    int amountOfColors = coloring.greedyColoring(nodeOrdering);
    coloring.copyColoringTo(color);
    int colorBy = 0;

    // only try in the case where there is more than two.
    if (amountOfColors > 2) {
      // check coloring by degree and weight
      int amountOfColorsByDegree = coloring.greedyColoringByDegree();
      if (amountOfColors > amountOfColorsByDegree) {
        amountOfColors = amountOfColorsByDegree;
        coloring.copyColoringTo(color);
        colorBy = 1;
      }
      final int tries = theGraph.getNodeNum() / 4;
      for (int i = 0; i < tries; i++) {
        nodeOrdering.shuffle(rand);
        int tmp = coloring.greedyColoring(nodeOrdering);
        if (tmp < amountOfColors) {
          amountOfColors = tmp;
          coloring.copyColoringTo(color);
          colorBy = 2;
        }

      }
    }
    stats[colorBy + 6]++;
    return amountOfColors;
  }

  /**
   * @param amountOfColors
   * @param color
   * @param weights
   * @param mandatory
   * @param colorClassesRegular
   * @param colorClassesMandatory
   */
  private void fillColorClasses(int amountOfColors1, int amountOfColors2,
      int[] color1, int[] color2, double[] weights,
      TIntArrayList mandatory,
      ArrayList<ArrayList<IntDoublePair>> colorClassesRegular,
      ArrayList<ArrayList<IntDoublePair>> colorClassesMandatory) {

    if (mandatory != null) {
      mandatory.sort();
    }
    colorClassesRegular.clear();
    colorClassesMandatory.clear();
    for (int i = 0; i < amountOfColors1; i++) {

      colorClassesMandatory.add(new ArrayList<IntDoublePair>());
    }
    for (int i = 0; i < amountOfColors2; i++) {
      colorClassesRegular.add(new ArrayList<IntDoublePair>());
    }

    for (int node = 0; node < graph.getNodeNum(); node++) {
      if (mandatory == null || mandatory.binarySearch(node) >= 0) {

        colorClassesMandatory.get(color1[node]).add(
            new IntDoublePair(node, weights[node]));
      } else {
        colorClassesRegular.get(color2[node]).add(
            new IntDoublePair(node, weights[node]));
      }
    }

  }

  private boolean shouldPrune(WeightedClique currentClique, int currentIndex,
      double[] maxGainInFuture, double[][] maxGainSorted,
      double bestWeight) {
    if (currentClique.weight() > bestWeight || currentClique.size() == 0) {
      return false;
    }

    boolean prune = currentClique.weight() + maxGainInFuture[currentIndex] < bestWeight;
    if (prune) {
      type1++;
    } else {
      final int amountToAdd = currentClique.potential();
      final double[] mg = maxGainSorted[currentIndex];
      double maxGain = 0;
      if (amountToAdd < mg.length) {
        for (int i = 0; i < amountToAdd; i++) {
          maxGain += mg[i];
        }
        prune = currentClique.weight() + maxGain < bestWeight;
        if (prune) {
          type2++;
        }
      }

    }
    return prune;
  }

  private boolean canJoin(WeightedClique currentClique,
      IntDoublePair candidate) {
    if (candidate == dummy) {
      // can always add dummy
      return true;
    }
    if (graph.deg(candidate.getFirst()) < currentClique.size()) {
      return false;
    }
    return currentClique.canJoin(candidate);
  }

  /**
   * @param amountOfColors
   * @param colorClasses
   */
  private void sortEachClassByWeight(
      ArrayList<ArrayList<IntDoublePair>> colorClasses) {
    for (int i = 0; i < colorClasses.size(); i++) {
      Collections.sort(colorClasses.get(i), nodeComparator);
    }
  }

  /**
   * Set the graph to be used in the algorithm
   *
   * @param graph
   *            the input graph for the algorithm
   */
  public void setGraph(FlexibleUndirectedGraph graph) {
    this.graph = graph;
  }

  /**
   * set the timeout limit for the algorithm
   *
   * @param timeInMilliseconds
   *            time limit in milliseconds
   */
  public void setTimeout(long timeInMilliseconds) {
    if (timeInMilliseconds > 0) {
      this.timeout = timeInMilliseconds;
    }
  }

  /**
   * @return true if the last call to
   *         {@link #findMaximumWeightedClique(double[])} or
   *         {@link #findMaximumWeightedClique(double[], TIntArrayList)} timed
   *         out (i.e., {@link #timeout} was reached)
   */
  public boolean timedout() {
    return timedout;
  }

  /**
   * clear the current time out
   */
  public void clearTimeout() {
    this.timeout = 0;
  }

  /**
   * Returns the statistics of the class
   *
   * @return A string representation of the statistics
   */
  public String statistics() {
    StringBuilder sb = new StringBuilder();
    sb.append(statsNames[0]);
    sb.append(": ");
    sb.append((int) stats[0]);
    sb.append("\n");
    for (int i = 1; i < stats.length - 1; i++) {
      sb.append(statsNames[i]);
      sb.append(": ");
      sb.append(numberFormat.format(stats[i] / stats[0]));
      sb.append("\n");
    }
    sb.append(statsNames[stats.length - 1]);
    sb.append(": ");
    sb.append((int) stats[stats.length - 1]);
    sb.append("\n");
    return sb.toString();
  }

  public long[] typesOfPrunings() {
    return new long[] { type1, type2 };
  }

  public void setClassesComparator(Comparator<ArrayList<IntDoublePair>> comp) {
    this.classesComparator = comp;
  }

  public void setNodeComparator(Comparator<IntDoublePair> comp) {
    this.nodeComparator = comp;
  }

  public ArrayList<Comparator<ArrayList<IntDoublePair>>> getClassesComparators() {
    ArrayList<Comparator<ArrayList<IntDoublePair>>> list = new ArrayList<Comparator<ArrayList<IntDoublePair>>>();
    list.add(new ClassesComparatorByWeight(false));
    list.add(new ClassesComparatorByWeight(true));
    list.add(new ClassesComparatorByAverageWeight(false));
    list.add(new ClassesComparatorByAverageWeight(true));
    list.add(new ClassesComparatorByDegree(false));
    list.add(new ClassesComparatorByDegree(true));
    return list;
  }

  private class ClassesComparatorByWeight implements
      Comparator<ArrayList<IntDoublePair>> {

    private final int mult;

    public ClassesComparatorByWeight(boolean increasing) {
      this.mult = increasing ? 1 : -1;
    }

    @Override
    public int compare(ArrayList<IntDoublePair> o1,
        ArrayList<IntDoublePair> o2) {
      if (o1.size() == 0 && o2.size() == 0) {
        return 0;
      } else if (o1.size() == 0) {
        return -1 * mult;
      } else if (o2.size() == 0) {
        return 1 * mult;
      }
      return compare(o1, o2, 0);
    }

    private int compare(ArrayList<IntDoublePair> o1,
        ArrayList<IntDoublePair> o2, int index) {

      final double diff = o1.get(index).getSecond()
          - o2.get(index).getSecond();
      if (diff > 0) {
        return 1 * mult;
      } else if (diff < 0) {
        return -1 * mult;
      }

      index++;
      if (o2.size() <= index && o1.size() <= index) {
        return 0;
      } else if (o2.size() <= index) {
        return 1 * mult;
      } else if (o1.size() <= index) {
        return -1 * mult;
      }

      return compare(o1, o2, index);
    }
  }

  private class ClassesComparatorByDegree implements
      Comparator<ArrayList<IntDoublePair>> {

    private final int mult;

    public ClassesComparatorByDegree(boolean increasing) {
      this.mult = increasing ? 1 : -1;
    }

    @Override
    public int compare(ArrayList<IntDoublePair> o1,
        ArrayList<IntDoublePair> o2) {
      final int deg1 = maxDeg(o1);
      final int deg2 = maxDeg(o2);

      return mult * (deg1 - deg2);
    }

    private int maxDeg(ArrayList<IntDoublePair> list) {
      int maxDeg = 0;
      for (int i = 0; i < list.size(); i++) {
        final int deg = graph.deg(list.get(i).getFirst());
        if (deg > maxDeg) {
          maxDeg = deg;
        }
      }
      return maxDeg;
    }

  }

  private class ClassesComparatorByAverageWeight implements
      Comparator<ArrayList<IntDoublePair>> {

    private final int mult;

    public ClassesComparatorByAverageWeight(boolean increasing) {
      this.mult = increasing ? 1 : -1;
    }

    @Override
    public int compare(ArrayList<IntDoublePair> o1,
        ArrayList<IntDoublePair> o2) {
      double sum1 = sum(o1) / o1.size();
      double sum2 = sum(o2) / o2.size();
      if (sum1 - sum2 < 0) {
        return -1 * mult;
      } else if (sum1 - sum2 > 0) {
        return 1 * mult;
      } else {
        return 0;
      }
    }

    private double sum(ArrayList<IntDoublePair> list) {
      double sum = 0;
      for (int i = 0; i < list.size(); i++) {
        sum += list.get(i).getSecond();
      }
      return sum;
    }

  }

  private class NodeInformationComparator implements
      Comparator<IntDoublePair> {

    @Override
    public int compare(IntDoublePair o1, IntDoublePair o2) {
      double diff = o1.getSecond() - o2.getSecond();
      if (diff == 0) {
        return 0;
      }
      if (diff < 0) {
        return 1;
      }
      return -1;
    }
  }
}
TOP

Related Classes of bgu.bio.algorithms.graphs.MaximalWeightedClique$NodeInformationComparator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.