diff options
Diffstat (limited to 'Metrics/Metrics-Calculation/ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator/src/ca')
27 files changed, 2169 insertions, 0 deletions
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 @@ | |||
1 | package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.app | ||
2 | |||
3 | enum Domain{ | ||
4 | Yakindumm, | ||
5 | Ecore, | ||
6 | Github | ||
7 | } \ 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 @@ | |||
1 | package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.app | ||
2 | |||
3 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.graph.EMFGraph | ||
4 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.io.CsvFileWriter | ||
5 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.io.GraphReader | ||
6 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.validation.ViolationCheck | ||
7 | import hu.bme.mit.inf.dslreasoner.domains.yakindu.sgraph.yakindumm.impl.YakindummPackageImpl | ||
8 | import java.io.File | ||
9 | import java.util.ArrayList | ||
10 | import org.eclipse.emf.ecore.EPackage | ||
11 | import org.eclipse.emf.ecore.EcorePackage | ||
12 | import org.eclipse.viatra.query.runtime.rete.matcher.ReteEngine | ||
13 | import org.eclipse.emf.ecore.impl.EcorePackageImpl | ||
14 | import org.eclipse.emf.ecore.EReference | ||
15 | |||
16 | //import yakindumm2.impl.Yakindumm2PackageImpl | ||
17 | |||
18 | class Main { | ||
19 | var static Domain d = Domain.Yakindumm; | ||
20 | val static String suffix = '.xmi' | ||
21 | val static String OUTPUT_FOLDER = "Inputs/measurement2/yakindu/Alloy/"; | ||
22 | val static String INPUT_FOLDER = "outputs/measurement2/yakindu/Alloy/"; | ||
23 | val static int NUM_RUNS = 1; | ||
24 | |||
25 | static class RWInformation{ | ||
26 | public var String inputFolder; | ||
27 | public var String outputFolder; | ||
28 | public var int numRuns; | ||
29 | |||
30 | new(String inputFolder, String outputFolder, int numRuns){ | ||
31 | this.inputFolder = inputFolder; | ||
32 | this.outputFolder = outputFolder; | ||
33 | this.numRuns = numRuns; | ||
34 | } | ||
35 | } | ||
36 | |||
37 | def static void main(String[] args){ | ||
38 | //init model | ||
39 | var EPackage metamodel; | ||
40 | //init viatra engine for the violation checker | ||
41 | ReteEngine.getClass(); | ||
42 | |||
43 | if(d == Domain.Yakindumm){ | ||
44 | YakindummPackageImpl.eINSTANCE.eClass; | ||
45 | metamodel = YakindummPackageImpl.eINSTANCE; | ||
46 | }else if (d == Domain.Ecore){ | ||
47 | EcorePackage.eINSTANCE.eClass; | ||
48 | metamodel = EcorePackageImpl.eINSTANCE; | ||
49 | }else if (d == Domain.Github){ | ||
50 | //TODO: Initialize Github Package | ||
51 | } | ||
52 | |||
53 | |||
54 | println("Start Reading Models..."); | ||
55 | var reader = new GraphReader(metamodel, suffix); | ||
56 | |||
57 | val models = new RWInformation(OUTPUT_FOLDER, INPUT_FOLDER, NUM_RUNS); | ||
58 | calculateAllModels(models.inputFolder, models.outputFolder,models.numRuns, reader); | ||
59 | println("finished"); | ||
60 | } | ||
61 | |||
62 | static def calculateAllModels(String inputFolder, String outputFolder, int numRuns, GraphReader reader){ | ||
63 | (new File(outputFolder)).mkdir(); | ||
64 | for(var i = 1; i <= numRuns; i++){ | ||
65 | val models = new ArrayList<EMFGraph>(); | ||
66 | models.addAll(reader.readModels(inputFolder + "run" + i)); | ||
67 | for(model : models){ | ||
68 | calculateAndOutputMetrics(model, YakindummPackageImpl.eNAME, outputFolder+model.name+"_run_"+i+".csv"); | ||
69 | } | ||
70 | } | ||
71 | println("output results Ended for: " + outputFolder); | ||
72 | |||
73 | |||
74 | } | ||
75 | |||
76 | static def calculateAndOutputMetrics(EMFGraph model, String metaModel, String fileName){ | ||
77 | //println("evaluating for " + model.name); | ||
78 | model.metaModel = metaModel; | ||
79 | |||
80 | //remove eGenericType for Ecore domain | ||
81 | if(d == Domain.Ecore){ | ||
82 | var refsToRemove = EcorePackageImpl.eINSTANCE.eAllContents.filter(EReference).filter[ | ||
83 | it.name.equals('eGenericType') || it.name.equals('eGenericSuperTypes') || it.name.equals('eFactoryInstance')|| | ||
84 | it.name.equals('eGenericExceptions') || it.name.equals('references') || it.name.equals('contents'); | ||
85 | ]; | ||
86 | refsToRemove.forEach[model.removeReference(it)]; | ||
87 | } | ||
88 | |||
89 | var outputs = model.evaluateAllMetrics(); | ||
90 | var violations = ViolationCheck.calculateViolationCounts(model.root, d); | ||
91 | println(violations); | ||
92 | var violationsOutput = newArrayList('violations', violations+''); | ||
93 | outputs.add(violationsOutput); | ||
94 | CsvFileWriter.write(outputs, fileName); | ||
95 | } | ||
96 | } \ 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 @@ | |||
1 | package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.app | ||
2 | |||
3 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.distance.JSDistance | ||
4 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.distance.KSDistance | ||
5 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.graph.PartialInterpretationGraph | ||
6 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.io.CsvFileWriter | ||
7 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.Metric | ||
8 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.MultiplexParticipationCoefficientMetric | ||
9 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.NodeActivityMetric | ||
10 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.OutDegreeMetric | ||
11 | import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.partialinterpretation.PartialInterpretation | ||
12 | import java.io.File | ||
13 | import java.io.FileNotFoundException | ||
14 | import java.io.PrintWriter | ||
15 | import java.util.ArrayList | ||
16 | import java.util.List | ||
17 | import org.eclipse.emf.ecore.util.EcoreUtil | ||
18 | import org.eclipse.viatra.dse.api.Solution | ||
19 | |||
20 | class PartialInterpretationMetric { | ||
21 | var static KSDistance ks; | ||
22 | var static JSDistance js; | ||
23 | |||
24 | def static void initPaths(){ | ||
25 | new File("debug/metric/").mkdir(); | ||
26 | new File("debug/metric/trajectories/").mkdir(); | ||
27 | } | ||
28 | |||
29 | // calculate the metrics for a state | ||
30 | def static void calculateMetric(PartialInterpretation partial, String path, String currentStateId, Integer counter){ | ||
31 | val metrics = new ArrayList<Metric>(); | ||
32 | metrics.add(new OutDegreeMetric()); | ||
33 | metrics.add(new NodeActivityMetric()); | ||
34 | metrics.add(new MultiplexParticipationCoefficientMetric()); | ||
35 | |||
36 | //make dir since the folder can be none existing | ||
37 | new File(path).mkdir(); | ||
38 | val filename = path + "/state_"+currentStateId+"-"+counter+".csv"; | ||
39 | val metricCalculator = new PartialInterpretationGraph(partial, metrics, currentStateId); | ||
40 | |||
41 | CsvFileWriter.write(metricCalculator.evaluateAllMetrics(), filename); | ||
42 | } | ||
43 | |||
44 | def static void outputTrajectories(PartialInterpretation empty, List<Solution> solutions){ | ||
45 | for(solution : solutions){ | ||
46 | |||
47 | //need to copy the empty solution because the transition directly worked on the graph | ||
48 | val emptySolutionCopy = EcoreUtil.copy(empty) | ||
49 | val trajectory = solution.shortestTrajectory; | ||
50 | trajectory.model = emptySolutionCopy | ||
51 | |||
52 | // state codes that will record the trajectory | ||
53 | val stateCodes = newArrayList() | ||
54 | var counter = 0 | ||
55 | |||
56 | //transform and record the state codes for each state | ||
57 | while(trajectory.doNextTransformation){ | ||
58 | //println(trajectory.stateCoder.createStateCode) | ||
59 | val stateId = trajectory.stateCoder.createStateCode.toString | ||
60 | val interpretation = trajectory.getModel(); | ||
61 | println(stateId) | ||
62 | //calculate metrics of current state | ||
63 | calculateMetric(interpretation as PartialInterpretation, "debug/metric/output", stateId, counter) | ||
64 | stateCodes.add(stateId) | ||
65 | counter++ | ||
66 | } | ||
67 | |||
68 | |||
69 | //output the trajectory | ||
70 | try{ | ||
71 | new File("debug/metric/trajectories/").mkdir(); | ||
72 | val path = "debug/metric/trajectories/trajectory"+trajectory.stateCoder.createStateCode.toString+".csv" | ||
73 | val PrintWriter writer = new PrintWriter(new File(path)) | ||
74 | val output = new StringBuilder | ||
75 | for(stateCode : stateCodes){ | ||
76 | output.append(stateCode+'\n') | ||
77 | } | ||
78 | writer.write(output.toString()) | ||
79 | writer.close() | ||
80 | }catch(FileNotFoundException e) { | ||
81 | e.printStackTrace() | ||
82 | } | ||
83 | } | ||
84 | } | ||
85 | } \ 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 @@ | |||
1 | package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.app | ||
2 | |||
3 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.distance.EuclideanDistance | ||
4 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.distance.JSDistance | ||
5 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.distance.KSDistance | ||
6 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.distance.StateData | ||
7 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.graph.PartialInterpretationGraph | ||
8 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.io.RepMetricsReader | ||
9 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.Metric | ||
10 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.MetricSampleGroup | ||
11 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.MultiplexParticipationCoefficientMetric | ||
12 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.NodeActivityMetric | ||
13 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.NodeTypeMetric | ||
14 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.OutDegreeMetric | ||
15 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.predictor.LinearModel | ||
16 | import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.partialinterpretation.PartialInterpretation | ||
17 | import java.util.ArrayList | ||
18 | import java.util.HashMap | ||
19 | import java.util.List | ||
20 | import java.util.Map | ||
21 | import org.apache.commons.math3.stat.regression.OLSMultipleLinearRegression | ||
22 | import org.eclipse.xtend.lib.annotations.Accessors | ||
23 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.EdgeTypeMetric | ||
24 | |||
25 | class PartialInterpretationMetricDistance { | ||
26 | |||
27 | var KSDistance ks; | ||
28 | var JSDistance js; | ||
29 | var EuclideanDistance ed; | ||
30 | var Map<Object, StateData> stateAndHistory; | ||
31 | var OLSMultipleLinearRegression regression; | ||
32 | List<StateData> samples; | ||
33 | var MetricSampleGroup g; | ||
34 | @Accessors(PUBLIC_GETTER) | ||
35 | var LinearModel linearModel; | ||
36 | |||
37 | |||
38 | new(Domain d){ | ||
39 | var metrics = RepMetricsReader.read(d); | ||
40 | this.g = metrics; | ||
41 | ks = new KSDistance(g); | ||
42 | js = new JSDistance(g); | ||
43 | ed = new EuclideanDistance(g); | ||
44 | regression = new OLSMultipleLinearRegression(); | ||
45 | regression.noIntercept = false; | ||
46 | stateAndHistory = new HashMap<Object, StateData>(); | ||
47 | samples = new ArrayList<StateData>(); | ||
48 | linearModel = new LinearModel(0.01); | ||
49 | } | ||
50 | |||
51 | def MetricDistanceGroup calculateMetricDistanceKS(PartialInterpretation partial){ | ||
52 | val metrics = new ArrayList<Metric>(); | ||
53 | metrics.add(new OutDegreeMetric()); | ||
54 | metrics.add(new NodeActivityMetric()); | ||
55 | metrics.add(new MultiplexParticipationCoefficientMetric()); | ||
56 | metrics.add(new NodeTypeMetric()); | ||
57 | val metricCalculator = new PartialInterpretationGraph(partial, metrics, null); | ||
58 | var metricSamples = metricCalculator.evaluateAllMetricsToSamples(); | ||
59 | |||
60 | var mpc = ks.mpcDistance(metricSamples.mpcSamples); | ||
61 | var na = ks.naDistance(metricSamples.naSamples); | ||
62 | var outDegree = ks.outDegreeDistance(metricSamples.outDegreeSamples); | ||
63 | var nodeType = ks.nodeTypeDistance(metricSamples.nodeTypeSamples); | ||
64 | //var typedOutDegree = ks.typedOutDegreeDistance(metricSamples.typedOutDegreeSamples); | ||
65 | var distance = new MetricDistanceGroup(mpc, na, outDegree, nodeType); | ||
66 | distance.nodeTypeInfo = metricSamples.nodeTypeSamples; | ||
67 | return distance; | ||
68 | } | ||
69 | |||
70 | def MetricDistanceGroup calculateMetricEuclidean(PartialInterpretation partial){ | ||
71 | val metrics = new ArrayList<Metric>(); | ||
72 | metrics.add(new OutDegreeMetric()); | ||
73 | metrics.add(new NodeActivityMetric()); | ||
74 | metrics.add(new MultiplexParticipationCoefficientMetric()); | ||
75 | |||
76 | val metricCalculator = new PartialInterpretationGraph(partial, metrics, null); | ||
77 | var metricSamples = metricCalculator.evaluateAllMetricsToSamples(); | ||
78 | |||
79 | var mpc = ed.mpcDistance(metricSamples.mpcSamples); | ||
80 | var na = ed.naDistance(metricSamples.naSamples); | ||
81 | var outDegree = ed.outDegreeDistance(metricSamples.outDegreeSamples); | ||
82 | |||
83 | return new MetricDistanceGroup(mpc, na, outDegree); | ||
84 | } | ||
85 | |||
86 | def MetricDistanceGroup calculateMetricDistance(PartialInterpretation partial){ | ||
87 | val metrics = new ArrayList<Metric>(); | ||
88 | metrics.add(new OutDegreeMetric()); | ||
89 | metrics.add(new NodeActivityMetric()); | ||
90 | metrics.add(new MultiplexParticipationCoefficientMetric()); | ||
91 | |||
92 | val metricCalculator = new PartialInterpretationGraph(partial, metrics, null); | ||
93 | var metricSamples = metricCalculator.evaluateAllMetricsToSamples(); | ||
94 | |||
95 | var mpc = js.mpcDistance(metricSamples.mpcSamples); | ||
96 | var na = js.naDistance(metricSamples.naSamples); | ||
97 | var outDegree = js.outDegreeDistance(metricSamples.outDegreeSamples); | ||
98 | |||
99 | return new MetricDistanceGroup(mpc, na, outDegree); | ||
100 | } | ||
101 | |||
102 | def resetRegression(Object state){ | ||
103 | samples.clear(); | ||
104 | |||
105 | if(stateAndHistory.containsKey(state)){ | ||
106 | var data = stateAndHistory.get(state); | ||
107 | |||
108 | var curState = state; | ||
109 | |||
110 | samples.add(data); | ||
111 | |||
112 | while(stateAndHistory.containsKey(data.lastState) && data.lastState != curState){ | ||
113 | curState = data.lastState; | ||
114 | data = stateAndHistory.get(data.lastState); | ||
115 | samples.add(data); | ||
116 | } | ||
117 | |||
118 | if(samples.size == 0){ | ||
119 | println('state: ' + state); | ||
120 | println('last state: ' + data.lastState); | ||
121 | } | ||
122 | } | ||
123 | println("trajectory sample size:" + samples.size) | ||
124 | } | ||
125 | |||
126 | def feedData(Object state, double[] features, double value, Object lastState){ | ||
127 | var data = new StateData(features, value, lastState); | ||
128 | stateAndHistory.put(state, data); | ||
129 | samples.add(data); | ||
130 | } | ||
131 | |||
132 | def getPredictionForNextDataSample(double[] features, double value, double[] featuresToPredict){ | ||
133 | if(samples.size <= 4){ | ||
134 | println('OK'); | ||
135 | } | ||
136 | var data = new StateData(features, value, null); | ||
137 | samples.add(data); | ||
138 | |||
139 | // create training set from current data | ||
140 | var double[][] xSamples = samples.map[it.features]; | ||
141 | var double[] ySamples = samples.map[it.value]; | ||
142 | |||
143 | |||
144 | regression.newSampleData(ySamples, xSamples); | ||
145 | var prediction = predict(featuresToPredict); | ||
146 | |||
147 | //remove the last element just added | ||
148 | samples.remove(samples.size - 1); | ||
149 | return prediction; | ||
150 | } | ||
151 | |||
152 | def private predict(double[] featuresToPredict){ | ||
153 | var parameters = regression.estimateRegressionParameters(); | ||
154 | // the regression will add an initial column for 1's, the first parameter is constant term | ||
155 | var result = parameters.get(0); | ||
156 | for(var i = 0; i < featuresToPredict.length; i++){ | ||
157 | result += parameters.get(i+1) * featuresToPredict.get(i); | ||
158 | } | ||
159 | return result; | ||
160 | } | ||
161 | |||
162 | def double[] calculateFeature(int step, int violations){ | ||
163 | var features = newDoubleArrayOfSize(2); | ||
164 | //constant term | ||
165 | features.set(0, 1); //a | ||
166 | features.set(0, Math.sqrt(step) + 30) // b | ||
167 | features.set(1, 1.0 / (step + 30) );// c | ||
168 | |||
169 | |||
170 | // features.set(2, violations); | ||
171 | // features.set(3, Math.pow(violations, 2)); | ||
172 | |||
173 | return features; | ||
174 | } | ||
175 | } | ||
176 | |||
177 | class MetricDistanceGroup{ | ||
178 | var double mpcDistance; | ||
179 | var double naDistance; | ||
180 | var double outDegreeDistance; | ||
181 | var double nodeTypeDistance; | ||
182 | protected var HashMap<String, Double> nodeTypeInfo; | ||
183 | |||
184 | new(double mpcDistance, double naDistance, double outDegreeDistance, double nodeTypeDistance){ | ||
185 | this.mpcDistance = mpcDistance; | ||
186 | this.naDistance = naDistance; | ||
187 | this.outDegreeDistance = outDegreeDistance; | ||
188 | this.nodeTypeDistance = nodeTypeDistance; | ||
189 | } | ||
190 | |||
191 | new(double mpcDistance, double naDistance, double outDegreeDistance){ | ||
192 | this.mpcDistance = mpcDistance; | ||
193 | this.naDistance = naDistance; | ||
194 | this.outDegreeDistance = outDegreeDistance; | ||
195 | } | ||
196 | |||
197 | def double getNodeTypeDistance(){ | ||
198 | return this.nodeTypeDistance; | ||
199 | } | ||
200 | |||
201 | def double getMPCDistance(){ | ||
202 | return this.mpcDistance | ||
203 | } | ||
204 | |||
205 | def double getNADistance(){ | ||
206 | return this.naDistance | ||
207 | } | ||
208 | |||
209 | def double getOutDegreeDistance(){ | ||
210 | return this.outDegreeDistance | ||
211 | } | ||
212 | |||
213 | def double getNodeTypePercentage(String typeName){ | ||
214 | return nodeTypeInfo.getOrDefault(typeName, 0.0); | ||
215 | } | ||
216 | } \ 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 @@ | |||
1 | package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.distance | ||
2 | |||
3 | import java.text.DecimalFormat | ||
4 | import java.util.HashMap | ||
5 | import java.util.List | ||
6 | import org.eclipse.xtend.lib.annotations.Accessors | ||
7 | |||
8 | abstract class CostDistance { | ||
9 | def abstract double naDistance(List<Double> samples); | ||
10 | def abstract double mpcDistance(List<Double> samples); | ||
11 | def abstract double outDegreeDistance(List<Double> samples); | ||
12 | |||
13 | def protected pmfFromSamples(double[] samples, DecimalFormat formatter){ | ||
14 | var length = samples.length; | ||
15 | var pmfMap = new HashMap<String, Double>(); | ||
16 | |||
17 | for(sample : samples){ | ||
18 | pmfMap.put(formatter.format(sample), pmfMap.getOrDefault(formatter.format(sample), 0.0) + 1.0 / length); | ||
19 | } | ||
20 | |||
21 | return pmfMap; | ||
22 | } | ||
23 | } | ||
24 | |||
25 | class StateData{ | ||
26 | @Accessors(PUBLIC_GETTER) | ||
27 | var double[] features; | ||
28 | @Accessors(PUBLIC_GETTER) | ||
29 | var double value; | ||
30 | @Accessors(PUBLIC_GETTER) | ||
31 | var Object lastState; | ||
32 | |||
33 | new(double[] features, double value, Object lastState){ | ||
34 | this.features = features; | ||
35 | this.value = value | ||
36 | this.lastState = lastState; | ||
37 | } | ||
38 | } \ 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 @@ | |||
1 | package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.distance | ||
2 | |||
3 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.app.Domain | ||
4 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.io.RepMetricsReader | ||
5 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.MetricSampleGroup | ||
6 | import com.google.common.collect.Sets | ||
7 | import java.text.DecimalFormat | ||
8 | import java.util.ArrayList | ||
9 | import java.util.HashMap | ||
10 | import java.util.List | ||
11 | import java.util.Map | ||
12 | import java.util.Set | ||
13 | |||
14 | class EuclideanDistance extends CostDistance{ | ||
15 | var MetricSampleGroup g; | ||
16 | var HashMap<String, Double> mpcPMF; | ||
17 | var HashMap<String, Double> naPMF; | ||
18 | var HashMap<String, Double> outDegreePMF; | ||
19 | var DecimalFormat formatter; | ||
20 | |||
21 | new(MetricSampleGroup g){ | ||
22 | this.g = g; | ||
23 | |||
24 | var mpcSamples = g.mpcSamples; | ||
25 | var naSamples = g.naSamples.stream.mapToDouble([it]).toArray(); | ||
26 | var outDegreeSamples = g.outDegreeSamples.stream.mapToDouble([it]).toArray(); | ||
27 | |||
28 | //needs to format the number to string avoid precision issue | ||
29 | formatter = new DecimalFormat("#0.00000"); | ||
30 | |||
31 | mpcPMF = pmfFromSamples(mpcSamples, formatter); | ||
32 | naPMF = pmfFromSamples(naSamples, formatter); | ||
33 | outDegreePMF = pmfFromSamples(outDegreeSamples, formatter); | ||
34 | } | ||
35 | |||
36 | override naDistance(List<Double> samples) { | ||
37 | var pmfMap = pmfFromSamples(samples, formatter); | ||
38 | return euclideanDistance(pmfMap, naPMF); | ||
39 | } | ||
40 | |||
41 | override mpcDistance(List<Double> samples) { | ||
42 | var pmfMap = pmfFromSamples(samples, formatter); | ||
43 | return euclideanDistance(pmfMap, mpcPMF); | ||
44 | } | ||
45 | |||
46 | override outDegreeDistance(List<Double> samples) { | ||
47 | var pmfMap = pmfFromSamples(samples, formatter); | ||
48 | return euclideanDistance(pmfMap, outDegreePMF); | ||
49 | } | ||
50 | |||
51 | |||
52 | def private euclideanDistance(HashMap<String, Double> pmf1, HashMap<String, Double> pmf2){ | ||
53 | var keys = Sets.union(pmf1.keySet(), pmf2.keySet()); | ||
54 | var pmfList1 = pmfMapToList(pmf1, keys); | ||
55 | var pmfList2 = pmfMapToList(pmf2, keys); | ||
56 | var distance = 0.0; | ||
57 | for(var i = 0; i < pmfList1.size(); i++){ | ||
58 | distance += Math.pow(pmfList1.get(i) + pmfList2.get(i), 2); | ||
59 | } | ||
60 | |||
61 | return Math.sqrt(distance); | ||
62 | } | ||
63 | |||
64 | def private pmfMapToList(Map<String, Double> map, Set<String> keys){ | ||
65 | var list = new ArrayList<Double>(); | ||
66 | for(key : keys){ | ||
67 | var value = map.getOrDefault(key, 0.0); | ||
68 | list.add(value); | ||
69 | } | ||
70 | return list; | ||
71 | } | ||
72 | } \ 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 @@ | |||
1 | package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.distance | ||
2 | |||
3 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.MetricSampleGroup | ||
4 | import com.google.common.collect.Sets | ||
5 | import java.text.DecimalFormat | ||
6 | import java.util.HashMap | ||
7 | import java.util.List | ||
8 | |||
9 | class JSDistance extends CostDistance { | ||
10 | var HashMap<String, Double> mpcPMF; | ||
11 | var HashMap<String, Double> naPMF; | ||
12 | var HashMap<String, Double> outDegreePMF; | ||
13 | var HashMap<String, Double> nodeTypesPMF; | ||
14 | var DecimalFormat formatter; | ||
15 | |||
16 | new(MetricSampleGroup g){ | ||
17 | var mpcSamples = g.mpcSamples; | ||
18 | var naSamples = g.naSamples.stream.mapToDouble([it]).toArray(); | ||
19 | var outDegreeSamples = g.outDegreeSamples.stream.mapToDouble([it]).toArray(); | ||
20 | |||
21 | //needs to format the number to string avoid precision issue | ||
22 | formatter = new DecimalFormat("#0.00000"); | ||
23 | |||
24 | mpcPMF = pmfFromSamples(mpcSamples, formatter); | ||
25 | naPMF = pmfFromSamples(naSamples, formatter); | ||
26 | outDegreePMF = pmfFromSamples(outDegreeSamples, formatter); | ||
27 | nodeTypesPMF = g.nodeTypeSamples; | ||
28 | } | ||
29 | |||
30 | def private combinePMF(HashMap<String, Double> pmf1, HashMap<String, Double> pmf2){ | ||
31 | var pmfMap = new HashMap<String, Double>(); | ||
32 | |||
33 | var union = Sets.union(pmf1.keySet(), pmf2.keySet()); | ||
34 | |||
35 | for(key : union){ | ||
36 | // corresponding to M in JS distance | ||
37 | var value = 1.0/2 * (pmf1.getOrDefault(key, 0.0) + pmf2.getOrDefault(key, 0.0)); | ||
38 | pmfMap.put(key, value); | ||
39 | } | ||
40 | return pmfMap; | ||
41 | } | ||
42 | |||
43 | def private jsDivergence(HashMap<String, Double> p, HashMap<String, Double> q){ | ||
44 | val m = combinePMF(q, p); | ||
45 | var distance = 1.0/2 * klDivergence(p, m) + 1.0/2 * klDivergence(q, m); | ||
46 | return distance; | ||
47 | } | ||
48 | |||
49 | def klDivergence(HashMap<String, Double> p, HashMap<String, Double> q){ | ||
50 | var distance = 0.0; | ||
51 | for(key : q.keySet()){ | ||
52 | //need to convert log e to log 2 | ||
53 | if(p.containsKey(key)){ | ||
54 | distance -= p.get(key) * Math.log(q.get(key) / p.get(key)) / Math.log(2); | ||
55 | } | ||
56 | } | ||
57 | return distance; | ||
58 | } | ||
59 | |||
60 | override double mpcDistance(List<Double> samples){ | ||
61 | // map list to array | ||
62 | var map = pmfFromSamples(samples.stream().mapToDouble([it]).toArray(), formatter); | ||
63 | //if the size of array is smaller than 2, ks distance cannot be performed, simply return 1 | ||
64 | if(map.size < 2) return 1; | ||
65 | return jsDivergence(map, mpcPMF); | ||
66 | } | ||
67 | |||
68 | override double naDistance(List<Double> samples){ | ||
69 | // map list to array | ||
70 | var map = pmfFromSamples(samples.stream().mapToDouble([it]).toArray(), formatter); | ||
71 | |||
72 | //if the size of array is smaller than 2, ks distance cannot be performed, simply return 1 | ||
73 | if(map.size < 2) return 1; | ||
74 | return jsDivergence(map, naPMF); | ||
75 | } | ||
76 | |||
77 | override double outDegreeDistance(List<Double> samples){ | ||
78 | // map list to array | ||
79 | var map = pmfFromSamples(samples.stream().mapToDouble([it]).toArray(), formatter); | ||
80 | //if the size of array is smaller than 2, ks distance cannot be performed, simply return 1 | ||
81 | if(map.size < 2) return 1; | ||
82 | return jsDivergence(map, outDegreePMF); | ||
83 | } | ||
84 | |||
85 | def nodeTypeDistance(HashMap<String, Double> samples){ | ||
86 | return klDivergence(samples, nodeTypesPMF); | ||
87 | } | ||
88 | } \ 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 @@ | |||
1 | package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.distance | ||
2 | |||
3 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.MetricSampleGroup | ||
4 | import java.util.HashMap | ||
5 | import java.util.HashSet | ||
6 | import java.util.List | ||
7 | import org.apache.commons.math3.stat.inference.KolmogorovSmirnovTest | ||
8 | |||
9 | class KSDistance extends CostDistance { | ||
10 | var static ksTester = new KolmogorovSmirnovTest(); | ||
11 | var MetricSampleGroup g; | ||
12 | |||
13 | new(MetricSampleGroup g){ | ||
14 | this.g = g; | ||
15 | } | ||
16 | override double mpcDistance(List<Double> samples){ | ||
17 | //if the size of array is smaller than 2, ks distance cannot be performed, simply return 1 | ||
18 | if(samples.size < 2) return 1; | ||
19 | return ksTester.kolmogorovSmirnovStatistic(g.mpcSamples, samples); | ||
20 | } | ||
21 | |||
22 | override double naDistance(List<Double> samples){ | ||
23 | //if the size of array is smaller than 2, ks distance cannot be performed, simply return 1 | ||
24 | if(samples.size < 2) return 1; | ||
25 | return ksTester.kolmogorovSmirnovStatistic(g.naSamples as double[], samples); | ||
26 | } | ||
27 | |||
28 | override double outDegreeDistance(List<Double> samples){ | ||
29 | //if the size of array is smaller than 2, ks distance cannot be performed, simply return 1 | ||
30 | if(samples.size < 2) return 1; | ||
31 | return ksTester.kolmogorovSmirnovStatistic(g.outDegreeSamples, samples); | ||
32 | } | ||
33 | |||
34 | def double typedOutDegreeDistance(HashMap<String, List<Integer>> map){ | ||
35 | var value = 0.0; | ||
36 | // map list to array | ||
37 | val keySet = new HashSet<String>(map.keySet); | ||
38 | keySet.addAll(g.typedOutDegreeSamples.keySet); | ||
39 | for(key : keySet){ | ||
40 | if(!map.containsKey(key) ){ | ||
41 | value += 1; | ||
42 | }else if(!g.typedOutDegreeSamples.containsKey(key)){ | ||
43 | value += map.get(key).size * 100; | ||
44 | }else{ | ||
45 | var double[] rep = g.typedOutDegreeSamples.get(key).stream().mapToDouble([it|it]).toArray(); | ||
46 | var double[] ins = map.get(key).stream().mapToDouble([it|it]).toArray(); | ||
47 | if((rep.size < 2 || ins.size < 2) ){ | ||
48 | if(rep.size < 2 && rep.containsAll(ins)){ | ||
49 | value += 0; | ||
50 | }else{ | ||
51 | value += 1; | ||
52 | } | ||
53 | }else if(rep.size >= 2 && ins.size >= 2){ | ||
54 | value += ksTester.kolmogorovSmirnovStatistic(rep, ins); | ||
55 | } | ||
56 | } | ||
57 | } | ||
58 | |||
59 | |||
60 | return value; | ||
61 | } | ||
62 | |||
63 | def nodeTypeDistance(HashMap<String, Double> samples){ | ||
64 | var typesDistMap = g.nodeTypeSamples; | ||
65 | var sourceDist = newArrayList(); | ||
66 | var instanceDist = newArrayList(); | ||
67 | |||
68 | for(key : typesDistMap.keySet()){ | ||
69 | sourceDist.add(typesDistMap.get(key)); | ||
70 | instanceDist.add(samples.getOrDefault(key, 0.0)); | ||
71 | } | ||
72 | |||
73 | return ks_distance_two_dist(sourceDist, instanceDist); | ||
74 | } | ||
75 | |||
76 | def edgeTypeDistance(HashMap<String, Double> samples){ | ||
77 | var typesDistMap = g.edgeTypeSamples; | ||
78 | var sourceDist = newArrayList(); | ||
79 | var instanceDist = newArrayList(); | ||
80 | |||
81 | for(key : typesDistMap.keySet()){ | ||
82 | sourceDist.add(typesDistMap.get(key)); | ||
83 | instanceDist.add(samples.getOrDefault(key, 0.0)); | ||
84 | } | ||
85 | |||
86 | return ks_distance_two_dist(sourceDist, instanceDist); | ||
87 | } | ||
88 | |||
89 | def double ks_distance_two_dist(List<Double> dist1, List<Double> dist2){ | ||
90 | // Since we already know the pdf, we compute the ks-test manully | ||
91 | var ksStatistics = 0.0; | ||
92 | var sum1 = 0.0; | ||
93 | var sum2 = 0.0; | ||
94 | for(var i = 0; i < dist1.size(); i++){ | ||
95 | sum1 += dist1.get(i); | ||
96 | sum2 += dist2.get(i); | ||
97 | |||
98 | ksStatistics = Math.max(ksStatistics, Math.abs(sum1 - sum2)); | ||
99 | } | ||
100 | return ksStatistics; | ||
101 | } | ||
102 | } \ 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 @@ | |||
1 | package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.graph | ||
2 | |||
3 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.Metric | ||
4 | import java.util.ArrayList | ||
5 | import java.util.HashSet | ||
6 | import java.util.List | ||
7 | import org.eclipse.emf.common.util.EList | ||
8 | import org.eclipse.emf.ecore.EObject | ||
9 | import org.eclipse.emf.ecore.EReference | ||
10 | import org.eclipse.xtend.lib.annotations.Accessors | ||
11 | |||
12 | class EMFGraph extends Graph{ | ||
13 | @Accessors(PUBLIC_GETTER) | ||
14 | var EObject root; | ||
15 | |||
16 | def void init (EObject root, List<Metric> metrics, String name, List<EReference> referenceTypes){ | ||
17 | val otherContents = root.eAllContents.toList(); | ||
18 | this.root = root; | ||
19 | otherContents.add(root); | ||
20 | init(otherContents, metrics, name, referenceTypes); | ||
21 | } | ||
22 | |||
23 | /** | ||
24 | * init the graph with all nodes and reference types in the meta model | ||
25 | * @param objects: objects in the instance model (exclude root) | ||
26 | * @param metrics: metrics to be evaluated | ||
27 | * @param name: name of the instance model | ||
28 | * @param ReferenceTypes: reference types defined in the meta model | ||
29 | */ | ||
30 | def void init(List<EObject> objects, List<Metric> metrics, String name, List<EReference> referenceTypes){ | ||
31 | objects.forEach[it| | ||
32 | // TODO: Maybe want to consider all the super types as well | ||
33 | var types = new HashSet(); | ||
34 | types.add(it.eClass.name); | ||
35 | statistic.addNodeWithAllTypes(it, types); | ||
36 | ] | ||
37 | |||
38 | referenceTypes.forEach[it| | ||
39 | // Only consider the edges that are not derived to preserve the statistical property | ||
40 | if(!it.derived){ | ||
41 | statistic.addEdgeType(it.name); | ||
42 | } | ||
43 | ]; | ||
44 | |||
45 | objects.forEach[source| | ||
46 | source.eClass.EAllReferences.forEach[r| | ||
47 | //many references | ||
48 | if(r.isMany){ | ||
49 | source.getNeighbours(r).forEach[target| | ||
50 | addEdge(source, target, r); | ||
51 | ] | ||
52 | }else{ | ||
53 | //single references | ||
54 | val target = source.eGet(r) as EObject; | ||
55 | addEdge(source, target, r); | ||
56 | } | ||
57 | ] | ||
58 | ] | ||
59 | |||
60 | this.metrics = metrics; | ||
61 | this.name = name; | ||
62 | } | ||
63 | |||
64 | def void removeReference(EReference r){ | ||
65 | if (statistic.containsEdgeType(r.name)){ | ||
66 | statistic.removeReference(r.name, r.containment); | ||
67 | } | ||
68 | } | ||
69 | |||
70 | /** | ||
71 | * Set basic information for the output | ||
72 | */ | ||
73 | override setBasicInformation(ArrayList<ArrayList<String>> output){ | ||
74 | val metaInfo = new ArrayList<String>(); | ||
75 | metaInfo.add(META_MODEL_HEADER); | ||
76 | metaInfo.add(this.metaModel); | ||
77 | |||
78 | val edgeInfo = new ArrayList<String>(); | ||
79 | edgeInfo.add(NUM_EDGE_TYPE_HEADER); | ||
80 | edgeInfo.add(this.statistic.allTypes.size()+""); | ||
81 | |||
82 | val nodeInfo = new ArrayList<String>(); | ||
83 | nodeInfo.add(NUM_NODE_HEADER); | ||
84 | nodeInfo.add(this.statistic.allNodes.size()+""); | ||
85 | |||
86 | val stateInfo = new ArrayList<String>(); | ||
87 | stateInfo.add(STATE_ID_HEADER); | ||
88 | stateInfo.add(this.name); | ||
89 | |||
90 | |||
91 | output.add(metaInfo); | ||
92 | output.add(edgeInfo); | ||
93 | output.add(nodeInfo); | ||
94 | output.add(stateInfo); | ||
95 | } | ||
96 | |||
97 | def EList<EObject> getNeighbours(EObject o, EReference r){ | ||
98 | return (o.eGet(r, true) as EList<EObject>); | ||
99 | } | ||
100 | |||
101 | def addEdge(EObject source, EObject target, EReference r){ | ||
102 | //Only add the edge if the reference is not derived to preserve the statistical property | ||
103 | if(target !== null && r !== null && !r.derived){ | ||
104 | statistic.addEdge(source, target, r.name); | ||
105 | } | ||
106 | } | ||
107 | |||
108 | override GraphStatistic getStatistic(){ | ||
109 | return this.statistic; | ||
110 | } | ||
111 | |||
112 | override String getName(){ | ||
113 | return this.name; | ||
114 | } | ||
115 | |||
116 | def void setMetaModel(String model){ | ||
117 | this.metaModel = model; | ||
118 | } | ||
119 | |||
120 | def String getMetaModel(){ | ||
121 | return this.metaModel; | ||
122 | } | ||
123 | |||
124 | } \ 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 @@ | |||
1 | package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.graph | ||
2 | |||
3 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.Metric | ||
4 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.MetricSampleGroup | ||
5 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.MultiplexParticipationCoefficientMetric | ||
6 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.NodeActivityMetric | ||
7 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.OutDegreeMetric | ||
8 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.TypedOutDegree | ||
9 | import java.util.ArrayList | ||
10 | import java.util.HashMap | ||
11 | import java.util.List | ||
12 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.NodeTypeMetric | ||
13 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.EdgeTypeMetric | ||
14 | |||
15 | abstract class Graph { | ||
16 | |||
17 | protected static val String META_MODEL_HEADER = "Meta Mode" | ||
18 | protected static val String NUM_NODE_HEADER = "Number Of Nodes"; | ||
19 | protected static val String NUM_EDGE_TYPE_HEADER = "Number of Edge types"; | ||
20 | protected static val String STATE_ID_HEADER = "State Id"; | ||
21 | |||
22 | protected val statistic = new GraphStatistic(); | ||
23 | protected var List<Metric> metrics; | ||
24 | protected var String name = ""; | ||
25 | protected var String metaModel = ""; | ||
26 | |||
27 | /** | ||
28 | * evaluate all metrics for this model | ||
29 | * return the result as a two dimentional list | ||
30 | */ | ||
31 | def ArrayList<ArrayList<String>> evaluateAllMetrics(){ | ||
32 | val result = new ArrayList<ArrayList<String>>(); | ||
33 | setBasicInformation(result); | ||
34 | |||
35 | for(metric : this.metrics){ | ||
36 | val datas = metric.evaluate(this.statistic); | ||
37 | for(row : datas){ | ||
38 | result.add(new ArrayList<String>(row)); | ||
39 | } | ||
40 | } | ||
41 | return result; | ||
42 | } | ||
43 | |||
44 | def MetricSampleGroup evaluateAllMetricsToSamples(){ | ||
45 | var sample = new MetricSampleGroup(); | ||
46 | |||
47 | for(metric : this.metrics){ | ||
48 | if(metric instanceof MultiplexParticipationCoefficientMetric){ | ||
49 | sample.mpcSamples = metric.evaluateSamples(this.statistic) as ArrayList<Double>; | ||
50 | }else if(metric instanceof NodeActivityMetric){ | ||
51 | sample.naSamples = metric.evaluateSamples(this.statistic) as ArrayList<Double>; | ||
52 | }else if(metric instanceof OutDegreeMetric){ | ||
53 | sample.outDegreeSamples = metric.evaluateSamples(this.statistic) as ArrayList<Double>; | ||
54 | }else if(metric instanceof TypedOutDegree){ | ||
55 | sample.typedOutDegreeSamples = metric.evaluateSamples(this.statistic) as HashMap<String, List<Integer>>; | ||
56 | }else if(metric instanceof NodeTypeMetric){ | ||
57 | sample.nodeTypeSamples = metric.evaluateSamples(this.statistic) as HashMap<String, Double>; | ||
58 | }else if (metric instanceof EdgeTypeMetric){ | ||
59 | sample.edgeTypeSamples = metric.evaluateSamples(this.statistic) as HashMap<String, Double>; | ||
60 | } | ||
61 | } | ||
62 | |||
63 | return sample; | ||
64 | } | ||
65 | |||
66 | def void setBasicInformation(ArrayList<ArrayList<String>> result); | ||
67 | |||
68 | def GraphStatistic getStatistic(); | ||
69 | |||
70 | def String getName(); | ||
71 | } \ 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 @@ | |||
1 | package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.graph | ||
2 | |||
3 | import com.google.common.collect.ArrayListMultimap | ||
4 | import com.google.common.collect.Multimap | ||
5 | import java.util.HashMap | ||
6 | import java.util.HashSet | ||
7 | import java.util.List | ||
8 | import java.util.Map | ||
9 | import java.util.Set | ||
10 | import org.eclipse.emf.ecore.EObject | ||
11 | import org.eclipse.emf.ecore.EReference | ||
12 | |||
13 | class GraphStatistic { | ||
14 | val incomingEdges = new HashMap<String, Multimap<EObject, EObject>>; | ||
15 | val outgoingEdges = new HashMap<String, Multimap<EObject, EObject>>; | ||
16 | |||
17 | val edgeTypes = new HashSet<String>(); | ||
18 | val nodeToType = new HashMap<EObject, Set<String>>(); | ||
19 | |||
20 | /** | ||
21 | * Add an edge type to to the graph | ||
22 | * @param type: type to add | ||
23 | */ | ||
24 | def void addEdgeType(String type){ | ||
25 | if(edgeTypes.contains(type)){ | ||
26 | return; | ||
27 | } | ||
28 | |||
29 | edgeTypes.add(type); | ||
30 | incomingEdges.put(type, ArrayListMultimap.create()); | ||
31 | outgoingEdges.put(type, ArrayListMultimap.create()); | ||
32 | } | ||
33 | |||
34 | /** | ||
35 | * Add a node to the graph with one type in its type hierarchy | ||
36 | * @param node: node to add | ||
37 | */ | ||
38 | def void addNodeWithType(EObject n, String Type){ | ||
39 | var types = nodeToType.getOrDefault(n, new HashSet<String>()); | ||
40 | types.add(Type); | ||
41 | nodeToType.put(n, types); | ||
42 | } | ||
43 | |||
44 | def boolean containsNode(EObject o){ | ||
45 | return nodeToType.containsKey(o); | ||
46 | } | ||
47 | |||
48 | def Set<String> getTypesForNode(EObject o){ | ||
49 | return nodeToType.getOrDefault(o, new HashSet<String>()); | ||
50 | } | ||
51 | |||
52 | def void overwriteCurrentType(EObject o, String type){ | ||
53 | var typeSet = nodeToType.getOrDefault(o, new HashSet<String>()); | ||
54 | |||
55 | // clear current types | ||
56 | typeSet.clear(); | ||
57 | typeSet.add(type); | ||
58 | nodeToType.put(o, typeSet); | ||
59 | } | ||
60 | |||
61 | /** | ||
62 | * Add a node to the graph with all types in its type hierarchy | ||
63 | */ | ||
64 | def void addNodeWithAllTypes(EObject n, Set<String> types){ | ||
65 | nodeToType.put(n, types); | ||
66 | } | ||
67 | |||
68 | /** | ||
69 | * Add an edge to the graph | ||
70 | * @param source: source node | ||
71 | * @param target: target node | ||
72 | * @param type: type of the reference | ||
73 | */ | ||
74 | def void addEdge(EObject source, EObject target, String type){ | ||
75 | outgoingEdges.get(type).put(source, target); | ||
76 | incomingEdges.get(type).put(target, source); | ||
77 | } | ||
78 | |||
79 | /** | ||
80 | * check if this graph contains a specific edge type | ||
81 | */ | ||
82 | def boolean containsEdgeType(String typeName){ | ||
83 | if(outgoingEdges.containsKey(typeName) && incomingEdges.containsKey(typeName)){ | ||
84 | return true; | ||
85 | } | ||
86 | return false; | ||
87 | } | ||
88 | |||
89 | /** | ||
90 | * remove references from the statistics, potentially remove the nodes associated with it | ||
91 | * @Param name: name of the reference | ||
92 | * @Param isContainment: if true then the corresponding nodes on the incoming side will also be removed | ||
93 | */ | ||
94 | def removeReference(String name, boolean isContainment){ | ||
95 | if(!edgeTypes.contains(name)){ | ||
96 | return; | ||
97 | } | ||
98 | |||
99 | edgeTypes.remove(name); | ||
100 | var incomingSet = incomingEdges.remove(name); | ||
101 | outgoingEdges.remove(name); | ||
102 | |||
103 | // if the reference is not a containment, then removing the reference is enough | ||
104 | if(!isContainment){ | ||
105 | return; | ||
106 | } | ||
107 | |||
108 | // else remove all corresponding nodes | ||
109 | val nodesToRemove = incomingSet.keySet(); | ||
110 | |||
111 | //remove nodes from node sets | ||
112 | nodesToRemove.forEach[nodeToType.remove(it)]; | ||
113 | |||
114 | val removeForMultimap = [Multimap<EObject, EObject> refMap| | ||
115 | nodesToRemove.forEach[refMap.removeAll(it)]; | ||
116 | var values = refMap.values() | ||
117 | //remove the values from the list is equavalent to remove it in the multimap | ||
118 | values.removeAll(nodesToRemove); | ||
119 | return; | ||
120 | ]; | ||
121 | |||
122 | //remove nodes from all other references on incomingEdges | ||
123 | incomingEdges.values.forEach(removeForMultimap); | ||
124 | outgoingEdges.values.forEach(removeForMultimap); | ||
125 | } | ||
126 | |||
127 | /** | ||
128 | * calculate the out degree for an object | ||
129 | */ | ||
130 | def int outDegree(EObject o){ | ||
131 | var count = 0; | ||
132 | |||
133 | for (String type : edgeTypes){ | ||
134 | count += outgoingEdges.get(type).get(o).size(); | ||
135 | } | ||
136 | return count; | ||
137 | } | ||
138 | |||
139 | /** | ||
140 | * calculate the in degree of an object | ||
141 | */ | ||
142 | def int inDegree(EObject o){ | ||
143 | var count = 0; | ||
144 | |||
145 | for (String type : edgeTypes){ | ||
146 | count += incomingEdges.get(type).get(o).size(); | ||
147 | } | ||
148 | return count; | ||
149 | } | ||
150 | |||
151 | /** | ||
152 | * calculate the dimentional degree of a node | ||
153 | */ | ||
154 | def int dimentionalDegree(EObject o, String type){ | ||
155 | return incomingEdges.get(type).get(o).size() + outgoingEdges.get(type).get(o).size(); | ||
156 | } | ||
157 | |||
158 | /** | ||
159 | * calculate the number of edge types for a given node. | ||
160 | */ | ||
161 | def int numOfEdgeTypes(EObject o){ | ||
162 | var count = 0; | ||
163 | |||
164 | for(String type : edgeTypes){ | ||
165 | if(dimentionalDegree(o, type) > 0){ | ||
166 | count++; | ||
167 | } | ||
168 | } | ||
169 | |||
170 | return count; | ||
171 | } | ||
172 | |||
173 | def List<String> getAllTypes(){ | ||
174 | return edgeTypes.toList(); | ||
175 | } | ||
176 | |||
177 | def Map<EObject, Set<String>> getNodeToTypesMap(){ | ||
178 | return nodeToType; | ||
179 | } | ||
180 | |||
181 | def List<EObject> getAllNodes(){ | ||
182 | return nodeToType.keySet().toList(); | ||
183 | } | ||
184 | |||
185 | def HashMap<String, Multimap<EObject, EObject>> getOutgoingEdges(){ | ||
186 | return outgoingEdges; | ||
187 | } | ||
188 | |||
189 | def HashMap<String, Multimap<EObject, EObject>> incomingEdges(){ | ||
190 | return incomingEdges; | ||
191 | } | ||
192 | |||
193 | } | ||
194 | |||
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 @@ | |||
1 | package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.graph | ||
2 | |||
3 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.Metric | ||
4 | import hu.bme.mit.inf.dslreasoner.logic.model.logiclanguage.RelationDeclaration | ||
5 | import hu.bme.mit.inf.dslreasoner.logic.model.logiclanguage.Type | ||
6 | import hu.bme.mit.inf.dslreasoner.logic.model.logiclanguage.TypeDefinition | ||
7 | import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.partialinterpretation.BinaryElementRelationLink | ||
8 | import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.partialinterpretation.PartialInterpretation | ||
9 | import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.partialinterpretation.impl.PartialComplexTypeInterpretationImpl | ||
10 | import java.util.ArrayList | ||
11 | import java.util.List | ||
12 | |||
13 | class PartialInterpretationGraph extends Graph{ | ||
14 | val typeToExclude = "undefinedpart"; | ||
15 | val classSuffix = " class"; | ||
16 | |||
17 | /** | ||
18 | * Define a new PartialInterpretationGraph by parse every element from a PartialInterpretation | ||
19 | */ | ||
20 | new(PartialInterpretation partial, List<Metric> metrics, String name){ | ||
21 | //the edge types are defined in terms of RelationDeclaration | ||
22 | partial.problem.relations.filter(RelationDeclaration).forEach[ | ||
23 | //only need the name of the reference type (remove everything with and after "reference") | ||
24 | var n = it.name.split(" ").get(0); | ||
25 | this.statistic.addEdgeType(n); | ||
26 | ] | ||
27 | // add all elements | ||
28 | val typeInterpretations = getTypes(partial); | ||
29 | for(type : typeInterpretations){ | ||
30 | //Only consider the most concrete class | ||
31 | if(isConcreteType(type.interpretationOf)){ | ||
32 | var typeName = type.interpretationOf.name.replace(classSuffix, ''); | ||
33 | for(node : type.elements){ | ||
34 | if(!this.statistic.containsNode(node)){ | ||
35 | this.statistic.addNodeWithType(node, typeName); | ||
36 | }else{ | ||
37 | // if the current type of the node is a super type of the type to check, | ||
38 | // substitute the current type with the new type | ||
39 | var currentType = statistic.getTypesForNode(node).get(0); | ||
40 | if(isSuperType(currentType, type.interpretationOf)){ | ||
41 | statistic.overwriteCurrentType(node, typeName); | ||
42 | } | ||
43 | } | ||
44 | } | ||
45 | } | ||
46 | } | ||
47 | |||
48 | for(relationInterpretation : partial.partialrelationinterpretation) { | ||
49 | //only need the name of the reference type (remove everything with and after "reference") | ||
50 | val type = relationInterpretation.interpretationOf.name.split(" ").get(0); | ||
51 | for(edge : relationInterpretation.relationlinks.filter(BinaryElementRelationLink)){ | ||
52 | statistic.addEdge(edge.param1, edge.param2, type); | ||
53 | } | ||
54 | } | ||
55 | |||
56 | this.name = name; | ||
57 | this.metrics = metrics; | ||
58 | } | ||
59 | |||
60 | /** | ||
61 | * recursively check if a type is the super type of another | ||
62 | */ | ||
63 | def boolean isSuperType(String typeName, Type subtypeToCheck){ | ||
64 | var superTypes = subtypeToCheck.supertypes; | ||
65 | if(superTypes.size == 0){ | ||
66 | return false; | ||
67 | }else if(subtypeToCheck.supertypes.map[it.name.replace(classSuffix, '')].contains(typeName)){ | ||
68 | return true; | ||
69 | }else{ | ||
70 | for(superType : superTypes){ | ||
71 | if(isSuperType(typeName, superType)){ | ||
72 | return true; | ||
73 | } | ||
74 | } | ||
75 | return false; | ||
76 | } | ||
77 | } | ||
78 | |||
79 | /** | ||
80 | * Check if a Type object is the class that we want to consider | ||
81 | * A type object is to be considered if it satisfy one of the following: | ||
82 | * 1. if it is not abstract | ||
83 | * 2. if it is abstract but has a subclass of type TypeDefinition (This means the generation is | ||
84 | * started with nodes in this type) | ||
85 | */ | ||
86 | def boolean isConcreteType(Type t){ | ||
87 | if(!t.isAbstract || t.subtypes.findFirst[it instanceof TypeDefinition] !== null){ | ||
88 | return true; | ||
89 | } | ||
90 | return false; | ||
91 | } | ||
92 | |||
93 | /** | ||
94 | * Set basic information for the output | ||
95 | */ | ||
96 | override setBasicInformation(ArrayList<ArrayList<String>> output){ | ||
97 | val metaInfo = new ArrayList<String>(); | ||
98 | metaInfo.add(META_MODEL_HEADER); | ||
99 | metaInfo.add(this.metaModel); | ||
100 | |||
101 | val edgeInfo = new ArrayList<String>(); | ||
102 | edgeInfo.add(NUM_EDGE_TYPE_HEADER); | ||
103 | edgeInfo.add(this.statistic.allTypes.size()+""); | ||
104 | |||
105 | val nodeInfo = new ArrayList<String>(); | ||
106 | nodeInfo.add(NUM_NODE_HEADER); | ||
107 | nodeInfo.add(this.statistic.allNodes.size()+""); | ||
108 | |||
109 | val stateInfo = new ArrayList<String>(); | ||
110 | stateInfo.add(STATE_ID_HEADER); | ||
111 | stateInfo.add(this.name); | ||
112 | |||
113 | output.add(metaInfo); | ||
114 | output.add(edgeInfo); | ||
115 | output.add(nodeInfo); | ||
116 | output.add(stateInfo); | ||
117 | } | ||
118 | |||
119 | private def getTypes(PartialInterpretation partial){ | ||
120 | //only the complex type interpretations are the ones defined in meta model | ||
121 | //do not care about undefined types as it will be included in the class type | ||
122 | return partial.partialtypeinterpratation.filter(PartialComplexTypeInterpretationImpl) | ||
123 | .filter[!it.interpretationOf.name.toLowerCase.contains(typeToExclude)]; | ||
124 | } | ||
125 | |||
126 | override getStatistic() { | ||
127 | throw new UnsupportedOperationException("TODO: auto-generated method stub") | ||
128 | } | ||
129 | |||
130 | override getName() { | ||
131 | return name; | ||
132 | } | ||
133 | |||
134 | } \ 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 @@ | |||
1 | package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.io; | ||
2 | |||
3 | import java.io.File | ||
4 | import java.io.FileNotFoundException | ||
5 | import java.io.FileOutputStream | ||
6 | import java.io.PrintWriter | ||
7 | import java.util.ArrayList | ||
8 | import java.util.List | ||
9 | |||
10 | class CsvFileWriter { | ||
11 | |||
12 | def static void write(ArrayList<ArrayList<String>> datas, String uri) { | ||
13 | if(datas.size() <= 0) { | ||
14 | return; | ||
15 | } | ||
16 | val PrintWriter writer = new PrintWriter(new File(uri)); | ||
17 | output(writer, datas, uri); | ||
18 | } | ||
19 | |||
20 | def static void append(ArrayList<ArrayList<String>> datas, String uri) { | ||
21 | if(datas.size() <= 0) { | ||
22 | return; | ||
23 | } | ||
24 | val PrintWriter writer = new PrintWriter(new FileOutputStream(new File(uri), true)); | ||
25 | output(writer, datas, uri); | ||
26 | } | ||
27 | |||
28 | |||
29 | def private static void output(PrintWriter writer, ArrayList<ArrayList<String>> datas, String uri) { | ||
30 | //println("Output csv for " + uri); | ||
31 | try { | ||
32 | |||
33 | val output = new StringBuilder; | ||
34 | for(List<String> datarow : datas){ | ||
35 | for(var i = 0; i < datarow.size() - 1; i++){ | ||
36 | output.append(datarow.get(i) + ','); | ||
37 | } | ||
38 | |||
39 | if(datarow.size >= 1){ | ||
40 | output.append(datarow.get(datarow.size() - 1)); | ||
41 | output.append('\n'); | ||
42 | } | ||
43 | } | ||
44 | |||
45 | writer.write(output.toString()); | ||
46 | writer.close(); | ||
47 | //println("Output csv finished"); | ||
48 | }catch(FileNotFoundException e) { | ||
49 | e.printStackTrace(); | ||
50 | } | ||
51 | } | ||
52 | } | ||
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 @@ | |||
1 | package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.io; | ||
2 | |||
3 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.graph.EMFGraph | ||
4 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.EdgeTypeMetric | ||
5 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.Metric | ||
6 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.MultiplexParticipationCoefficientMetric | ||
7 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.NodeActivityMetric | ||
8 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.NodeTypeMetric | ||
9 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.OutDegreeMetric | ||
10 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.TypedOutDegree | ||
11 | import java.io.File | ||
12 | import java.io.FileNotFoundException | ||
13 | import java.util.ArrayList | ||
14 | import java.util.List | ||
15 | import org.eclipse.emf.common.util.URI | ||
16 | import org.eclipse.emf.ecore.EObject | ||
17 | import org.eclipse.emf.ecore.EPackage | ||
18 | import org.eclipse.emf.ecore.EReference | ||
19 | import org.eclipse.emf.ecore.resource.Resource | ||
20 | import org.eclipse.emf.ecore.resource.ResourceSet | ||
21 | import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl | ||
22 | import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl | ||
23 | import org.eclipse.emf.ecore.EGenericType | ||
24 | import org.eclipse.emf.ecore.EStructuralFeature | ||
25 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.TypedClusteringCoefficientMetric | ||
26 | |||
27 | class GraphReader{ | ||
28 | val ResourceSet resSet = new ResourceSetImpl(); | ||
29 | val referenceTypes = new ArrayList<EReference>(); | ||
30 | var String suffix; | ||
31 | |||
32 | new(EPackage metaModel, String suffix) { | ||
33 | Resource.Factory.Registry.INSTANCE.extensionToFactoryMap.put("*",new XMIResourceFactoryImpl) | ||
34 | this.suffix = suffix; | ||
35 | |||
36 | //find all reference types in the meta model | ||
37 | metaModel.eAllContents.forEach[ | ||
38 | if(it instanceof EReference){ | ||
39 | referenceTypes.add(it); | ||
40 | } | ||
41 | ] | ||
42 | } | ||
43 | |||
44 | def List<EMFGraph> readModels(String path){ | ||
45 | val dir = new File(path); | ||
46 | if(!dir.isDirectory){ | ||
47 | throw new Exception("expecting a directory"); | ||
48 | } | ||
49 | |||
50 | val graphs = new ArrayList<EMFGraph>(); | ||
51 | |||
52 | val metrics = new ArrayList<Metric>(); | ||
53 | metrics.add(new OutDegreeMetric()); | ||
54 | metrics.add(new NodeActivityMetric()); | ||
55 | metrics.add(new MultiplexParticipationCoefficientMetric()); | ||
56 | metrics.add(new TypedOutDegree()); | ||
57 | metrics.add(new NodeTypeMetric()); | ||
58 | metrics.add(new EdgeTypeMetric()); | ||
59 | var count = 1 | ||
60 | //check all files in the directory with suffix | ||
61 | for(String name : dir.list.filter[it| it.endsWith(suffix)]){ | ||
62 | val file = new File(name); | ||
63 | val roots = readModel(EObject, path, file.name); | ||
64 | //add a list of metrics | ||
65 | val g = new EMFGraph(); | ||
66 | for(root : roots){ | ||
67 | g.init(root, metrics, name.replaceFirst(suffix, ""), referenceTypes); | ||
68 | } | ||
69 | |||
70 | count ++; | ||
71 | graphs.add(g); | ||
72 | } | ||
73 | |||
74 | return graphs; | ||
75 | } | ||
76 | |||
77 | def EMFGraph readModel(String path, String filename){ | ||
78 | val metrics = new ArrayList<Metric>(); | ||
79 | metrics.add(new OutDegreeMetric()); | ||
80 | metrics.add(new NodeActivityMetric()); | ||
81 | metrics.add(new MultiplexParticipationCoefficientMetric()); | ||
82 | metrics.add(new TypedOutDegree()); | ||
83 | metrics.add(new NodeTypeMetric()); | ||
84 | metrics.add(new EdgeTypeMetric()); | ||
85 | |||
86 | val file = new File(filename); | ||
87 | val roots = readModel(EObject, path, file.name); | ||
88 | //add a list of metrics | ||
89 | val g = new EMFGraph(); | ||
90 | for(root : roots){ | ||
91 | g.init(root, metrics, filename.replaceFirst(suffix, ""), referenceTypes); | ||
92 | } | ||
93 | return g | ||
94 | } | ||
95 | |||
96 | def <RootType extends EObject> List<RootType> readModel(Class<RootType> type, String path, String name) { | ||
97 | try { | ||
98 | val resource = resSet.getResource(getURI(path, name),true); | ||
99 | if(resource === null) throw new FileNotFoundException(getURI(path, name).toString) | ||
100 | else { | ||
101 | return resource.contents as List<RootType> | ||
102 | } | ||
103 | } catch(Exception e) { | ||
104 | e.printStackTrace(); | ||
105 | throw new Exception(getURI(path, name).toString()); | ||
106 | } | ||
107 | } | ||
108 | |||
109 | def static getURI(String path, String name) { | ||
110 | URI.createFileURI(path + "/" + name) | ||
111 | } | ||
112 | } \ 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 @@ | |||
1 | package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.io | ||
2 | |||
3 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.app.Domain | ||
4 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics.MetricSampleGroup | ||
5 | import github.impl.GithubPackageImpl | ||
6 | import hu.bme.mit.inf.dslreasoner.domains.yakindu.sgraph.yakindumm.impl.YakindummPackageImpl | ||
7 | import java.util.HashMap | ||
8 | import org.eclipse.emf.ecore.EReference | ||
9 | import org.eclipse.emf.ecore.impl.EcorePackageImpl | ||
10 | |||
11 | /** | ||
12 | * Read the sample of the distribution of a metric provided the csv file of the metric | ||
13 | */ | ||
14 | class RepMetricsReader { | ||
15 | static var Domain domain; | ||
16 | static def read(Domain d){ | ||
17 | var GraphReader reader; | ||
18 | if(d == Domain.Yakindumm){ | ||
19 | reader = new GraphReader(YakindummPackageImpl.eINSTANCE, '.xmi'); | ||
20 | }else if (d == Domain.Ecore){ | ||
21 | reader = new GraphReader(EcorePackageImpl.eINSTANCE, '.ecore'); | ||
22 | }else if (d == Domain.Github){ | ||
23 | reader = new GraphReader(GithubPackageImpl.eINSTANCE, '.githubmodel') | ||
24 | } | ||
25 | |||
26 | domain = d; | ||
27 | |||
28 | var domainRepPath = DataName.REP_PATH + d.name + '/'; | ||
29 | var rep = new MetricSampleGroup() | ||
30 | var out_d = readMetrics(reader, domainRepPath + DataName.OUT_D_REP); | ||
31 | var mpc = readMetrics(reader, domainRepPath + DataName.MPC_REP); | ||
32 | rep.mpcSamples = mpc.mpcSamples; | ||
33 | rep.outDegreeSamples = out_d.outDegreeSamples; | ||
34 | rep.naSamples = readMetrics(reader, domainRepPath + DataName.NA_REP).naSamples; | ||
35 | rep.typedOutDegreeSamples = out_d.typedOutDegreeSamples; | ||
36 | rep.edgeTypeSamples = mpc.edgeTypeSamples; | ||
37 | |||
38 | //TODO: Parameterize the prior node distribution | ||
39 | var nodeTypeSamples = new HashMap<String, Double>(); | ||
40 | if(d == Domain.Yakindumm){ | ||
41 | nodeTypeSamples.put('Entry', 0.04257802080554814); | ||
42 | nodeTypeSamples.put('Choice', 0.1267671379034409); | ||
43 | nodeTypeSamples.put('State', 0.1596092291277674); | ||
44 | nodeTypeSamples.put('Transition', 0.6138636969858629); | ||
45 | nodeTypeSamples.put('Statechart', 0.010136036276340358); | ||
46 | nodeTypeSamples.put('Region', 0.04467858095492131); | ||
47 | nodeTypeSamples.put('Exit', 0.0018338223526273673); | ||
48 | nodeTypeSamples.put('FinalState', 0.0005334755934915977); | ||
49 | }else if(d ==Domain.Ecore){ | ||
50 | nodeTypeSamples.put('EAttribute', 0.23539778449144008); | ||
51 | nodeTypeSamples.put('EClass', 0.33081570996978854); | ||
52 | nodeTypeSamples.put('EReference', 0.30996978851963747); | ||
53 | nodeTypeSamples.put('EPackage', 0.012789526686807653); | ||
54 | nodeTypeSamples.put('EAnnotation', 0.002517623363544813); | ||
55 | nodeTypeSamples.put('EEnumLiteral', 0.07275931520644502); | ||
56 | nodeTypeSamples.put('EEnum', 0.013645518630412891); | ||
57 | nodeTypeSamples.put('EDataType', 0.004028197381671702); | ||
58 | nodeTypeSamples.put('EParameter', 0.005941591137965764); | ||
59 | nodeTypeSamples.put('EGenericType', 0.002014098690835851); | ||
60 | nodeTypeSamples.put('EOperation', 0.009415911379657605); | ||
61 | nodeTypeSamples.put('ETypeParameter', 0.0007049345417925478); | ||
62 | }else if (d == Domain.Github){ | ||
63 | nodeTypeSamples.put('Project', 0.012636538873420432); | ||
64 | nodeTypeSamples.put('Commit', 0.5525808524309276); | ||
65 | nodeTypeSamples.put('User', 0.05847076461769116); | ||
66 | nodeTypeSamples.put('Issue', 0.12743628185907047); | ||
67 | nodeTypeSamples.put('PullRequest', 0.07560505461554937); | ||
68 | nodeTypeSamples.put('IssueEvent', 0.17327050760334123); | ||
69 | } | ||
70 | |||
71 | |||
72 | |||
73 | rep.nodeTypeSamples = nodeTypeSamples; | ||
74 | return rep; | ||
75 | } | ||
76 | |||
77 | /** | ||
78 | * Read representative model | ||
79 | */ | ||
80 | private static def readMetrics(GraphReader r, String path){ | ||
81 | val model = r.readModels(path).head; | ||
82 | if(domain == Domain.Ecore){ | ||
83 | var refsToRemove = EcorePackageImpl.eINSTANCE.eAllContents.filter(EReference).filter[ | ||
84 | it.name.equals('eGenericType') || it.name.equals('eGenericSuperTypes') || it.name.equals('eFactoryInstance') || | ||
85 | it.name.equals('eGenericExceptions') || it.name.equals('references') || it.name.equals('contents'); | ||
86 | ]; | ||
87 | refsToRemove.forEach[model.removeReference(it)]; | ||
88 | } | ||
89 | return model.evaluateAllMetricsToSamples(); | ||
90 | } | ||
91 | |||
92 | } | ||
93 | |||
94 | class DataName{ | ||
95 | public static val REP_PATH = 'data/'; | ||
96 | public static val MPC_REP = 'mpc_rep'; | ||
97 | public static val NA_REP = 'na_rep'; | ||
98 | public static val OUT_D_REP = 'out_d_rep'; | ||
99 | } | ||
100 | |||
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 @@ | |||
1 | package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics | ||
2 | |||
3 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.graph.GraphStatistic | ||
4 | import java.util.ArrayList | ||
5 | import java.util.HashMap | ||
6 | |||
7 | class EdgeTypeMetric extends Metric { | ||
8 | |||
9 | override evaluate(GraphStatistic g) { | ||
10 | var map = evaluateSamples(g) as HashMap<String, Double>; | ||
11 | var output = new ArrayList<String[]>(); | ||
12 | output.add(newArrayList('Edge Type')); | ||
13 | |||
14 | var keys = map.keySet; | ||
15 | var values = newArrayList(); | ||
16 | for(key : keys){ | ||
17 | values.add(map.get(key)+''); | ||
18 | } | ||
19 | |||
20 | output.add(keys); | ||
21 | output.add(values); | ||
22 | |||
23 | return output; | ||
24 | } | ||
25 | |||
26 | override evaluateSamples(GraphStatistic g) { | ||
27 | val map = new HashMap<String, Double>(); | ||
28 | var outgoingEdges = g.outgoingEdges; | ||
29 | |||
30 | //get the total number of edges | ||
31 | val edgeCount = outgoingEdges.values.fold(0, [r, t| r + t.asMap.values.fold(0, [r1, t1| r1 + t1.size])]) as double; | ||
32 | outgoingEdges.forEach[k, v| | ||
33 | var value = v.asMap.values.fold(0, [r, t| r + t.size]) / edgeCount; | ||
34 | map.put(k, value); | ||
35 | ] | ||
36 | |||
37 | |||
38 | return map; | ||
39 | } | ||
40 | |||
41 | } \ 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 @@ | |||
1 | package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics | ||
2 | |||
3 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.graph.GraphStatistic | ||
4 | |||
5 | abstract class Metric { | ||
6 | abstract def String[][] evaluate(GraphStatistic g); | ||
7 | abstract def Object evaluateSamples(GraphStatistic g); | ||
8 | } \ 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 @@ | |||
1 | package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics | ||
2 | |||
3 | import java.util.HashMap | ||
4 | import java.util.List | ||
5 | |||
6 | class MetricSampleGroup{ | ||
7 | public var List<Double> mpcSamples; | ||
8 | public var List<Double> naSamples; | ||
9 | public var List<Double> outDegreeSamples; | ||
10 | public var HashMap<String, List<Integer>> typedOutDegreeSamples; | ||
11 | public var HashMap<String, Double> nodeTypeSamples; | ||
12 | public var HashMap<String, Double> edgeTypeSamples; | ||
13 | } \ 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 @@ | |||
1 | package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics | ||
2 | |||
3 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.graph.GraphStatistic | ||
4 | import java.text.DecimalFormat | ||
5 | import java.util.ArrayList | ||
6 | import java.util.HashMap | ||
7 | import org.eclipse.emf.ecore.EObject | ||
8 | |||
9 | class MultiplexParticipationCoefficientMetric extends Metric { | ||
10 | public static val countName = "MPCCount"; | ||
11 | public static val valueName = "MPCValue"; | ||
12 | val formatter = new DecimalFormat("#0.00000"); | ||
13 | |||
14 | override evaluate(GraphStatistic g) { | ||
15 | //because the precision issue of double, we translate double values into String to be the key | ||
16 | |||
17 | |||
18 | //get number of different types | ||
19 | val typeCounts = g.allTypes.size; | ||
20 | val map = new HashMap<String, Integer>(); | ||
21 | //calculate the metric distribution | ||
22 | g.allNodes.forEach[n| | ||
23 | var coef = calculateMPC(n, g, typeCounts); | ||
24 | |||
25 | //format number to String | ||
26 | val value = formatter.format(coef); | ||
27 | if(!map.containsKey(value)){ | ||
28 | map.put(value, 1); | ||
29 | }else{ | ||
30 | map.put(value, map.get(value) + 1); | ||
31 | } | ||
32 | |||
33 | ] | ||
34 | |||
35 | //convert it into a 2 dimentional array | ||
36 | val String[][] datas = newArrayOfSize(2, map.size+1); | ||
37 | datas.get(0).set(0, valueName); | ||
38 | datas.get(1).set(0, countName) | ||
39 | var count = 1; | ||
40 | for(entry : map.entrySet.sortBy[it.key]){ | ||
41 | datas.get(0).set(count, entry.key+""); | ||
42 | datas.get(1).set(count, entry.value+""); | ||
43 | count++; | ||
44 | } | ||
45 | |||
46 | return datas; | ||
47 | } | ||
48 | |||
49 | override evaluateSamples(GraphStatistic g){ | ||
50 | val samples = new ArrayList<Double>(); | ||
51 | val typeCounts = g.allTypes.size; | ||
52 | //calculate the metric distribution | ||
53 | g.allNodes.forEach[ | ||
54 | samples.add(calculateMPC(it, g, typeCounts)); | ||
55 | ] | ||
56 | |||
57 | return samples; | ||
58 | } | ||
59 | |||
60 | def double calculateMPC(EObject n, GraphStatistic g, int typeCounts){ | ||
61 | val edgeCounts = g.outDegree(n) + g.inDegree(n); | ||
62 | |||
63 | var coef = 0.0; | ||
64 | |||
65 | for(type : g.allTypes){ | ||
66 | val degree = g.dimentionalDegree(n, type) as double; | ||
67 | coef += Math.pow(degree / edgeCounts, 2); | ||
68 | } | ||
69 | coef = 1 - coef; | ||
70 | coef = coef * typeCounts / (typeCounts-1); | ||
71 | |||
72 | //Consider the case that either typeCounts-1 or the edgeCounts could be 0 in some situation | ||
73 | //in this case the metric should be evaluated to 0 | ||
74 | if(Double.isNaN(coef)){ | ||
75 | coef = 0; | ||
76 | } | ||
77 | |||
78 | return Double.parseDouble(formatter.format(coef)); | ||
79 | } | ||
80 | } \ 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 @@ | |||
1 | package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics | ||
2 | |||
3 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.graph.GraphStatistic | ||
4 | import java.util.ArrayList | ||
5 | import java.util.HashMap | ||
6 | |||
7 | class NodeActivityMetric extends Metric { | ||
8 | public static val countName = "NACount"; | ||
9 | public static val valueName = "NAValue"; | ||
10 | |||
11 | override evaluate(GraphStatistic g) { | ||
12 | val map = new HashMap<Integer, Integer>(); | ||
13 | |||
14 | //calculate the metric distribution | ||
15 | g.allNodes.forEach[ | ||
16 | val value = g.numOfEdgeTypes(it); | ||
17 | if(!map.containsKey(value)){ | ||
18 | map.put(value, 1); | ||
19 | }else{ | ||
20 | map.put(value, map.get(value) + 1); | ||
21 | } | ||
22 | ] | ||
23 | |||
24 | //convert it into a 2 dimentional array | ||
25 | val String[][] datas = newArrayOfSize(2, map.size+1); | ||
26 | datas.get(0).set(0, valueName); | ||
27 | datas.get(1).set(0, countName) | ||
28 | |||
29 | var count = 1; | ||
30 | for(entry : map.entrySet.sortBy[it.key]){ | ||
31 | datas.get(0).set(count, entry.key+""); | ||
32 | datas.get(1).set(count, entry.value+""); | ||
33 | count++; | ||
34 | } | ||
35 | |||
36 | return datas; | ||
37 | } | ||
38 | |||
39 | override evaluateSamples(GraphStatistic g){ | ||
40 | val samples = new ArrayList<Double>(); | ||
41 | |||
42 | //calculate the metric distribution | ||
43 | g.allNodes.forEach[ | ||
44 | samples.add(g.numOfEdgeTypes(it) as double); | ||
45 | ] | ||
46 | |||
47 | return samples; | ||
48 | } | ||
49 | } \ 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 @@ | |||
1 | package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics | ||
2 | |||
3 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.graph.GraphStatistic | ||
4 | import java.util.ArrayList | ||
5 | import java.util.HashMap | ||
6 | |||
7 | class NodeTypeMetric extends Metric { | ||
8 | |||
9 | |||
10 | override evaluate(GraphStatistic g) { | ||
11 | var map = evaluateSamples(g) as HashMap<String, Double>; | ||
12 | var output = new ArrayList<String[]>(); | ||
13 | output.add(newArrayList('Node Type')); | ||
14 | var keys = map.keySet; | ||
15 | var values = newArrayList(); | ||
16 | |||
17 | for(key : keys){ | ||
18 | values.add(map.get(key)+''); | ||
19 | } | ||
20 | |||
21 | output.add(keys); | ||
22 | output.add(values); | ||
23 | |||
24 | return output; | ||
25 | } | ||
26 | |||
27 | override evaluateSamples(GraphStatistic g) { | ||
28 | var map = new HashMap<String, Double>(); | ||
29 | var nodes = g.allNodes; | ||
30 | var single = 1.0 / nodes.size(); | ||
31 | var nodeToType = g.nodeToTypesMap; | ||
32 | for(node : nodes){ | ||
33 | for(cl : nodeToType.get(node)){ | ||
34 | var value = map.getOrDefault(cl, 0.0); | ||
35 | map.put(cl, value + single); | ||
36 | } | ||
37 | } | ||
38 | |||
39 | return map; | ||
40 | } | ||
41 | |||
42 | } \ 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 @@ | |||
1 | package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics | ||
2 | |||
3 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.graph.GraphStatistic | ||
4 | import java.util.ArrayList | ||
5 | import java.util.HashMap | ||
6 | |||
7 | class OutDegreeMetric extends Metric { | ||
8 | public static val countName = "OutDegreeCount"; | ||
9 | public static val valueName = "OutDegreeValue"; | ||
10 | |||
11 | override evaluate(GraphStatistic g) { | ||
12 | val map = new HashMap<Integer, Integer>(); | ||
13 | |||
14 | //calculate the metric distribution | ||
15 | g.allNodes.forEach[ | ||
16 | val value = g.outDegree(it); | ||
17 | if(!map.containsKey(value)){ | ||
18 | map.put(value, 1); | ||
19 | }else{ | ||
20 | map.put(value, map.get(value) + 1); | ||
21 | } | ||
22 | ] | ||
23 | |||
24 | //convert it into a 2 dimentional array | ||
25 | val String[][] datas = newArrayOfSize(2, map.size+1); | ||
26 | datas.get(0).set(0, valueName); | ||
27 | datas.get(1).set(0, countName) | ||
28 | var count = 1; | ||
29 | for(entry : map.entrySet.sortBy[it.key]){ | ||
30 | datas.get(0).set(count, entry.key+""); | ||
31 | datas.get(1).set(count, entry.value+""); | ||
32 | count++; | ||
33 | } | ||
34 | |||
35 | return datas; | ||
36 | } | ||
37 | |||
38 | override evaluateSamples(GraphStatistic g){ | ||
39 | val samples = new ArrayList<Double>(); | ||
40 | |||
41 | //calculate the metric distribution | ||
42 | g.allNodes.forEach[ | ||
43 | samples.add(g.outDegree(it) as double); | ||
44 | ] | ||
45 | |||
46 | return samples; | ||
47 | } | ||
48 | |||
49 | } \ 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 @@ | |||
1 | package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics | ||
2 | |||
3 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.graph.GraphStatistic | ||
4 | import java.text.DecimalFormat | ||
5 | import java.util.ArrayList | ||
6 | import java.util.HashMap | ||
7 | import java.util.HashSet | ||
8 | import org.eclipse.emf.ecore.EObject | ||
9 | |||
10 | class TypedClusteringCoefficientMetric extends Metric { | ||
11 | public static val countName = "TCCCount"; | ||
12 | public static val valueName = "TCCValue"; | ||
13 | val formatter = new DecimalFormat("#0.00000"); | ||
14 | |||
15 | override evaluate(GraphStatistic g) { | ||
16 | //because the precision issue of double, we translate double values into String to be the key | ||
17 | |||
18 | val map = new HashMap<String, Integer>(); | ||
19 | //calculate the metric distribution | ||
20 | g.allNodes.forEach[n| | ||
21 | var coef = calculateTCC1(n, g); | ||
22 | if(coef > 0){ | ||
23 | println(n); | ||
24 | } | ||
25 | //format number to String | ||
26 | val value = formatter.format(coef); | ||
27 | if(!map.containsKey(value)){ | ||
28 | map.put(value, 1); | ||
29 | }else{ | ||
30 | map.put(value, map.get(value) + 1); | ||
31 | } | ||
32 | |||
33 | ] | ||
34 | |||
35 | //convert it into a 2 dimentional array | ||
36 | val String[][] datas = newArrayOfSize(2, map.size+1); | ||
37 | datas.get(0).set(0, valueName); | ||
38 | datas.get(1).set(0, countName) | ||
39 | var count = 1; | ||
40 | for(entry : map.entrySet.sortBy[it.key]){ | ||
41 | datas.get(0).set(count, entry.key+""); | ||
42 | datas.get(1).set(count, entry.value+""); | ||
43 | count++; | ||
44 | } | ||
45 | |||
46 | return datas; | ||
47 | } | ||
48 | |||
49 | override evaluateSamples(GraphStatistic g){ | ||
50 | val samples = new ArrayList<Double>(); | ||
51 | //calculate the metric distribution | ||
52 | g.allNodes.forEach[ | ||
53 | samples.add(calculateTCC1(it, g)); | ||
54 | ] | ||
55 | |||
56 | return samples; | ||
57 | } | ||
58 | |||
59 | /** | ||
60 | * Compute TCC1 metric for node n | ||
61 | */ | ||
62 | def double calculateTCC1(EObject n, GraphStatistic g){ | ||
63 | var wedges = 0; | ||
64 | var triangles = 0; | ||
65 | |||
66 | for(type1 : g.allTypes){ | ||
67 | val typed1RelatedOfN = new HashSet<EObject>(g.outgoingEdges.get(type1).get(n)); | ||
68 | val type1EdgeSourceNodesOfN = new HashSet<EObject>(g.incomingEdges.get(type1).get(n)); | ||
69 | |||
70 | typed1RelatedOfN.addAll(type1EdgeSourceNodesOfN); | ||
71 | |||
72 | |||
73 | |||
74 | // number of wedges | ||
75 | val d = typed1RelatedOfN.size | ||
76 | wedges += d * (d-1) // we will also count each closed triangle twice | ||
77 | |||
78 | // pairs of neighbors | ||
79 | for (n1: typed1RelatedOfN) { | ||
80 | for (n2: typed1RelatedOfN) { | ||
81 | for(type2 : g.allTypes){ | ||
82 | if ((type1 != type2) && | ||
83 | (g.outgoingEdges.get(type2).containsEntry(n1, n2) || | ||
84 | g.outgoingEdges.get(type2).containsEntry(n2, n1) | ||
85 | )) { | ||
86 | triangles++ | ||
87 | } | ||
88 | } | ||
89 | } | ||
90 | } | ||
91 | } | ||
92 | |||
93 | if (wedges == 0.0) { | ||
94 | return 0.0 | ||
95 | } else { | ||
96 | return (triangles as double)/wedges | ||
97 | } | ||
98 | } | ||
99 | } \ 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 @@ | |||
1 | package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.metrics | ||
2 | |||
3 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.graph.GraphStatistic | ||
4 | import java.util.ArrayList | ||
5 | import java.util.HashMap | ||
6 | import java.util.List | ||
7 | |||
8 | class TypedOutDegree extends Metric{ | ||
9 | |||
10 | def private calculateMetric(GraphStatistic g){ | ||
11 | var outgoingEdges = g.outgoingEdges; | ||
12 | //record metric as a list of samples for each node type / edge type pair | ||
13 | var metric = new HashMap<String, List<Integer>>(); | ||
14 | var nodeToTypes = g.nodeToTypesMap; | ||
15 | |||
16 | for(edgeType : outgoingEdges.keySet){ | ||
17 | for(node : outgoingEdges.get(edgeType).keySet){ | ||
18 | //find all classes the node belongs to | ||
19 | var classes = nodeToTypes.get(node); | ||
20 | for(cl : classes){ | ||
21 | // get or create entry for node type / edge type pair | ||
22 | var key = cl + ' ' + edgeType; | ||
23 | var typeCount = metric.get(key); | ||
24 | if(typeCount === null){ | ||
25 | typeCount = new ArrayList<Integer>(); | ||
26 | metric.put(key, typeCount); | ||
27 | } | ||
28 | |||
29 | // get or create sample list | ||
30 | typeCount.add(outgoingEdges.get(edgeType).get(node).size); | ||
31 | } | ||
32 | } | ||
33 | } | ||
34 | |||
35 | return metric; | ||
36 | } | ||
37 | |||
38 | override evaluate(GraphStatistic g) { | ||
39 | var metric = calculateMetric(g); | ||
40 | var output = new ArrayList<String[]>(); | ||
41 | |||
42 | output.add(newArrayList('Typed Out Degree')); | ||
43 | for(key : metric.keySet){ | ||
44 | var samples = metric.get(key); | ||
45 | var String[] outputForOnePair = newArrayOfSize(samples.size+1); | ||
46 | outputForOnePair.set(0, key); | ||
47 | for(var i = 0; i < samples.size; i++){ | ||
48 | outputForOnePair.set(i+1, samples.get(i)+''); | ||
49 | } | ||
50 | output.add(outputForOnePair); | ||
51 | } | ||
52 | |||
53 | return output; | ||
54 | } | ||
55 | |||
56 | override evaluateSamples(GraphStatistic g) { | ||
57 | return calculateMetric(g); | ||
58 | } | ||
59 | |||
60 | } \ 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 @@ | |||
1 | package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.predictor | ||
2 | |||
3 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.distance.StateData | ||
4 | import java.util.ArrayList | ||
5 | import java.util.HashMap | ||
6 | import java.util.List | ||
7 | import java.util.Map | ||
8 | import weka.core.matrix.LinearRegression | ||
9 | import weka.core.matrix.Matrix | ||
10 | |||
11 | class LinearModel { | ||
12 | var double ridge; | ||
13 | var Map<Object, StateData> stateAndHistory; | ||
14 | List<StateData> samples; | ||
15 | |||
16 | new(double ridge){ | ||
17 | this.ridge = ridge; | ||
18 | stateAndHistory = new HashMap<Object, StateData>(); | ||
19 | samples = new ArrayList<StateData>(); | ||
20 | } | ||
21 | |||
22 | /** | ||
23 | * reset the current train data for regression to a new trajectory | ||
24 | * @param state: the last state of the trajectory | ||
25 | */ | ||
26 | def resetRegression(Object state){ | ||
27 | samples.clear(); | ||
28 | |||
29 | if(stateAndHistory.containsKey(state)){ | ||
30 | var data = stateAndHistory.get(state); | ||
31 | var curState = state; | ||
32 | |||
33 | samples.add(data); | ||
34 | |||
35 | //loop through data until the oldest state in the record | ||
36 | while(stateAndHistory.containsKey(data.lastState) && data.lastState != curState){ | ||
37 | curState = data.lastState; | ||
38 | data = stateAndHistory.get(data.lastState); | ||
39 | samples.add(data); | ||
40 | } | ||
41 | } | ||
42 | } | ||
43 | |||
44 | /** | ||
45 | * Add a new data point to the current training set | ||
46 | * @param state: the state on which the new data point is calculated | ||
47 | * @param features: the set of feature value(x) | ||
48 | * @param value: the value of the state (y) | ||
49 | * @param lastState: the state which transformed to current state, used to record the trajectory | ||
50 | */ | ||
51 | def feedData(Object state, double[] features, double value, Object lastState){ | ||
52 | var data = new StateData(features, value, lastState); | ||
53 | stateAndHistory.put(state, data); | ||
54 | samples.add(data); | ||
55 | } | ||
56 | |||
57 | /** | ||
58 | * get prediction for next state, without storing the data point into the training set | ||
59 | * @param features: the feature values of current state | ||
60 | * @param value: the value of the current state | ||
61 | * @param: featuresToPredict: the features of the state wanted to be predected | ||
62 | * @return the value of the state to be predicted | ||
63 | */ | ||
64 | def double getPredictionForNextDataSample(double[] features, double value, double[] featuresToPredict){ | ||
65 | var data = new StateData(features, value, null); | ||
66 | samples.add(data); | ||
67 | |||
68 | // create training set from current data | ||
69 | val double[][] xSamples = samples.map[it.features]; | ||
70 | val double[] ySamples = samples.map[it.value]; | ||
71 | |||
72 | val x = new Matrix(xSamples); | ||
73 | val y = new Matrix(ySamples, ySamples.size()); | ||
74 | |||
75 | val regression = new LinearRegression(x, y, ridge); | ||
76 | var prediction = predict(regression.coefficients, featuresToPredict); | ||
77 | |||
78 | //remove the last element just added | ||
79 | samples.remove(samples.size - 1); | ||
80 | return prediction; | ||
81 | } | ||
82 | |||
83 | def private predict(double[] parameters, double[] featuresToPredict){ | ||
84 | // the regression will add an initial column for 1's, the first parameter is constant term | ||
85 | var result = parameters.get(0); | ||
86 | for(var i = 0; i < featuresToPredict.length; i++){ | ||
87 | result += parameters.get(i) * featuresToPredict.get(i); | ||
88 | } | ||
89 | return result; | ||
90 | } | ||
91 | } \ 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 @@ | |||
1 | package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.validation | ||
2 | |||
3 | import java.util.ArrayList | ||
4 | import java.util.HashMap | ||
5 | import java.util.List | ||
6 | import java.util.Map | ||
7 | import org.eclipse.emf.common.notify.Notifier | ||
8 | import org.eclipse.emf.common.util.URI | ||
9 | import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl | ||
10 | import org.eclipse.viatra.addon.validation.core.api.IConstraintSpecification | ||
11 | import org.eclipse.viatra.query.runtime.api.ViatraQueryEngine | ||
12 | import org.eclipse.viatra.query.runtime.api.impl.BaseGeneratedPatternGroup | ||
13 | import org.eclipse.viatra.query.runtime.emf.EMFScope | ||
14 | |||
15 | class ConstraintCollection{ | ||
16 | val constraints = new ArrayList<IConstraintSpecification>(); | ||
17 | var BaseGeneratedPatternGroup patterns; | ||
18 | var List<Notifier> resources = new ArrayList<Notifier>(); | ||
19 | |||
20 | |||
21 | new(List<IConstraintSpecification> constraints, List<String> uris, BaseGeneratedPatternGroup patterns){ | ||
22 | this.constraints.addAll(constraints); | ||
23 | this.patterns = patterns; | ||
24 | setURIs(uris); | ||
25 | } | ||
26 | |||
27 | new(List<IConstraintSpecification> constraints, BaseGeneratedPatternGroup patterns){ | ||
28 | this.constraints.addAll(constraints); | ||
29 | this.patterns = patterns; | ||
30 | } | ||
31 | |||
32 | def addModel(Notifier n ){ | ||
33 | resources.add(n); | ||
34 | } | ||
35 | |||
36 | def setURIs(List<String> uris){ | ||
37 | val resSet = new ResourceSetImpl(); | ||
38 | |||
39 | for(uri : uris){ | ||
40 | var resource = resSet.getResource(URI.createURI(uri), true); | ||
41 | resources.add(resource); | ||
42 | } | ||
43 | |||
44 | println('reading model finished') | ||
45 | } | ||
46 | |||
47 | def List<Integer> calculateViolations(){ | ||
48 | var results = new ArrayList<Integer>(); | ||
49 | |||
50 | for(resource : resources){ | ||
51 | val engine = initEngine(resource); | ||
52 | var matches = constraints.stream.mapToInt([ele| ele.querySpecification.getMatcher(engine).countMatches]).sum(); | ||
53 | results.add(matches); | ||
54 | } | ||
55 | |||
56 | return results; | ||
57 | } | ||
58 | |||
59 | def ArrayList<Map<String, Integer>> calculateViolationMaps(){ | ||
60 | val result = new ArrayList<Map<String, Integer>>() | ||
61 | |||
62 | for(resource : resources){ | ||
63 | val map = new HashMap<String, Integer>(); | ||
64 | val engine = initEngine(resource); | ||
65 | constraints.forEach[ | ||
66 | var count = it.querySpecification.getMatcher(engine).countMatches; | ||
67 | map.put(it.querySpecification.simpleName, count); | ||
68 | ]; | ||
69 | result.add(map); | ||
70 | } | ||
71 | return result; | ||
72 | } | ||
73 | |||
74 | private def initEngine(Notifier r){ | ||
75 | var engine = ViatraQueryEngine.on(new EMFScope(r)); | ||
76 | //init patterns with the new engine | ||
77 | patterns.prepare(engine); | ||
78 | return engine; | ||
79 | } | ||
80 | } | ||
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 @@ | |||
1 | package ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.validation | ||
2 | |||
3 | import ca.mcgill.ecse.dslreasoner.realistic.metrics.calculator.app.Domain | ||
4 | import com.google.common.reflect.ClassPath | ||
5 | import ecore.Ecore | ||
6 | import hu.bme.mit.inf.dslreasoner.partialsnapshot_mavo.yakindu.Patterns | ||
7 | import java.util.ArrayList | ||
8 | import org.eclipse.emf.ecore.EObject | ||
9 | import org.eclipse.viatra.addon.validation.core.api.IConstraintSpecification | ||
10 | |||
11 | class ViolationCheck { | ||
12 | /** | ||
13 | * Return the total number of violations | ||
14 | */ | ||
15 | def static int calculateViolationCounts(EObject root, Domain d){ | ||
16 | var packageName = ''; | ||
17 | if(d == Domain.Yakindumm){ | ||
18 | packageName = 'constraints.yakindumm'; | ||
19 | }else if (d == Domain.Ecore){ | ||
20 | packageName = 'constraints.ecore'; | ||
21 | }else if (d == Domain.Github){ | ||
22 | return -1; | ||
23 | } | ||
24 | |||
25 | var constriants = loadConstraints(packageName); | ||
26 | var collections = new ConstraintCollection(constriants, Ecore.instance); | ||
27 | collections.addModel(root); | ||
28 | var results = collections.calculateViolations(); | ||
29 | if(results.size > 0){ | ||
30 | return results.get(0); | ||
31 | }else{ | ||
32 | throw new IllegalArgumentException("Calculate Violation Failed"); | ||
33 | } | ||
34 | } | ||
35 | |||
36 | /** | ||
37 | * return a map contain the count for each type of violation | ||
38 | */ | ||
39 | def static violationMaps(EObject root){ | ||
40 | var constriants = loadConstraints('hu.bme.mit.inf.dslreasoner.partialsnapshot_mavo.yakindu'); | ||
41 | var collections = new ConstraintCollection(constriants, Patterns.instance); | ||
42 | collections.addModel(root); | ||
43 | var results = collections.calculateViolationMaps(); | ||
44 | if(results.size > 0){ | ||
45 | return results.get(0); | ||
46 | }else{ | ||
47 | throw new IllegalArgumentException("Calculate Violation Failed"); | ||
48 | } | ||
49 | } | ||
50 | |||
51 | def static loadConstraints(String packageName){ | ||
52 | val constraints = new ArrayList<IConstraintSpecification>(); | ||
53 | |||
54 | val classPath = ClassPath.from(ClassLoader.systemClassLoader); | ||
55 | val classInfos = classPath.getTopLevelClasses(packageName); | ||
56 | for(info : classInfos){ | ||
57 | if(info.load.interfaces.contains(IConstraintSpecification)){ | ||
58 | //IConstraintSpecification only has one constructor with empty argument list | ||
59 | var constructor = info.load.constructors.get(0); | ||
60 | var instance = constructor.newInstance(); | ||
61 | constraints.add(instance as IConstraintSpecification); | ||
62 | } | ||
63 | } | ||
64 | return constraints | ||
65 | } | ||
66 | } | ||