aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/ModelInitializer.java
diff options
context:
space:
mode:
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.java674
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;
9import org.eclipse.collections.api.factory.primitive.ObjectIntMaps; 9import org.eclipse.collections.api.factory.primitive.ObjectIntMaps;
10import org.eclipse.collections.api.map.primitive.MutableObjectIntMap; 10import org.eclipse.collections.api.map.primitive.MutableObjectIntMap;
11import tools.refinery.language.model.problem.*; 11import tools.refinery.language.model.problem.*;
12import tools.refinery.language.semantics.model.internal.DecisionTree; 12import tools.refinery.language.semantics.model.internal.MutableSeed;
13import tools.refinery.language.utils.BuiltinSymbols;
13import tools.refinery.language.utils.ProblemDesugarer; 14import tools.refinery.language.utils.ProblemDesugarer;
14import tools.refinery.language.utils.RelationInfo; 15import tools.refinery.language.utils.ProblemUtil;
15import tools.refinery.store.representation.Symbol; 16import tools.refinery.store.dse.propagation.PropagationBuilder;
17import tools.refinery.store.model.ModelStoreBuilder;
18import tools.refinery.store.query.Constraint;
19import tools.refinery.store.query.dnf.InvalidClauseException;
20import tools.refinery.store.query.dnf.Query;
21import tools.refinery.store.query.dnf.RelationalQuery;
22import tools.refinery.store.query.literal.*;
23import tools.refinery.store.query.term.NodeVariable;
24import tools.refinery.store.query.term.Variable;
25import tools.refinery.store.reasoning.ReasoningAdapter;
26import tools.refinery.store.reasoning.representation.AnyPartialSymbol;
27import tools.refinery.store.reasoning.representation.PartialRelation;
28import tools.refinery.store.reasoning.scope.ScopePropagator;
29import tools.refinery.store.reasoning.seed.ModelSeed;
30import tools.refinery.store.reasoning.seed.Seed;
31import tools.refinery.store.reasoning.translator.containment.ContainmentHierarchyTranslator;
32import tools.refinery.store.reasoning.translator.metamodel.Metamodel;
33import tools.refinery.store.reasoning.translator.metamodel.MetamodelBuilder;
34import tools.refinery.store.reasoning.translator.metamodel.MetamodelTranslator;
35import tools.refinery.store.reasoning.translator.multiobject.MultiObjectTranslator;
36import tools.refinery.store.reasoning.translator.multiplicity.ConstrainedMultiplicity;
37import tools.refinery.store.reasoning.translator.multiplicity.Multiplicity;
38import tools.refinery.store.reasoning.translator.multiplicity.UnconstrainedMultiplicity;
39import tools.refinery.store.reasoning.translator.predicate.PredicateTranslator;
16import tools.refinery.store.representation.TruthValue; 40import tools.refinery.store.representation.TruthValue;
41import tools.refinery.store.representation.cardinality.CardinalityInterval;
42import tools.refinery.store.representation.cardinality.CardinalityIntervals;
43import tools.refinery.store.representation.cardinality.UpperCardinalities;
17import tools.refinery.store.tuple.Tuple; 44import tools.refinery.store.tuple.Tuple;
18 45
19import java.util.HashMap; 46import java.util.*;
20import java.util.Map;
21 47
22public class ModelInitializer { 48public 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}