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