The used metric to evaluate clustering quality can now be selected

parent dcf72acd
......@@ -105,7 +105,8 @@ Clustering Parameters:
| numberOfClusters | int | ❓ | Number of clusters the subcomponents should be divided into<br> Overrides numberOfClusters in algorithmParameters |
| flatten | bool | ❓ | Replace all components with their subcomponents execpt when it is atomic or the flatten level is reached |
| flattenLevel | int | ❓ | Maximal level of component flattening |
| chooseBy | String | ❓ | Strategie to choose from the resulting clusterings<br> bestWithFittingN(Default): if numberOfClusters is set, all results with a different number of clusters are ignored<br> bestOverall: ignore numberOfClusters, choose result with best score |
| metric | String | ❓ | Metric to evaluate the quality of the resulting clusters. Available: "CommunicationCost"(Default), "Silhouette"|
| chooseBy | String | ❓ | Strategy to choose from the resulting clusterings<br> bestWithFittingN(Default): if numberOfClusters is set, all results with a different number of clusters are ignored<br> bestOverall: ignore numberOfClusters, choose result with best score |
| algorithmParameters | List<Object> | ❓ | Used to specify which algorithms(and their parameters) are used for clustering |
There are 4 different Clustering Algorithms with distinct parameters
......
......@@ -108,7 +108,7 @@ public class DistributedTargetGenerator extends CMakeGenerator {
//Cluster
if(clusteringParameters.getAlgorithmParameters().size() > 0) {
clusteringResults = ClusteringResultList.fromParametersList(componentInstanceSymbol, clusteringParameters.getAlgorithmParameters());
clusteringResults = ClusteringResultList.fromParametersList(componentInstanceSymbol, clusteringParameters.getAlgorithmParameters(), clusteringParameters.getMetric());
Optional<Integer> nOpt = clusteringParameters.getNumberOfClusters();
for(ClusteringResult c : clusteringResults){
String prefix = nOpt.isPresent() && !c.hasNumberOfClusters(nOpt.get()) ? "[IGNORED]" : "";
......
......@@ -47,7 +47,7 @@ public class CliParametersLoader {
JsonDeserializer<CliParameters> desCliParameters = new StrictJsonDeserializer<>(Arrays.asList("emadlBackend","writeTagFile","clusteringParameters","modelsDir","outputDir","rootModel","generators"), delegateGson);
JsonDeserializer<ClusteringParameters> desClustering = new StrictJsonDeserializer<>(Arrays.asList("numberOfClusters","flatten","flattenLevel","chooseBy","algorithmParameters"), delegateGson);
JsonDeserializer<ClusteringParameters> desClustering = new StrictJsonDeserializer<>(Arrays.asList("numberOfClusters","flatten","flattenLevel","chooseBy","algorithmParameters", "metric"), delegateGson);
JsonDeserializer<DynamicSpectralClusteringCliParameters> desSpectral = new StrictJsonDeserializer<>(Arrays.asList("numberOfClusters","l","sigma"), delegateGson);
JsonDeserializer<DynamicMarkovCliParameters> desMarkov = new StrictJsonDeserializer<>(Arrays.asList("max_residual","gamma_exp","loop_gain","zero_max"), delegateGson);
......
package de.monticore.lang.monticar.generator.middleware.cli;
import de.monticore.lang.monticar.generator.middleware.cli.algorithms.AlgorithmCliParameters;
import de.monticore.lang.monticar.generator.middleware.cli.algorithms.SpectralClusteringCliParameters;
import de.monticore.lang.monticar.generator.middleware.cli.algorithms.dynamic.DynamicAlgorithmCliParameters;
import de.monticore.lang.monticar.generator.middleware.cli.algorithms.dynamic.DynamicParameter;
import de.monticore.lang.monticar.generator.middleware.cli.algorithms.dynamic.DynamicSpectralClusteringCliParameters;
import de.monticore.lang.monticar.generator.middleware.cli.algorithms.dynamic.ListParameter;
import de.monticore.lang.monticar.generator.middleware.clustering.qualityMetric.Metric;
import de.monticore.lang.monticar.generator.middleware.clustering.qualityMetric.MetricType;
import java.util.ArrayList;
import java.util.List;
......@@ -18,6 +19,7 @@ public class ClusteringParameters {
private Integer flattenLevel;
private ResultChoosingStrategy chooseBy = ResultChoosingStrategy.bestWithFittingN;
private List<DynamicAlgorithmCliParameters> algorithmParameters = new ArrayList<>();
private MetricType metric;
public ClusteringParameters() {
}
......@@ -54,4 +56,12 @@ public class ClusteringParameters {
public Optional<Integer> getFlattenLevel() {
return Optional.ofNullable(flattenLevel);
}
public Metric getMetric() {
if(metric == null){
return MetricType.CommunicationCost.toMetric();
}else{
return metric.toMetric();
}
}
}
......@@ -6,6 +6,7 @@ import com.google.gson.JsonParser;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.instanceStructure.EMAComponentInstanceSymbol;
import de.monticore.lang.monticar.generator.FileContent;
import de.monticore.lang.monticar.generator.middleware.cli.algorithms.AlgorithmCliParameters;
import de.monticore.lang.monticar.generator.middleware.clustering.qualityMetric.Metric;
import de.monticore.lang.monticar.generator.middleware.clustering.visualization.ModelVisualizer;
import de.monticore.lang.monticar.generator.middleware.impls.MiddlewareTagGenImpl;
import de.se_rwth.commons.logging.Log;
......@@ -21,25 +22,26 @@ import java.util.Optional;
import java.util.Set;
public class ClusteringResult {
Double score = null;
private ClusteringInput clusteringInput;
private AlgorithmCliParameters parameters;
private List<Set<EMAComponentInstanceSymbol>> clustering;
private long duration;
private int componentNumber;
private boolean valid;
private Metric metric;
private ClusteringResult(ClusteringInput clusteringInput, AlgorithmCliParameters parameters,
List<Set<EMAComponentInstanceSymbol>> clustering, long duration, int componentNumber, boolean valid) {
List<Set<EMAComponentInstanceSymbol>> clustering, long duration, int componentNumber, boolean valid, Metric metric) {
this.clusteringInput = clusteringInput;
this.parameters = parameters;
this.clustering = clustering;
this.duration = duration;
this.componentNumber = componentNumber;
this.valid = valid;
this.metric = metric;
}
public static ClusteringResult fromParameters(ClusteringInput clusteringInput, AlgorithmCliParameters parameters) {
public static ClusteringResult fromParameters(ClusteringInput clusteringInput, AlgorithmCliParameters parameters, Metric metric) {
List<Set<EMAComponentInstanceSymbol>> res;
long startTime = System.currentTimeMillis();
......@@ -47,7 +49,7 @@ public class ClusteringResult {
res = parameters.asClusteringAlgorithm().clusterWithState(clusteringInput);
} catch (Exception e) {
Log.warn("Marking this result as invalid. Error clustering the component.", e);
return new ClusteringResult(clusteringInput, parameters, new ArrayList<>(), -1, clusteringInput.getComponent().getSubComponents().size(), false);
return new ClusteringResult(clusteringInput, parameters, new ArrayList<>(), -1, clusteringInput.getComponent().getSubComponents().size(), false, metric);
}
long endTime = System.currentTimeMillis();
......@@ -69,14 +71,20 @@ public class ClusteringResult {
for (Set<EMAComponentInstanceSymbol> cluster : res) {
componentNumber += cluster.size();
}
return new ClusteringResult(clusteringInput, parameters, res, endTime - startTime, componentNumber, curValid);
return new ClusteringResult(clusteringInput, parameters, res, endTime - startTime, componentNumber, curValid, metric);
}
public Metric getMetric() {
return metric;
}
public void setMetric(Metric metric){
this.metric = metric;
}
public double getScore(){
if(score == null){
score = AutomaticClusteringHelper.getTypeCostHeuristic(clusteringInput.getComponent(), clustering);
}
return score;
return metric.getScore(this);
}
public EMAComponentInstanceSymbol getComponent() {
......
......@@ -3,6 +3,7 @@ package de.monticore.lang.monticar.generator.middleware.clustering;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.instanceStructure.EMAComponentInstanceSymbol;
import de.monticore.lang.monticar.generator.FileContent;
import de.monticore.lang.monticar.generator.middleware.cli.algorithms.AlgorithmCliParameters;
import de.monticore.lang.monticar.generator.middleware.clustering.qualityMetric.Metric;
import de.se_rwth.commons.logging.Log;
import java.util.ArrayList;
......@@ -16,7 +17,7 @@ public class ClusteringResultList extends ArrayList<ClusteringResult> {
super();
}
public static ClusteringResultList fromParametersList(EMAComponentInstanceSymbol emaComponentInstance, List<AlgorithmCliParameters> algoParams) {
public static ClusteringResultList fromParametersList(EMAComponentInstanceSymbol emaComponentInstance, List<AlgorithmCliParameters> algoParams, Metric metric) {
ClusteringResultList res = new ClusteringResultList();
ClusteringInput clusteringInput = new ClusteringInput(emaComponentInstance);
//create AdjacencyMatrix to make execution speed comparision fairer
......@@ -24,7 +25,7 @@ public class ClusteringResultList extends ArrayList<ClusteringResult> {
for (int i = 0; i < algoParams.size(); i++) {
System.out.println("Clustering with algorithm " + (i + 1) + "/" + algoParams.size() + ": " + algoParams.get(i).toString());
ClusteringResult result = ClusteringResult.fromParameters(clusteringInput, algoParams.get(i));
ClusteringResult result = ClusteringResult.fromParameters(clusteringInput, algoParams.get(i), metric);
if (result.isValid()) {
res.add(result);
} else {
......@@ -48,8 +49,16 @@ public class ClusteringResultList extends ArrayList<ClusteringResult> {
}
public Optional<ClusteringResult> getBestResultOverall(){
this.sort(Comparator.comparing(ClusteringResult::getScore));
return this.size() == 0 ? Optional.empty() : Optional.of(this.get(0));
if(this.isEmpty()){
return Optional.empty();
}
ClusteringResultList copiedList = new ClusteringResultList();
copiedList.addAll(this);
copiedList.sort(Comparator.comparing(ClusteringResult::getScore));
int index = copiedList.get(0).getMetric().higherIsBetter() ? copiedList.size() - 1 : 0;
return Optional.of(copiedList.get(index));
}
public List<FileContent> getAllTagFiles(String baseName){
......
package de.monticore.lang.monticar.generator.middleware.clustering.qualityMetric;
import de.monticore.lang.monticar.generator.middleware.clustering.AutomaticClusteringHelper;
import de.monticore.lang.monticar.generator.middleware.clustering.ClusteringResult;
public class CommunicationCostMetric implements Metric {
@Override
public boolean higherIsBetter() {
return false;
}
@Override
public double getScore(ClusteringResult clusteringResult) {
return AutomaticClusteringHelper.getTypeCostHeuristic(clusteringResult.getComponent(), clusteringResult.getClustering());
}
}
package de.monticore.lang.monticar.generator.middleware.clustering.qualityMetric;
import de.monticore.lang.monticar.generator.middleware.clustering.ClusteringResult;
public interface Metric {
boolean higherIsBetter();
double getScore(ClusteringResult clusteringResult);
}
package de.monticore.lang.monticar.generator.middleware.clustering.qualityMetric;
import de.se_rwth.commons.logging.Log;
public enum MetricType {
CommunicationCost,
Silhouette;
public Metric toMetric(){
switch (this){
case CommunicationCost: return new CommunicationCostMetric();
case Silhouette: return new SilhouetteMetric();
default:{
Log.error("Unknown metric: " + this);
return null;
}
}
}
}
package de.monticore.lang.monticar.generator.middleware.clustering.qualityMetric;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.instanceStructure.EMAComponentInstanceSymbol;
import de.monticore.lang.monticar.generator.middleware.clustering.ClusteringInput;
import de.monticore.lang.monticar.generator.middleware.clustering.ClusteringResult;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class SilhouetteMetric implements Metric {
@Override
public boolean higherIsBetter() {
return true;
}
@Override
public double getScore(ClusteringResult clusteringResult) {
ClusteringInput clusteringInput = clusteringResult.getClusteringInput();
int[] labels = getClusteringLabels(clusteringInput, clusteringResult.getClustering());
return new SilhouetteIndex(clusteringInput.getDistanceMatrix(), labels).getSilhouetteScore();
}
private int[] getClusteringLabels(ClusteringInput clusteringInput, List<Set<EMAComponentInstanceSymbol>> clustering) {
int[] labels = new int[clusteringInput.getSubcompsOrderedByName().size()];
Map<String, Integer> subcompLabels = clusteringInput.getLabelsForSubcomps();
for (int i = 0; i < clustering.size(); i++) {
for (EMAComponentInstanceSymbol emaComponentInstanceSymbol : clustering.get(i)) {
labels[subcompLabels.get(emaComponentInstanceSymbol.getFullName())] = i;
}
}
return labels;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment