Pareto.java

101 lines | 4.431 kB Blame History Raw Download
package br.ufrgs.inf.prosoft.tfcache;

import br.ufrgs.inf.prosoft.tfcache.configuration.Configuration;

import java.math.BigDecimal;
import java.math.MathContext;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Pareto {

  private final Map<Long, Metrics> savedTimeHasMetrics;
  private Metrics bestMetrics;
  private BigDecimal minEuclideanDistance;

  public Pareto() {
    savedTimeHasMetrics = new ConcurrentHashMap<>();
  }

  public Pareto(Collection<Metrics> metrics) {
    this();
    metrics.forEach(this::addIfPareto);
  }

  public Pareto(Stream<Metrics> metrics) {
    this();
    metrics.forEach(this::addIfPareto);
  }

  public static void removeDominatedMetrics(Collection<Metrics> allMetrics) {
    Map<Long, List<Metrics>> groupBySavedTime = allMetrics.stream().collect(Collectors.groupingBy(Metrics::getSavedTime));
    groupBySavedTime.remove(0L);
    groupBySavedTime.forEach((savedTime, metrics) -> {
      double minIdleTime = metrics.stream().mapToDouble(Metrics::getIdleTime).min().orElse(0);
      metrics.removeIf(metric -> metric.getIdleTime() > minIdleTime);
    });

    List<Metrics> localMaxima = groupBySavedTime.values().stream()
      .map(Collection::stream)
      .reduce(Stream::concat)
      .orElse(Stream.empty())
      .collect(Collectors.toList());

    allMetrics.removeIf(metrics -> !localMaxima.contains(metrics));
  }

  public synchronized void addIfPareto(Metrics metrics) {
    savedTimeHasMetrics.merge(metrics.getSavedTime(), metrics, (existing, incoming) -> existing.getIdleTime() < incoming.getIdleTime() ? existing : incoming);
  }

  public Collection<Metrics> values() {
    return savedTimeHasMetrics.values();
  }

  public Set<Long> getTtls() {
    return values().stream().map(Metrics::getTtl).collect(Collectors.toSet());
  }

  public BigDecimal getNormalisedMinEuclideanDistance() {
    if (savedTimeHasMetrics.isEmpty()) throw new RuntimeException("savedTimeHasMetrics is empty");

    double minSavedTime = values().stream().mapToDouble(Metrics::getSavedTime).min().orElseThrow();
    double maxSavedTime = values().stream().mapToDouble(Metrics::getSavedTime).max().orElseThrow();
    double distanceSavedTime = maxSavedTime - minSavedTime;
    double minIdleTime = values().stream().mapToDouble(Metrics::getIdleTime).min().orElseThrow();
    double maxIdleTime = values().stream().mapToDouble(Metrics::getIdleTime).max().orElseThrow();
    double distanceIdleTime = maxIdleTime - minIdleTime;

    List<BigDecimal> normalisedEuclideanDistances = values().stream().map(it -> {
      BigDecimal normalisedSavedTime = new BigDecimal(it.getSavedTime() - minSavedTime).divide(BigDecimal.valueOf(distanceSavedTime), MathContext.DECIMAL128);
      BigDecimal normalisedIdleTime = new BigDecimal(it.getIdleTime() - minIdleTime).divide(BigDecimal.valueOf(distanceIdleTime), MathContext.DECIMAL128);
      return Metrics.calculateEuclideanDistance(normalisedSavedTime, normalisedIdleTime, Configuration.getPreferences().get(0), Configuration.getPreferences().get(1));
    }).collect(Collectors.toList());
    return normalisedEuclideanDistances.stream().min(BigDecimal::compareTo).orElseThrow();
  }

  public BigDecimal getMinEuclideanDistance() {
    getBestMetrics();
    return minEuclideanDistance;
  }

  public Metrics getBestMetrics() {
    if (bestMetrics == null) bestMetrics = getBestMetrics(Configuration.getPreferences().get(0), Configuration.getPreferences().get(1));
    return bestMetrics;
  }

  private Metrics getBestMetrics(double percentageObjectiveSavedTime, double percentageObjectiveIdleTime) {
    if (percentageObjectiveSavedTime < 0 || percentageObjectiveSavedTime > 1) throw new RuntimeException("invalid objective saved time");
    if (percentageObjectiveIdleTime < 0 || percentageObjectiveIdleTime > 1) throw new RuntimeException("invalid objective idle time");
    if (savedTimeHasMetrics.isEmpty()) return new Metrics();
    double minIdleTime = values().stream().mapToDouble(Metrics::getIdleTime).min().orElseThrow() * percentageObjectiveIdleTime;
    double maxSavedTime = values().stream().mapToDouble(Metrics::getSavedTime).max().orElseThrow() * percentageObjectiveSavedTime;
    bestMetrics = values().stream().min(Comparator.comparing(it -> it.calculateEuclideanDistance(maxSavedTime, minIdleTime))).orElseThrow();
    minEuclideanDistance = bestMetrics.calculateEuclideanDistance(maxSavedTime, minIdleTime);
    return bestMetrics;
  }

}