diff options
Diffstat (limited to 'Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra/src/hu/bme/mit/inf/dslreasoner/viatrasolver/logic2viatra/cardinality/PolyhedronScopePropagator.xtend')
-rw-r--r-- | Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra/src/hu/bme/mit/inf/dslreasoner/viatrasolver/logic2viatra/cardinality/PolyhedronScopePropagator.xtend | 516 |
1 files changed, 516 insertions, 0 deletions
diff --git a/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra/src/hu/bme/mit/inf/dslreasoner/viatrasolver/logic2viatra/cardinality/PolyhedronScopePropagator.xtend b/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra/src/hu/bme/mit/inf/dslreasoner/viatrasolver/logic2viatra/cardinality/PolyhedronScopePropagator.xtend new file mode 100644 index 00000000..7c05e818 --- /dev/null +++ b/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra/src/hu/bme/mit/inf/dslreasoner/viatrasolver/logic2viatra/cardinality/PolyhedronScopePropagator.xtend | |||
@@ -0,0 +1,516 @@ | |||
1 | package hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra.cardinality | ||
2 | |||
3 | import com.google.common.collect.ImmutableList | ||
4 | import com.google.common.collect.ImmutableMap | ||
5 | import com.google.common.collect.ImmutableSet | ||
6 | import com.google.common.collect.Maps | ||
7 | import com.google.common.collect.Sets | ||
8 | import hu.bme.mit.inf.dslreasoner.logic.model.logiclanguage.Relation | ||
9 | import hu.bme.mit.inf.dslreasoner.logic.model.logiclanguage.Type | ||
10 | import hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra.ModelGenerationStatistics | ||
11 | import hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra.patterns.UnifinishedMultiplicityQueries | ||
12 | import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.partialinterpretation.PartialComplexTypeInterpretation | ||
13 | import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.partialinterpretation.PartialInterpretation | ||
14 | import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.partialinterpretation.PartialPrimitiveInterpretation | ||
15 | import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.partialinterpretation.Scope | ||
16 | import java.util.ArrayDeque | ||
17 | import java.util.ArrayList | ||
18 | import java.util.HashMap | ||
19 | import java.util.HashSet | ||
20 | import java.util.List | ||
21 | import java.util.Map | ||
22 | import java.util.Set | ||
23 | import javax.naming.OperationNotSupportedException | ||
24 | import org.eclipse.viatra.query.runtime.api.IPatternMatch | ||
25 | import org.eclipse.viatra.query.runtime.api.IQuerySpecification | ||
26 | import org.eclipse.viatra.query.runtime.api.ViatraQueryEngine | ||
27 | import org.eclipse.viatra.query.runtime.api.ViatraQueryMatcher | ||
28 | import org.eclipse.viatra.query.runtime.emf.EMFScope | ||
29 | import org.eclipse.xtend.lib.annotations.FinalFieldsConstructor | ||
30 | |||
31 | class PolyhedronScopePropagator extends TypeHierarchyScopePropagator { | ||
32 | val Map<Scope, LinearBoundedExpression> scopeBounds | ||
33 | val LinearBoundedExpression topLevelBounds | ||
34 | val Polyhedron polyhedron | ||
35 | val PolyhedronSaturationOperator operator | ||
36 | val Set<Relation> relevantRelations | ||
37 | List<RelationConstraintUpdater> updaters = emptyList | ||
38 | |||
39 | new(PartialInterpretation p, ModelGenerationStatistics statistics, Set<? extends Type> possibleNewDynamicTypes, | ||
40 | Map<RelationMultiplicityConstraint, UnifinishedMultiplicityQueries> unfinishedMultiplicityQueries, | ||
41 | IQuerySpecification<? extends ViatraQueryMatcher<? extends IPatternMatch>> hasElementInContainmentQuery, | ||
42 | PolyhedronSolver solver, boolean propagateRelations) { | ||
43 | super(p, statistics) | ||
44 | val builder = new PolyhedronBuilder(p) | ||
45 | builder.buildPolyhedron(possibleNewDynamicTypes) | ||
46 | scopeBounds = builder.scopeBounds | ||
47 | topLevelBounds = builder.topLevelBounds | ||
48 | polyhedron = builder.polyhedron | ||
49 | operator = solver.createSaturationOperator(polyhedron) | ||
50 | if (propagateRelations) { | ||
51 | propagateAllScopeConstraints() | ||
52 | val maximumNumberOfNewNodes = topLevelBounds.upperBound | ||
53 | if (maximumNumberOfNewNodes === null) { | ||
54 | throw new IllegalStateException("Could not determine maximum number of new nodes, it may be unbounded") | ||
55 | } | ||
56 | if (maximumNumberOfNewNodes <= 0) { | ||
57 | throw new IllegalStateException("Maximum number of new nodes is not positive") | ||
58 | } | ||
59 | builder.buildMultiplicityConstraints(unfinishedMultiplicityQueries, hasElementInContainmentQuery, | ||
60 | maximumNumberOfNewNodes) | ||
61 | relevantRelations = builder.relevantRelations | ||
62 | updaters = builder.updaters | ||
63 | } else { | ||
64 | relevantRelations = emptySet | ||
65 | } | ||
66 | } | ||
67 | |||
68 | override void doPropagateAllScopeConstraints() { | ||
69 | resetBounds() | ||
70 | populatePolyhedronFromScope() | ||
71 | // println(polyhedron) | ||
72 | val result = operator.saturate() | ||
73 | // println(polyhedron) | ||
74 | if (result == PolyhedronSaturationResult.EMPTY) { | ||
75 | setScopesInvalid() | ||
76 | } else { | ||
77 | populateScopesFromPolyhedron() | ||
78 | if (result != PolyhedronSaturationResult.SATURATED) { | ||
79 | super.propagateAllScopeConstraints() | ||
80 | } | ||
81 | } | ||
82 | } | ||
83 | |||
84 | override propagateAdditionToRelation(Relation r) { | ||
85 | super.propagateAdditionToRelation(r) | ||
86 | if (relevantRelations.contains(r)) { | ||
87 | propagateAllScopeConstraints() | ||
88 | } | ||
89 | } | ||
90 | |||
91 | def resetBounds() { | ||
92 | for (dimension : polyhedron.dimensions) { | ||
93 | dimension.lowerBound = 0 | ||
94 | dimension.upperBound = null | ||
95 | } | ||
96 | for (constraint : polyhedron.constraints) { | ||
97 | constraint.lowerBound = null | ||
98 | constraint.upperBound = null | ||
99 | } | ||
100 | } | ||
101 | |||
102 | private def populatePolyhedronFromScope() { | ||
103 | topLevelBounds.tightenLowerBound(partialInterpretation.minNewElements) | ||
104 | if (partialInterpretation.maxNewElements >= 0) { | ||
105 | topLevelBounds.tightenUpperBound(partialInterpretation.maxNewElements) | ||
106 | } | ||
107 | for (pair : scopeBounds.entrySet) { | ||
108 | val scope = pair.key | ||
109 | val bounds = pair.value | ||
110 | bounds.tightenLowerBound(scope.minNewElements) | ||
111 | if (scope.maxNewElements >= 0) { | ||
112 | bounds.tightenUpperBound(scope.maxNewElements) | ||
113 | } | ||
114 | } | ||
115 | for (updater : updaters) { | ||
116 | updater.update(partialInterpretation) | ||
117 | } | ||
118 | } | ||
119 | |||
120 | private def populateScopesFromPolyhedron() { | ||
121 | checkBounds(topLevelBounds) | ||
122 | if (partialInterpretation.minNewElements > topLevelBounds.lowerBound) { | ||
123 | throw new IllegalArgumentException('''Lower bound of «topLevelBounds» smaller than top-level scope: «partialInterpretation.minNewElements»''') | ||
124 | } else if (partialInterpretation.minNewElements != topLevelBounds.lowerBound) { | ||
125 | partialInterpretation.minNewElements = topLevelBounds.lowerBound | ||
126 | } | ||
127 | val topLevelUpperBound = topLevelBounds.upperBound ?: -1 | ||
128 | if (partialInterpretation.maxNewElements >= 0 && topLevelUpperBound >= 0 && | ||
129 | partialInterpretation.maxNewElements < topLevelUpperBound) { | ||
130 | throw new IllegalArgumentException('''Upper bound of «topLevelBounds» larger than top-level scope: «partialInterpretation.maxNewElements»''') | ||
131 | } else if (partialInterpretation.maxNewElements != topLevelUpperBound) { | ||
132 | partialInterpretation.maxNewElements = topLevelUpperBound | ||
133 | } | ||
134 | for (pair : scopeBounds.entrySet) { | ||
135 | val scope = pair.key | ||
136 | val bounds = pair.value | ||
137 | checkBounds(bounds) | ||
138 | if (scope.minNewElements > bounds.lowerBound) { | ||
139 | throw new IllegalArgumentException('''Lower bound of «bounds» smaller than «scope.targetTypeInterpretation» scope: «scope.minNewElements»''') | ||
140 | } else if (scope.minNewElements != bounds.lowerBound) { | ||
141 | scope.minNewElements = bounds.lowerBound | ||
142 | } | ||
143 | val upperBound = bounds.upperBound ?: -1 | ||
144 | if (scope.maxNewElements >= 0 && upperBound >= 0 && scope.maxNewElements < upperBound) { | ||
145 | throw new IllegalArgumentException('''Upper bound of «bounds» larger than «scope.targetTypeInterpretation» scope: «scope.maxNewElements»''') | ||
146 | } else if (scope.maxNewElements != upperBound) { | ||
147 | scope.maxNewElements = upperBound | ||
148 | } | ||
149 | } | ||
150 | } | ||
151 | |||
152 | private def checkBounds(LinearBoundedExpression bounds) { | ||
153 | if (bounds.lowerBound === null) { | ||
154 | throw new IllegalArgumentException("Infinite lower bound: " + bounds) | ||
155 | } else if (bounds.lowerBound < 0) { | ||
156 | throw new IllegalArgumentException("Negative lower bound: " + bounds) | ||
157 | } | ||
158 | if (bounds.upperBound !== null && bounds.upperBound < 0) { | ||
159 | throw new IllegalArgumentException("Negative upper bound: " + bounds) | ||
160 | } | ||
161 | } | ||
162 | |||
163 | private def setScopesInvalid() { | ||
164 | partialInterpretation.minNewElements = Integer.MAX_VALUE | ||
165 | partialInterpretation.maxNewElements = 0 | ||
166 | for (scope : partialInterpretation.scopes) { | ||
167 | scope.minNewElements = Integer.MAX_VALUE | ||
168 | scope.maxNewElements = 0 | ||
169 | } | ||
170 | } | ||
171 | |||
172 | private static def <T extends IPatternMatch> getCalculatedMultiplicity(ViatraQueryMatcher<T> matcher, | ||
173 | PartialInterpretation p) { | ||
174 | val match = matcher.newEmptyMatch | ||
175 | match.set(0, p.problem) | ||
176 | match.set(1, p) | ||
177 | val iterator = matcher.streamAllMatches(match).iterator | ||
178 | if (!iterator.hasNext) { | ||
179 | return null | ||
180 | } | ||
181 | val value = iterator.next.get(2) as Integer | ||
182 | if (iterator.hasNext) { | ||
183 | throw new IllegalArgumentException("Multiplicity calculation query has more than one match") | ||
184 | } | ||
185 | value | ||
186 | } | ||
187 | |||
188 | @FinalFieldsConstructor | ||
189 | private static class PolyhedronBuilder { | ||
190 | static val INFINITY_SCALE = 10 | ||
191 | |||
192 | val PartialInterpretation p | ||
193 | |||
194 | Map<Type, Dimension> instanceCounts | ||
195 | Map<Type, Map<Dimension, Integer>> subtypeDimensions | ||
196 | Map<Map<Dimension, Integer>, LinearBoundedExpression> expressionsCache | ||
197 | Map<Type, LinearBoundedExpression> typeBounds | ||
198 | int infinity | ||
199 | ViatraQueryEngine queryEngine | ||
200 | ImmutableList.Builder<RelationConstraintUpdater> updatersBuilder | ||
201 | |||
202 | Map<Scope, LinearBoundedExpression> scopeBounds | ||
203 | LinearBoundedExpression topLevelBounds | ||
204 | Polyhedron polyhedron | ||
205 | Set<Relation> relevantRelations | ||
206 | List<RelationConstraintUpdater> updaters | ||
207 | |||
208 | def buildPolyhedron(Set<? extends Type> possibleNewDynamicTypes) { | ||
209 | instanceCounts = possibleNewDynamicTypes.toInvertedMap[new Dimension(name, 0, null)] | ||
210 | val types = p.problem.types | ||
211 | expressionsCache = Maps.newHashMapWithExpectedSize(types.size) | ||
212 | subtypeDimensions = types.toInvertedMap[findSubtypeDimensions.toInvertedMap[1]] | ||
213 | typeBounds = ImmutableMap.copyOf(subtypeDimensions.mapValues[toExpression]) | ||
214 | scopeBounds = buildScopeBounds | ||
215 | topLevelBounds = instanceCounts.values.toInvertedMap[1].toExpression | ||
216 | val dimensions = ImmutableList.copyOf(instanceCounts.values) | ||
217 | val expressionsToSaturate = ImmutableList.copyOf(scopeBounds.values) | ||
218 | polyhedron = new Polyhedron(dimensions, new ArrayList, expressionsToSaturate) | ||
219 | addCachedConstraintsToPolyhedron() | ||
220 | } | ||
221 | |||
222 | def buildMultiplicityConstraints( | ||
223 | Map<RelationMultiplicityConstraint, UnifinishedMultiplicityQueries> constraints, | ||
224 | IQuerySpecification<? extends ViatraQueryMatcher<? extends IPatternMatch>> hasElementInContainmentQuery, | ||
225 | int maximumNuberOfNewNodes) { | ||
226 | infinity = maximumNuberOfNewNodes * INFINITY_SCALE | ||
227 | queryEngine = ViatraQueryEngine.on(new EMFScope(p)) | ||
228 | updatersBuilder = ImmutableList.builder | ||
229 | val containmentConstraints = constraints.entrySet.filter[key.containment].groupBy[key.targetType] | ||
230 | for (pair : containmentConstraints.entrySet) { | ||
231 | buildContainmentConstraints(pair.key, pair.value) | ||
232 | } | ||
233 | buildConstainmentRootConstraints(containmentConstraints.keySet, hasElementInContainmentQuery) | ||
234 | for (pair : constraints.entrySet) { | ||
235 | val constraint = pair.key | ||
236 | if (!constraint.containment) { | ||
237 | buildNonContainmentConstraints(constraint, pair.value) | ||
238 | } | ||
239 | } | ||
240 | buildRelevantRelations(constraints.keySet) | ||
241 | updaters = updatersBuilder.build | ||
242 | addCachedConstraintsToPolyhedron() | ||
243 | } | ||
244 | |||
245 | private def buildRelevantRelations(Set<RelationMultiplicityConstraint> constraints) { | ||
246 | val builder = ImmutableSet.builder | ||
247 | for (constraint : constraints) { | ||
248 | builder.add(constraint.relation) | ||
249 | if (constraint.inverseRelation !== null) { | ||
250 | builder.add(constraint.inverseRelation) | ||
251 | } | ||
252 | } | ||
253 | relevantRelations = builder.build | ||
254 | } | ||
255 | |||
256 | private def addCachedConstraintsToPolyhedron() { | ||
257 | val constraints = new HashSet | ||
258 | constraints.addAll(expressionsCache.values.filter(LinearConstraint)) | ||
259 | constraints.removeAll(polyhedron.constraints) | ||
260 | polyhedron.constraints.addAll(constraints) | ||
261 | } | ||
262 | |||
263 | private def buildContainmentConstraints(Type containedType, | ||
264 | List<Map.Entry<RelationMultiplicityConstraint, UnifinishedMultiplicityQueries>> constraints) { | ||
265 | val typeCoefficients = subtypeDimensions.get(containedType) | ||
266 | val orphansLowerBoundCoefficients = new HashMap(typeCoefficients) | ||
267 | val orphansUpperBoundCoefficients = new HashMap(typeCoefficients) | ||
268 | val unfinishedMultiplicitiesMatchersBuilder = ImmutableList.builder | ||
269 | val remainingContentsQueriesBuilder = ImmutableList.builder | ||
270 | for (pair : constraints) { | ||
271 | val constraint = pair.key | ||
272 | val containerCoefficients = subtypeDimensions.get(constraint.sourceType) | ||
273 | if (constraint.isUpperBoundFinite) { | ||
274 | orphansLowerBoundCoefficients.addCoefficients(-constraint.upperBound, containerCoefficients) | ||
275 | } else { | ||
276 | orphansLowerBoundCoefficients.addCoefficients(-infinity, containerCoefficients) | ||
277 | } | ||
278 | orphansUpperBoundCoefficients.addCoefficients(-constraint.lowerBound, containerCoefficients) | ||
279 | val queries = pair.value | ||
280 | if (constraint.constrainsUnfinished) { | ||
281 | if (queries.unfinishedMultiplicityQuery === null) { | ||
282 | throw new IllegalArgumentException( | ||
283 | "Containment constraints need unfinished multiplicity queries") | ||
284 | } | ||
285 | unfinishedMultiplicitiesMatchersBuilder.add( | ||
286 | queries.unfinishedMultiplicityQuery.getMatcher(queryEngine)) | ||
287 | } | ||
288 | if (queries.remainingContentsQuery === null) { | ||
289 | throw new IllegalArgumentException("Containment constraints need remaining contents queries") | ||
290 | } | ||
291 | remainingContentsQueriesBuilder.add(queries.remainingContentsQuery.getMatcher(queryEngine)) | ||
292 | } | ||
293 | val orphanLowerBound = orphansLowerBoundCoefficients.toExpression | ||
294 | val orphanUpperBound = orphansUpperBoundCoefficients.toExpression | ||
295 | val updater = new ContainmentConstraintUpdater(containedType.name, orphanLowerBound, orphanUpperBound, | ||
296 | unfinishedMultiplicitiesMatchersBuilder.build, remainingContentsQueriesBuilder.build) | ||
297 | updatersBuilder.add(updater) | ||
298 | } | ||
299 | |||
300 | private def buildConstainmentRootConstraints(Set<Type> containedTypes, | ||
301 | IQuerySpecification<? extends ViatraQueryMatcher<? extends IPatternMatch>> hasElementInContainmentQuery) { | ||
302 | val matcher = hasElementInContainmentQuery.getMatcher(queryEngine) | ||
303 | val rootDimensions = Sets.newHashSet(instanceCounts.values) | ||
304 | for (type : containedTypes) { | ||
305 | val containedDimensions = subtypeDimensions.get(type).keySet | ||
306 | rootDimensions.removeAll(containedDimensions) | ||
307 | } | ||
308 | for (dimension : rootDimensions) { | ||
309 | updatersBuilder.add(new ContainmentRootConstraintUpdater(dimension, matcher)) | ||
310 | } | ||
311 | } | ||
312 | |||
313 | private def buildNonContainmentConstraints(RelationMultiplicityConstraint constraint, | ||
314 | UnifinishedMultiplicityQueries queries) { | ||
315 | if (constraint.constrainsRemainingInverse) { | ||
316 | if (queries.unfinishedMultiplicityQuery === null) { | ||
317 | throw new IllegalArgumentException("Reference constraints need unfinished multiplicity queries") | ||
318 | } | ||
319 | val unfinishedMultiplicityMatcher = queries.unfinishedMultiplicityQuery.getMatcher(queryEngine) | ||
320 | if (queries.remainingInverseMultiplicityQuery === null) { | ||
321 | throw new IllegalArgumentException( | ||
322 | "Reference constraints need remaining inverse multiplicity queries") | ||
323 | } | ||
324 | val remainingInverseMultiplicityMatcher = queries.remainingInverseMultiplicityQuery.getMatcher( | ||
325 | queryEngine) | ||
326 | val availableMultiplicityCoefficients = new HashMap | ||
327 | availableMultiplicityCoefficients.addCoefficients(constraint.inverseUpperBound, | ||
328 | subtypeDimensions.get(constraint.targetType)) | ||
329 | availableMultiplicityCoefficients.addCoefficients(-constraint.lowerBound, | ||
330 | subtypeDimensions.get(constraint.targetType)) | ||
331 | val availableMultiplicity = availableMultiplicityCoefficients.toExpression | ||
332 | updatersBuilder.add( | ||
333 | new UnfinishedMultiplicityConstraintUpdater(constraint.relation.name, availableMultiplicity, | ||
334 | unfinishedMultiplicityMatcher, remainingInverseMultiplicityMatcher)) | ||
335 | } | ||
336 | if (constraint.constrainsUnrepairable) { | ||
337 | if (queries.unrepairableMultiplicityQuery === null) { | ||
338 | throw new IllegalArgumentException("Reference constraints need unrepairable multiplicity queries") | ||
339 | } | ||
340 | val unrepairableMultiplicityMatcher = queries.unrepairableMultiplicityQuery.getMatcher(queryEngine) | ||
341 | val targetTypeCardinality = typeBounds.get(constraint.targetType) | ||
342 | updatersBuilder.add( | ||
343 | new UnrepairableMultiplicityConstraintUpdater(constraint.relation.name, targetTypeCardinality, | ||
344 | unrepairableMultiplicityMatcher)) | ||
345 | } | ||
346 | } | ||
347 | |||
348 | private def addCoefficients(Map<Dimension, Integer> accumulator, int scale, Map<Dimension, Integer> a) { | ||
349 | for (pair : a.entrySet) { | ||
350 | val dimension = pair.key | ||
351 | val currentValue = accumulator.get(pair.key) ?: 0 | ||
352 | val newValue = currentValue + scale * pair.value | ||
353 | if (newValue == 0) { | ||
354 | accumulator.remove(dimension) | ||
355 | } else { | ||
356 | accumulator.put(dimension, newValue) | ||
357 | } | ||
358 | } | ||
359 | } | ||
360 | |||
361 | private def findSubtypeDimensions(Type type) { | ||
362 | val subtypes = new HashSet | ||
363 | val dimensions = new HashSet | ||
364 | val stack = new ArrayDeque | ||
365 | stack.addLast(type) | ||
366 | while (!stack.empty) { | ||
367 | val subtype = stack.removeLast | ||
368 | if (subtypes.add(subtype)) { | ||
369 | val dimension = instanceCounts.get(subtype) | ||
370 | if (dimension !== null) { | ||
371 | dimensions.add(dimension) | ||
372 | } | ||
373 | stack.addAll(subtype.subtypes) | ||
374 | } | ||
375 | } | ||
376 | dimensions | ||
377 | } | ||
378 | |||
379 | private def toExpression(Map<Dimension, Integer> coefficients) { | ||
380 | expressionsCache.computeIfAbsent(coefficients) [ c | | ||
381 | if (c.size == 1 && c.entrySet.head.value == 1) { | ||
382 | c.entrySet.head.key | ||
383 | } else { | ||
384 | new LinearConstraint(c, null, null) | ||
385 | } | ||
386 | ] | ||
387 | } | ||
388 | |||
389 | private def buildScopeBounds() { | ||
390 | val scopeBoundsBuilder = ImmutableMap.builder | ||
391 | for (scope : p.scopes) { | ||
392 | switch (targetTypeInterpretation : scope.targetTypeInterpretation) { | ||
393 | PartialPrimitiveInterpretation: | ||
394 | throw new OperationNotSupportedException("Primitive type scopes are not yet implemented") | ||
395 | PartialComplexTypeInterpretation: { | ||
396 | val complexType = targetTypeInterpretation.interpretationOf | ||
397 | val typeBound = typeBounds.get(complexType) | ||
398 | if (typeBound === null) { | ||
399 | if (scope.minNewElements > 0) { | ||
400 | throw new IllegalArgumentException("Found scope for " + complexType.name + | ||
401 | ", but the type cannot be instantiated") | ||
402 | } | ||
403 | } else { | ||
404 | scopeBoundsBuilder.put(scope, typeBound) | ||
405 | } | ||
406 | } | ||
407 | default: | ||
408 | throw new IllegalArgumentException("Unknown PartialTypeInterpretation: " + | ||
409 | targetTypeInterpretation) | ||
410 | } | ||
411 | } | ||
412 | scopeBoundsBuilder.build | ||
413 | } | ||
414 | } | ||
415 | |||
416 | private static interface RelationConstraintUpdater { | ||
417 | def void update(PartialInterpretation p) | ||
418 | } | ||
419 | |||
420 | @FinalFieldsConstructor | ||
421 | static class ContainmentConstraintUpdater implements RelationConstraintUpdater { | ||
422 | val String name | ||
423 | val LinearBoundedExpression orphansLowerBound | ||
424 | val LinearBoundedExpression orphansUpperBound | ||
425 | val List<ViatraQueryMatcher<? extends IPatternMatch>> unfinishedMultiplicitiesMatchers | ||
426 | val List<ViatraQueryMatcher<? extends IPatternMatch>> remainingContentsQueries | ||
427 | |||
428 | override update(PartialInterpretation p) { | ||
429 | tightenLowerBound(p) | ||
430 | tightenUpperBound(p) | ||
431 | } | ||
432 | |||
433 | private def tightenLowerBound(PartialInterpretation p) { | ||
434 | var int sum = 0 | ||
435 | for (matcher : remainingContentsQueries) { | ||
436 | val value = matcher.getCalculatedMultiplicity(p) | ||
437 | if (value === null) { | ||
438 | throw new IllegalArgumentException("Remaining contents count is missing for " + name) | ||
439 | } | ||
440 | if (value == -1) { | ||
441 | // Infinite upper bound, no need to tighten. | ||
442 | return | ||
443 | } | ||
444 | sum += value | ||
445 | } | ||
446 | orphansLowerBound.tightenUpperBound(sum) | ||
447 | } | ||
448 | |||
449 | private def tightenUpperBound(PartialInterpretation p) { | ||
450 | var int sum = 0 | ||
451 | for (matcher : unfinishedMultiplicitiesMatchers) { | ||
452 | val value = matcher.getCalculatedMultiplicity(p) | ||
453 | if (value === null) { | ||
454 | throw new IllegalArgumentException("Unfinished multiplicity is missing for " + name) | ||
455 | } | ||
456 | sum += value | ||
457 | } | ||
458 | orphansUpperBound.tightenLowerBound(sum) | ||
459 | } | ||
460 | } | ||
461 | |||
462 | @FinalFieldsConstructor | ||
463 | static class ContainmentRootConstraintUpdater implements RelationConstraintUpdater { | ||
464 | val LinearBoundedExpression typeCardinality | ||
465 | val ViatraQueryMatcher<? extends IPatternMatch> hasElementInContainmentMatcher | ||
466 | |||
467 | override update(PartialInterpretation p) { | ||
468 | if (hasElementInContainmentMatcher.hasMatch(p)) { | ||
469 | typeCardinality.tightenUpperBound(0) | ||
470 | } else { | ||
471 | typeCardinality.tightenUpperBound(1) | ||
472 | } | ||
473 | } | ||
474 | |||
475 | private static def <T extends IPatternMatch> hasMatch(ViatraQueryMatcher<T> matcher, PartialInterpretation p) { | ||
476 | val match = matcher.newMatch(p.problem, p) | ||
477 | matcher.countMatches(match) != 0 | ||
478 | } | ||
479 | } | ||
480 | |||
481 | @FinalFieldsConstructor | ||
482 | static class UnfinishedMultiplicityConstraintUpdater implements RelationConstraintUpdater { | ||
483 | val String name | ||
484 | val LinearBoundedExpression availableMultiplicityExpression | ||
485 | val ViatraQueryMatcher<? extends IPatternMatch> unfinishedMultiplicityMatcher | ||
486 | val ViatraQueryMatcher<? extends IPatternMatch> remainingInverseMultiplicityMatcher | ||
487 | |||
488 | override update(PartialInterpretation p) { | ||
489 | val unfinishedMultiplicity = unfinishedMultiplicityMatcher.getCalculatedMultiplicity(p) | ||
490 | if (unfinishedMultiplicity === null) { | ||
491 | throw new IllegalArgumentException("Unfinished multiplicity is missing for " + name) | ||
492 | } | ||
493 | val remainingInverseMultiplicity = remainingInverseMultiplicityMatcher.getCalculatedMultiplicity(p) | ||
494 | if (remainingInverseMultiplicity === null) { | ||
495 | throw new IllegalArgumentException("Remaining inverse multiplicity is missing for " + name) | ||
496 | } | ||
497 | val int requiredMultiplicity = unfinishedMultiplicity - remainingInverseMultiplicity | ||
498 | availableMultiplicityExpression.tightenLowerBound(requiredMultiplicity) | ||
499 | } | ||
500 | } | ||
501 | |||
502 | @FinalFieldsConstructor | ||
503 | static class UnrepairableMultiplicityConstraintUpdater implements RelationConstraintUpdater { | ||
504 | val String name | ||
505 | val LinearBoundedExpression targetCardinalityExpression | ||
506 | val ViatraQueryMatcher<? extends IPatternMatch> unrepairableMultiplicityMatcher | ||
507 | |||
508 | override update(PartialInterpretation p) { | ||
509 | val value = unrepairableMultiplicityMatcher.getCalculatedMultiplicity(p) | ||
510 | if (value === null) { | ||
511 | throw new IllegalArgumentException("Unrepairable multiplicity is missing for " + name) | ||
512 | } | ||
513 | targetCardinalityExpression.tightenLowerBound(value) | ||
514 | } | ||
515 | } | ||
516 | } | ||