From 945f487a08b643392a5d5918c631640b9a0e4605 Mon Sep 17 00:00:00 2001 From: 20001LastOrder Date: Tue, 3 Nov 2020 22:52:26 -0500 Subject: add realistic solver --- .../realistic/metrics/calculator/app/Domain.xtend | 7 + .../realistic/metrics/calculator/app/Main.xtend | 96 +++++++++ .../app/PartialInterpretationMetric.xtend | 85 ++++++++ .../app/PartialInterpretationMetricDistance.xtend | 216 +++++++++++++++++++++ .../metrics/calculator/distance/CostDistance.xtend | 38 ++++ .../calculator/distance/EuclideanDistance.xtend | 72 +++++++ .../metrics/calculator/distance/JSDistance.xtend | 88 +++++++++ .../metrics/calculator/distance/KSDistance.xtend | 102 ++++++++++ .../metrics/calculator/graph/EMFGraph.xtend | 124 ++++++++++++ .../realistic/metrics/calculator/graph/Graph.xtend | 71 +++++++ .../metrics/calculator/graph/GraphStatistic.xtend | 194 ++++++++++++++++++ .../graph/PartialInterpretationGraph.xtend | 134 +++++++++++++ .../metrics/calculator/io/CsvFileWriter.xtend | 52 +++++ .../metrics/calculator/io/GraphReader.xtend | 112 +++++++++++ .../metrics/calculator/io/RepMetricsReader.xtend | 100 ++++++++++ .../calculator/metrics/EdgeTypeMetric.xtend | 41 ++++ .../metrics/calculator/metrics/Metric.xtend | 8 + .../calculator/metrics/MetricSampleGroup.xtend | 13 ++ .../MultiplexParticipationCoefficientMetric.xtend | 80 ++++++++ .../calculator/metrics/NodeActivityMetric.xtend | 49 +++++ .../calculator/metrics/NodeTypeMetric.xtend | 42 ++++ .../calculator/metrics/OutDegreeMetric.xtend | 49 +++++ .../metrics/TypedClusteringCoefficientMetric.xtend | 99 ++++++++++ .../calculator/metrics/TypedOutDegree.xtend | 60 ++++++ .../metrics/calculator/predictor/LinearModel.xtend | 91 +++++++++ .../validation/ConstraintCollection.xtend | 80 ++++++++ .../calculator/validation/ViolationCheck.xtend | 66 +++++++ 27 files changed, 2169 insertions(+) create mode 100644 Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/app/Domain.xtend create mode 100644 Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/app/Main.xtend create mode 100644 Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/app/PartialInterpretationMetric.xtend create mode 100644 Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/app/PartialInterpretationMetricDistance.xtend create mode 100644 Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/distance/CostDistance.xtend create mode 100644 Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/distance/EuclideanDistance.xtend create mode 100644 Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/distance/JSDistance.xtend create mode 100644 Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/distance/KSDistance.xtend create mode 100644 Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/graph/EMFGraph.xtend create mode 100644 Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/graph/Graph.xtend create mode 100644 Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/graph/GraphStatistic.xtend create mode 100644 Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/graph/PartialInterpretationGraph.xtend create mode 100644 Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/io/CsvFileWriter.xtend create mode 100644 Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/io/GraphReader.xtend create mode 100644 Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/io/RepMetricsReader.xtend create mode 100644 Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/metrics/EdgeTypeMetric.xtend create mode 100644 Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/metrics/Metric.xtend create mode 100644 Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/metrics/MetricSampleGroup.xtend create mode 100644 Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/metrics/MultiplexParticipationCoefficientMetric.xtend create mode 100644 Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/metrics/NodeActivityMetric.xtend create mode 100644 Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/metrics/NodeTypeMetric.xtend create mode 100644 Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/metrics/OutDegreeMetric.xtend create mode 100644 Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/metrics/TypedClusteringCoefficientMetric.xtend create mode 100644 Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/metrics/TypedOutDegree.xtend create mode 100644 Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/predictor/LinearModel.xtend create mode 100644 Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/validation/ConstraintCollection.xtend create mode 100644 Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/validation/ViolationCheck.xtend (limited to 'Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src') diff --git a/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/app/Domain.xtend b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/app/Domain.xtend new file mode 100644 index 00000000..c8fd435b --- /dev/null +++ b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/app/Domain.xtend @@ -0,0 +1,7 @@ +package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.app + +enum Domain{ + Yakindumm, + Ecore, + Github +} \ No newline at end of file diff --git a/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/app/Main.xtend b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/app/Main.xtend new file mode 100644 index 00000000..dfde6593 --- /dev/null +++ b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/app/Main.xtend @@ -0,0 +1,96 @@ +package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.app + +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.graph.EMFGraph +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.io.CsvFileWriter +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.io.GraphReader +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.validation.ViolationCheck +import hu.bme.mit.inf.dslreasoner.domains.yakindu.sgraph.yakindumm.impl.YakindummPackageImpl +import java.io.File +import java.util.ArrayList +import org.eclipse.emf.ecore.EPackage +import org.eclipse.emf.ecore.EcorePackage +import org.eclipse.viatra.query.runtime.rete.matcher.ReteEngine +import org.eclipse.emf.ecore.impl.EcorePackageImpl +import org.eclipse.emf.ecore.EReference + +//import yakindumm2.impl.Yakindumm2PackageImpl + +class Main { + var static Domain d = Domain.Yakindumm; + val static String suffix = '.xmi' + val static String OUTPUT_FOLDER = "Inputs/measurement2/yakindu/Alloy/"; + val static String INPUT_FOLDER = "outputs/measurement2/yakindu/Alloy/"; + val static int NUM_RUNS = 1; + + static class RWInformation{ + public var String inputFolder; + public var String outputFolder; + public var int numRuns; + + new(String inputFolder, String outputFolder, int numRuns){ + this.inputFolder = inputFolder; + this.outputFolder = outputFolder; + this.numRuns = numRuns; + } + } + + def static void main(String[] args){ + //init model + var EPackage metamodel; + //init viatra engine for the violation checker + ReteEngine.getClass(); + + if(d == Domain.Yakindumm){ + YakindummPackageImpl.eINSTANCE.eClass; + metamodel = YakindummPackageImpl.eINSTANCE; + }else if (d == Domain.Ecore){ + EcorePackage.eINSTANCE.eClass; + metamodel = EcorePackageImpl.eINSTANCE; + }else if (d == Domain.Github){ + //TODO: Initialize Github Package + } + + + println("Start Reading Models..."); + var reader = new GraphReader(metamodel, suffix); + + val models = new RWInformation(OUTPUT_FOLDER, INPUT_FOLDER, NUM_RUNS); + calculateAllModels(models.inputFolder, models.outputFolder,models.numRuns, reader); + println("finished"); + } + + static def calculateAllModels(String inputFolder, String outputFolder, int numRuns, GraphReader reader){ + (new File(outputFolder)).mkdir(); + for(var i = 1; i <= numRuns; i++){ + val models = new ArrayList(); + models.addAll(reader.readModels(inputFolder + "run" + i)); + for(model : models){ + calculateAndOutputMetrics(model, YakindummPackageImpl.eNAME, outputFolder+model.name+"_run_"+i+".csv"); + } + } + println("output results Ended for: " + outputFolder); + + + } + + static def calculateAndOutputMetrics(EMFGraph model, String metaModel, String fileName){ + //println("evaluating for " + model.name); + model.metaModel = metaModel; + + //remove eGenericType for Ecore domain + if(d == Domain.Ecore){ + var refsToRemove = EcorePackageImpl.eINSTANCE.eAllContents.filter(EReference).filter[ + it.name.equals('eGenericType') || it.name.equals('eGenericSuperTypes') || it.name.equals('eFactoryInstance')|| + it.name.equals('eGenericExceptions') || it.name.equals('references') || it.name.equals('contents'); + ]; + refsToRemove.forEach[model.removeReference(it)]; + } + + var outputs = model.evaluateAllMetrics(); + var violations = ViolationCheck.calculateViolationCounts(model.root, d); + println(violations); + var violationsOutput = newArrayList('violations', violations+''); + outputs.add(violationsOutput); + CsvFileWriter.write(outputs, fileName); + } +} \ No newline at end of file diff --git a/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/app/PartialInterpretationMetric.xtend b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/app/PartialInterpretationMetric.xtend new file mode 100644 index 00000000..5e62b586 --- /dev/null +++ b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/app/PartialInterpretationMetric.xtend @@ -0,0 +1,85 @@ +package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.app + +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.distance.JSDistance +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.distance.KSDistance +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.graph.PartialInterpretationGraph +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.io.CsvFileWriter +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.Metric +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.MultiplexParticipationCoefficientMetric +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.NodeActivityMetric +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.OutDegreeMetric +import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.partialinterpretation.PartialInterpretation +import java.io.File +import java.io.FileNotFoundException +import java.io.PrintWriter +import java.util.ArrayList +import java.util.List +import org.eclipse.emf.ecore.util.EcoreUtil +import org.eclipse.viatra.dse.api.Solution + +class PartialInterpretationMetric { + var static KSDistance ks; + var static JSDistance js; + + def static void initPaths(){ + new File("debug/metric/").mkdir(); + new File("debug/metric/trajectories/").mkdir(); + } + + // calculate the metrics for a state + def static void calculateMetric(PartialInterpretation partial, String path, String currentStateId, Integer counter){ + val metrics = new ArrayList(); + metrics.add(new OutDegreeMetric()); + metrics.add(new NodeActivityMetric()); + metrics.add(new MultiplexParticipationCoefficientMetric()); + + //make dir since the folder can be none existing + new File(path).mkdir(); + val filename = path + "/state_"+currentStateId+"-"+counter+".csv"; + val metricCalculator = new PartialInterpretationGraph(partial, metrics, currentStateId); + + CsvFileWriter.write(metricCalculator.evaluateAllMetrics(), filename); + } + + def static void outputTrajectories(PartialInterpretation empty, List solutions){ + for(solution : solutions){ + + //need to copy the empty solution because the transition directly worked on the graph + val emptySolutionCopy = EcoreUtil.copy(empty) + val trajectory = solution.shortestTrajectory; + trajectory.model = emptySolutionCopy + + // state codes that will record the trajectory + val stateCodes = newArrayList() + var counter = 0 + + //transform and record the state codes for each state + while(trajectory.doNextTransformation){ + //println(trajectory.stateCoder.createStateCode) + val stateId = trajectory.stateCoder.createStateCode.toString + val interpretation = trajectory.getModel(); + println(stateId) + //calculate metrics of current state + calculateMetric(interpretation as PartialInterpretation, "debug/metric/output", stateId, counter) + stateCodes.add(stateId) + counter++ + } + + + //output the trajectory + try{ + new File("debug/metric/trajectories/").mkdir(); + val path = "debug/metric/trajectories/trajectory"+trajectory.stateCoder.createStateCode.toString+".csv" + val PrintWriter writer = new PrintWriter(new File(path)) + val output = new StringBuilder + for(stateCode : stateCodes){ + output.append(stateCode+'\n') + } + writer.write(output.toString()) + writer.close() + }catch(FileNotFoundException e) { + e.printStackTrace() + } + } + } +} \ No newline at end of file diff --git a/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/app/PartialInterpretationMetricDistance.xtend b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/app/PartialInterpretationMetricDistance.xtend new file mode 100644 index 00000000..697b2639 --- /dev/null +++ b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/app/PartialInterpretationMetricDistance.xtend @@ -0,0 +1,216 @@ +package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.app + +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.distance.EuclideanDistance +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.distance.JSDistance +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.distance.KSDistance +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.distance.StateData +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.graph.PartialInterpretationGraph +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.io.RepMetricsReader +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.Metric +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.MetricSampleGroup +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.MultiplexParticipationCoefficientMetric +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.NodeActivityMetric +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.NodeTypeMetric +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.OutDegreeMetric +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.predictor.LinearModel +import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.partialinterpretation.PartialInterpretation +import java.util.ArrayList +import java.util.HashMap +import java.util.List +import java.util.Map +import org.apache.commons.math3.stat.regression.OLSMultipleLinearRegression +import org.eclipse.xtend.lib.annotations.Accessors +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.EdgeTypeMetric + +class PartialInterpretationMetricDistance { + + var KSDistance ks; + var JSDistance js; + var EuclideanDistance ed; + var Map stateAndHistory; + var OLSMultipleLinearRegression regression; + List samples; + var MetricSampleGroup g; + @Accessors(PUBLIC_GETTER) + var LinearModel linearModel; + + + new(Domain d){ + var metrics = RepMetricsReader.read(d); + this.g = metrics; + ks = new KSDistance(g); + js = new JSDistance(g); + ed = new EuclideanDistance(g); + regression = new OLSMultipleLinearRegression(); + regression.noIntercept = false; + stateAndHistory = new HashMap(); + samples = new ArrayList(); + linearModel = new LinearModel(0.01); + } + + def MetricDistanceGroup calculateMetricDistanceKS(PartialInterpretation partial){ + val metrics = new ArrayList(); + metrics.add(new OutDegreeMetric()); + metrics.add(new NodeActivityMetric()); + metrics.add(new MultiplexParticipationCoefficientMetric()); + metrics.add(new NodeTypeMetric()); + val metricCalculator = new PartialInterpretationGraph(partial, metrics, null); + var metricSamples = metricCalculator.evaluateAllMetricsToSamples(); + + var mpc = ks.mpcDistance(metricSamples.mpcSamples); + var na = ks.naDistance(metricSamples.naSamples); + var outDegree = ks.outDegreeDistance(metricSamples.outDegreeSamples); + var nodeType = ks.nodeTypeDistance(metricSamples.nodeTypeSamples); + //var typedOutDegree = ks.typedOutDegreeDistance(metricSamples.typedOutDegreeSamples); + var distance = new MetricDistanceGroup(mpc, na, outDegree, nodeType); + distance.nodeTypeInfo = metricSamples.nodeTypeSamples; + return distance; + } + + def MetricDistanceGroup calculateMetricEuclidean(PartialInterpretation partial){ + val metrics = new ArrayList(); + metrics.add(new OutDegreeMetric()); + metrics.add(new NodeActivityMetric()); + metrics.add(new MultiplexParticipationCoefficientMetric()); + + val metricCalculator = new PartialInterpretationGraph(partial, metrics, null); + var metricSamples = metricCalculator.evaluateAllMetricsToSamples(); + + var mpc = ed.mpcDistance(metricSamples.mpcSamples); + var na = ed.naDistance(metricSamples.naSamples); + var outDegree = ed.outDegreeDistance(metricSamples.outDegreeSamples); + + return new MetricDistanceGroup(mpc, na, outDegree); + } + + def MetricDistanceGroup calculateMetricDistance(PartialInterpretation partial){ + val metrics = new ArrayList(); + metrics.add(new OutDegreeMetric()); + metrics.add(new NodeActivityMetric()); + metrics.add(new MultiplexParticipationCoefficientMetric()); + + val metricCalculator = new PartialInterpretationGraph(partial, metrics, null); + var metricSamples = metricCalculator.evaluateAllMetricsToSamples(); + + var mpc = js.mpcDistance(metricSamples.mpcSamples); + var na = js.naDistance(metricSamples.naSamples); + var outDegree = js.outDegreeDistance(metricSamples.outDegreeSamples); + + return new MetricDistanceGroup(mpc, na, outDegree); + } + + def resetRegression(Object state){ + samples.clear(); + + if(stateAndHistory.containsKey(state)){ + var data = stateAndHistory.get(state); + + var curState = state; + + samples.add(data); + + while(stateAndHistory.containsKey(data.lastState) && data.lastState != curState){ + curState = data.lastState; + data = stateAndHistory.get(data.lastState); + samples.add(data); + } + + if(samples.size == 0){ + println('state: ' + state); + println('last state: ' + data.lastState); + } + } + println("trajectory sample size:" + samples.size) + } + + def feedData(Object state, double[] features, double value, Object lastState){ + var data = new StateData(features, value, lastState); + stateAndHistory.put(state, data); + samples.add(data); + } + + def getPredictionForNextDataSample(double[] features, double value, double[] featuresToPredict){ + if(samples.size <= 4){ + println('OK'); + } + var data = new StateData(features, value, null); + samples.add(data); + + // create training set from current data + var double[][] xSamples = samples.map[it.features]; + var double[] ySamples = samples.map[it.value]; + + + regression.newSampleData(ySamples, xSamples); + var prediction = predict(featuresToPredict); + + //remove the last element just added + samples.remove(samples.size - 1); + return prediction; + } + + def private predict(double[] featuresToPredict){ + var parameters = regression.estimateRegressionParameters(); + // the regression will add an initial column for 1's, the first parameter is constant term + var result = parameters.get(0); + for(var i = 0; i < featuresToPredict.length; i++){ + result += parameters.get(i+1) * featuresToPredict.get(i); + } + return result; + } + + def double[] calculateFeature(int step, int violations){ + var features = newDoubleArrayOfSize(2); + //constant term + features.set(0, 1); //a + features.set(0, Math.sqrt(step) + 30) // b + features.set(1, 1.0 / (step + 30) );// c + + +// features.set(2, violations); +// features.set(3, Math.pow(violations, 2)); + + return features; + } +} + +class MetricDistanceGroup{ + var double mpcDistance; + var double naDistance; + var double outDegreeDistance; + var double nodeTypeDistance; + protected var HashMap nodeTypeInfo; + + new(double mpcDistance, double naDistance, double outDegreeDistance, double nodeTypeDistance){ + this.mpcDistance = mpcDistance; + this.naDistance = naDistance; + this.outDegreeDistance = outDegreeDistance; + this.nodeTypeDistance = nodeTypeDistance; + } + + new(double mpcDistance, double naDistance, double outDegreeDistance){ + this.mpcDistance = mpcDistance; + this.naDistance = naDistance; + this.outDegreeDistance = outDegreeDistance; + } + + def double getNodeTypeDistance(){ + return this.nodeTypeDistance; + } + + def double getMPCDistance(){ + return this.mpcDistance + } + + def double getNADistance(){ + return this.naDistance + } + + def double getOutDegreeDistance(){ + return this.outDegreeDistance + } + + def double getNodeTypePercentage(String typeName){ + return nodeTypeInfo.getOrDefault(typeName, 0.0); + } +} \ No newline at end of file diff --git a/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/distance/CostDistance.xtend b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/distance/CostDistance.xtend new file mode 100644 index 00000000..613f0f43 --- /dev/null +++ b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/distance/CostDistance.xtend @@ -0,0 +1,38 @@ +package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.distance + +import java.text.DecimalFormat +import java.util.HashMap +import java.util.List +import org.eclipse.xtend.lib.annotations.Accessors + +abstract class CostDistance { + def abstract double naDistance(List samples); + def abstract double mpcDistance(List samples); + def abstract double outDegreeDistance(List samples); + + def protected pmfFromSamples(double[] samples, DecimalFormat formatter){ + var length = samples.length; + var pmfMap = new HashMap(); + + for(sample : samples){ + pmfMap.put(formatter.format(sample), pmfMap.getOrDefault(formatter.format(sample), 0.0) + 1.0 / length); + } + + return pmfMap; + } +} + +class StateData{ + @Accessors(PUBLIC_GETTER) + var double[] features; + @Accessors(PUBLIC_GETTER) + var double value; + @Accessors(PUBLIC_GETTER) + var Object lastState; + + new(double[] features, double value, Object lastState){ + this.features = features; + this.value = value + this.lastState = lastState; + } +} \ No newline at end of file diff --git a/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/distance/EuclideanDistance.xtend b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/distance/EuclideanDistance.xtend new file mode 100644 index 00000000..d6adcc9a --- /dev/null +++ b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/distance/EuclideanDistance.xtend @@ -0,0 +1,72 @@ +package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.distance + +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.app.Domain +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.io.RepMetricsReader +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.MetricSampleGroup +import com.google.common.collect.Sets +import java.text.DecimalFormat +import java.util.ArrayList +import java.util.HashMap +import java.util.List +import java.util.Map +import java.util.Set + +class EuclideanDistance extends CostDistance{ + var MetricSampleGroup g; + var HashMap mpcPMF; + var HashMap naPMF; + var HashMap outDegreePMF; + var DecimalFormat formatter; + + new(MetricSampleGroup g){ + this.g = g; + + var mpcSamples = g.mpcSamples; + var naSamples = g.naSamples.stream.mapToDouble([it]).toArray(); + var outDegreeSamples = g.outDegreeSamples.stream.mapToDouble([it]).toArray(); + + //needs to format the number to string avoid precision issue + formatter = new DecimalFormat("#0.00000"); + + mpcPMF = pmfFromSamples(mpcSamples, formatter); + naPMF = pmfFromSamples(naSamples, formatter); + outDegreePMF = pmfFromSamples(outDegreeSamples, formatter); + } + + override naDistance(List samples) { + var pmfMap = pmfFromSamples(samples, formatter); + return euclideanDistance(pmfMap, naPMF); + } + + override mpcDistance(List samples) { + var pmfMap = pmfFromSamples(samples, formatter); + return euclideanDistance(pmfMap, mpcPMF); + } + + override outDegreeDistance(List samples) { + var pmfMap = pmfFromSamples(samples, formatter); + return euclideanDistance(pmfMap, outDegreePMF); + } + + + def private euclideanDistance(HashMap pmf1, HashMap pmf2){ + var keys = Sets.union(pmf1.keySet(), pmf2.keySet()); + var pmfList1 = pmfMapToList(pmf1, keys); + var pmfList2 = pmfMapToList(pmf2, keys); + var distance = 0.0; + for(var i = 0; i < pmfList1.size(); i++){ + distance += Math.pow(pmfList1.get(i) + pmfList2.get(i), 2); + } + + return Math.sqrt(distance); + } + + def private pmfMapToList(Map map, Set keys){ + var list = new ArrayList(); + for(key : keys){ + var value = map.getOrDefault(key, 0.0); + list.add(value); + } + return list; + } +} \ No newline at end of file diff --git a/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/distance/JSDistance.xtend b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/distance/JSDistance.xtend new file mode 100644 index 00000000..4a0a0dc3 --- /dev/null +++ b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/distance/JSDistance.xtend @@ -0,0 +1,88 @@ +package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.distance + +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.MetricSampleGroup +import com.google.common.collect.Sets +import java.text.DecimalFormat +import java.util.HashMap +import java.util.List + +class JSDistance extends CostDistance { + var HashMap mpcPMF; + var HashMap naPMF; + var HashMap outDegreePMF; + var HashMap nodeTypesPMF; + var DecimalFormat formatter; + + new(MetricSampleGroup g){ + var mpcSamples = g.mpcSamples; + var naSamples = g.naSamples.stream.mapToDouble([it]).toArray(); + var outDegreeSamples = g.outDegreeSamples.stream.mapToDouble([it]).toArray(); + + //needs to format the number to string avoid precision issue + formatter = new DecimalFormat("#0.00000"); + + mpcPMF = pmfFromSamples(mpcSamples, formatter); + naPMF = pmfFromSamples(naSamples, formatter); + outDegreePMF = pmfFromSamples(outDegreeSamples, formatter); + nodeTypesPMF = g.nodeTypeSamples; + } + + def private combinePMF(HashMap pmf1, HashMap pmf2){ + var pmfMap = new HashMap(); + + var union = Sets.union(pmf1.keySet(), pmf2.keySet()); + + for(key : union){ + // corresponding to M in JS distance + var value = 1.0/2 * (pmf1.getOrDefault(key, 0.0) + pmf2.getOrDefault(key, 0.0)); + pmfMap.put(key, value); + } + return pmfMap; + } + + def private jsDivergence(HashMap p, HashMap q){ + val m = combinePMF(q, p); + var distance = 1.0/2 * klDivergence(p, m) + 1.0/2 * klDivergence(q, m); + return distance; + } + + def klDivergence(HashMap p, HashMap q){ + var distance = 0.0; + for(key : q.keySet()){ + //need to convert log e to log 2 + if(p.containsKey(key)){ + distance -= p.get(key) * Math.log(q.get(key) / p.get(key)) / Math.log(2); + } + } + return distance; + } + + override double mpcDistance(List samples){ + // map list to array + var map = pmfFromSamples(samples.stream().mapToDouble([it]).toArray(), formatter); + //if the size of array is smaller than 2, ks distance cannot be performed, simply return 1 + if(map.size < 2) return 1; + return jsDivergence(map, mpcPMF); + } + + override double naDistance(List samples){ + // map list to array + var map = pmfFromSamples(samples.stream().mapToDouble([it]).toArray(), formatter); + + //if the size of array is smaller than 2, ks distance cannot be performed, simply return 1 + if(map.size < 2) return 1; + return jsDivergence(map, naPMF); + } + + override double outDegreeDistance(List samples){ + // map list to array + var map = pmfFromSamples(samples.stream().mapToDouble([it]).toArray(), formatter); + //if the size of array is smaller than 2, ks distance cannot be performed, simply return 1 + if(map.size < 2) return 1; + return jsDivergence(map, outDegreePMF); + } + + def nodeTypeDistance(HashMap samples){ + return klDivergence(samples, nodeTypesPMF); + } +} \ No newline at end of file diff --git a/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/distance/KSDistance.xtend b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/distance/KSDistance.xtend new file mode 100644 index 00000000..c486a328 --- /dev/null +++ b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/distance/KSDistance.xtend @@ -0,0 +1,102 @@ +package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.distance + +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.MetricSampleGroup +import java.util.HashMap +import java.util.HashSet +import java.util.List +import org.apache.commons.math3.stat.inference.KolmogorovSmirnovTest + +class KSDistance extends CostDistance { + var static ksTester = new KolmogorovSmirnovTest(); + var MetricSampleGroup g; + + new(MetricSampleGroup g){ + this.g = g; + } + override double mpcDistance(List samples){ + //if the size of array is smaller than 2, ks distance cannot be performed, simply return 1 + if(samples.size < 2) return 1; + return ksTester.kolmogorovSmirnovStatistic(g.mpcSamples, samples); + } + + override double naDistance(List samples){ + //if the size of array is smaller than 2, ks distance cannot be performed, simply return 1 + if(samples.size < 2) return 1; + return ksTester.kolmogorovSmirnovStatistic(g.naSamples as double[], samples); + } + + override double outDegreeDistance(List samples){ + //if the size of array is smaller than 2, ks distance cannot be performed, simply return 1 + if(samples.size < 2) return 1; + return ksTester.kolmogorovSmirnovStatistic(g.outDegreeSamples, samples); + } + + def double typedOutDegreeDistance(HashMap> map){ + var value = 0.0; + // map list to array + val keySet = new HashSet(map.keySet); + keySet.addAll(g.typedOutDegreeSamples.keySet); + for(key : keySet){ + if(!map.containsKey(key) ){ + value += 1; + }else if(!g.typedOutDegreeSamples.containsKey(key)){ + value += map.get(key).size * 100; + }else{ + var double[] rep = g.typedOutDegreeSamples.get(key).stream().mapToDouble([it|it]).toArray(); + var double[] ins = map.get(key).stream().mapToDouble([it|it]).toArray(); + if((rep.size < 2 || ins.size < 2) ){ + if(rep.size < 2 && rep.containsAll(ins)){ + value += 0; + }else{ + value += 1; + } + }else if(rep.size >= 2 && ins.size >= 2){ + value += ksTester.kolmogorovSmirnovStatistic(rep, ins); + } + } + } + + + return value; + } + + def nodeTypeDistance(HashMap samples){ + var typesDistMap = g.nodeTypeSamples; + var sourceDist = newArrayList(); + var instanceDist = newArrayList(); + + for(key : typesDistMap.keySet()){ + sourceDist.add(typesDistMap.get(key)); + instanceDist.add(samples.getOrDefault(key, 0.0)); + } + + return ks_distance_two_dist(sourceDist, instanceDist); + } + + def edgeTypeDistance(HashMap samples){ + var typesDistMap = g.edgeTypeSamples; + var sourceDist = newArrayList(); + var instanceDist = newArrayList(); + + for(key : typesDistMap.keySet()){ + sourceDist.add(typesDistMap.get(key)); + instanceDist.add(samples.getOrDefault(key, 0.0)); + } + + return ks_distance_two_dist(sourceDist, instanceDist); + } + + def double ks_distance_two_dist(List dist1, List dist2){ + // Since we already know the pdf, we compute the ks-test manully + var ksStatistics = 0.0; + var sum1 = 0.0; + var sum2 = 0.0; + for(var i = 0; i < dist1.size(); i++){ + sum1 += dist1.get(i); + sum2 += dist2.get(i); + + ksStatistics = Math.max(ksStatistics, Math.abs(sum1 - sum2)); + } + return ksStatistics; + } +} \ No newline at end of file diff --git a/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/graph/EMFGraph.xtend b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/graph/EMFGraph.xtend new file mode 100644 index 00000000..8fa29fe6 --- /dev/null +++ b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/graph/EMFGraph.xtend @@ -0,0 +1,124 @@ +package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.graph + +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.Metric +import java.util.ArrayList +import java.util.HashSet +import java.util.List +import org.eclipse.emf.common.util.EList +import org.eclipse.emf.ecore.EObject +import org.eclipse.emf.ecore.EReference +import org.eclipse.xtend.lib.annotations.Accessors + +class EMFGraph extends Graph{ + @Accessors(PUBLIC_GETTER) + var EObject root; + + def void init (EObject root, List metrics, String name, List referenceTypes){ + val otherContents = root.eAllContents.toList(); + this.root = root; + otherContents.add(root); + init(otherContents, metrics, name, referenceTypes); + } + + /** + * init the graph with all nodes and reference types in the meta model + * @param objects: objects in the instance model (exclude root) + * @param metrics: metrics to be evaluated + * @param name: name of the instance model + * @param ReferenceTypes: reference types defined in the meta model + */ + def void init(List objects, List metrics, String name, List referenceTypes){ + objects.forEach[it| + // TODO: Maybe want to consider all the super types as well + var types = new HashSet(); + types.add(it.eClass.name); + statistic.addNodeWithAllTypes(it, types); + ] + + referenceTypes.forEach[it| + // Only consider the edges that are not derived to preserve the statistical property + if(!it.derived){ + statistic.addEdgeType(it.name); + } + ]; + + objects.forEach[source| + source.eClass.EAllReferences.forEach[r| + //many references + if(r.isMany){ + source.getNeighbours(r).forEach[target| + addEdge(source, target, r); + ] + }else{ + //single references + val target = source.eGet(r) as EObject; + addEdge(source, target, r); + } + ] + ] + + this.metrics = metrics; + this.name = name; + } + + def void removeReference(EReference r){ + if (statistic.containsEdgeType(r.name)){ + statistic.removeReference(r.name, r.containment); + } + } + + /** + * Set basic information for the output + */ + override setBasicInformation(ArrayList> output){ + val metaInfo = new ArrayList(); + metaInfo.add(META_MODEL_HEADER); + metaInfo.add(this.metaModel); + + val edgeInfo = new ArrayList(); + edgeInfo.add(NUM_EDGE_TYPE_HEADER); + edgeInfo.add(this.statistic.allTypes.size()+""); + + val nodeInfo = new ArrayList(); + nodeInfo.add(NUM_NODE_HEADER); + nodeInfo.add(this.statistic.allNodes.size()+""); + + val stateInfo = new ArrayList(); + stateInfo.add(STATE_ID_HEADER); + stateInfo.add(this.name); + + + output.add(metaInfo); + output.add(edgeInfo); + output.add(nodeInfo); + output.add(stateInfo); + } + + def EList getNeighbours(EObject o, EReference r){ + return (o.eGet(r, true) as EList); + } + + def addEdge(EObject source, EObject target, EReference r){ + //Only add the edge if the reference is not derived to preserve the statistical property + if(target !== null && r !== null && !r.derived){ + statistic.addEdge(source, target, r.name); + } + } + + override GraphStatistic getStatistic(){ + return this.statistic; + } + + override String getName(){ + return this.name; + } + + def void setMetaModel(String model){ + this.metaModel = model; + } + + def String getMetaModel(){ + return this.metaModel; + } + +} \ No newline at end of file diff --git a/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/graph/Graph.xtend b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/graph/Graph.xtend new file mode 100644 index 00000000..6b400b0d --- /dev/null +++ b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/graph/Graph.xtend @@ -0,0 +1,71 @@ +package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.graph + +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.Metric +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.MetricSampleGroup +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.MultiplexParticipationCoefficientMetric +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.NodeActivityMetric +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.OutDegreeMetric +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.TypedOutDegree +import java.util.ArrayList +import java.util.HashMap +import java.util.List +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.NodeTypeMetric +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.EdgeTypeMetric + +abstract class Graph { + + protected static val String META_MODEL_HEADER = "Meta Mode" + protected static val String NUM_NODE_HEADER = "Number Of Nodes"; + protected static val String NUM_EDGE_TYPE_HEADER = "Number of Edge types"; + protected static val String STATE_ID_HEADER = "State Id"; + + protected val statistic = new GraphStatistic(); + protected var List metrics; + protected var String name = ""; + protected var String metaModel = ""; + + /** + * evaluate all metrics for this model + * return the result as a two dimentional list + */ + def ArrayList> evaluateAllMetrics(){ + val result = new ArrayList>(); + setBasicInformation(result); + + for(metric : this.metrics){ + val datas = metric.evaluate(this.statistic); + for(row : datas){ + result.add(new ArrayList(row)); + } + } + return result; + } + + def MetricSampleGroup evaluateAllMetricsToSamples(){ + var sample = new MetricSampleGroup(); + + for(metric : this.metrics){ + if(metric instanceof MultiplexParticipationCoefficientMetric){ + sample.mpcSamples = metric.evaluateSamples(this.statistic) as ArrayList; + }else if(metric instanceof NodeActivityMetric){ + sample.naSamples = metric.evaluateSamples(this.statistic) as ArrayList; + }else if(metric instanceof OutDegreeMetric){ + sample.outDegreeSamples = metric.evaluateSamples(this.statistic) as ArrayList; + }else if(metric instanceof TypedOutDegree){ + sample.typedOutDegreeSamples = metric.evaluateSamples(this.statistic) as HashMap>; + }else if(metric instanceof NodeTypeMetric){ + sample.nodeTypeSamples = metric.evaluateSamples(this.statistic) as HashMap; + }else if (metric instanceof EdgeTypeMetric){ + sample.edgeTypeSamples = metric.evaluateSamples(this.statistic) as HashMap; + } + } + + return sample; + } + + def void setBasicInformation(ArrayList> result); + + def GraphStatistic getStatistic(); + + def String getName(); +} \ No newline at end of file diff --git a/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/graph/GraphStatistic.xtend b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/graph/GraphStatistic.xtend new file mode 100644 index 00000000..31788bb2 --- /dev/null +++ b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/graph/GraphStatistic.xtend @@ -0,0 +1,194 @@ +package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.graph + +import com.google.common.collect.ArrayListMultimap +import com.google.common.collect.Multimap +import java.util.HashMap +import java.util.HashSet +import java.util.List +import java.util.Map +import java.util.Set +import org.eclipse.emf.ecore.EObject +import org.eclipse.emf.ecore.EReference + +class GraphStatistic { + val incomingEdges = new HashMap>; + val outgoingEdges = new HashMap>; + + val edgeTypes = new HashSet(); + val nodeToType = new HashMap>(); + + /** + * Add an edge type to to the graph + * @param type: type to add + */ + def void addEdgeType(String type){ + if(edgeTypes.contains(type)){ + return; + } + + edgeTypes.add(type); + incomingEdges.put(type, ArrayListMultimap.create()); + outgoingEdges.put(type, ArrayListMultimap.create()); + } + + /** + * Add a node to the graph with one type in its type hierarchy + * @param node: node to add + */ + def void addNodeWithType(EObject n, String Type){ + var types = nodeToType.getOrDefault(n, new HashSet()); + types.add(Type); + nodeToType.put(n, types); + } + + def boolean containsNode(EObject o){ + return nodeToType.containsKey(o); + } + + def Set getTypesForNode(EObject o){ + return nodeToType.getOrDefault(o, new HashSet()); + } + + def void overwriteCurrentType(EObject o, String type){ + var typeSet = nodeToType.getOrDefault(o, new HashSet()); + + // clear current types + typeSet.clear(); + typeSet.add(type); + nodeToType.put(o, typeSet); + } + + /** + * Add a node to the graph with all types in its type hierarchy + */ + def void addNodeWithAllTypes(EObject n, Set types){ + nodeToType.put(n, types); + } + + /** + * Add an edge to the graph + * @param source: source node + * @param target: target node + * @param type: type of the reference + */ + def void addEdge(EObject source, EObject target, String type){ + outgoingEdges.get(type).put(source, target); + incomingEdges.get(type).put(target, source); + } + + /** + * check if this graph contains a specific edge type + */ + def boolean containsEdgeType(String typeName){ + if(outgoingEdges.containsKey(typeName) && incomingEdges.containsKey(typeName)){ + return true; + } + return false; + } + + /** + * remove references from the statistics, potentially remove the nodes associated with it + * @Param name: name of the reference + * @Param isContainment: if true then the corresponding nodes on the incoming side will also be removed + */ + def removeReference(String name, boolean isContainment){ + if(!edgeTypes.contains(name)){ + return; + } + + edgeTypes.remove(name); + var incomingSet = incomingEdges.remove(name); + outgoingEdges.remove(name); + + // if the reference is not a containment, then removing the reference is enough + if(!isContainment){ + return; + } + + // else remove all corresponding nodes + val nodesToRemove = incomingSet.keySet(); + + //remove nodes from node sets + nodesToRemove.forEach[nodeToType.remove(it)]; + + val removeForMultimap = [Multimap refMap| + nodesToRemove.forEach[refMap.removeAll(it)]; + var values = refMap.values() + //remove the values from the list is equavalent to remove it in the multimap + values.removeAll(nodesToRemove); + return; + ]; + + //remove nodes from all other references on incomingEdges + incomingEdges.values.forEach(removeForMultimap); + outgoingEdges.values.forEach(removeForMultimap); + } + + /** + * calculate the out degree for an object + */ + def int outDegree(EObject o){ + var count = 0; + + for (String type : edgeTypes){ + count += outgoingEdges.get(type).get(o).size(); + } + return count; + } + + /** + * calculate the in degree of an object + */ + def int inDegree(EObject o){ + var count = 0; + + for (String type : edgeTypes){ + count += incomingEdges.get(type).get(o).size(); + } + return count; + } + + /** + * calculate the dimentional degree of a node + */ + def int dimentionalDegree(EObject o, String type){ + return incomingEdges.get(type).get(o).size() + outgoingEdges.get(type).get(o).size(); + } + + /** + * calculate the number of edge types for a given node. + */ + def int numOfEdgeTypes(EObject o){ + var count = 0; + + for(String type : edgeTypes){ + if(dimentionalDegree(o, type) > 0){ + count++; + } + } + + return count; + } + + def List getAllTypes(){ + return edgeTypes.toList(); + } + + def Map> getNodeToTypesMap(){ + return nodeToType; + } + + def List getAllNodes(){ + return nodeToType.keySet().toList(); + } + + def HashMap> getOutgoingEdges(){ + return outgoingEdges; + } + + def HashMap> incomingEdges(){ + return incomingEdges; + } + +} + diff --git a/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/graph/PartialInterpretationGraph.xtend b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/graph/PartialInterpretationGraph.xtend new file mode 100644 index 00000000..a2934eb9 --- /dev/null +++ b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/graph/PartialInterpretationGraph.xtend @@ -0,0 +1,134 @@ +package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.graph + +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.Metric +import hu.bme.mit.inf.dslreasoner.logic.model.logiclanguage.RelationDeclaration +import hu.bme.mit.inf.dslreasoner.logic.model.logiclanguage.Type +import hu.bme.mit.inf.dslreasoner.logic.model.logiclanguage.TypeDefinition +import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.partialinterpretation.BinaryElementRelationLink +import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.partialinterpretation.PartialInterpretation +import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.partialinterpretation.impl.PartialComplexTypeInterpretationImpl +import java.util.ArrayList +import java.util.List + +class PartialInterpretationGraph extends Graph{ + val typeToExclude = "undefinedpart"; + val classSuffix = " class"; + + /** + * Define a new PartialInterpretationGraph by parse every element from a PartialInterpretation + */ + new(PartialInterpretation partial, List metrics, String name){ + //the edge types are defined in terms of RelationDeclaration + partial.problem.relations.filter(RelationDeclaration).forEach[ + //only need the name of the reference type (remove everything with and after "reference") + var n = it.name.split(" ").get(0); + this.statistic.addEdgeType(n); + ] + // add all elements + val typeInterpretations = getTypes(partial); + for(type : typeInterpretations){ + //Only consider the most concrete class + if(isConcreteType(type.interpretationOf)){ + var typeName = type.interpretationOf.name.replace(classSuffix, ''); + for(node : type.elements){ + if(!this.statistic.containsNode(node)){ + this.statistic.addNodeWithType(node, typeName); + }else{ + // if the current type of the node is a super type of the type to check, + // substitute the current type with the new type + var currentType = statistic.getTypesForNode(node).get(0); + if(isSuperType(currentType, type.interpretationOf)){ + statistic.overwriteCurrentType(node, typeName); + } + } + } + } + } + + for(relationInterpretation : partial.partialrelationinterpretation) { + //only need the name of the reference type (remove everything with and after "reference") + val type = relationInterpretation.interpretationOf.name.split(" ").get(0); + for(edge : relationInterpretation.relationlinks.filter(BinaryElementRelationLink)){ + statistic.addEdge(edge.param1, edge.param2, type); + } + } + + this.name = name; + this.metrics = metrics; + } + + /** + * recursively check if a type is the super type of another + */ + def boolean isSuperType(String typeName, Type subtypeToCheck){ + var superTypes = subtypeToCheck.supertypes; + if(superTypes.size == 0){ + return false; + }else if(subtypeToCheck.supertypes.map[it.name.replace(classSuffix, '')].contains(typeName)){ + return true; + }else{ + for(superType : superTypes){ + if(isSuperType(typeName, superType)){ + return true; + } + } + return false; + } + } + + /** + * Check if a Type object is the class that we want to consider + * A type object is to be considered if it satisfy one of the following: + * 1. if it is not abstract + * 2. if it is abstract but has a subclass of type TypeDefinition (This means the generation is + * started with nodes in this type) + */ + def boolean isConcreteType(Type t){ + if(!t.isAbstract || t.subtypes.findFirst[it instanceof TypeDefinition] !== null){ + return true; + } + return false; + } + + /** + * Set basic information for the output + */ + override setBasicInformation(ArrayList> output){ + val metaInfo = new ArrayList(); + metaInfo.add(META_MODEL_HEADER); + metaInfo.add(this.metaModel); + + val edgeInfo = new ArrayList(); + edgeInfo.add(NUM_EDGE_TYPE_HEADER); + edgeInfo.add(this.statistic.allTypes.size()+""); + + val nodeInfo = new ArrayList(); + nodeInfo.add(NUM_NODE_HEADER); + nodeInfo.add(this.statistic.allNodes.size()+""); + + val stateInfo = new ArrayList(); + stateInfo.add(STATE_ID_HEADER); + stateInfo.add(this.name); + + output.add(metaInfo); + output.add(edgeInfo); + output.add(nodeInfo); + output.add(stateInfo); + } + + private def getTypes(PartialInterpretation partial){ + //only the complex type interpretations are the ones defined in meta model + //do not care about undefined types as it will be included in the class type + return partial.partialtypeinterpratation.filter(PartialComplexTypeInterpretationImpl) + .filter[!it.interpretationOf.name.toLowerCase.contains(typeToExclude)]; + } + + override getStatistic() { + throw new UnsupportedOperationException("TODO: auto-generated method stub") + } + + override getName() { + return name; + } + +} \ No newline at end of file diff --git a/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/io/CsvFileWriter.xtend b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/io/CsvFileWriter.xtend new file mode 100644 index 00000000..00b38d90 --- /dev/null +++ b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/io/CsvFileWriter.xtend @@ -0,0 +1,52 @@ +package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.io; + +import java.io.File +import java.io.FileNotFoundException +import java.io.FileOutputStream +import java.io.PrintWriter +import java.util.ArrayList +import java.util.List + +class CsvFileWriter { + + def static void write(ArrayList> datas, String uri) { + if(datas.size() <= 0) { + return; + } + val PrintWriter writer = new PrintWriter(new File(uri)); + output(writer, datas, uri); + } + + def static void append(ArrayList> datas, String uri) { + if(datas.size() <= 0) { + return; + } + val PrintWriter writer = new PrintWriter(new FileOutputStream(new File(uri), true)); + output(writer, datas, uri); + } + + + def private static void output(PrintWriter writer, ArrayList> datas, String uri) { + //println("Output csv for " + uri); + try { + + val output = new StringBuilder; + for(List datarow : datas){ + for(var i = 0; i < datarow.size() - 1; i++){ + output.append(datarow.get(i) + ','); + } + + if(datarow.size >= 1){ + output.append(datarow.get(datarow.size() - 1)); + output.append('\n'); + } + } + + writer.write(output.toString()); + writer.close(); + //println("Output csv finished"); + }catch(FileNotFoundException e) { + e.printStackTrace(); + } + } +} diff --git a/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/io/GraphReader.xtend b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/io/GraphReader.xtend new file mode 100644 index 00000000..053e0da3 --- /dev/null +++ b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/io/GraphReader.xtend @@ -0,0 +1,112 @@ +package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.io; + +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.graph.EMFGraph +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.EdgeTypeMetric +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.Metric +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.MultiplexParticipationCoefficientMetric +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.NodeActivityMetric +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.NodeTypeMetric +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.OutDegreeMetric +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.TypedOutDegree +import java.io.File +import java.io.FileNotFoundException +import java.util.ArrayList +import java.util.List +import org.eclipse.emf.common.util.URI +import org.eclipse.emf.ecore.EObject +import org.eclipse.emf.ecore.EPackage +import org.eclipse.emf.ecore.EReference +import org.eclipse.emf.ecore.resource.Resource +import org.eclipse.emf.ecore.resource.ResourceSet +import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl +import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl +import org.eclipse.emf.ecore.EGenericType +import org.eclipse.emf.ecore.EStructuralFeature +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.TypedClusteringCoefficientMetric + +class GraphReader{ + val ResourceSet resSet = new ResourceSetImpl(); + val referenceTypes = new ArrayList(); + var String suffix; + + new(EPackage metaModel, String suffix) { + Resource.Factory.Registry.INSTANCE.extensionToFactoryMap.put("*",new XMIResourceFactoryImpl) + this.suffix = suffix; + + //find all reference types in the meta model + metaModel.eAllContents.forEach[ + if(it instanceof EReference){ + referenceTypes.add(it); + } + ] + } + + def List readModels(String path){ + val dir = new File(path); + if(!dir.isDirectory){ + throw new Exception("expecting a directory"); + } + + val graphs = new ArrayList(); + + val metrics = new ArrayList(); + metrics.add(new OutDegreeMetric()); + metrics.add(new NodeActivityMetric()); + metrics.add(new MultiplexParticipationCoefficientMetric()); + metrics.add(new TypedOutDegree()); + metrics.add(new NodeTypeMetric()); + metrics.add(new EdgeTypeMetric()); + var count = 1 + //check all files in the directory with suffix + for(String name : dir.list.filter[it| it.endsWith(suffix)]){ + val file = new File(name); + val roots = readModel(EObject, path, file.name); + //add a list of metrics + val g = new EMFGraph(); + for(root : roots){ + g.init(root, metrics, name.replaceFirst(suffix, ""), referenceTypes); + } + + count ++; + graphs.add(g); + } + + return graphs; + } + + def EMFGraph readModel(String path, String filename){ + val metrics = new ArrayList(); + metrics.add(new OutDegreeMetric()); + metrics.add(new NodeActivityMetric()); + metrics.add(new MultiplexParticipationCoefficientMetric()); + metrics.add(new TypedOutDegree()); + metrics.add(new NodeTypeMetric()); + metrics.add(new EdgeTypeMetric()); + + val file = new File(filename); + val roots = readModel(EObject, path, file.name); + //add a list of metrics + val g = new EMFGraph(); + for(root : roots){ + g.init(root, metrics, filename.replaceFirst(suffix, ""), referenceTypes); + } + return g + } + + def List readModel(Class type, String path, String name) { + try { + val resource = resSet.getResource(getURI(path, name),true); + if(resource === null) throw new FileNotFoundException(getURI(path, name).toString) + else { + return resource.contents as List + } + } catch(Exception e) { + e.printStackTrace(); + throw new Exception(getURI(path, name).toString()); + } + } + + def static getURI(String path, String name) { + URI.createFileURI(path + "/" + name) + } +} \ No newline at end of file diff --git a/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/io/RepMetricsReader.xtend b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/io/RepMetricsReader.xtend new file mode 100644 index 00000000..2e7be586 --- /dev/null +++ b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/io/RepMetricsReader.xtend @@ -0,0 +1,100 @@ +package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.io + +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.app.Domain +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.MetricSampleGroup +import github.impl.GithubPackageImpl +import hu.bme.mit.inf.dslreasoner.domains.yakindu.sgraph.yakindumm.impl.YakindummPackageImpl +import java.util.HashMap +import org.eclipse.emf.ecore.EReference +import org.eclipse.emf.ecore.impl.EcorePackageImpl + +/** + * Read the sample of the distribution of a metric provided the csv file of the metric + */ +class RepMetricsReader { + static var Domain domain; + static def read(Domain d){ + var GraphReader reader; + if(d == Domain.Yakindumm){ + reader = new GraphReader(YakindummPackageImpl.eINSTANCE, '.xmi'); + }else if (d == Domain.Ecore){ + reader = new GraphReader(EcorePackageImpl.eINSTANCE, '.ecore'); + }else if (d == Domain.Github){ + reader = new GraphReader(GithubPackageImpl.eINSTANCE, '.githubmodel') + } + + domain = d; + + var domainRepPath = DataName.REP_PATH + d.name + '/'; + var rep = new MetricSampleGroup() + var out_d = readMetrics(reader, domainRepPath + DataName.OUT_D_REP); + var mpc = readMetrics(reader, domainRepPath + DataName.MPC_REP); + rep.mpcSamples = mpc.mpcSamples; + rep.outDegreeSamples = out_d.outDegreeSamples; + rep.naSamples = readMetrics(reader, domainRepPath + DataName.NA_REP).naSamples; + rep.typedOutDegreeSamples = out_d.typedOutDegreeSamples; + rep.edgeTypeSamples = mpc.edgeTypeSamples; + + //TODO: Parameterize the prior node distribution + var nodeTypeSamples = new HashMap(); + if(d == Domain.Yakindumm){ + nodeTypeSamples.put('Entry', 0.04257802080554814); + nodeTypeSamples.put('Choice', 0.1267671379034409); + nodeTypeSamples.put('State', 0.1596092291277674); + nodeTypeSamples.put('Transition', 0.6138636969858629); + nodeTypeSamples.put('Statechart', 0.010136036276340358); + nodeTypeSamples.put('Region', 0.04467858095492131); + nodeTypeSamples.put('Exit', 0.0018338223526273673); + nodeTypeSamples.put('FinalState', 0.0005334755934915977); + }else if(d ==Domain.Ecore){ + nodeTypeSamples.put('EAttribute', 0.23539778449144008); + nodeTypeSamples.put('EClass', 0.33081570996978854); + nodeTypeSamples.put('EReference', 0.30996978851963747); + nodeTypeSamples.put('EPackage', 0.012789526686807653); + nodeTypeSamples.put('EAnnotation', 0.002517623363544813); + nodeTypeSamples.put('EEnumLiteral', 0.07275931520644502); + nodeTypeSamples.put('EEnum', 0.013645518630412891); + nodeTypeSamples.put('EDataType', 0.004028197381671702); + nodeTypeSamples.put('EParameter', 0.005941591137965764); + nodeTypeSamples.put('EGenericType', 0.002014098690835851); + nodeTypeSamples.put('EOperation', 0.009415911379657605); + nodeTypeSamples.put('ETypeParameter', 0.0007049345417925478); + }else if (d == Domain.Github){ + nodeTypeSamples.put('Project', 0.012636538873420432); + nodeTypeSamples.put('Commit', 0.5525808524309276); + nodeTypeSamples.put('User', 0.05847076461769116); + nodeTypeSamples.put('Issue', 0.12743628185907047); + nodeTypeSamples.put('PullRequest', 0.07560505461554937); + nodeTypeSamples.put('IssueEvent', 0.17327050760334123); + } + + + + rep.nodeTypeSamples = nodeTypeSamples; + return rep; + } + + /** + * Read representative model + */ + private static def readMetrics(GraphReader r, String path){ + val model = r.readModels(path).head; + if(domain == Domain.Ecore){ + var refsToRemove = EcorePackageImpl.eINSTANCE.eAllContents.filter(EReference).filter[ + it.name.equals('eGenericType') || it.name.equals('eGenericSuperTypes') || it.name.equals('eFactoryInstance') || + it.name.equals('eGenericExceptions') || it.name.equals('references') || it.name.equals('contents'); + ]; + refsToRemove.forEach[model.removeReference(it)]; + } + return model.evaluateAllMetricsToSamples(); + } + +} + +class DataName{ + public static val REP_PATH = 'data/'; + public static val MPC_REP = 'mpc_rep'; + public static val NA_REP = 'na_rep'; + public static val OUT_D_REP = 'out_d_rep'; +} + diff --git a/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/metrics/EdgeTypeMetric.xtend b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/metrics/EdgeTypeMetric.xtend new file mode 100644 index 00000000..0c0fe3b8 --- /dev/null +++ b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/metrics/EdgeTypeMetric.xtend @@ -0,0 +1,41 @@ +package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics + +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.graph.GraphStatistic +import java.util.ArrayList +import java.util.HashMap + +class EdgeTypeMetric extends Metric { + + override evaluate(GraphStatistic g) { + var map = evaluateSamples(g) as HashMap; + var output = new ArrayList(); + output.add(newArrayList('Edge Type')); + + var keys = map.keySet; + var values = newArrayList(); + for(key : keys){ + values.add(map.get(key)+''); + } + + output.add(keys); + output.add(values); + + return output; + } + + override evaluateSamples(GraphStatistic g) { + val map = new HashMap(); + var outgoingEdges = g.outgoingEdges; + + //get the total number of edges + val edgeCount = outgoingEdges.values.fold(0, [r, t| r + t.asMap.values.fold(0, [r1, t1| r1 + t1.size])]) as double; + outgoingEdges.forEach[k, v| + var value = v.asMap.values.fold(0, [r, t| r + t.size]) / edgeCount; + map.put(k, value); + ] + + + return map; + } + +} \ No newline at end of file diff --git a/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/metrics/Metric.xtend b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/metrics/Metric.xtend new file mode 100644 index 00000000..cb242a5b --- /dev/null +++ b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/metrics/Metric.xtend @@ -0,0 +1,8 @@ +package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics + +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.graph.GraphStatistic + +abstract class Metric { + abstract def String[][] evaluate(GraphStatistic g); + abstract def Object evaluateSamples(GraphStatistic g); +} \ No newline at end of file diff --git a/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/metrics/MetricSampleGroup.xtend b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/metrics/MetricSampleGroup.xtend new file mode 100644 index 00000000..4e25bb86 --- /dev/null +++ b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/metrics/MetricSampleGroup.xtend @@ -0,0 +1,13 @@ +package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics + +import java.util.HashMap +import java.util.List + +class MetricSampleGroup{ + public var List mpcSamples; + public var List naSamples; + public var List outDegreeSamples; + public var HashMap> typedOutDegreeSamples; + public var HashMap nodeTypeSamples; + public var HashMap edgeTypeSamples; +} \ No newline at end of file diff --git a/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/metrics/MultiplexParticipationCoefficientMetric.xtend b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/metrics/MultiplexParticipationCoefficientMetric.xtend new file mode 100644 index 00000000..eafa49b2 --- /dev/null +++ b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/metrics/MultiplexParticipationCoefficientMetric.xtend @@ -0,0 +1,80 @@ +package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics + +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.graph.GraphStatistic +import java.text.DecimalFormat +import java.util.ArrayList +import java.util.HashMap +import org.eclipse.emf.ecore.EObject + +class MultiplexParticipationCoefficientMetric extends Metric { + public static val countName = "MPCCount"; + public static val valueName = "MPCValue"; + val formatter = new DecimalFormat("#0.00000"); + + override evaluate(GraphStatistic g) { + //because the precision issue of double, we translate double values into String to be the key + + + //get number of different types + val typeCounts = g.allTypes.size; + val map = new HashMap(); + //calculate the metric distribution + g.allNodes.forEach[n| + var coef = calculateMPC(n, g, typeCounts); + + //format number to String + val value = formatter.format(coef); + if(!map.containsKey(value)){ + map.put(value, 1); + }else{ + map.put(value, map.get(value) + 1); + } + + ] + + //convert it into a 2 dimentional array + val String[][] datas = newArrayOfSize(2, map.size+1); + datas.get(0).set(0, valueName); + datas.get(1).set(0, countName) + var count = 1; + for(entry : map.entrySet.sortBy[it.key]){ + datas.get(0).set(count, entry.key+""); + datas.get(1).set(count, entry.value+""); + count++; + } + + return datas; + } + + override evaluateSamples(GraphStatistic g){ + val samples = new ArrayList(); + val typeCounts = g.allTypes.size; + //calculate the metric distribution + g.allNodes.forEach[ + samples.add(calculateMPC(it, g, typeCounts)); + ] + + return samples; + } + + def double calculateMPC(EObject n, GraphStatistic g, int typeCounts){ + val edgeCounts = g.outDegree(n) + g.inDegree(n); + + var coef = 0.0; + + for(type : g.allTypes){ + val degree = g.dimentionalDegree(n, type) as double; + coef += Math.pow(degree / edgeCounts, 2); + } + coef = 1 - coef; + coef = coef * typeCounts / (typeCounts-1); + + //Consider the case that either typeCounts-1 or the edgeCounts could be 0 in some situation + //in this case the metric should be evaluated to 0 + if(Double.isNaN(coef)){ + coef = 0; + } + + return Double.parseDouble(formatter.format(coef)); + } +} \ No newline at end of file diff --git a/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/metrics/NodeActivityMetric.xtend b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/metrics/NodeActivityMetric.xtend new file mode 100644 index 00000000..fbf06c47 --- /dev/null +++ b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/metrics/NodeActivityMetric.xtend @@ -0,0 +1,49 @@ +package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics + +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.graph.GraphStatistic +import java.util.ArrayList +import java.util.HashMap + +class NodeActivityMetric extends Metric { + public static val countName = "NACount"; + public static val valueName = "NAValue"; + + override evaluate(GraphStatistic g) { + val map = new HashMap(); + + //calculate the metric distribution + g.allNodes.forEach[ + val value = g.numOfEdgeTypes(it); + if(!map.containsKey(value)){ + map.put(value, 1); + }else{ + map.put(value, map.get(value) + 1); + } + ] + + //convert it into a 2 dimentional array + val String[][] datas = newArrayOfSize(2, map.size+1); + datas.get(0).set(0, valueName); + datas.get(1).set(0, countName) + + var count = 1; + for(entry : map.entrySet.sortBy[it.key]){ + datas.get(0).set(count, entry.key+""); + datas.get(1).set(count, entry.value+""); + count++; + } + + return datas; + } + + override evaluateSamples(GraphStatistic g){ + val samples = new ArrayList(); + + //calculate the metric distribution + g.allNodes.forEach[ + samples.add(g.numOfEdgeTypes(it) as double); + ] + + return samples; + } +} \ No newline at end of file diff --git a/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/metrics/NodeTypeMetric.xtend b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/metrics/NodeTypeMetric.xtend new file mode 100644 index 00000000..7cec2513 --- /dev/null +++ b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/metrics/NodeTypeMetric.xtend @@ -0,0 +1,42 @@ +package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics + +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.graph.GraphStatistic +import java.util.ArrayList +import java.util.HashMap + +class NodeTypeMetric extends Metric { + + + override evaluate(GraphStatistic g) { + var map = evaluateSamples(g) as HashMap; + var output = new ArrayList(); + output.add(newArrayList('Node Type')); + var keys = map.keySet; + var values = newArrayList(); + + for(key : keys){ + values.add(map.get(key)+''); + } + + output.add(keys); + output.add(values); + + return output; + } + + override evaluateSamples(GraphStatistic g) { + var map = new HashMap(); + var nodes = g.allNodes; + var single = 1.0 / nodes.size(); + var nodeToType = g.nodeToTypesMap; + for(node : nodes){ + for(cl : nodeToType.get(node)){ + var value = map.getOrDefault(cl, 0.0); + map.put(cl, value + single); + } + } + + return map; + } + +} \ No newline at end of file diff --git a/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/metrics/OutDegreeMetric.xtend b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/metrics/OutDegreeMetric.xtend new file mode 100644 index 00000000..55046b14 --- /dev/null +++ b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/metrics/OutDegreeMetric.xtend @@ -0,0 +1,49 @@ +package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics + +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.graph.GraphStatistic +import java.util.ArrayList +import java.util.HashMap + +class OutDegreeMetric extends Metric { + public static val countName = "OutDegreeCount"; + public static val valueName = "OutDegreeValue"; + + override evaluate(GraphStatistic g) { + val map = new HashMap(); + + //calculate the metric distribution + g.allNodes.forEach[ + val value = g.outDegree(it); + if(!map.containsKey(value)){ + map.put(value, 1); + }else{ + map.put(value, map.get(value) + 1); + } + ] + + //convert it into a 2 dimentional array + val String[][] datas = newArrayOfSize(2, map.size+1); + datas.get(0).set(0, valueName); + datas.get(1).set(0, countName) + var count = 1; + for(entry : map.entrySet.sortBy[it.key]){ + datas.get(0).set(count, entry.key+""); + datas.get(1).set(count, entry.value+""); + count++; + } + + return datas; + } + + override evaluateSamples(GraphStatistic g){ + val samples = new ArrayList(); + + //calculate the metric distribution + g.allNodes.forEach[ + samples.add(g.outDegree(it) as double); + ] + + return samples; + } + +} \ No newline at end of file diff --git a/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/metrics/TypedClusteringCoefficientMetric.xtend b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/metrics/TypedClusteringCoefficientMetric.xtend new file mode 100644 index 00000000..0b6d61c6 --- /dev/null +++ b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/metrics/TypedClusteringCoefficientMetric.xtend @@ -0,0 +1,99 @@ +package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics + +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.graph.GraphStatistic +import java.text.DecimalFormat +import java.util.ArrayList +import java.util.HashMap +import java.util.HashSet +import org.eclipse.emf.ecore.EObject + +class TypedClusteringCoefficientMetric extends Metric { + public static val countName = "TCCCount"; + public static val valueName = "TCCValue"; + val formatter = new DecimalFormat("#0.00000"); + + override evaluate(GraphStatistic g) { + //because the precision issue of double, we translate double values into String to be the key + + val map = new HashMap(); + //calculate the metric distribution + g.allNodes.forEach[n| + var coef = calculateTCC1(n, g); + if(coef > 0){ + println(n); + } + //format number to String + val value = formatter.format(coef); + if(!map.containsKey(value)){ + map.put(value, 1); + }else{ + map.put(value, map.get(value) + 1); + } + + ] + + //convert it into a 2 dimentional array + val String[][] datas = newArrayOfSize(2, map.size+1); + datas.get(0).set(0, valueName); + datas.get(1).set(0, countName) + var count = 1; + for(entry : map.entrySet.sortBy[it.key]){ + datas.get(0).set(count, entry.key+""); + datas.get(1).set(count, entry.value+""); + count++; + } + + return datas; + } + + override evaluateSamples(GraphStatistic g){ + val samples = new ArrayList(); + //calculate the metric distribution + g.allNodes.forEach[ + samples.add(calculateTCC1(it, g)); + ] + + return samples; + } + + /** + * Compute TCC1 metric for node n + */ + def double calculateTCC1(EObject n, GraphStatistic g){ + var wedges = 0; + var triangles = 0; + + for(type1 : g.allTypes){ + val typed1RelatedOfN = new HashSet(g.outgoingEdges.get(type1).get(n)); + val type1EdgeSourceNodesOfN = new HashSet(g.incomingEdges.get(type1).get(n)); + + typed1RelatedOfN.addAll(type1EdgeSourceNodesOfN); + + + + // number of wedges + val d = typed1RelatedOfN.size + wedges += d * (d-1) // we will also count each closed triangle twice + + // pairs of neighbors + for (n1: typed1RelatedOfN) { + for (n2: typed1RelatedOfN) { + for(type2 : g.allTypes){ + if ((type1 != type2) && + (g.outgoingEdges.get(type2).containsEntry(n1, n2) || + g.outgoingEdges.get(type2).containsEntry(n2, n1) + )) { + triangles++ + } + } + } + } + } + + if (wedges == 0.0) { + return 0.0 + } else { + return (triangles as double)/wedges + } + } +} \ No newline at end of file diff --git a/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/metrics/TypedOutDegree.xtend b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/metrics/TypedOutDegree.xtend new file mode 100644 index 00000000..3b5dbcc5 --- /dev/null +++ b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/metrics/TypedOutDegree.xtend @@ -0,0 +1,60 @@ +package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics + +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.graph.GraphStatistic +import java.util.ArrayList +import java.util.HashMap +import java.util.List + +class TypedOutDegree extends Metric{ + + def private calculateMetric(GraphStatistic g){ + var outgoingEdges = g.outgoingEdges; + //record metric as a list of samples for each node type / edge type pair + var metric = new HashMap>(); + var nodeToTypes = g.nodeToTypesMap; + + for(edgeType : outgoingEdges.keySet){ + for(node : outgoingEdges.get(edgeType).keySet){ + //find all classes the node belongs to + var classes = nodeToTypes.get(node); + for(cl : classes){ + // get or create entry for node type / edge type pair + var key = cl + ' ' + edgeType; + var typeCount = metric.get(key); + if(typeCount === null){ + typeCount = new ArrayList(); + metric.put(key, typeCount); + } + + // get or create sample list + typeCount.add(outgoingEdges.get(edgeType).get(node).size); + } + } + } + + return metric; + } + + override evaluate(GraphStatistic g) { + var metric = calculateMetric(g); + var output = new ArrayList(); + + output.add(newArrayList('Typed Out Degree')); + for(key : metric.keySet){ + var samples = metric.get(key); + var String[] outputForOnePair = newArrayOfSize(samples.size+1); + outputForOnePair.set(0, key); + for(var i = 0; i < samples.size; i++){ + outputForOnePair.set(i+1, samples.get(i)+''); + } + output.add(outputForOnePair); + } + + return output; + } + + override evaluateSamples(GraphStatistic g) { + return calculateMetric(g); + } + +} \ No newline at end of file diff --git a/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/predictor/LinearModel.xtend b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/predictor/LinearModel.xtend new file mode 100644 index 00000000..f0ded347 --- /dev/null +++ b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/predictor/LinearModel.xtend @@ -0,0 +1,91 @@ +package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.predictor + +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.distance.StateData +import java.util.ArrayList +import java.util.HashMap +import java.util.List +import java.util.Map +import weka.core.matrix.LinearRegression +import weka.core.matrix.Matrix + +class LinearModel { + var double ridge; + var Map stateAndHistory; + List samples; + + new(double ridge){ + this.ridge = ridge; + stateAndHistory = new HashMap(); + samples = new ArrayList(); + } + + /** + * reset the current train data for regression to a new trajectory + * @param state: the last state of the trajectory + */ + def resetRegression(Object state){ + samples.clear(); + + if(stateAndHistory.containsKey(state)){ + var data = stateAndHistory.get(state); + var curState = state; + + samples.add(data); + + //loop through data until the oldest state in the record + while(stateAndHistory.containsKey(data.lastState) && data.lastState != curState){ + curState = data.lastState; + data = stateAndHistory.get(data.lastState); + samples.add(data); + } + } + } + + /** + * Add a new data point to the current training set + * @param state: the state on which the new data point is calculated + * @param features: the set of feature value(x) + * @param value: the value of the state (y) + * @param lastState: the state which transformed to current state, used to record the trajectory + */ + def feedData(Object state, double[] features, double value, Object lastState){ + var data = new StateData(features, value, lastState); + stateAndHistory.put(state, data); + samples.add(data); + } + + /** + * get prediction for next state, without storing the data point into the training set + * @param features: the feature values of current state + * @param value: the value of the current state + * @param: featuresToPredict: the features of the state wanted to be predected + * @return the value of the state to be predicted + */ + def double getPredictionForNextDataSample(double[] features, double value, double[] featuresToPredict){ + var data = new StateData(features, value, null); + samples.add(data); + + // create training set from current data + val double[][] xSamples = samples.map[it.features]; + val double[] ySamples = samples.map[it.value]; + + val x = new Matrix(xSamples); + val y = new Matrix(ySamples, ySamples.size()); + + val regression = new LinearRegression(x, y, ridge); + var prediction = predict(regression.coefficients, featuresToPredict); + + //remove the last element just added + samples.remove(samples.size - 1); + return prediction; + } + + def private predict(double[] parameters, double[] featuresToPredict){ + // the regression will add an initial column for 1's, the first parameter is constant term + var result = parameters.get(0); + for(var i = 0; i < featuresToPredict.length; i++){ + result += parameters.get(i) * featuresToPredict.get(i); + } + return result; + } +} \ No newline at end of file diff --git a/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/validation/ConstraintCollection.xtend b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/validation/ConstraintCollection.xtend new file mode 100644 index 00000000..685c5836 --- /dev/null +++ b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/validation/ConstraintCollection.xtend @@ -0,0 +1,80 @@ +package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.validation + +import java.util.ArrayList +import java.util.HashMap +import java.util.List +import java.util.Map +import org.eclipse.emf.common.notify.Notifier +import org.eclipse.emf.common.util.URI +import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl +import org.eclipse.viatra.addon.validation.core.api.IConstraintSpecification +import org.eclipse.viatra.query.runtime.api.ViatraQueryEngine +import org.eclipse.viatra.query.runtime.api.impl.BaseGeneratedPatternGroup +import org.eclipse.viatra.query.runtime.emf.EMFScope + +class ConstraintCollection{ + val constraints = new ArrayList(); + var BaseGeneratedPatternGroup patterns; + var List resources = new ArrayList(); + + + new(List constraints, List uris, BaseGeneratedPatternGroup patterns){ + this.constraints.addAll(constraints); + this.patterns = patterns; + setURIs(uris); + } + + new(List constraints, BaseGeneratedPatternGroup patterns){ + this.constraints.addAll(constraints); + this.patterns = patterns; + } + + def addModel(Notifier n ){ + resources.add(n); + } + + def setURIs(List uris){ + val resSet = new ResourceSetImpl(); + + for(uri : uris){ + var resource = resSet.getResource(URI.createURI(uri), true); + resources.add(resource); + } + + println('reading model finished') + } + + def List calculateViolations(){ + var results = new ArrayList(); + + for(resource : resources){ + val engine = initEngine(resource); + var matches = constraints.stream.mapToInt([ele| ele.querySpecification.getMatcher(engine).countMatches]).sum(); + results.add(matches); + } + + return results; + } + + def ArrayList> calculateViolationMaps(){ + val result = new ArrayList>() + + for(resource : resources){ + val map = new HashMap(); + val engine = initEngine(resource); + constraints.forEach[ + var count = it.querySpecification.getMatcher(engine).countMatches; + map.put(it.querySpecification.simpleName, count); + ]; + result.add(map); + } + return result; + } + + private def initEngine(Notifier r){ + var engine = ViatraQueryEngine.on(new EMFScope(r)); + //init patterns with the new engine + patterns.prepare(engine); + return engine; + } +} diff --git a/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/validation/ViolationCheck.xtend b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/validation/ViolationCheck.xtend new file mode 100644 index 00000000..4db5f940 --- /dev/null +++ b/Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca/mcgill/ecse/dslreasoner/realistic/metrics/calculator/validation/ViolationCheck.xtend @@ -0,0 +1,66 @@ +package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.validation + +import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.app.Domain +import com.google.common.reflect.ClassPath +import ecore.Ecore +import hu.bme.mit.inf.dslreasoner.partialsnapshot_mavo.yakindu.Patterns +import java.util.ArrayList +import org.eclipse.emf.ecore.EObject +import org.eclipse.viatra.addon.validation.core.api.IConstraintSpecification + +class ViolationCheck { + /** + * Return the total number of violations + */ + def static int calculateViolationCounts(EObject root, Domain d){ + var packageName = ''; + if(d == Domain.Yakindumm){ + packageName = 'constraints.yakindumm'; + }else if (d == Domain.Ecore){ + packageName = 'constraints.ecore'; + }else if (d == Domain.Github){ + return -1; + } + + var constriants = loadConstraints(packageName); + var collections = new ConstraintCollection(constriants, Ecore.instance); + collections.addModel(root); + var results = collections.calculateViolations(); + if(results.size > 0){ + return results.get(0); + }else{ + throw new IllegalArgumentException("Calculate Violation Failed"); + } + } + + /** + * return a map contain the count for each type of violation + */ + def static violationMaps(EObject root){ + var constriants = loadConstraints('hu.bme.mit.inf.dslreasoner.partialsnapshot_mavo.yakindu'); + var collections = new ConstraintCollection(constriants, Patterns.instance); + collections.addModel(root); + var results = collections.calculateViolationMaps(); + if(results.size > 0){ + return results.get(0); + }else{ + throw new IllegalArgumentException("Calculate Violation Failed"); + } + } + + def static loadConstraints(String packageName){ + val constraints = new ArrayList(); + + val classPath = ClassPath.from(ClassLoader.systemClassLoader); + val classInfos = classPath.getTopLevelClasses(packageName); + for(info : classInfos){ + if(info.load.interfaces.contains(IConstraintSpecification)){ + //IConstraintSpecification only has one constructor with empty argument list + var constructor = info.load.constructors.get(0); + var instance = constructor.newInstance(); + constraints.add(instance as IConstraintSpecification); + } + } + return constraints + } +} -- cgit v1.2.3-54-g00ecf