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.xtend516
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 @@
1package hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra.cardinality
2
3import com.google.common.collect.ImmutableList
4import com.google.common.collect.ImmutableMap
5import com.google.common.collect.ImmutableSet
6import com.google.common.collect.Maps
7import com.google.common.collect.Sets
8import hu.bme.mit.inf.dslreasoner.logic.model.logiclanguage.Relation
9import hu.bme.mit.inf.dslreasoner.logic.model.logiclanguage.Type
10import hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra.ModelGenerationStatistics
11import hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra.patterns.UnifinishedMultiplicityQueries
12import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.partialinterpretation.PartialComplexTypeInterpretation
13import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.partialinterpretation.PartialInterpretation
14import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.partialinterpretation.PartialPrimitiveInterpretation
15import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.partialinterpretation.Scope
16import java.util.ArrayDeque
17import java.util.ArrayList
18import java.util.HashMap
19import java.util.HashSet
20import java.util.List
21import java.util.Map
22import java.util.Set
23import javax.naming.OperationNotSupportedException
24import org.eclipse.viatra.query.runtime.api.IPatternMatch
25import org.eclipse.viatra.query.runtime.api.IQuerySpecification
26import org.eclipse.viatra.query.runtime.api.ViatraQueryEngine
27import org.eclipse.viatra.query.runtime.api.ViatraQueryMatcher
28import org.eclipse.viatra.query.runtime.emf.EMFScope
29import org.eclipse.xtend.lib.annotations.FinalFieldsConstructor
30
31class 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}