diff options
Diffstat (limited to 'subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/ModelInitializer.java')
-rw-r--r-- | subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/ModelInitializer.java | 674 |
1 files changed, 630 insertions, 44 deletions
diff --git a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/ModelInitializer.java b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/ModelInitializer.java index 06b8ad77..85c8d701 100644 --- a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/ModelInitializer.java +++ b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/ModelInitializer.java | |||
@@ -9,67 +9,408 @@ import com.google.inject.Inject; | |||
9 | import org.eclipse.collections.api.factory.primitive.ObjectIntMaps; | 9 | import org.eclipse.collections.api.factory.primitive.ObjectIntMaps; |
10 | import org.eclipse.collections.api.map.primitive.MutableObjectIntMap; | 10 | import org.eclipse.collections.api.map.primitive.MutableObjectIntMap; |
11 | import tools.refinery.language.model.problem.*; | 11 | import tools.refinery.language.model.problem.*; |
12 | import tools.refinery.language.semantics.model.internal.DecisionTree; | 12 | import tools.refinery.language.semantics.model.internal.MutableSeed; |
13 | import tools.refinery.language.utils.BuiltinSymbols; | ||
13 | import tools.refinery.language.utils.ProblemDesugarer; | 14 | import tools.refinery.language.utils.ProblemDesugarer; |
14 | import tools.refinery.language.utils.RelationInfo; | 15 | import tools.refinery.language.utils.ProblemUtil; |
15 | import tools.refinery.store.representation.Symbol; | 16 | import tools.refinery.store.dse.propagation.PropagationBuilder; |
17 | import tools.refinery.store.model.ModelStoreBuilder; | ||
18 | import tools.refinery.store.query.Constraint; | ||
19 | import tools.refinery.store.query.dnf.InvalidClauseException; | ||
20 | import tools.refinery.store.query.dnf.Query; | ||
21 | import tools.refinery.store.query.dnf.RelationalQuery; | ||
22 | import tools.refinery.store.query.literal.*; | ||
23 | import tools.refinery.store.query.term.NodeVariable; | ||
24 | import tools.refinery.store.query.term.Variable; | ||
25 | import tools.refinery.store.reasoning.ReasoningAdapter; | ||
26 | import tools.refinery.store.reasoning.representation.AnyPartialSymbol; | ||
27 | import tools.refinery.store.reasoning.representation.PartialRelation; | ||
28 | import tools.refinery.store.reasoning.scope.ScopePropagator; | ||
29 | import tools.refinery.store.reasoning.seed.ModelSeed; | ||
30 | import tools.refinery.store.reasoning.seed.Seed; | ||
31 | import tools.refinery.store.reasoning.translator.containment.ContainmentHierarchyTranslator; | ||
32 | import tools.refinery.store.reasoning.translator.metamodel.Metamodel; | ||
33 | import tools.refinery.store.reasoning.translator.metamodel.MetamodelBuilder; | ||
34 | import tools.refinery.store.reasoning.translator.metamodel.MetamodelTranslator; | ||
35 | import tools.refinery.store.reasoning.translator.multiobject.MultiObjectTranslator; | ||
36 | import tools.refinery.store.reasoning.translator.multiplicity.ConstrainedMultiplicity; | ||
37 | import tools.refinery.store.reasoning.translator.multiplicity.Multiplicity; | ||
38 | import tools.refinery.store.reasoning.translator.multiplicity.UnconstrainedMultiplicity; | ||
39 | import tools.refinery.store.reasoning.translator.predicate.PredicateTranslator; | ||
16 | import tools.refinery.store.representation.TruthValue; | 40 | import tools.refinery.store.representation.TruthValue; |
41 | import tools.refinery.store.representation.cardinality.CardinalityInterval; | ||
42 | import tools.refinery.store.representation.cardinality.CardinalityIntervals; | ||
43 | import tools.refinery.store.representation.cardinality.UpperCardinalities; | ||
17 | import tools.refinery.store.tuple.Tuple; | 44 | import tools.refinery.store.tuple.Tuple; |
18 | 45 | ||
19 | import java.util.HashMap; | 46 | import java.util.*; |
20 | import java.util.Map; | ||
21 | 47 | ||
22 | public class ModelInitializer { | 48 | public class ModelInitializer { |
23 | @Inject | 49 | @Inject |
24 | private ProblemDesugarer desugarer; | 50 | private ProblemDesugarer desugarer; |
25 | 51 | ||
52 | @Inject | ||
53 | private SemanticsUtils semanticsUtils; | ||
54 | |||
55 | private Problem problem; | ||
56 | |||
57 | private ModelStoreBuilder storeBuilder; | ||
58 | |||
59 | private BuiltinSymbols builtinSymbols; | ||
60 | |||
61 | private PartialRelation nodeRelation; | ||
62 | |||
26 | private final MutableObjectIntMap<Node> nodeTrace = ObjectIntMaps.mutable.empty(); | 63 | private final MutableObjectIntMap<Node> nodeTrace = ObjectIntMaps.mutable.empty(); |
27 | 64 | ||
28 | private final Map<tools.refinery.language.model.problem.Relation, Symbol<TruthValue>> relationTrace = | 65 | private final Map<Relation, RelationInfo> relationInfoMap = new LinkedHashMap<>(); |
29 | new HashMap<>(); | 66 | |
67 | private final Map<PartialRelation, RelationInfo> partialRelationInfoMap = new HashMap<>(); | ||
68 | |||
69 | private final Map<AnyPartialSymbol, Relation> inverseTrace = new HashMap<>(); | ||
70 | |||
71 | private Map<Relation, PartialRelation> relationTrace; | ||
72 | |||
73 | private final MetamodelBuilder metamodelBuilder = Metamodel.builder(); | ||
74 | |||
75 | private Metamodel metamodel; | ||
76 | |||
77 | private final Map<Tuple, CardinalityInterval> countSeed = new LinkedHashMap<>(); | ||
78 | |||
79 | private ScopePropagator scopePropagator; | ||
80 | |||
81 | private ModelSeed modelSeed; | ||
82 | |||
83 | public Problem getProblem() { | ||
84 | return problem; | ||
85 | } | ||
86 | |||
87 | public int getNodeCount() { | ||
88 | return nodeTrace.size(); | ||
89 | } | ||
90 | |||
91 | public MutableObjectIntMap<Node> getNodeTrace() { | ||
92 | return nodeTrace; | ||
93 | } | ||
94 | |||
95 | public Map<Relation, PartialRelation> getRelationTrace() { | ||
96 | return relationTrace; | ||
97 | } | ||
30 | 98 | ||
31 | private int nodeCount = 0; | 99 | public Relation getInverseTrace(AnyPartialSymbol partialRelation) { |
100 | return inverseTrace.get(partialRelation); | ||
101 | } | ||
102 | |||
103 | public Metamodel getMetamodel() { | ||
104 | return metamodel; | ||
105 | } | ||
32 | 106 | ||
33 | public void createModel(Problem problem) { | 107 | public ModelSeed createModel(Problem problem, ModelStoreBuilder storeBuilder) { |
34 | var builtinSymbols = desugarer.getBuiltinSymbols(problem).orElseThrow(() -> new IllegalArgumentException( | 108 | this.problem = problem; |
109 | this.storeBuilder = storeBuilder; | ||
110 | builtinSymbols = desugarer.getBuiltinSymbols(problem).orElseThrow(() -> new IllegalArgumentException( | ||
35 | "Problem has no builtin library")); | 111 | "Problem has no builtin library")); |
36 | var collectedSymbols = desugarer.collectSymbols(problem); | 112 | var nodeInfo = collectPartialRelation(builtinSymbols.node(), 1, TruthValue.TRUE, TruthValue.TRUE); |
37 | for (var node : collectedSymbols.nodes().keySet()) { | 113 | nodeRelation = nodeInfo.partialRelation(); |
38 | nodeTrace.put(node, nodeCount); | 114 | metamodelBuilder.type(nodeRelation); |
39 | nodeCount += 1; | 115 | putRelationInfo(builtinSymbols.exists(), new RelationInfo(ReasoningAdapter.EXISTS_SYMBOL, null, |
40 | } | 116 | TruthValue.TRUE)); |
41 | for (var pair : collectedSymbols.relations().entrySet()) { | 117 | putRelationInfo(builtinSymbols.equals(), new RelationInfo(ReasoningAdapter.EQUALS_SYMBOL, |
42 | var relation = pair.getKey(); | 118 | (TruthValue) null, |
43 | var relationInfo = pair.getValue(); | 119 | null)); |
44 | var isEqualsRelation = relation == builtinSymbols.equals(); | 120 | putRelationInfo(builtinSymbols.contained(), new RelationInfo(ContainmentHierarchyTranslator.CONTAINED_SYMBOL, |
45 | var decisionTree = mergeAssertions(relationInfo, isEqualsRelation); | 121 | null, TruthValue.UNKNOWN)); |
46 | var defaultValue = isEqualsRelation ? TruthValue.FALSE : TruthValue.UNKNOWN; | 122 | putRelationInfo(builtinSymbols.contains(), new RelationInfo(ContainmentHierarchyTranslator.CONTAINS_SYMBOL, |
47 | relationTrace.put(relation, Symbol.of( | 123 | null, TruthValue.UNKNOWN)); |
48 | relationInfo.name(), relationInfo.arity(), TruthValue.class, defaultValue)); | 124 | putRelationInfo(builtinSymbols.invalidContainer(), |
49 | } | 125 | new RelationInfo(ContainmentHierarchyTranslator.INVALID_CONTAINER, TruthValue.FALSE, |
50 | } | 126 | TruthValue.FALSE)); |
51 | 127 | collectNodes(); | |
52 | private DecisionTree mergeAssertions(RelationInfo relationInfo, boolean isEqualsRelation) { | 128 | collectPartialSymbols(); |
53 | var arity = relationInfo.arity(); | 129 | collectMetamodel(); |
54 | var defaultAssertions = new DecisionTree(arity, isEqualsRelation ? null : TruthValue.UNKNOWN); | 130 | metamodel = metamodelBuilder.build(); |
55 | var assertions = new DecisionTree(arity); | 131 | collectAssertions(); |
56 | for (var assertion : relationInfo.assertions()) { | 132 | storeBuilder.with(new MultiObjectTranslator()); |
57 | var tuple = getTuple(assertion); | 133 | storeBuilder.with(new MetamodelTranslator(metamodel)); |
58 | var value = getTruthValue(assertion.getValue()); | 134 | relationTrace = new LinkedHashMap<>(relationInfoMap.size()); |
59 | if (assertion.isDefault()) { | 135 | int nodeCount = getNodeCount(); |
60 | defaultAssertions.mergeValue(tuple, value); | 136 | var modelSeedBuilder = ModelSeed.builder(nodeCount); |
137 | for (var entry : relationInfoMap.entrySet()) { | ||
138 | var relation = entry.getKey(); | ||
139 | var info = entry.getValue(); | ||
140 | var partialRelation = info.partialRelation(); | ||
141 | relationTrace.put(relation, partialRelation); | ||
142 | modelSeedBuilder.seed(partialRelation, info.toSeed(nodeCount)); | ||
143 | } | ||
144 | collectScopes(); | ||
145 | if (scopePropagator != null) { | ||
146 | if (storeBuilder.tryGetAdapter(PropagationBuilder.class).isEmpty()) { | ||
147 | throw new TracedException(problem, "Type scopes require a PropagationBuilder"); | ||
148 | } | ||
149 | storeBuilder.with(scopePropagator); | ||
150 | } | ||
151 | modelSeedBuilder.seed(MultiObjectTranslator.COUNT_SYMBOL, builder -> builder | ||
152 | .reducedValue(CardinalityIntervals.SET) | ||
153 | .putAll(countSeed)); | ||
154 | modelSeed = modelSeedBuilder.build(); | ||
155 | collectPredicates(); | ||
156 | return modelSeed; | ||
157 | } | ||
158 | |||
159 | private void collectNodes() { | ||
160 | for (var statement : problem.getStatements()) { | ||
161 | if (statement instanceof IndividualDeclaration individualDeclaration) { | ||
162 | for (var individual : individualDeclaration.getNodes()) { | ||
163 | collectNode(individual); | ||
164 | } | ||
165 | } else if (statement instanceof ClassDeclaration classDeclaration) { | ||
166 | var newNode = classDeclaration.getNewNode(); | ||
167 | if (newNode != null) { | ||
168 | collectNode(newNode); | ||
169 | } | ||
170 | } else if (statement instanceof EnumDeclaration enumDeclaration) { | ||
171 | for (var literal : enumDeclaration.getLiterals()) { | ||
172 | collectNode(literal); | ||
173 | } | ||
174 | } | ||
175 | } | ||
176 | for (var node : problem.getNodes()) { | ||
177 | collectNode(node); | ||
178 | } | ||
179 | } | ||
180 | |||
181 | private void collectNode(Node node) { | ||
182 | nodeTrace.getIfAbsentPut(node, this::getNodeCount); | ||
183 | } | ||
184 | |||
185 | private void collectPartialSymbols() { | ||
186 | for (var statement : problem.getStatements()) { | ||
187 | if (statement instanceof ClassDeclaration classDeclaration) { | ||
188 | collectClassDeclarationSymbols(classDeclaration); | ||
189 | } else if (statement instanceof EnumDeclaration enumDeclaration) { | ||
190 | collectPartialRelation(enumDeclaration, 1, TruthValue.FALSE, TruthValue.FALSE); | ||
191 | } else if (statement instanceof PredicateDefinition predicateDefinition) { | ||
192 | collectPredicateDefinitionSymbol(predicateDefinition); | ||
193 | } | ||
194 | } | ||
195 | } | ||
196 | |||
197 | private void collectClassDeclarationSymbols(ClassDeclaration classDeclaration) { | ||
198 | collectPartialRelation(classDeclaration, 1, null, TruthValue.UNKNOWN); | ||
199 | for (var featureDeclaration : classDeclaration.getFeatureDeclarations()) { | ||
200 | if (featureDeclaration instanceof ReferenceDeclaration referenceDeclaration) { | ||
201 | collectPartialRelation(referenceDeclaration, 2, null, TruthValue.UNKNOWN); | ||
202 | var invalidMultiplicityConstraint = referenceDeclaration.getInvalidMultiplicity(); | ||
203 | if (invalidMultiplicityConstraint != null) { | ||
204 | collectPartialRelation(invalidMultiplicityConstraint, 1, TruthValue.FALSE, TruthValue.FALSE); | ||
205 | } | ||
61 | } else { | 206 | } else { |
62 | assertions.mergeValue(tuple, value); | 207 | throw new TracedException(featureDeclaration, "Unknown feature declaration"); |
208 | } | ||
209 | } | ||
210 | } | ||
211 | |||
212 | private void collectPredicateDefinitionSymbol(PredicateDefinition predicateDefinition) { | ||
213 | int arity = predicateDefinition.getParameters().size(); | ||
214 | if (predicateDefinition.isError()) { | ||
215 | collectPartialRelation(predicateDefinition, arity, TruthValue.FALSE, TruthValue.FALSE); | ||
216 | } else { | ||
217 | collectPartialRelation(predicateDefinition, arity, null, TruthValue.UNKNOWN); | ||
218 | } | ||
219 | } | ||
220 | |||
221 | private void putRelationInfo(Relation relation, RelationInfo info) { | ||
222 | relationInfoMap.put(relation, info); | ||
223 | partialRelationInfoMap.put(info.partialRelation(), info); | ||
224 | inverseTrace.put(info.partialRelation(), relation); | ||
225 | } | ||
226 | |||
227 | private RelationInfo collectPartialRelation(Relation relation, int arity, TruthValue value, | ||
228 | TruthValue defaultValue) { | ||
229 | return relationInfoMap.computeIfAbsent(relation, key -> { | ||
230 | var name = getName(relation); | ||
231 | var info = new RelationInfo(name, arity, value, defaultValue); | ||
232 | partialRelationInfoMap.put(info.partialRelation(), info); | ||
233 | inverseTrace.put(info.partialRelation(), relation); | ||
234 | return info; | ||
235 | }); | ||
236 | } | ||
237 | |||
238 | private String getName(Relation relation) { | ||
239 | return semanticsUtils.getName(relation).orElseGet(() -> "::" + relationInfoMap.size()); | ||
240 | } | ||
241 | |||
242 | private void collectMetamodel() { | ||
243 | for (var statement : problem.getStatements()) { | ||
244 | if (statement instanceof ClassDeclaration classDeclaration) { | ||
245 | collectClassDeclarationMetamodel(classDeclaration); | ||
246 | } else if (statement instanceof EnumDeclaration enumDeclaration) { | ||
247 | collectEnumMetamodel(enumDeclaration); | ||
248 | } | ||
249 | } | ||
250 | } | ||
251 | |||
252 | private void collectEnumMetamodel(EnumDeclaration enumDeclaration) { | ||
253 | try { | ||
254 | metamodelBuilder.type(getPartialRelation(enumDeclaration), nodeRelation); | ||
255 | } catch (RuntimeException e) { | ||
256 | throw TracedException.addTrace(enumDeclaration, e); | ||
257 | } | ||
258 | } | ||
259 | |||
260 | private void collectClassDeclarationMetamodel(ClassDeclaration classDeclaration) { | ||
261 | var superTypes = classDeclaration.getSuperTypes(); | ||
262 | var partialSuperTypes = new ArrayList<PartialRelation>(superTypes.size() + 1); | ||
263 | partialSuperTypes.add(nodeRelation); | ||
264 | for (var superType : superTypes) { | ||
265 | partialSuperTypes.add(getPartialRelation(superType)); | ||
266 | } | ||
267 | try { | ||
268 | metamodelBuilder.type(getPartialRelation(classDeclaration), classDeclaration.isAbstract(), | ||
269 | partialSuperTypes); | ||
270 | } catch (RuntimeException e) { | ||
271 | throw TracedException.addTrace(classDeclaration, e); | ||
272 | } | ||
273 | for (var featureDeclaration : classDeclaration.getFeatureDeclarations()) { | ||
274 | if (featureDeclaration instanceof ReferenceDeclaration referenceDeclaration) { | ||
275 | collectReferenceDeclarationMetamodel(classDeclaration, referenceDeclaration); | ||
63 | } | 276 | } |
64 | } | 277 | } |
65 | defaultAssertions.overwriteValues(assertions); | 278 | } |
66 | if (isEqualsRelation) { | 279 | |
67 | for (int i = 0; i < nodeCount; i++) { | 280 | private void collectReferenceDeclarationMetamodel(ClassDeclaration classDeclaration, |
68 | defaultAssertions.setIfMissing(Tuple.of(i, i), TruthValue.TRUE); | 281 | ReferenceDeclaration referenceDeclaration) { |
282 | var relation = getPartialRelation(referenceDeclaration); | ||
283 | var source = getPartialRelation(classDeclaration); | ||
284 | var target = getPartialRelation(referenceDeclaration.getReferenceType()); | ||
285 | boolean containment = referenceDeclaration.getKind() == ReferenceKind.CONTAINMENT; | ||
286 | var opposite = referenceDeclaration.getOpposite(); | ||
287 | PartialRelation oppositeRelation = null; | ||
288 | if (opposite != null) { | ||
289 | oppositeRelation = getPartialRelation(opposite); | ||
290 | } | ||
291 | var multiplicity = getMultiplicityConstraint(referenceDeclaration); | ||
292 | try { | ||
293 | metamodelBuilder.reference(relation, source, containment, multiplicity, target, oppositeRelation); | ||
294 | } catch (RuntimeException e) { | ||
295 | throw TracedException.addTrace(classDeclaration, e); | ||
296 | } | ||
297 | } | ||
298 | |||
299 | private Multiplicity getMultiplicityConstraint(ReferenceDeclaration referenceDeclaration) { | ||
300 | if (!ProblemUtil.hasMultiplicityConstraint(referenceDeclaration)) { | ||
301 | return UnconstrainedMultiplicity.INSTANCE; | ||
302 | } | ||
303 | var problemMultiplicity = referenceDeclaration.getMultiplicity(); | ||
304 | CardinalityInterval interval; | ||
305 | if (problemMultiplicity == null) { | ||
306 | interval = CardinalityIntervals.LONE; | ||
307 | } else { | ||
308 | interval = getCardinalityInterval(problemMultiplicity); | ||
309 | } | ||
310 | var constraint = getRelationInfo(referenceDeclaration.getInvalidMultiplicity()).partialRelation(); | ||
311 | return ConstrainedMultiplicity.of(interval, constraint); | ||
312 | } | ||
313 | |||
314 | private static CardinalityInterval getCardinalityInterval( | ||
315 | tools.refinery.language.model.problem.Multiplicity problemMultiplicity) { | ||
316 | if (problemMultiplicity instanceof ExactMultiplicity exactMultiplicity) { | ||
317 | return CardinalityIntervals.exactly(exactMultiplicity.getExactValue()); | ||
318 | } else if (problemMultiplicity instanceof RangeMultiplicity rangeMultiplicity) { | ||
319 | var upperBound = rangeMultiplicity.getUpperBound(); | ||
320 | return CardinalityIntervals.between(rangeMultiplicity.getLowerBound(), | ||
321 | upperBound < 0 ? UpperCardinalities.UNBOUNDED : UpperCardinalities.atMost(upperBound)); | ||
322 | } else { | ||
323 | throw new TracedException(problemMultiplicity, "Unknown multiplicity"); | ||
324 | } | ||
325 | } | ||
326 | |||
327 | private void collectAssertions() { | ||
328 | for (var statement : problem.getStatements()) { | ||
329 | if (statement instanceof ClassDeclaration classDeclaration) { | ||
330 | collectClassDeclarationAssertions(classDeclaration); | ||
331 | } else if (statement instanceof EnumDeclaration enumDeclaration) { | ||
332 | collectEnumAssertions(enumDeclaration); | ||
333 | } else if (statement instanceof IndividualDeclaration individualDeclaration) { | ||
334 | for (var individual : individualDeclaration.getNodes()) { | ||
335 | collectIndividualAssertions(individual); | ||
336 | } | ||
337 | } else if (statement instanceof Assertion assertion) { | ||
338 | collectAssertion(assertion); | ||
69 | } | 339 | } |
70 | defaultAssertions.setAllMissing(TruthValue.FALSE); | ||
71 | } | 340 | } |
72 | return defaultAssertions; | 341 | } |
342 | |||
343 | private void collectClassDeclarationAssertions(ClassDeclaration classDeclaration) { | ||
344 | var newNode = classDeclaration.getNewNode(); | ||
345 | if (newNode == null) { | ||
346 | return; | ||
347 | } | ||
348 | var newNodeId = getNodeId(newNode); | ||
349 | collectCardinalityAssertions(newNodeId, TruthValue.UNKNOWN); | ||
350 | var tuple = Tuple.of(newNodeId); | ||
351 | mergeValue(classDeclaration, tuple, TruthValue.TRUE); | ||
352 | var typeInfo = metamodel.typeHierarchy().getAnalysisResult(getPartialRelation(classDeclaration)); | ||
353 | for (var subType : typeInfo.getDirectSubtypes()) { | ||
354 | partialRelationInfoMap.get(subType).assertions().mergeValue(tuple, TruthValue.FALSE); | ||
355 | } | ||
356 | } | ||
357 | |||
358 | private void collectEnumAssertions(EnumDeclaration enumDeclaration) { | ||
359 | var overlay = MutableSeed.of(1, null); | ||
360 | for (var literal : enumDeclaration.getLiterals()) { | ||
361 | collectIndividualAssertions(literal); | ||
362 | var nodeId = getNodeId(literal); | ||
363 | overlay.mergeValue(Tuple.of(nodeId), TruthValue.TRUE); | ||
364 | } | ||
365 | var info = getRelationInfo(enumDeclaration); | ||
366 | info.assertions().overwriteValues(overlay); | ||
367 | } | ||
368 | |||
369 | private void collectIndividualAssertions(Node node) { | ||
370 | var nodeId = getNodeId(node); | ||
371 | collectCardinalityAssertions(nodeId, TruthValue.TRUE); | ||
372 | } | ||
373 | |||
374 | private void collectCardinalityAssertions(int nodeId, TruthValue value) { | ||
375 | mergeValue(builtinSymbols.exists(), Tuple.of(nodeId), value); | ||
376 | mergeValue(builtinSymbols.equals(), Tuple.of(nodeId, nodeId), value); | ||
377 | } | ||
378 | |||
379 | private void collectAssertion(Assertion assertion) { | ||
380 | var tuple = getTuple(assertion); | ||
381 | var value = getTruthValue(assertion.getValue()); | ||
382 | var relation = assertion.getRelation(); | ||
383 | var info = getRelationInfo(relation); | ||
384 | var partialRelation = info.partialRelation(); | ||
385 | if (partialRelation.arity() != tuple.getSize()) { | ||
386 | throw new TracedException(assertion, "Expected %d arguments for %s, got %d instead" | ||
387 | .formatted(partialRelation.arity(), partialRelation, tuple.getSize())); | ||
388 | } | ||
389 | if (assertion.isDefault()) { | ||
390 | info.defaultAssertions().mergeValue(tuple, value); | ||
391 | } else { | ||
392 | info.assertions().mergeValue(tuple, value); | ||
393 | } | ||
394 | } | ||
395 | |||
396 | private void mergeValue(Relation relation, Tuple key, TruthValue value) { | ||
397 | getRelationInfo(relation).assertions().mergeValue(key, value); | ||
398 | } | ||
399 | |||
400 | private RelationInfo getRelationInfo(Relation relation) { | ||
401 | var info = relationInfoMap.get(relation); | ||
402 | if (info == null) { | ||
403 | throw new IllegalArgumentException("Unknown relation: " + relation); | ||
404 | } | ||
405 | return info; | ||
406 | } | ||
407 | |||
408 | private PartialRelation getPartialRelation(Relation relation) { | ||
409 | return getRelationInfo(relation).partialRelation(); | ||
410 | } | ||
411 | |||
412 | private int getNodeId(Node node) { | ||
413 | return nodeTrace.getOrThrow(node); | ||
73 | } | 414 | } |
74 | 415 | ||
75 | private Tuple getTuple(Assertion assertion) { | 416 | private Tuple getTuple(Assertion assertion) { |
@@ -79,11 +420,11 @@ public class ModelInitializer { | |||
79 | for (int i = 0; i < arity; i++) { | 420 | for (int i = 0; i < arity; i++) { |
80 | var argument = arguments.get(i); | 421 | var argument = arguments.get(i); |
81 | if (argument instanceof NodeAssertionArgument nodeArgument) { | 422 | if (argument instanceof NodeAssertionArgument nodeArgument) { |
82 | nodes[i] = nodeTrace.getOrThrow(nodeArgument.getNode()); | 423 | nodes[i] = getNodeId(nodeArgument.getNode()); |
83 | } else if (argument instanceof WildcardAssertionArgument) { | 424 | } else if (argument instanceof WildcardAssertionArgument) { |
84 | nodes[i] = -1; | 425 | nodes[i] = -1; |
85 | } else { | 426 | } else { |
86 | throw new IllegalArgumentException("Unknown assertion argument: " + argument); | 427 | throw new TracedException(argument, "Unsupported assertion argument"); |
87 | } | 428 | } |
88 | } | 429 | } |
89 | return Tuple.of(nodes); | 430 | return Tuple.of(nodes); |
@@ -100,4 +441,249 @@ public class ModelInitializer { | |||
100 | case ERROR -> TruthValue.ERROR; | 441 | case ERROR -> TruthValue.ERROR; |
101 | }; | 442 | }; |
102 | } | 443 | } |
444 | |||
445 | private void collectPredicates() { | ||
446 | for (var statement : problem.getStatements()) { | ||
447 | if (statement instanceof PredicateDefinition predicateDefinition) { | ||
448 | collectPredicateDefinitionTraced(predicateDefinition); | ||
449 | } | ||
450 | } | ||
451 | } | ||
452 | |||
453 | private void collectPredicateDefinitionTraced(PredicateDefinition predicateDefinition) { | ||
454 | try { | ||
455 | collectPredicateDefinition(predicateDefinition); | ||
456 | } catch (InvalidClauseException e) { | ||
457 | int clauseIndex = e.getClauseIndex(); | ||
458 | var bodies = predicateDefinition.getBodies(); | ||
459 | if (clauseIndex < bodies.size()) { | ||
460 | throw new TracedException(bodies.get(clauseIndex), e); | ||
461 | } else { | ||
462 | throw new TracedException(predicateDefinition, e); | ||
463 | } | ||
464 | } catch (RuntimeException e) { | ||
465 | throw TracedException.addTrace(predicateDefinition, e); | ||
466 | } | ||
467 | } | ||
468 | |||
469 | private void collectPredicateDefinition(PredicateDefinition predicateDefinition) { | ||
470 | var partialRelation = getPartialRelation(predicateDefinition); | ||
471 | var query = toQuery(partialRelation.name(), predicateDefinition); | ||
472 | boolean mutable; | ||
473 | TruthValue defaultValue; | ||
474 | if (predicateDefinition.isError()) { | ||
475 | mutable = false; | ||
476 | defaultValue = TruthValue.FALSE; | ||
477 | } else { | ||
478 | var seed = modelSeed.getSeed(partialRelation); | ||
479 | defaultValue = seed.reducedValue() == TruthValue.FALSE ? TruthValue.FALSE : TruthValue.UNKNOWN; | ||
480 | var cursor = seed.getCursor(defaultValue, getNodeCount()); | ||
481 | // The symbol should be mutable if there is at least one non-default entry in the seed. | ||
482 | mutable = cursor.move(); | ||
483 | } | ||
484 | var translator = new PredicateTranslator(partialRelation, query, mutable, defaultValue); | ||
485 | storeBuilder.with(translator); | ||
486 | } | ||
487 | |||
488 | private RelationalQuery toQuery(String name, PredicateDefinition predicateDefinition) { | ||
489 | var problemParameters = predicateDefinition.getParameters(); | ||
490 | int arity = problemParameters.size(); | ||
491 | var parameters = new NodeVariable[arity]; | ||
492 | var parameterMap = new HashMap<tools.refinery.language.model.problem.Variable, Variable>(arity); | ||
493 | var commonLiterals = new ArrayList<Literal>(); | ||
494 | for (int i = 0; i < arity; i++) { | ||
495 | var problemParameter = problemParameters.get(i); | ||
496 | var parameter = Variable.of(problemParameter.getName()); | ||
497 | parameters[i] = parameter; | ||
498 | parameterMap.put(problemParameter, parameter); | ||
499 | var parameterType = problemParameter.getParameterType(); | ||
500 | if (parameterType != null) { | ||
501 | var partialType = getPartialRelation(parameterType); | ||
502 | commonLiterals.add(partialType.call(parameter)); | ||
503 | } | ||
504 | } | ||
505 | var builder = Query.builder(name).parameters(parameters); | ||
506 | for (var body : predicateDefinition.getBodies()) { | ||
507 | try { | ||
508 | var localScope = extendScope(parameterMap, body.getImplicitVariables()); | ||
509 | var problemLiterals = body.getLiterals(); | ||
510 | var literals = new ArrayList<>(commonLiterals); | ||
511 | for (var problemLiteral : problemLiterals) { | ||
512 | toLiteralsTraced(problemLiteral, localScope, literals); | ||
513 | } | ||
514 | builder.clause(literals); | ||
515 | } catch (RuntimeException e) { | ||
516 | throw TracedException.addTrace(body, e); | ||
517 | } | ||
518 | } | ||
519 | return builder.build(); | ||
520 | } | ||
521 | |||
522 | private Map<tools.refinery.language.model.problem.Variable, Variable> extendScope( | ||
523 | Map<tools.refinery.language.model.problem.Variable, Variable> existing, | ||
524 | Collection<? extends tools.refinery.language.model.problem.Variable> newVariables) { | ||
525 | if (newVariables.isEmpty()) { | ||
526 | return existing; | ||
527 | } | ||
528 | int localScopeSize = existing.size() + newVariables.size(); | ||
529 | var localScope = new HashMap<tools.refinery.language.model.problem.Variable, Variable>(localScopeSize); | ||
530 | localScope.putAll(existing); | ||
531 | for (var newVariable : newVariables) { | ||
532 | localScope.put(newVariable, Variable.of(newVariable.getName())); | ||
533 | } | ||
534 | return localScope; | ||
535 | } | ||
536 | |||
537 | private void toLiteralsTraced(Expr expr, Map<tools.refinery.language.model.problem.Variable, Variable> localScope, | ||
538 | List<Literal> literals) { | ||
539 | try { | ||
540 | toLiterals(expr, localScope, literals); | ||
541 | } catch (RuntimeException e) { | ||
542 | throw TracedException.addTrace(expr, e); | ||
543 | } | ||
544 | } | ||
545 | |||
546 | private void toLiterals(Expr expr, Map<tools.refinery.language.model.problem.Variable, Variable> localScope, | ||
547 | List<Literal> literals) { | ||
548 | if (expr instanceof LogicConstant logicConstant) { | ||
549 | switch (logicConstant.getLogicValue()) { | ||
550 | case TRUE -> literals.add(BooleanLiteral.TRUE); | ||
551 | case FALSE -> literals.add(BooleanLiteral.FALSE); | ||
552 | default -> throw new TracedException(logicConstant, "Unsupported literal"); | ||
553 | } | ||
554 | } else if (expr instanceof Atom atom) { | ||
555 | var target = getPartialRelation(atom.getRelation()); | ||
556 | var polarity = atom.isTransitiveClosure() ? CallPolarity.TRANSITIVE : CallPolarity.POSITIVE; | ||
557 | var argumentList = toArgumentList(atom.getArguments(), localScope, literals); | ||
558 | literals.add(target.call(polarity, argumentList)); | ||
559 | } else if (expr instanceof NegationExpr negationExpr) { | ||
560 | var body = negationExpr.getBody(); | ||
561 | if (!(body instanceof Atom atom)) { | ||
562 | throw new TracedException(body, "Cannot negate literal"); | ||
563 | } | ||
564 | var target = getPartialRelation(atom.getRelation()); | ||
565 | Constraint constraint; | ||
566 | if (atom.isTransitiveClosure()) { | ||
567 | constraint = Query.of(target.name() + "#transitive", (builder, p1, p2) -> builder.clause( | ||
568 | target.callTransitive(p1, p2) | ||
569 | )).getDnf(); | ||
570 | } else { | ||
571 | constraint = target; | ||
572 | } | ||
573 | var negatedScope = extendScope(localScope, negationExpr.getImplicitVariables()); | ||
574 | var argumentList = toArgumentList(atom.getArguments(), negatedScope, literals); | ||
575 | literals.add(constraint.call(CallPolarity.NEGATIVE, argumentList)); | ||
576 | } else if (expr instanceof ComparisonExpr comparisonExpr) { | ||
577 | var argumentList = toArgumentList(List.of(comparisonExpr.getLeft(), comparisonExpr.getRight()), | ||
578 | localScope, literals); | ||
579 | boolean positive = switch (comparisonExpr.getOp()) { | ||
580 | case EQ -> true; | ||
581 | case NOT_EQ -> false; | ||
582 | default -> throw new TracedException( | ||
583 | comparisonExpr, "Unsupported operator"); | ||
584 | }; | ||
585 | literals.add(new EquivalenceLiteral(positive, argumentList.get(0), argumentList.get(1))); | ||
586 | } else { | ||
587 | throw new TracedException(expr, "Unsupported literal"); | ||
588 | } | ||
589 | } | ||
590 | |||
591 | private List<Variable> toArgumentList( | ||
592 | List<Expr> expressions, Map<tools.refinery.language.model.problem.Variable, Variable> localScope, | ||
593 | List<Literal> literals) { | ||
594 | var argumentList = new ArrayList<Variable>(expressions.size()); | ||
595 | for (var expr : expressions) { | ||
596 | if (!(expr instanceof VariableOrNodeExpr variableOrNodeExpr)) { | ||
597 | throw new TracedException(expr, "Unsupported argument"); | ||
598 | } | ||
599 | var variableOrNode = variableOrNodeExpr.getVariableOrNode(); | ||
600 | if (variableOrNode instanceof Node node) { | ||
601 | int nodeId = getNodeId(node); | ||
602 | var tempVariable = Variable.of(semanticsUtils.getName(node).orElse("_" + nodeId)); | ||
603 | literals.add(new ConstantLiteral(tempVariable, nodeId)); | ||
604 | argumentList.add(tempVariable); | ||
605 | } else if (variableOrNode instanceof tools.refinery.language.model.problem.Variable problemVariable) { | ||
606 | if (variableOrNodeExpr.getSingletonVariable() == problemVariable) { | ||
607 | argumentList.add(Variable.of(problemVariable.getName())); | ||
608 | } else { | ||
609 | var variable = localScope.get(problemVariable); | ||
610 | if (variable == null) { | ||
611 | throw new TracedException(variableOrNode, "Unknown variable: " + problemVariable.getName()); | ||
612 | } | ||
613 | argumentList.add(variable); | ||
614 | } | ||
615 | } else { | ||
616 | throw new TracedException(variableOrNode, "Unknown argument"); | ||
617 | } | ||
618 | } | ||
619 | return argumentList; | ||
620 | } | ||
621 | |||
622 | private void collectScopes() { | ||
623 | for (var statement : problem.getStatements()) { | ||
624 | if (statement instanceof ScopeDeclaration scopeDeclaration) { | ||
625 | for (var typeScope : scopeDeclaration.getTypeScopes()) { | ||
626 | if (typeScope.isIncrement()) { | ||
627 | collectTypeScopeIncrement(typeScope); | ||
628 | } else { | ||
629 | collectTypeScope(typeScope); | ||
630 | } | ||
631 | } | ||
632 | } | ||
633 | } | ||
634 | } | ||
635 | |||
636 | private void collectTypeScopeIncrement(TypeScope typeScope) { | ||
637 | if (!(typeScope.getTargetType() instanceof ClassDeclaration classDeclaration)) { | ||
638 | throw new TracedException(typeScope, "Target of incremental type scope must be a class declaration"); | ||
639 | } | ||
640 | var newNode = classDeclaration.getNewNode(); | ||
641 | if (newNode == null) { | ||
642 | throw new TracedException(typeScope, "Target of incremental type scope must be concrete class"); | ||
643 | } | ||
644 | int newNodeId = nodeTrace.get(newNode); | ||
645 | var type = relationTrace.get(classDeclaration); | ||
646 | var typeInfo = metamodel.typeHierarchy().getAnalysisResult(type); | ||
647 | if (!typeInfo.getDirectSubtypes().isEmpty()) { | ||
648 | throw new TracedException(typeScope, "Target of incremental type scope cannot have any subclasses"); | ||
649 | } | ||
650 | var interval = getCardinalityInterval(typeScope.getMultiplicity()); | ||
651 | countSeed.compute(Tuple.of(newNodeId), (key, oldValue) -> | ||
652 | oldValue == null ? interval : oldValue.meet(interval)); | ||
653 | } | ||
654 | |||
655 | private void collectTypeScope(TypeScope typeScope) { | ||
656 | var type = relationTrace.get(typeScope.getTargetType()); | ||
657 | if (type == null) { | ||
658 | throw new TracedException(typeScope, "Unknown target type"); | ||
659 | } | ||
660 | var interval = getCardinalityInterval(typeScope.getMultiplicity()); | ||
661 | if (scopePropagator == null) { | ||
662 | scopePropagator = new ScopePropagator(); | ||
663 | } | ||
664 | scopePropagator.scope(type, interval); | ||
665 | } | ||
666 | |||
667 | private record RelationInfo(PartialRelation partialRelation, MutableSeed<TruthValue> assertions, | ||
668 | MutableSeed<TruthValue> defaultAssertions) { | ||
669 | public RelationInfo(String name, int arity, TruthValue value, TruthValue defaultValue) { | ||
670 | this(new PartialRelation(name, arity), value, defaultValue); | ||
671 | } | ||
672 | |||
673 | public RelationInfo(PartialRelation partialRelation, TruthValue value, TruthValue defaultValue) { | ||
674 | this(partialRelation, MutableSeed.of(partialRelation.arity(), value), | ||
675 | MutableSeed.of(partialRelation.arity(), defaultValue)); | ||
676 | } | ||
677 | |||
678 | public Seed<TruthValue> toSeed(int nodeCount) { | ||
679 | defaultAssertions.overwriteValues(assertions); | ||
680 | if (partialRelation.equals(ReasoningAdapter.EQUALS_SYMBOL)) { | ||
681 | for (int i = 0; i < nodeCount; i++) { | ||
682 | defaultAssertions.setIfMissing(Tuple.of(i, i), TruthValue.TRUE); | ||
683 | } | ||
684 | defaultAssertions.setAllMissing(TruthValue.FALSE); | ||
685 | } | ||
686 | return defaultAssertions; | ||
687 | } | ||
688 | } | ||
103 | } | 689 | } |