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.xtend355
1 files changed, 289 insertions, 66 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
index cebd89da..4f0c8f20 100644
--- 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
@@ -2,90 +2,60 @@ package hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra.cardinality
2 2
3import com.google.common.collect.ImmutableList 3import com.google.common.collect.ImmutableList
4import com.google.common.collect.ImmutableMap 4import com.google.common.collect.ImmutableMap
5import com.google.common.collect.Maps
5import hu.bme.mit.inf.dslreasoner.logic.model.logiclanguage.Type 6import hu.bme.mit.inf.dslreasoner.logic.model.logiclanguage.Type
7import hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra.patterns.UnifinishedMultiplicityQueries
6import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.partialinterpretation.PartialComplexTypeInterpretation 8import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.partialinterpretation.PartialComplexTypeInterpretation
7import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.partialinterpretation.PartialInterpretation 9import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.partialinterpretation.PartialInterpretation
8import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.partialinterpretation.PartialPrimitiveInterpretation 10import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.partialinterpretation.PartialPrimitiveInterpretation
9import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.partialinterpretation.Scope 11import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.partialinterpretation.Scope
10import java.util.ArrayDeque 12import java.util.ArrayDeque
13import java.util.ArrayList
11import java.util.HashMap 14import java.util.HashMap
12import java.util.HashSet 15import java.util.HashSet
16import java.util.List
13import java.util.Map 17import java.util.Map
14import java.util.Set 18import java.util.Set
19import javax.naming.OperationNotSupportedException
20import org.eclipse.viatra.query.runtime.api.IPatternMatch
21import org.eclipse.viatra.query.runtime.api.ViatraQueryEngine
22import org.eclipse.viatra.query.runtime.api.ViatraQueryMatcher
23import org.eclipse.viatra.query.runtime.emf.EMFScope
24import org.eclipse.xtend.lib.annotations.FinalFieldsConstructor
15 25
16class PolyhedronScopePropagator extends ScopePropagator { 26class PolyhedronScopePropagator extends ScopePropagator {
17 val Map<Scope, LinearBoundedExpression> scopeBounds 27 val Map<Scope, LinearBoundedExpression> scopeBounds
18 val LinearConstraint topLevelBounds 28 val LinearBoundedExpression topLevelBounds
29 val Polyhedron polyhedron
19 val PolyhedronSaturationOperator operator 30 val PolyhedronSaturationOperator operator
31 List<RelationConstraintUpdater> updaters = emptyList
20 32
21 new(PartialInterpretation p, Set<? extends Type> possibleNewDynamicTypes, PolyhedronSolver solver) { 33 new(PartialInterpretation p, Set<? extends Type> possibleNewDynamicTypes,
34 Map<RelationMultiplicityConstraint, UnifinishedMultiplicityQueries> unfinishedMultiplicityQueries,
35 PolyhedronSolver solver, boolean propagateRelations) {
22 super(p) 36 super(p)
23 val instanceCounts = possibleNewDynamicTypes.toInvertedMap[new Dimension(name, 0, null)] 37 val builder = new PolyhedronBuilder(p)
24 val primitiveDimensions = new HashMap 38 builder.buildPolyhedron(possibleNewDynamicTypes)
25 val constraintsBuilder = ImmutableList.builder 39 scopeBounds = builder.scopeBounds
26 val scopeBoundsBuilder = ImmutableMap.builder 40 topLevelBounds = builder.topLevelBounds
27 // Dimensions for instantiable types were created according to the type analysis, 41 polyhedron = builder.polyhedron
28 // but for any possible primitive types, we create them on demand,
29 // as there is no Type directly associated with a PartialPrimitiveInterpretation.
30 // Below we will assume that each PartialTypeInterpretation has at most one Scope.
31 for (scope : p.scopes) {
32 switch (targetTypeInterpretation : scope.targetTypeInterpretation) {
33 PartialPrimitiveInterpretation: {
34 val dimension = primitiveDimensions.computeIfAbsent(targetTypeInterpretation) [ interpretation |
35 new Dimension(interpretation.eClass.name, 0, null)
36 ]
37 scopeBoundsBuilder.put(scope, dimension)
38 }
39 PartialComplexTypeInterpretation: {
40 val complexType = targetTypeInterpretation.interpretationOf
41 val dimensions = findSubtypeDimensions(complexType, instanceCounts)
42 switch (dimensions.size) {
43 case 0:
44 if (scope.minNewElements > 0) {
45 throw new IllegalArgumentException("Found scope for " + complexType.name +
46 ", but the type cannot be instantiated")
47 }
48 case 1:
49 scopeBoundsBuilder.put(scope, dimensions.head)
50 default: {
51 val constraint = new LinearConstraint(dimensions.toInvertedMap[1], null, null)
52 constraintsBuilder.add(constraint)
53 scopeBoundsBuilder.put(scope, constraint)
54 }
55 }
56 }
57 default:
58 throw new IllegalArgumentException("Unknown PartialTypeInterpretation: " + targetTypeInterpretation)
59 }
60 }
61 val allDimensions = ImmutableList.builder.addAll(instanceCounts.values).addAll(primitiveDimensions.values).build
62 scopeBounds = scopeBoundsBuilder.build
63 topLevelBounds = new LinearConstraint(allDimensions.toInvertedMap[1], null, null)
64 constraintsBuilder.add(topLevelBounds)
65 val expressionsToSaturate = ImmutableList.builder.addAll(scopeBounds.values).add(topLevelBounds).build
66 val polyhedron = new Polyhedron(allDimensions, constraintsBuilder.build, expressionsToSaturate)
67 operator = solver.createSaturationOperator(polyhedron) 42 operator = solver.createSaturationOperator(polyhedron)
68 } 43 if (propagateRelations) {
69 44 propagateAllScopeConstraints()
70 private def findSubtypeDimensions(Type type, Map<Type, Dimension> instanceCounts) { 45 val maximumNumberOfNewNodes = topLevelBounds.upperBound
71 val subtypes = new HashSet 46 if (maximumNumberOfNewNodes === null) {
72 val dimensions = new HashSet 47 throw new IllegalStateException("Could not determine maximum number of new nodes, it may be unbounded")
73 val stack = new ArrayDeque 48 }
74 stack.addLast(type) 49 if (maximumNumberOfNewNodes <= 0) {
75 while (!stack.empty) { 50 throw new IllegalStateException("Maximum number of new nodes is negative")
76 val subtype = stack.removeLast
77 if (subtypes.add(subtype)) {
78 val dimension = instanceCounts.get(subtype)
79 if (dimension !== null) {
80 dimensions.add(dimension)
81 }
82 stack.addAll(subtype.subtypes)
83 } 51 }
52 builder.buildMultiplicityConstraints(unfinishedMultiplicityQueries, maximumNumberOfNewNodes)
53 updaters = builder.updaters
84 } 54 }
85 dimensions
86 } 55 }
87 56
88 override void propagateAllScopeConstraints() { 57 override void propagateAllScopeConstraints() {
58 resetBounds()
89 populatePolyhedronFromScope() 59 populatePolyhedronFromScope()
90 val result = operator.saturate() 60 val result = operator.saturate()
91 if (result == PolyhedronSaturationResult.EMPTY) { 61 if (result == PolyhedronSaturationResult.EMPTY) {
@@ -96,21 +66,36 @@ class PolyhedronScopePropagator extends ScopePropagator {
96 super.propagateAllScopeConstraints() 66 super.propagateAllScopeConstraints()
97 } 67 }
98 } 68 }
69 // println(polyhedron)
70 }
71
72 def resetBounds() {
73 for (dimension : polyhedron.dimensions) {
74 dimension.lowerBound = 0
75 dimension.upperBound = null
76 }
77 for (constraint : polyhedron.constraints) {
78 constraint.lowerBound = null
79 constraint.upperBound = null
80 }
99 } 81 }
100 82
101 private def populatePolyhedronFromScope() { 83 private def populatePolyhedronFromScope() {
102 topLevelBounds.lowerBound = partialInterpretation.minNewElements 84 topLevelBounds.tightenLowerBound(partialInterpretation.minNewElements)
103 if (partialInterpretation.maxNewElements >= 0) { 85 if (partialInterpretation.maxNewElements >= 0) {
104 topLevelBounds.upperBound = partialInterpretation.maxNewElements 86 topLevelBounds.tightenUpperBound(partialInterpretation.maxNewElements)
105 } 87 }
106 for (pair : scopeBounds.entrySet) { 88 for (pair : scopeBounds.entrySet) {
107 val scope = pair.key 89 val scope = pair.key
108 val bounds = pair.value 90 val bounds = pair.value
109 bounds.lowerBound = scope.minNewElements 91 bounds.tightenLowerBound(scope.minNewElements)
110 if (scope.maxNewElements >= 0) { 92 if (scope.maxNewElements >= 0) {
111 bounds.upperBound = scope.maxNewElements 93 bounds.tightenUpperBound(scope.maxNewElements)
112 } 94 }
113 } 95 }
96 for (updater : updaters) {
97 updater.update(partialInterpretation)
98 }
114 } 99 }
115 100
116 private def populateScopesFromPolyhedron() { 101 private def populateScopesFromPolyhedron() {
@@ -151,4 +136,242 @@ class PolyhedronScopePropagator extends ScopePropagator {
151 throw new IllegalArgumentException("Infinite upper bound: " + bounds) 136 throw new IllegalArgumentException("Infinite upper bound: " + bounds)
152 } 137 }
153 } 138 }
139
140 private static def <T extends IPatternMatch> getCalculatedMultiplicity(ViatraQueryMatcher<T> matcher,
141 PartialInterpretation p) {
142 val match = matcher.newEmptyMatch
143 match.set(0, p.problem)
144 match.set(1, p)
145 val iterator = matcher.streamAllMatches(match).iterator
146 if (!iterator.hasNext) {
147 return null
148 }
149 val value = iterator.next.get(2) as Integer
150 if (iterator.hasNext) {
151 throw new IllegalArgumentException("Multiplicity calculation query has more than one match")
152 }
153 value
154 }
155
156 @FinalFieldsConstructor
157 private static class PolyhedronBuilder {
158 static val INFINITY_SCALE = 10
159
160 val PartialInterpretation p
161
162 Map<Type, Dimension> instanceCounts
163 Map<Type, Map<Dimension, Integer>> subtypeDimensions
164 Map<Map<Dimension, Integer>, LinearBoundedExpression> expressionsCache
165 Map<Type, LinearBoundedExpression> typeBounds
166 int infinity
167 ViatraQueryEngine queryEngine
168 ImmutableList.Builder<RelationConstraintUpdater> updatersBuilder
169
170 Map<Scope, LinearBoundedExpression> scopeBounds
171 LinearBoundedExpression topLevelBounds
172 Polyhedron polyhedron
173 List<RelationConstraintUpdater> updaters
174
175 def buildPolyhedron(Set<? extends Type> possibleNewDynamicTypes) {
176 instanceCounts = possibleNewDynamicTypes.toInvertedMap[new Dimension(name, 0, null)]
177 val types = p.problem.types
178 expressionsCache = Maps.newHashMapWithExpectedSize(types.size)
179 subtypeDimensions = types.toInvertedMap[findSubtypeDimensions.toInvertedMap[1]]
180 typeBounds = ImmutableMap.copyOf(subtypeDimensions.mapValues[toExpression])
181 scopeBounds = buildScopeBounds
182 topLevelBounds = instanceCounts.values.toInvertedMap[1].toExpression
183 val dimensions = ImmutableList.copyOf(instanceCounts.values)
184 val expressionsToSaturate = ImmutableList.copyOf(scopeBounds.values)
185 polyhedron = new Polyhedron(dimensions, new ArrayList, expressionsToSaturate)
186 addCachedConstraintsToPolyhedron()
187 }
188
189 def buildMultiplicityConstraints(
190 Map<RelationMultiplicityConstraint, UnifinishedMultiplicityQueries> constraints,
191 int maximumNuberOfNewNodes) {
192 infinity = maximumNuberOfNewNodes * INFINITY_SCALE
193 queryEngine = ViatraQueryEngine.on(new EMFScope(p))
194 updatersBuilder = ImmutableList.builder
195 for (pair : constraints.entrySet.filter[key.containment].groupBy[key.targetType].entrySet) {
196 buildContainmentConstraints(pair.key, pair.value)
197 }
198 for (pair : constraints.entrySet) {
199 val constraint = pair.key
200 if (!constraint.containment) {
201 buildNonContainmentConstraints(constraint, pair.value)
202 }
203 }
204 updaters = updatersBuilder.build
205 addCachedConstraintsToPolyhedron()
206 }
207
208 private def addCachedConstraintsToPolyhedron() {
209 val constraints = new HashSet
210 constraints.addAll(expressionsCache.values.filter(LinearConstraint))
211 constraints.removeAll(polyhedron.constraints)
212 polyhedron.constraints.addAll(constraints)
213 }
214
215 private def buildContainmentConstraints(Type containedType,
216 List<Map.Entry<RelationMultiplicityConstraint, UnifinishedMultiplicityQueries>> constraints) {
217 val typeCoefficients = subtypeDimensions.get(containedType)
218 val orphansLowerBoundCoefficients = new HashMap(typeCoefficients)
219 val orphansUpperBoundCoefficients = new HashMap(typeCoefficients)
220 val unfinishedMultiplicitiesMatchersBuilder = ImmutableList.builder
221 val remainingContentsQueriesBuilder = ImmutableList.builder
222 for (pair : constraints) {
223 val constraint = pair.key
224 val containerCoefficients = subtypeDimensions.get(constraint.sourceType)
225 if (constraint.isUpperBoundFinite) {
226 orphansLowerBoundCoefficients.addCoefficients(-constraint.upperBound, containerCoefficients)
227 } else {
228 orphansLowerBoundCoefficients.addCoefficients(-infinity, containerCoefficients)
229 }
230 orphansUpperBoundCoefficients.addCoefficients(-constraint.lowerBound, containerCoefficients)
231 val queries = pair.value
232 if (constraint.constrainsUnfinished) {
233 if (queries.unfinishedMultiplicityQuery === null) {
234 throw new IllegalArgumentException(
235 "Containment constraints need unfinished multiplicity queries")
236 }
237 unfinishedMultiplicitiesMatchersBuilder.add(
238 queries.unfinishedMultiplicityQuery.getMatcher(queryEngine))
239 }
240 if (queries.remainingContentsQuery === null) {
241 throw new IllegalArgumentException("Containment constraints need remaining contents queries")
242 }
243 remainingContentsQueriesBuilder.add(queries.remainingContentsQuery.getMatcher(queryEngine))
244 }
245 val orphanLowerBound = orphansLowerBoundCoefficients.toExpression
246 val orphanUpperBound = orphansUpperBoundCoefficients.toExpression
247 val updater = new ContainmentConstraintUpdater(containedType.name, orphanLowerBound, orphanUpperBound,
248 unfinishedMultiplicitiesMatchersBuilder.build, remainingContentsQueriesBuilder.build)
249 updatersBuilder.add(updater)
250 }
251
252 private def buildNonContainmentConstraints(RelationMultiplicityConstraint constraint,
253 UnifinishedMultiplicityQueries queries) {
254 }
255
256 private def addCoefficients(Map<Dimension, Integer> accumulator, int scale, Map<Dimension, Integer> a) {
257 for (pair : a.entrySet) {
258 val dimension = pair.key
259 val currentValue = accumulator.get(pair.key) ?: 0
260 val newValue = currentValue + scale * pair.value
261 if (newValue == 0) {
262 accumulator.remove(dimension)
263 } else {
264 accumulator.put(dimension, newValue)
265 }
266 }
267 }
268
269 private def findSubtypeDimensions(Type type) {
270 val subtypes = new HashSet
271 val dimensions = new HashSet
272 val stack = new ArrayDeque
273 stack.addLast(type)
274 while (!stack.empty) {
275 val subtype = stack.removeLast
276 if (subtypes.add(subtype)) {
277 val dimension = instanceCounts.get(subtype)
278 if (dimension !== null) {
279 dimensions.add(dimension)
280 }
281 stack.addAll(subtype.subtypes)
282 }
283 }
284 dimensions
285 }
286
287 private def toExpression(Map<Dimension, Integer> coefficients) {
288 expressionsCache.computeIfAbsent(coefficients) [ c |
289 if (c.size == 1 && c.entrySet.head.value == 1) {
290 c.entrySet.head.key
291 } else {
292 new LinearConstraint(c, null, null)
293 }
294 ]
295 }
296
297 private def buildScopeBounds() {
298 val scopeBoundsBuilder = ImmutableMap.builder
299 for (scope : p.scopes) {
300 switch (targetTypeInterpretation : scope.targetTypeInterpretation) {
301 PartialPrimitiveInterpretation:
302 throw new OperationNotSupportedException("Primitive type scopes are not yet implemented")
303 PartialComplexTypeInterpretation: {
304 val complexType = targetTypeInterpretation.interpretationOf
305 val typeBound = typeBounds.get(complexType)
306 if (typeBound === null) {
307 if (scope.minNewElements > 0) {
308 throw new IllegalArgumentException("Found scope for " + complexType.name +
309 ", but the type cannot be instantiated")
310 }
311 } else {
312 scopeBoundsBuilder.put(scope, typeBound)
313 }
314 }
315 default:
316 throw new IllegalArgumentException("Unknown PartialTypeInterpretation: " +
317 targetTypeInterpretation)
318 }
319 }
320 scopeBoundsBuilder.build
321 }
322 }
323
324 private static interface RelationConstraintUpdater {
325 def void update(PartialInterpretation p)
326 }
327
328 @FinalFieldsConstructor
329 static class ContainmentConstraintUpdater implements RelationConstraintUpdater {
330 val String name
331 val LinearBoundedExpression orphansLowerBound
332 val LinearBoundedExpression orphansUpperBound
333 val List<ViatraQueryMatcher<? extends IPatternMatch>> unfinishedMultiplicitiesMatchers
334 val List<ViatraQueryMatcher<? extends IPatternMatch>> remainingContentsQueries
335
336 override update(PartialInterpretation p) {
337 tightenLowerBound(p)
338 tightenUpperBound(p)
339 }
340
341 private def tightenLowerBound(PartialInterpretation p) {
342 var int sum = 0
343 for (matcher : remainingContentsQueries) {
344 val value = matcher.getCalculatedMultiplicity(p)
345 if (value === null) {
346 throw new IllegalArgumentException("Remaining contents count is missing for " + name)
347 }
348 if (value == -1) {
349 // Infinite upper bound, no need to tighten.
350 return
351 }
352 sum += value
353 }
354 orphansLowerBound.tightenUpperBound(sum)
355 }
356
357 private def tightenUpperBound(PartialInterpretation p) {
358 var int sum = 0
359 for (matcher : unfinishedMultiplicitiesMatchers) {
360 val value = matcher.getCalculatedMultiplicity(p)
361 if (value === null) {
362 throw new IllegalArgumentException("Unfinished multiplicity is missing for " + name)
363 }
364 sum += value
365 }
366 orphansUpperBound.tightenLowerBound(sum)
367 }
368 }
369
370 @FinalFieldsConstructor
371 static class ContainmentRootConstraintUpdater implements RelationConstraintUpdater {
372
373 override update(PartialInterpretation p) {
374 throw new UnsupportedOperationException("TODO: auto-generated method stub")
375 }
376 }
154} 377}