aboutsummaryrefslogtreecommitdiffstats
path: root/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra/src/hu/bme/mit/inf/dslreasoner/viatrasolver/logic2viatra/cardinality
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <marussy@mit.bme.hu>2020-06-25 19:55:10 +0200
committerLibravatar Kristóf Marussy <marussy@mit.bme.hu>2020-06-25 19:55:10 +0200
commitc3a6d4b9cf3657070d180aa65ddbf0459e880329 (patch)
tree780c4fc61578dcb309af53fb0c164c7627e51676 /Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra/src/hu/bme/mit/inf/dslreasoner/viatrasolver/logic2viatra/cardinality
parentNew configuration language parser WIP (diff)
parentScope unsat benchmarks (diff)
downloadVIATRA-Generator-c3a6d4b9cf3657070d180aa65ddbf0459e880329.tar.gz
VIATRA-Generator-c3a6d4b9cf3657070d180aa65ddbf0459e880329.tar.zst
VIATRA-Generator-c3a6d4b9cf3657070d180aa65ddbf0459e880329.zip
Merge branch 'kris'
Diffstat (limited to 'Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra/src/hu/bme/mit/inf/dslreasoner/viatrasolver/logic2viatra/cardinality')
-rw-r--r--Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra/src/hu/bme/mit/inf/dslreasoner/viatrasolver/logic2viatra/cardinality/AbstractPolyhedronSaturationOperator.xtend53
-rw-r--r--Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra/src/hu/bme/mit/inf/dslreasoner/viatrasolver/logic2viatra/cardinality/CbcPolyhedronSolver.xtend241
-rw-r--r--Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra/src/hu/bme/mit/inf/dslreasoner/viatrasolver/logic2viatra/cardinality/LinearTypeConstraintHint.xtend30
-rw-r--r--Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra/src/hu/bme/mit/inf/dslreasoner/viatrasolver/logic2viatra/cardinality/MultiplicityGoalConstraintCalculator.xtend65
-rw-r--r--Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra/src/hu/bme/mit/inf/dslreasoner/viatrasolver/logic2viatra/cardinality/PolyhedronScopePropagator.xtend569
-rw-r--r--Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra/src/hu/bme/mit/inf/dslreasoner/viatrasolver/logic2viatra/cardinality/PolyhedronSolver.xtend179
-rw-r--r--Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra/src/hu/bme/mit/inf/dslreasoner/viatrasolver/logic2viatra/cardinality/RelationConstraintCalculator.xtend139
-rw-r--r--Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra/src/hu/bme/mit/inf/dslreasoner/viatrasolver/logic2viatra/cardinality/ScopePropagator.xtend134
-rw-r--r--Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra/src/hu/bme/mit/inf/dslreasoner/viatrasolver/logic2viatra/cardinality/ScopePropagatorStrategy.xtend71
-rw-r--r--Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra/src/hu/bme/mit/inf/dslreasoner/viatrasolver/logic2viatra/cardinality/TypeHierarchyScopePropagator.xtend85
-rw-r--r--Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra/src/hu/bme/mit/inf/dslreasoner/viatrasolver/logic2viatra/cardinality/Z3PolyhedronSolver.xtend272
11 files changed, 1838 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/AbstractPolyhedronSaturationOperator.xtend b/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra/src/hu/bme/mit/inf/dslreasoner/viatrasolver/logic2viatra/cardinality/AbstractPolyhedronSaturationOperator.xtend
new file mode 100644
index 00000000..94f97e94
--- /dev/null
+++ b/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra/src/hu/bme/mit/inf/dslreasoner/viatrasolver/logic2viatra/cardinality/AbstractPolyhedronSaturationOperator.xtend
@@ -0,0 +1,53 @@
1package hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra.cardinality
2
3import com.google.common.collect.ImmutableList
4import org.eclipse.xtend.lib.annotations.Accessors
5
6abstract class AbstractPolyhedronSaturationOperator implements PolyhedronSaturationOperator {
7 @Accessors val Polyhedron polyhedron
8
9 new(Polyhedron polyhedron) {
10 if (polyhedron.dimensions.empty) {
11 throw new IllegalArgumentException("Polyhedron must have at least one dimension.")
12 }
13 this.polyhedron = polyhedron
14 }
15
16 override saturate() {
17 if (polyhedron.expressionsToSaturate.empty) {
18 return PolyhedronSaturationResult.SATURATED
19 }
20 for (constraint : polyhedron.constraints) {
21 if (constraint.zero) {
22 if (constraint.lowerBound !== null && constraint.lowerBound > 0) {
23 return PolyhedronSaturationResult.EMPTY
24 }
25 if (constraint.upperBound !== null && constraint.upperBound < 0) {
26 return PolyhedronSaturationResult.EMPTY
27 }
28 } else {
29 if (constraint.lowerBound !== null && constraint.upperBound !== null &&
30 constraint.upperBound < constraint.lowerBound) {
31 return PolyhedronSaturationResult.EMPTY
32 }
33 }
34 }
35 doSaturate()
36 }
37
38 protected def PolyhedronSaturationResult doSaturate()
39
40 protected def getNonTrivialConstraints() {
41 ImmutableList.copyOf(polyhedron.constraints.filter [ constraint |
42 (constraint.lowerBound !== null || constraint.upperBound !== null) && !constraint.zero
43 ])
44 }
45
46 private static def isZero(LinearConstraint constraint) {
47 constraint.coefficients.values.forall[it == 0]
48 }
49
50 override close() throws Exception {
51 // Nothing to close by default.
52 }
53}
diff --git a/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra/src/hu/bme/mit/inf/dslreasoner/viatrasolver/logic2viatra/cardinality/CbcPolyhedronSolver.xtend b/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra/src/hu/bme/mit/inf/dslreasoner/viatrasolver/logic2viatra/cardinality/CbcPolyhedronSolver.xtend
new file mode 100644
index 00000000..708f93dc
--- /dev/null
+++ b/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra/src/hu/bme/mit/inf/dslreasoner/viatrasolver/logic2viatra/cardinality/CbcPolyhedronSolver.xtend
@@ -0,0 +1,241 @@
1package hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra.cardinality
2
3import com.google.common.collect.ImmutableList
4import com.google.common.collect.ImmutableMap
5import hu.bme.mit.inf.dslreasoner.ilp.cbc.CbcResult
6import hu.bme.mit.inf.dslreasoner.ilp.cbc.CbcSolver
7import java.util.HashSet
8import java.util.List
9import java.util.Map
10import java.util.Set
11import org.eclipse.xtend.lib.annotations.FinalFieldsConstructor
12
13@FinalFieldsConstructor
14class CbcPolyhedronSolver implements PolyhedronSolver {
15 val boolean lpRelaxation
16 val double timeoutSeconds
17 val boolean silent
18
19 new() {
20 this(false, -1, true)
21 }
22
23 override createSaturationOperator(Polyhedron polyhedron) {
24 new CbcSaturationOperator(polyhedron, lpRelaxation, timeoutSeconds, silent)
25 }
26}
27
28class CbcSaturationOperator extends AbstractPolyhedronSaturationOperator {
29 static val EPSILON = 1e-6
30
31 val boolean lpRelaxation
32 val double timeoutSeconds
33 val boolean silent
34 val double[] columnLowerBounds
35 val double[] columnUpperBounds
36 val double[] objective
37 val Map<Dimension, Integer> dimensionsToIndicesMap
38
39 new(Polyhedron polyhedron, boolean lpRelaxation, double timeoutSeconds, boolean silent) {
40 super(polyhedron)
41 this.lpRelaxation = lpRelaxation
42 this.timeoutSeconds = timeoutSeconds
43 this.silent = silent
44 val numDimensions = polyhedron.dimensions.size
45 columnLowerBounds = newDoubleArrayOfSize(numDimensions)
46 columnUpperBounds = newDoubleArrayOfSize(numDimensions)
47 objective = newDoubleArrayOfSize(numDimensions)
48 dimensionsToIndicesMap = ImmutableMap.copyOf(polyhedron.dimensions.indexed.toMap([value], [key]))
49 }
50
51 override doSaturate() {
52 val numDimensions = polyhedron.dimensions.size
53 for (var int j = 0; j < numDimensions; j++) {
54 val dimension = polyhedron.dimensions.get(j)
55 columnLowerBounds.set(j, dimension.lowerBound.toDouble(Double.NEGATIVE_INFINITY))
56 columnUpperBounds.set(j, dimension.upperBound.toDouble(Double.POSITIVE_INFINITY))
57 }
58 val constraints = nonTrivialConstraints
59 val numConstraints = constraints.size
60 val rowStarts = newIntArrayOfSize(numConstraints + 1)
61 val rowLowerBounds = newDoubleArrayOfSize(numConstraints)
62 val rowUpperBounds = newDoubleArrayOfSize(numConstraints)
63 val numEntries = constraints.map[coefficients.size].reduce[a, b|a + b] ?: 0
64 rowStarts.set(numConstraints, numEntries)
65 val columnIndices = newIntArrayOfSize(numEntries)
66 val entries = newDoubleArrayOfSize(numEntries)
67 val unconstrainedDimensions = new HashSet
68 for (dimension : polyhedron.dimensions) {
69 if (dimension.lowerBound === null && dimension.upperBound === null) {
70 unconstrainedDimensions += dimension
71 }
72 }
73 var int index = 0
74 for (var int i = 0; i < numConstraints; i++) {
75 rowStarts.set(i, index)
76 val constraint = constraints.get(i)
77 rowLowerBounds.set(i, constraint.lowerBound.toDouble(Double.NEGATIVE_INFINITY))
78 rowUpperBounds.set(i, constraint.upperBound.toDouble(Double.POSITIVE_INFINITY))
79 if (!dimensionsToIndicesMap.keySet.containsAll(constraint.coefficients.keySet)) {
80 throw new IllegalArgumentException("Constrains has unknown dimensions")
81 }
82 for (var int j = 0; j < numDimensions; j++) {
83 val dimension = polyhedron.dimensions.get(j)
84 val coefficient = constraint.coefficients.get(dimension)
85 if (coefficient !== null && coefficient != 0) {
86 unconstrainedDimensions -= dimension
87 columnIndices.set(index, j)
88 entries.set(index, coefficient)
89 index++
90 }
91 }
92 }
93 if (index != numEntries) {
94 throw new AssertionError("Last entry does not equal the number of entries in the constraint matrix")
95 }
96 for (expressionToSaturate : polyhedron.expressionsToSaturate) {
97 val result = saturate(expressionToSaturate, rowStarts, columnIndices, entries, rowLowerBounds,
98 rowUpperBounds, unconstrainedDimensions, constraints)
99 if (result != PolyhedronSaturationResult.SATURATED) {
100 return result
101 }
102 }
103 PolyhedronSaturationResult.SATURATED
104 }
105
106 protected def saturate(LinearBoundedExpression expressionToSaturate, int[] rowStarts, int[] columnIndices,
107 double[] entries, double[] rowLowerBounds, double[] rowUpperBounds, Set<Dimension> unconstrainedDimensions,
108 ImmutableList<LinearConstraint> constraints) {
109 val numDimensions = objective.size
110 for (var int j = 0; j < numDimensions; j++) {
111 objective.set(j, 0)
112 }
113 switch (expressionToSaturate) {
114 Dimension: {
115 // CBC will return nonsensical results or call free() with invalid arguments if
116 // it is passed a fully unconstrained (-Inf lower and +Int upper bound, no inequalities) variable
117 // in the objective function.
118 if (unconstrainedDimensions.contains(expressionToSaturate)) {
119 return PolyhedronSaturationResult.SATURATED
120 }
121 val j = getIndex(expressionToSaturate)
122 objective.set(j, 1)
123 }
124 LinearConstraint: {
125 for (pair : expressionToSaturate.coefficients.entrySet) {
126 val dimension = pair.key
127 // We also have to check for unconstrained dimensions here to avoid crashing.
128 if (unconstrainedDimensions.contains(dimension)) {
129 expressionToSaturate.lowerBound = null
130 expressionToSaturate.upperBound = null
131 return PolyhedronSaturationResult.SATURATED
132 }
133 val j = getIndex(dimension)
134 objective.set(j, pair.value)
135 }
136 }
137 default:
138 throw new IllegalArgumentException("Unknown expression: " + expressionToSaturate)
139 }
140 val minimizationResult = CbcSolver.solve(columnLowerBounds, columnUpperBounds, rowStarts, columnIndices,
141 entries, rowLowerBounds, rowUpperBounds, objective, lpRelaxation, timeoutSeconds, silent)
142 switch (minimizationResult) {
143 CbcResult.SolutionBounded: {
144 val doubleValue = minimizationResult.value
145 val roundedValue = Math.ceil(doubleValue - EPSILON)
146 val intValue = roundedValue as int
147 val oldBound = expressionToSaturate.lowerBound
148 if (oldBound === null || intValue >= oldBound) {
149 expressionToSaturate.lowerBound = intValue
150 setBound(expressionToSaturate, constraints, roundedValue, columnLowerBounds, rowLowerBounds)
151 } else {
152 throw new IllegalStateException("Unexpected decrease of lower bound by " + (oldBound - doubleValue))
153 }
154 }
155 case CbcResult.SOLUTION_UNBOUNDED: {
156 if (expressionToSaturate.lowerBound !== null) {
157 throw new IllegalStateException("Finite lower bound became infinite")
158 }
159 setBound(expressionToSaturate, constraints, Double.NEGATIVE_INFINITY, columnLowerBounds, rowLowerBounds)
160 }
161 case CbcResult.UNSAT:
162 return PolyhedronSaturationResult.EMPTY
163 case CbcResult.ABANDONED,
164 case CbcResult.TIMEOUT:
165 return PolyhedronSaturationResult.UNKNOWN
166 default:
167 throw new RuntimeException("Unknown CbcResult: " + minimizationResult)
168 }
169 for (var int j = 0; j < numDimensions; j++) {
170 val objectiveCoefficient = objective.get(j)
171 objective.set(j, -objectiveCoefficient)
172 }
173 val maximizationResult = CbcSolver.solve(columnLowerBounds, columnUpperBounds, rowStarts, columnIndices,
174 entries, rowLowerBounds, rowUpperBounds, objective, lpRelaxation, timeoutSeconds, silent)
175 switch (maximizationResult) {
176 CbcResult.SolutionBounded: {
177 val doubleValue = -maximizationResult.value
178 val roundedValue = Math.floor(doubleValue + EPSILON)
179 val intValue = roundedValue as int
180 val oldBound = expressionToSaturate.upperBound
181 if (oldBound === null || intValue <= oldBound) {
182 expressionToSaturate.upperBound = intValue
183 setBound(expressionToSaturate, constraints, roundedValue, columnUpperBounds, rowUpperBounds)
184 } else {
185 throw new IllegalStateException("Unexpected increase of upper bound by " + (doubleValue - oldBound))
186 }
187 }
188 case CbcResult.SOLUTION_UNBOUNDED: {
189 if (expressionToSaturate.lowerBound !== null) {
190 throw new IllegalStateException("Finite upper bound became infinite")
191 }
192 expressionToSaturate.upperBound = null
193 setBound(expressionToSaturate, constraints, Double.POSITIVE_INFINITY, columnUpperBounds, rowUpperBounds)
194 }
195 case CbcResult.UNSAT:
196 if (lpRelaxation) {
197 return PolyhedronSaturationResult.EMPTY
198 } else {
199 throw new RuntimeException("Minimization was SAT, but maximization is UNSAT")
200 }
201 case CbcResult.ABANDONED,
202 case CbcResult.TIMEOUT:
203 return PolyhedronSaturationResult.UNKNOWN
204 default:
205 throw new RuntimeException("Unknown CbcResult: " + maximizationResult)
206 }
207 return PolyhedronSaturationResult.SATURATED
208 }
209
210 private def toDouble(Integer nullableInt, double defaultValue) {
211 if (nullableInt === null) {
212 defaultValue
213 } else {
214 nullableInt.doubleValue
215 }
216 }
217
218 private def int getIndex(Dimension dimension) {
219 val index = dimensionsToIndicesMap.get(dimension)
220 if (index === null) {
221 throw new IllegalArgumentException("Unknown dimension: " + dimension)
222 }
223 index
224 }
225
226 private def void setBound(LinearBoundedExpression expression, List<LinearConstraint> constraints, double bound,
227 double[] columnBounds, double[] rowBounds) {
228 switch (expression) {
229 Dimension: {
230 val j = getIndex(expression)
231 columnBounds.set(j, bound)
232 }
233 LinearConstraint: {
234 val i = constraints.indexOf(expression)
235 if (i >= 0) {
236 rowBounds.set(i, bound)
237 }
238 }
239 }
240 }
241}
diff --git a/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra/src/hu/bme/mit/inf/dslreasoner/viatrasolver/logic2viatra/cardinality/LinearTypeConstraintHint.xtend b/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra/src/hu/bme/mit/inf/dslreasoner/viatrasolver/logic2viatra/cardinality/LinearTypeConstraintHint.xtend
new file mode 100644
index 00000000..8c21ca1d
--- /dev/null
+++ b/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra/src/hu/bme/mit/inf/dslreasoner/viatrasolver/logic2viatra/cardinality/LinearTypeConstraintHint.xtend
@@ -0,0 +1,30 @@
1package hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra.cardinality
2
3import hu.bme.mit.inf.dslreasoner.logic.model.logiclanguage.Type
4import hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra.patterns.PatternGenerator
5import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.partialinterpretation.PartialInterpretation
6import org.eclipse.viatra.query.runtime.api.IPatternMatch
7import org.eclipse.viatra.query.runtime.api.ViatraQueryMatcher
8
9interface LinearTypeExpressionBuilderFactory {
10 def ViatraQueryMatcher<? extends IPatternMatch> createMatcher(String queryName)
11
12 def LinearTypeExpressionBuilder createBuilder()
13}
14
15interface LinearTypeExpressionBuilder {
16 def LinearTypeExpressionBuilder add(int scale, Type type)
17
18 def LinearBoundedExpression build()
19}
20
21@FunctionalInterface
22interface RelationConstraintUpdater {
23 def void update(PartialInterpretation p)
24}
25
26interface LinearTypeConstraintHint {
27 def CharSequence getAdditionalPatterns(PatternGenerator patternGenerator)
28
29 def RelationConstraintUpdater createConstraintUpdater(LinearTypeExpressionBuilderFactory builderFactory)
30}
diff --git a/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra/src/hu/bme/mit/inf/dslreasoner/viatrasolver/logic2viatra/cardinality/MultiplicityGoalConstraintCalculator.xtend b/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra/src/hu/bme/mit/inf/dslreasoner/viatrasolver/logic2viatra/cardinality/MultiplicityGoalConstraintCalculator.xtend
new file mode 100644
index 00000000..034420d6
--- /dev/null
+++ b/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra/src/hu/bme/mit/inf/dslreasoner/viatrasolver/logic2viatra/cardinality/MultiplicityGoalConstraintCalculator.xtend
@@ -0,0 +1,65 @@
1package hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra.cardinality
2
3import org.eclipse.emf.common.notify.Notifier
4import org.eclipse.viatra.query.runtime.api.IQuerySpecification
5import org.eclipse.viatra.query.runtime.api.ViatraQueryEngine
6import org.eclipse.viatra.query.runtime.api.ViatraQueryMatcher
7import org.eclipse.viatra.query.runtime.emf.EMFScope
8
9class MultiplicityGoalConstraintCalculator {
10 val String targetRelationName
11 val IQuerySpecification<?> querySpecification
12 var ViatraQueryMatcher<?> matcher
13 val int minValue
14 val boolean containment
15 val int cost
16
17 public new(String targetRelationName, IQuerySpecification<?> querySpecification, int minValue, boolean containment, int cost) {
18 this.targetRelationName = targetRelationName
19 this.querySpecification = querySpecification
20 this.matcher = null
21 this.minValue = minValue
22 this.containment = containment
23 this.cost = cost
24 }
25
26 new(MultiplicityGoalConstraintCalculator other) {
27 this.targetRelationName = other.targetRelationName
28 this.querySpecification = other.querySpecification
29 this.matcher = null
30 this.minValue = other.minValue
31 this.containment = other.containment
32 this.cost = other.cost
33 }
34
35 def getName() {
36 targetRelationName
37 }
38
39 def isContainment() {
40 return containment
41 }
42
43 def init(Notifier notifier) {
44 val engine = ViatraQueryEngine.on(new EMFScope(notifier))
45 matcher = querySpecification.getMatcher(engine)
46 }
47
48 def calculateValue() {
49 var res = 0
50 val allMatches = this.matcher.allMatches
51 for(match : allMatches) {
52 val existingMultiplicity = match.get(4) as Integer
53 if(existingMultiplicity < this.minValue) {
54 val missingMultiplicity = this.minValue-existingMultiplicity
55 res += missingMultiplicity
56 }
57// if(missingMultiplicity!=0) {
58// println(targetRelationName+ " missing multiplicity: "+missingMultiplicity)
59// }
60 }
61// if(res>0)
62// println(targetRelationName+ " all missing multiplicities: "+res + "*"+cost+"="+res*cost)
63 return res*cost
64 }
65}
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}
diff --git a/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra/src/hu/bme/mit/inf/dslreasoner/viatrasolver/logic2viatra/cardinality/PolyhedronSolver.xtend b/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra/src/hu/bme/mit/inf/dslreasoner/viatrasolver/logic2viatra/cardinality/PolyhedronSolver.xtend
new file mode 100644
index 00000000..4e046190
--- /dev/null
+++ b/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra/src/hu/bme/mit/inf/dslreasoner/viatrasolver/logic2viatra/cardinality/PolyhedronSolver.xtend
@@ -0,0 +1,179 @@
1package hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra.cardinality
2
3import java.util.List
4import java.util.Map
5import org.eclipse.xtend.lib.annotations.Accessors
6import org.eclipse.xtend.lib.annotations.Data
7import org.eclipse.xtend.lib.annotations.FinalFieldsConstructor
8
9interface PolyhedronSolver {
10 def PolyhedronSaturationOperator createSaturationOperator(Polyhedron polyhedron)
11}
12
13enum PolyhedronSaturationResult {
14 SATURATED,
15 EMPTY,
16 UNKNOWN
17}
18
19interface PolyhedronSaturationOperator extends AutoCloseable {
20 def Polyhedron getPolyhedron()
21
22 def PolyhedronSaturationResult saturate()
23}
24
25@FinalFieldsConstructor
26@Accessors
27class Polyhedron {
28 /**
29 * The list of dimensions (variables) for this polyhedron.
30 *
31 * This list must not be modified after the polyhedron was created.
32 * However, lower and upper bounds of the dimensions may be changed.
33 *
34 * Names of dimensions in this list are assumed to be unique.
35 */
36 val List<Dimension> dimensions
37
38 /**
39 * The list of constraints defining this polyhedron.
40 *
41 * The list and its elements may be freely modified.
42 */
43 val List<LinearConstraint> constraints
44
45 /**
46 * The list of constraints that should be saturated (tightened)
47 * when a {@link PolyhedronSaturationOperator} is invoked.
48 *
49 * This list may be freely modified.
50 *
51 * Place all dimensions and constraints here to saturate all the bounds.
52 */
53 val List<LinearBoundedExpression> expressionsToSaturate
54
55 override toString() '''
56 Dimensions:
57 «FOR dimension : dimensions»
58 «dimension»
59 «ENDFOR»
60 Constraints:
61 «FOR constraint : constraints»
62 «constraint»
63 «ENDFOR»
64 '''
65
66 def createSignature() {
67 val size = dimensions.size + constraints.size
68 val lowerBounds = newArrayOfSize(size)
69 val upperBounds = newArrayOfSize(size)
70 var int i = 0
71 for (dimension : dimensions) {
72 lowerBounds.set(i, dimension.lowerBound)
73 upperBounds.set(i, dimension.upperBound)
74 i++
75 }
76 for (constraint : constraints) {
77 lowerBounds.set(i, constraint.lowerBound)
78 upperBounds.set(i, constraint.upperBound)
79 i++
80 }
81 new PolyhedronSignature.Bounds(lowerBounds, upperBounds)
82 }
83
84 def applySignature(PolyhedronSignature.Bounds signature) {
85 val lowerBounds = signature.lowerBounds
86 val upperBounds = signature.upperBounds
87 var int i = 0
88 for (dimension : dimensions) {
89 dimension.lowerBound = lowerBounds.get(i)
90 dimension.upperBound = upperBounds.get(i)
91 i++
92 }
93 for (constraint : constraints) {
94 constraint.lowerBound = lowerBounds.get(i)
95 constraint.upperBound = upperBounds.get(i)
96 i++
97 }
98 }
99}
100
101abstract class PolyhedronSignature {
102 public static val EMPTY = new PolyhedronSignature {
103 override toString() {
104 "PolyhedronSignature.EMPTY"
105 }
106 }
107
108 private new() {
109 }
110
111 @Data
112 static class Bounds extends PolyhedronSignature {
113 val Integer[] lowerBounds
114 val Integer[] upperBounds
115 }
116}
117
118@Accessors
119abstract class LinearBoundedExpression {
120 var Integer lowerBound
121 var Integer upperBound
122
123 def void tightenLowerBound(Integer tighterBound) {
124 if (lowerBound === null || (tighterBound !== null && lowerBound < tighterBound)) {
125 lowerBound = tighterBound
126 }
127 }
128
129 def void tightenUpperBound(Integer tighterBound) {
130 if (upperBound === null || (tighterBound !== null && upperBound > tighterBound)) {
131 upperBound = tighterBound
132 }
133 }
134
135 def void assertEqualsTo(int bound) {
136 tightenLowerBound(bound)
137 tightenUpperBound(bound)
138 }
139}
140
141@Accessors
142class Dimension extends LinearBoundedExpression {
143 val String name
144
145 @FinalFieldsConstructor
146 new() {
147 }
148
149 new(String name, Integer lowerBound, Integer upperBound) {
150 this(name)
151 this.lowerBound = lowerBound
152 this.upperBound = upperBound
153 }
154
155 override toString() {
156 '''«IF lowerBound !== null»«lowerBound» <= «ENDIF»«name»«IF upperBound !== null» <= «upperBound»«ENDIF»'''
157 }
158
159}
160
161@Accessors
162class LinearConstraint extends LinearBoundedExpression {
163 val Map<Dimension, Integer> coefficients
164
165 @FinalFieldsConstructor
166 new() {
167 }
168
169 new(Map<Dimension, Integer> coefficients, Integer lowerBound, Integer upperBound) {
170 this(coefficients)
171 this.lowerBound = lowerBound
172 this.upperBound = upperBound
173 }
174
175 override toString() {
176 '''«IF lowerBound !== null»«lowerBound» <= «ENDIF»«FOR pair : coefficients.entrySet SEPARATOR " + "»«IF pair.value != 1»«pair.value» * «ENDIF»«pair.key.name»«ENDFOR»«IF upperBound !== null» <= «upperBound»«ENDIF»'''
177 }
178
179}
diff --git a/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra/src/hu/bme/mit/inf/dslreasoner/viatrasolver/logic2viatra/cardinality/RelationConstraintCalculator.xtend b/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra/src/hu/bme/mit/inf/dslreasoner/viatrasolver/logic2viatra/cardinality/RelationConstraintCalculator.xtend
new file mode 100644
index 00000000..3e4fea8a
--- /dev/null
+++ b/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra/src/hu/bme/mit/inf/dslreasoner/viatrasolver/logic2viatra/cardinality/RelationConstraintCalculator.xtend
@@ -0,0 +1,139 @@
1package hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra.cardinality
2
3import com.google.common.collect.ImmutableList
4import com.google.common.collect.ImmutableSet
5import hu.bme.mit.inf.dslreasoner.ecore2logic.ecore2logicannotations.InverseRelationAssertion
6import hu.bme.mit.inf.dslreasoner.ecore2logic.ecore2logicannotations.LowerMultiplicityAssertion
7import hu.bme.mit.inf.dslreasoner.ecore2logic.ecore2logicannotations.UpperMultiplicityAssertion
8import hu.bme.mit.inf.dslreasoner.logic.model.logiclanguage.ComplexTypeReference
9import hu.bme.mit.inf.dslreasoner.logic.model.logiclanguage.Relation
10import hu.bme.mit.inf.dslreasoner.logic.model.logicproblem.LogicProblem
11import java.util.HashMap
12import java.util.List
13import org.eclipse.xtend.lib.annotations.Data
14
15@Data
16class RelationConstraints {
17 val List<RelationMultiplicityConstraint> multiplicityConstraints
18}
19
20@Data
21class RelationMultiplicityConstraint {
22 Relation relation
23 Relation inverseRelation
24 boolean containment
25 boolean container
26 int lowerBound
27 int upperBound
28 int inverseUpperBound
29
30 def isUpperBoundFinite() {
31 upperBound >= 0
32 }
33
34 private def isInverseUpperBoundFinite() {
35 inverseUpperBound >= 0
36 }
37
38 private def canHaveMultipleSourcesPerTarget() {
39 inverseUpperBound != 1
40 }
41
42 def constrainsUnfinished() {
43 lowerBound >= 1 && (!container || lowerBound >= 2)
44 }
45
46 def constrainsUnrepairable() {
47 // TODO Optimize the unrepairable matches computation,
48 // or come up with a heuristic when does computing unrepairables worth the overhead.
49 constrainsUnfinished && canHaveMultipleSourcesPerTarget && false
50 }
51
52 def constrainsRemainingInverse() {
53 lowerBound >= 1 && !containment && inverseUpperBoundFinite
54 }
55
56 def constrainsRemainingContents() {
57 containment
58 }
59
60 def isActive() {
61 constrainsUnfinished || constrainsUnrepairable || constrainsRemainingInverse || constrainsRemainingContents
62 }
63
64 def getSourceType() {
65 getParamType(0)
66 }
67
68 def getTargetType() {
69 getParamType(1)
70 }
71
72 private def getParamType(int i) {
73 val parameters = relation.parameters
74 if (i < parameters.size) {
75 val firstParam = parameters.get(i)
76 if (firstParam instanceof ComplexTypeReference) {
77 return firstParam.referred
78 }
79 }
80 throw new IllegalArgumentException("Constraint with unknown source type")
81 }
82}
83
84class RelationConstraintCalculator {
85 def calculateRelationConstraints(LogicProblem problem) {
86 val containmentRelations = switch (problem.containmentHierarchies.size) {
87 case 0:
88 <Relation>emptySet
89 case 1:
90 ImmutableSet.copyOf(problem.containmentHierarchies.head.containmentRelations)
91 default:
92 throw new IllegalArgumentException("Only a single containment hierarchy is supported")
93 }
94 val inverseRelations = new HashMap<Relation, Relation>
95 val lowerMultiplicities = new HashMap<Relation, Integer>
96 val upperMultiplicities = new HashMap<Relation, Integer>
97 for (relation : problem.relations) {
98 lowerMultiplicities.put(relation, 0)
99 upperMultiplicities.put(relation, -1)
100 }
101 for (annotation : problem.annotations) {
102 switch (annotation) {
103 InverseRelationAssertion: {
104 inverseRelations.put(annotation.inverseA, annotation.inverseB)
105 inverseRelations.put(annotation.inverseB, annotation.inverseA)
106 }
107 LowerMultiplicityAssertion:
108 lowerMultiplicities.put(annotation.relation, annotation.lower)
109 UpperMultiplicityAssertion:
110 upperMultiplicities.put(annotation.relation, annotation.upper)
111 }
112 }
113 val multiplicityConstraintsBuilder = ImmutableList.builder()
114 for (relation : problem.relations) {
115 val containment = containmentRelations.contains(relation)
116 val lowerMultiplicity = lowerMultiplicities.get(relation)
117 val upperMultiplicity = upperMultiplicities.get(relation)
118 var container = false
119 var inverseUpperMultiplicity = -1
120 val inverseRelation = inverseRelations.get(relation)
121 if (inverseRelation !== null) {
122 inverseUpperMultiplicity = upperMultiplicities.get(inverseRelation)
123 container = containmentRelations.contains(inverseRelation)
124 }
125 if (containment) {
126 inverseUpperMultiplicity = 1
127 }
128 val constraint = new RelationMultiplicityConstraint(relation, inverseRelation, containment, container,
129 lowerMultiplicity, upperMultiplicity, inverseUpperMultiplicity)
130 if (constraint.isActive) {
131 if (relation.parameters.size != 2) {
132 throw new IllegalArgumentException('''Relation «relation.name» has multiplicity or containment constraints, but it is not binary''')
133 }
134 multiplicityConstraintsBuilder.add(constraint)
135 }
136 }
137 new RelationConstraints(multiplicityConstraintsBuilder.build)
138 }
139}
diff --git a/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra/src/hu/bme/mit/inf/dslreasoner/viatrasolver/logic2viatra/cardinality/ScopePropagator.xtend b/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra/src/hu/bme/mit/inf/dslreasoner/viatrasolver/logic2viatra/cardinality/ScopePropagator.xtend
new file mode 100644
index 00000000..8f3a5bb0
--- /dev/null
+++ b/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra/src/hu/bme/mit/inf/dslreasoner/viatrasolver/logic2viatra/cardinality/ScopePropagator.xtend
@@ -0,0 +1,134 @@
1package hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra.cardinality
2
3import hu.bme.mit.inf.dslreasoner.logic.model.logiclanguage.Relation
4import hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra.ModelGenerationStatistics
5import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.partialinterpretation.PartialComplexTypeInterpretation
6import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.partialinterpretation.PartialInterpretation
7import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.partialinterpretation.PartialTypeInterpratation
8import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.partialinterpretation.Scope
9import java.util.HashMap
10import java.util.HashSet
11import java.util.Map
12import java.util.Set
13import org.eclipse.xtend.lib.annotations.Accessors
14
15class ScopePropagator {
16 @Accessors(PROTECTED_GETTER) val PartialInterpretation partialInterpretation
17 @Accessors(PROTECTED_GETTER) val ModelGenerationStatistics statistics
18 val Map<PartialTypeInterpratation, Scope> type2Scope
19 @Accessors(PROTECTED_GETTER) val Map<Scope, Set<Scope>> superScopes
20 @Accessors(PROTECTED_GETTER) val Map<Scope, Set<Scope>> subScopes
21
22 new(PartialInterpretation p, ModelGenerationStatistics statistics) {
23 partialInterpretation = p
24 this.statistics = statistics
25 type2Scope = new HashMap
26 for (scope : p.scopes) {
27 type2Scope.put(scope.targetTypeInterpretation, scope)
28 }
29
30 superScopes = new HashMap
31 subScopes = new HashMap
32 for (scope : p.scopes) {
33 superScopes.put(scope, new HashSet)
34 subScopes.put(scope, new HashSet)
35 }
36
37 for (scope : p.scopes) {
38 val target = scope.targetTypeInterpretation
39 if (target instanceof PartialComplexTypeInterpretation) {
40 val supertypeInterpretations = target.supertypeInterpretation
41 for (supertypeInterpretation : supertypeInterpretations) {
42 val supertypeScope = type2Scope.get(supertypeInterpretation)
43 superScopes.get(scope).add(supertypeScope)
44 subScopes.get(supertypeScope).add(scope)
45 }
46 }
47 }
48 var boolean changed
49 do {
50 changed = false
51 for (scope : p.scopes) {
52 val subScopeSet = subScopes.get(scope)
53 val superScopeSet = superScopes.get(scope)
54 for (subScope : subScopeSet) {
55 changed = changed || superScopes.get(subScope).addAll(superScopeSet)
56 }
57 for (superScope : superScopeSet) {
58 changed = changed || subScopes.get(superScope).addAll(subScopeSet)
59 }
60 }
61 } while (changed)
62
63 copyScopeBoundsToHeuristic()
64 }
65
66 def propagateAllScopeConstraints() {
67 statistics.incrementScopePropagationCount()
68 doPropagateAllScopeConstraints()
69 }
70
71 protected def copyScopeBoundsToHeuristic() {
72 partialInterpretation.minNewElementsHeuristic = partialInterpretation.minNewElements
73 for (scope : partialInterpretation.scopes) {
74 scope.minNewElementsHeuristic = scope.minNewElements
75 }
76 }
77
78 protected def void doPropagateAllScopeConstraints() {
79 // Nothing to propagate.
80 }
81
82 def decrementTypeScope(PartialTypeInterpratation t) {
83// println('''Adding to «(t as PartialComplexTypeInterpretation).interpretationOf.name»''')
84 val targetScope = type2Scope.get(t)
85 if (targetScope !== null) {
86 targetScope.removeOne
87 val sups = superScopes.get(targetScope)
88 sups.forEach[removeOne]
89 }
90 if (this.partialInterpretation.minNewElements > 0) {
91 this.partialInterpretation.minNewElements = this.partialInterpretation.minNewElements - 1
92 }
93 if (this.partialInterpretation.minNewElementsHeuristic > 0) {
94 this.partialInterpretation.minNewElementsHeuristic = this.partialInterpretation.minNewElementsHeuristic - 1
95 }
96 if (this.partialInterpretation.maxNewElements > 0) {
97 this.partialInterpretation.maxNewElements = this.partialInterpretation.maxNewElements - 1
98 } else if (this.partialInterpretation.maxNewElements === 0) {
99 setScopesInvalid()
100 }
101
102// println('''Target Scope: «targetScope.minNewElements» - «targetScope.maxNewElements»''')
103// println(''' «this.partialInterpretation.minNewElements» - «this.partialInterpretation.maxNewElements»''')
104// this.partialInterpretation.scopes.forEach[println(''' «(it.targetTypeInterpretation as PartialComplexTypeInterpretation).interpretationOf.name»: «it.minNewElements»-«it.maxNewElements»''')]
105// println('''All constraints are propagated upon increasing «(t as PartialComplexTypeInterpretation).interpretationOf.name»''')
106 }
107
108 protected def setScopesInvalid() {
109 partialInterpretation.minNewElements = Integer.MAX_VALUE
110 partialInterpretation.maxNewElements = 0
111 for (scope : partialInterpretation.scopes) {
112 scope.minNewElements = Integer.MAX_VALUE
113 scope.maxNewElements = 0
114 }
115 }
116
117 def void propagateAdditionToRelation(Relation r) {
118 // Nothing to propagate.
119 }
120
121 private def removeOne(Scope scope) {
122 if (scope.maxNewElements === 0) {
123 throw new IllegalArgumentException('''Inconsistent object creation: «scope.targetTypeInterpretation»''')
124 } else if (scope.maxNewElements > 0) {
125 scope.maxNewElements = scope.maxNewElements - 1
126 }
127 if (scope.minNewElements > 0) {
128 scope.minNewElements = scope.minNewElements - 1
129 }
130 if (scope.minNewElementsHeuristic > 0) {
131 scope.minNewElementsHeuristic = scope.minNewElementsHeuristic - 1
132 }
133 }
134}
diff --git a/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra/src/hu/bme/mit/inf/dslreasoner/viatrasolver/logic2viatra/cardinality/ScopePropagatorStrategy.xtend b/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra/src/hu/bme/mit/inf/dslreasoner/viatrasolver/logic2viatra/cardinality/ScopePropagatorStrategy.xtend
new file mode 100644
index 00000000..3165917a
--- /dev/null
+++ b/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra/src/hu/bme/mit/inf/dslreasoner/viatrasolver/logic2viatra/cardinality/ScopePropagatorStrategy.xtend
@@ -0,0 +1,71 @@
1package hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra.cardinality
2
3import org.eclipse.xtend.lib.annotations.Data
4import org.eclipse.xtend.lib.annotations.FinalFieldsConstructor
5
6enum PolyhedralScopePropagatorConstraints {
7 TypeHierarchy,
8 Relational
9}
10
11enum PolyhedralScopePropagatorSolver {
12 Z3Real,
13 Z3Integer,
14 Cbc,
15 Clp
16}
17
18abstract class ScopePropagatorStrategy {
19 public static val None = new Simple("None")
20
21 public static val Basic = new Simple("Basic")
22
23 public static val BasicTypeHierarchy = new Simple("BasicTypeHierarchy")
24
25 private new() {
26 }
27
28 def boolean requiresUpperBoundIndexing()
29
30 static class Simple extends ScopePropagatorStrategy {
31 val String name
32
33 @FinalFieldsConstructor
34 private new() {
35 }
36
37 override requiresUpperBoundIndexing() {
38 false
39 }
40
41 override toString() {
42 name
43 }
44 }
45
46 @Data
47 static class Polyhedral extends ScopePropagatorStrategy {
48 public static val UNLIMITED_TIME = -1
49
50 val PolyhedralScopePropagatorConstraints constraints
51 val PolyhedralScopePropagatorSolver solver
52 val boolean updateHeuristic
53 val double timeoutSeconds
54
55 @FinalFieldsConstructor
56 new() {
57 }
58
59 new(PolyhedralScopePropagatorConstraints constraints, PolyhedralScopePropagatorSolver solver, boolean updateHeuristic) {
60 this(constraints, solver, updateHeuristic, UNLIMITED_TIME)
61 }
62
63 new(PolyhedralScopePropagatorConstraints constraints, PolyhedralScopePropagatorSolver solver) {
64 this(constraints, solver, true)
65 }
66
67 override requiresUpperBoundIndexing() {
68 constraints == PolyhedralScopePropagatorConstraints.Relational
69 }
70 }
71}
diff --git a/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra/src/hu/bme/mit/inf/dslreasoner/viatrasolver/logic2viatra/cardinality/TypeHierarchyScopePropagator.xtend b/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra/src/hu/bme/mit/inf/dslreasoner/viatrasolver/logic2viatra/cardinality/TypeHierarchyScopePropagator.xtend
new file mode 100644
index 00000000..d1704b39
--- /dev/null
+++ b/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra/src/hu/bme/mit/inf/dslreasoner/viatrasolver/logic2viatra/cardinality/TypeHierarchyScopePropagator.xtend
@@ -0,0 +1,85 @@
1package hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra.cardinality
2
3import hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra.ModelGenerationStatistics
4import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.partialinterpretation.PartialInterpretation
5import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.partialinterpretation.Scope
6
7class TypeHierarchyScopePropagator extends ScopePropagator {
8
9 new(PartialInterpretation p, ModelGenerationStatistics statistics) {
10 super(p, statistics)
11 }
12
13 protected override doPropagateAllScopeConstraints() {
14 var boolean hadChanged
15 do {
16 hadChanged = false
17 for (superScopeEntry : superScopes.entrySet) {
18 val sub = superScopeEntry.key
19 hadChanged = propagateLowerLimitUp(sub, partialInterpretation) || hadChanged
20 hadChanged = propagateUpperLimitDown(sub, partialInterpretation) || hadChanged
21 for (sup : superScopeEntry.value) {
22 hadChanged = propagateLowerLimitUp(sub, sup) || hadChanged
23 hadChanged = propagateUpperLimitDown(sub, sup) || hadChanged
24 }
25 }
26 } while (hadChanged)
27 }
28
29 private def propagateLowerLimitUp(Scope subScope, Scope superScope) {
30 var changed = false
31 if (subScope.minNewElements > superScope.minNewElements) {
32 superScope.minNewElements = subScope.minNewElements
33 changed = true
34 }
35 if (subScope.minNewElementsHeuristic > superScope.minNewElementsHeuristic) {
36 superScope.minNewElementsHeuristic = subScope.minNewElementsHeuristic
37 changed = true
38 }
39 changed
40 }
41
42 private def propagateUpperLimitDown(Scope subScope, Scope superScope) {
43 if (superScope.maxNewElements >= 0 &&
44 (superScope.maxNewElements < subScope.maxNewElements || subScope.maxNewElements < 0)) {
45// println('''
46// «(subScope.targetTypeInterpretation as PartialComplexTypeInterpretation).interpretationOf.name» -> «(superScope.targetTypeInterpretation as PartialComplexTypeInterpretation).interpretationOf.name»
47// subScope.maxNewElements «subScope.maxNewElements» = superScope.maxNewElements «superScope.maxNewElements»
48// ''')
49 subScope.maxNewElements = superScope.maxNewElements
50 return true
51 } else {
52 return false
53 }
54 }
55
56 private def propagateLowerLimitUp(Scope subScope, PartialInterpretation p) {
57 var changed = false
58 if (subScope.minNewElements > p.minNewElements) {
59// println('''
60// «(subScope.targetTypeInterpretation as PartialComplexTypeInterpretation).interpretationOf.name» -> nodes
61// p.minNewElements «p.minNewElements» = subScope.minNewElements «subScope.minNewElements»
62// ''')
63 p.minNewElements = subScope.minNewElements
64 changed = true
65 }
66 if (subScope.minNewElementsHeuristic > p.minNewElementsHeuristic) {
67 p.minNewElementsHeuristic = subScope.minNewElementsHeuristic
68 changed = true
69 }
70 changed
71 }
72
73 private def propagateUpperLimitDown(Scope subScope, PartialInterpretation p) {
74 if (p.maxNewElements >= 0 && (p.maxNewElements < subScope.maxNewElements || subScope.maxNewElements < 0)) {
75// println('''
76// «(subScope.targetTypeInterpretation as PartialComplexTypeInterpretation).interpretationOf.name» -> nodes
77// subScope.maxNewElements «subScope.maxNewElements» = p.maxNewElements «p.maxNewElements»
78// ''')
79 subScope.maxNewElements = p.maxNewElements
80 return true
81 } else {
82 return false
83 }
84 }
85}
diff --git a/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra/src/hu/bme/mit/inf/dslreasoner/viatrasolver/logic2viatra/cardinality/Z3PolyhedronSolver.xtend b/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra/src/hu/bme/mit/inf/dslreasoner/viatrasolver/logic2viatra/cardinality/Z3PolyhedronSolver.xtend
new file mode 100644
index 00000000..3b831433
--- /dev/null
+++ b/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra/src/hu/bme/mit/inf/dslreasoner/viatrasolver/logic2viatra/cardinality/Z3PolyhedronSolver.xtend
@@ -0,0 +1,272 @@
1package hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra.cardinality
2
3import com.microsoft.z3.AlgebraicNum
4import com.microsoft.z3.ArithExpr
5import com.microsoft.z3.Context
6import com.microsoft.z3.Expr
7import com.microsoft.z3.IntNum
8import com.microsoft.z3.Optimize
9import com.microsoft.z3.RatNum
10import com.microsoft.z3.Status
11import com.microsoft.z3.Symbol
12import java.math.BigDecimal
13import java.math.MathContext
14import java.math.RoundingMode
15import java.util.Map
16import org.eclipse.xtend.lib.annotations.Accessors
17import org.eclipse.xtend.lib.annotations.FinalFieldsConstructor
18
19class Z3PolyhedronSolver implements PolyhedronSolver {
20 val boolean lpRelaxation
21 val double timeoutSeconds
22
23 @FinalFieldsConstructor
24 new() {
25 }
26
27 new() {
28 this(false, -1)
29 }
30
31 override createSaturationOperator(Polyhedron polyhedron) {
32 new DisposingZ3SaturationOperator(this, polyhedron)
33 }
34
35 def createPersistentSaturationOperator(Polyhedron polyhedron) {
36 new Z3SaturationOperator(polyhedron, lpRelaxation, timeoutSeconds)
37 }
38}
39
40@FinalFieldsConstructor
41class DisposingZ3SaturationOperator implements PolyhedronSaturationOperator {
42 val Z3PolyhedronSolver solver
43 @Accessors val Polyhedron polyhedron
44
45 override saturate() {
46 val persistentOperator = solver.createPersistentSaturationOperator(polyhedron)
47 try {
48 persistentOperator.saturate
49 } finally {
50 persistentOperator.close
51 }
52 }
53
54 override close() throws Exception {
55 // Nothing to close.
56 }
57}
58
59class Z3SaturationOperator extends AbstractPolyhedronSaturationOperator {
60 static val INFINITY_SYMBOL_NAME = "oo"
61 static val MULT_SYMBOL_NAME = "*"
62 static val TIMEOUT_SYMBOL_NAME = "timeout"
63 static val INTEGER_PRECISION = new BigDecimal(Integer.MAX_VALUE).precision
64 static val ROUND_DOWN = new MathContext(INTEGER_PRECISION, RoundingMode.FLOOR)
65 static val ROUND_UP = new MathContext(INTEGER_PRECISION, RoundingMode.CEILING)
66 // The interval isolating the number is smaller than 1/10^precision.
67 static val ALGEBRAIC_NUMBER_ROUNDING = 0
68
69 extension val Context context
70 val Symbol infinitySymbol
71 val Symbol multSymbol
72 val Map<Dimension, ArithExpr> variables
73 val int timeoutMilliseconds
74
75 new(Polyhedron polyhedron, boolean lpRelaxation, double timeoutSeconds) {
76 super(polyhedron)
77 context = new Context
78 infinitySymbol = context.mkSymbol(INFINITY_SYMBOL_NAME)
79 multSymbol = context.mkSymbol(MULT_SYMBOL_NAME)
80 variables = polyhedron.dimensions.toInvertedMap [ dimension |
81 val name = dimension.name
82 if (lpRelaxation) {
83 mkRealConst(name)
84 } else {
85 mkIntConst(name)
86 }
87 ]
88 timeoutMilliseconds = Math.ceil(timeoutSeconds * 1000) as int
89 }
90
91 override doSaturate() {
92 val status = executeSolver()
93 convertStatusToSaturationResult(status)
94 }
95
96 private def convertStatusToSaturationResult(Status status) {
97 switch (status) {
98 case SATISFIABLE:
99 PolyhedronSaturationResult.SATURATED
100 case UNSATISFIABLE:
101 PolyhedronSaturationResult.EMPTY
102 case UNKNOWN:
103 PolyhedronSaturationResult.UNKNOWN
104 default:
105 throw new IllegalArgumentException("Unknown Status: " + status)
106 }
107 }
108
109 private def executeSolver() {
110 for (expressionToSaturate : polyhedron.expressionsToSaturate) {
111 val expr = expressionToSaturate.toExpr
112 val lowerResult = saturateLowerBound(expr, expressionToSaturate)
113 if (lowerResult != Status.SATISFIABLE) {
114 return lowerResult
115 }
116 val upperResult = saturateUpperBound(expr, expressionToSaturate)
117 if (upperResult != Status.SATISFIABLE) {
118 return upperResult
119 }
120 }
121 Status.SATISFIABLE
122 }
123
124 private def saturateLowerBound(ArithExpr expr, LinearBoundedExpression expressionToSaturate) {
125 val optimize = prepareOptimize
126 val handle = optimize.MkMinimize(expr)
127 val status = optimize.Check()
128 if (status == Status.SATISFIABLE) {
129 val value = switch (resultExpr : handle.lower) {
130 IntNum:
131 resultExpr.getInt()
132 RatNum:
133 ceil(resultExpr)
134 AlgebraicNum:
135 ceil(resultExpr.toUpper(ALGEBRAIC_NUMBER_ROUNDING))
136 default:
137 if (isNegativeInfinity(resultExpr)) {
138 null
139 } else {
140 throw new IllegalArgumentException("Integer result expected, got: " + resultExpr)
141 }
142 }
143 expressionToSaturate.lowerBound = value
144 }
145 status
146 }
147
148 private def floor(RatNum ratNum) {
149 val numerator = new BigDecimal(ratNum.numerator.bigInteger)
150 val denominator = new BigDecimal(ratNum.denominator.bigInteger)
151 numerator.divide(denominator, ROUND_DOWN).setScale(0, RoundingMode.FLOOR).intValue
152 }
153
154 private def saturateUpperBound(ArithExpr expr, LinearBoundedExpression expressionToSaturate) {
155 val optimize = prepareOptimize
156 val handle = optimize.MkMaximize(expr)
157 val status = optimize.Check()
158 if (status == Status.SATISFIABLE) {
159 val value = switch (resultExpr : handle.upper) {
160 IntNum:
161 resultExpr.getInt()
162 RatNum:
163 floor(resultExpr)
164 AlgebraicNum:
165 floor(resultExpr.toLower(ALGEBRAIC_NUMBER_ROUNDING))
166 default:
167 if (isPositiveInfinity(resultExpr)) {
168 null
169 } else {
170 throw new IllegalArgumentException("Integer result expected, got: " + resultExpr)
171 }
172 }
173 expressionToSaturate.upperBound = value
174 }
175 status
176 }
177
178 private def ceil(RatNum ratNum) {
179 val numerator = new BigDecimal(ratNum.numerator.bigInteger)
180 val denominator = new BigDecimal(ratNum.denominator.bigInteger)
181 numerator.divide(denominator, ROUND_UP).setScale(0, RoundingMode.CEILING).intValue
182 }
183
184 private def isPositiveInfinity(Expr expr) {
185 expr.app && expr.getFuncDecl.name == infinitySymbol
186 }
187
188 private def isNegativeInfinity(Expr expr) {
189 // Negative infinity is represented as (* (- 1) oo)
190 if (!expr.app || expr.getFuncDecl.name != multSymbol || expr.numArgs != 2) {
191 return false
192 }
193 isPositiveInfinity(expr.args.get(1))
194 }
195
196 private def prepareOptimize() {
197 val optimize = mkOptimize()
198 if (timeoutMilliseconds >= 0) {
199 val params = mkParams()
200 // We cannot turn TIMEOUT_SYMBOL_NAME into a Symbol in the constructor,
201 // because there is no add(Symbol, int) overload.
202 params.add(TIMEOUT_SYMBOL_NAME, timeoutMilliseconds)
203 optimize.parameters = params
204 }
205 assertConstraints(optimize)
206 optimize
207 }
208
209 private def assertConstraints(Optimize it) {
210 for (pair : variables.entrySet) {
211 assertBounds(pair.value, pair.key)
212 }
213 for (constraint : nonTrivialConstraints) {
214 val expr = createLinearCombination(constraint.coefficients)
215 assertBounds(expr, constraint)
216 }
217 }
218
219 private def assertBounds(Optimize it, ArithExpr expression, LinearBoundedExpression bounds) {
220 val lowerBound = bounds.lowerBound
221 val upperBound = bounds.upperBound
222 if (lowerBound == upperBound) {
223 if (lowerBound === null) {
224 return
225 }
226 Assert(mkEq(expression, mkInt(lowerBound)))
227 } else {
228 if (lowerBound !== null) {
229 Assert(mkGe(expression, mkInt(lowerBound)))
230 }
231 if (upperBound !== null) {
232 Assert(mkLe(expression, mkInt(upperBound)))
233 }
234 }
235 }
236
237 private def toExpr(LinearBoundedExpression linearBoundedExpression) {
238 switch (linearBoundedExpression) {
239 Dimension: variables.get(linearBoundedExpression)
240 LinearConstraint: createLinearCombination(linearBoundedExpression.coefficients)
241 default: throw new IllegalArgumentException("Unknown linear bounded expression:" + linearBoundedExpression)
242 }
243 }
244
245 private def createLinearCombination(Map<Dimension, Integer> coefficients) {
246 val size = coefficients.size
247 if (size == 0) {
248 return mkInt(0)
249 }
250 val array = newArrayOfSize(size)
251 var int i = 0
252 for (pair : coefficients.entrySet) {
253 val variable = variables.get(pair.key)
254 if (variable === null) {
255 throw new IllegalArgumentException("Unknown dimension: " + pair.key.name)
256 }
257 val coefficient = pair.value
258 val term = if (coefficient == 1) {
259 variable
260 } else {
261 mkMul(mkInt(coefficient), variable)
262 }
263 array.set(i, term)
264 i++
265 }
266 mkAdd(array)
267 }
268
269 override close() throws Exception {
270 context.close()
271 }
272}