aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/language-semantics/src/main/java
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <marussy@mit.bme.hu>2023-09-14 19:29:36 +0200
committerLibravatar GitHub <noreply@github.com>2023-09-14 19:29:36 +0200
commit98ed3b6db5f4e51961a161050cc31c66015116e8 (patch)
tree8bfd6d9bc8d6ed23b9eb0f889dd40b6c24fe8f92 /subprojects/language-semantics/src/main/java
parentMerge pull request #38 from nagilooh/design-space-exploration (diff)
parentMerge remote-tracking branch 'upstream/main' into partial-interpretation (diff)
downloadrefinery-98ed3b6db5f4e51961a161050cc31c66015116e8.tar.gz
refinery-98ed3b6db5f4e51961a161050cc31c66015116e8.tar.zst
refinery-98ed3b6db5f4e51961a161050cc31c66015116e8.zip
Merge pull request #39 from kris7t/partial-interpretation
Implement partial interpretation based model generation
Diffstat (limited to 'subprojects/language-semantics/src/main/java')
-rw-r--r--subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/BuiltInDetail.java10
-rw-r--r--subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/ClassDetail.java16
-rw-r--r--subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/Metadata.java12
-rw-r--r--subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/MetadataCreator.java200
-rw-r--r--subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/NodeKind.java12
-rw-r--r--subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/NodeMetadata.java9
-rw-r--r--subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/OppositeReferenceDetail.java9
-rw-r--r--subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/PredicateDetail.java16
-rw-r--r--subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/ReferenceDetail.java16
-rw-r--r--subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/RelationDetail.java10
-rw-r--r--subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/RelationMetadata.java9
-rw-r--r--subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/ModelInitializer.java674
-rw-r--r--subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/SemanticsUtils.java31
-rw-r--r--subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/TracedException.java51
-rw-r--r--subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/internal/DecisionTree.java32
-rw-r--r--subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/internal/DecisionTreeCursor.java9
-rw-r--r--subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/internal/MutableSeed.java28
-rw-r--r--subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/internal/NullaryMutableSeed.java83
18 files changed, 1179 insertions, 48 deletions
diff --git a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/BuiltInDetail.java b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/BuiltInDetail.java
new file mode 100644
index 00000000..6f706069
--- /dev/null
+++ b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/BuiltInDetail.java
@@ -0,0 +1,10 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.language.semantics.metadata;
7
8public record BuiltInDetail() implements RelationDetail {
9 public static final BuiltInDetail INSTANCE = new BuiltInDetail();
10}
diff --git a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/ClassDetail.java b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/ClassDetail.java
new file mode 100644
index 00000000..1d3190f5
--- /dev/null
+++ b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/ClassDetail.java
@@ -0,0 +1,16 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.language.semantics.metadata;
7
8public record ClassDetail(boolean abstractClass) implements RelationDetail {
9 public static final ClassDetail CONCRETE_CLASS = new ClassDetail(false);
10
11 public static final ClassDetail ABSTRACT_CLASS = new ClassDetail(true);
12
13 public static ClassDetail ofAbstractClass(boolean abstractClass) {
14 return abstractClass ? ABSTRACT_CLASS : CONCRETE_CLASS;
15 }
16}
diff --git a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/Metadata.java b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/Metadata.java
new file mode 100644
index 00000000..d2dcb43a
--- /dev/null
+++ b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/Metadata.java
@@ -0,0 +1,12 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.language.semantics.metadata;
7
8public sealed interface Metadata permits NodeMetadata, RelationMetadata {
9 String name();
10
11 String simpleName();
12}
diff --git a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/MetadataCreator.java b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/MetadataCreator.java
new file mode 100644
index 00000000..cc262129
--- /dev/null
+++ b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/MetadataCreator.java
@@ -0,0 +1,200 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.language.semantics.metadata;
7
8import com.google.inject.Inject;
9import org.eclipse.emf.ecore.EObject;
10import org.eclipse.xtext.naming.IQualifiedNameConverter;
11import org.eclipse.xtext.naming.IQualifiedNameProvider;
12import org.eclipse.xtext.naming.QualifiedName;
13import org.eclipse.xtext.scoping.IScope;
14import org.eclipse.xtext.scoping.IScopeProvider;
15import tools.refinery.language.model.problem.*;
16import tools.refinery.language.semantics.model.ModelInitializer;
17import tools.refinery.language.semantics.model.TracedException;
18import tools.refinery.language.utils.ProblemUtil;
19import tools.refinery.store.reasoning.representation.PartialRelation;
20
21import java.util.*;
22
23public class MetadataCreator {
24 @Inject
25 private IScopeProvider scopeProvider;
26
27 @Inject
28 private IQualifiedNameProvider qualifiedNameProvider;
29
30 @Inject
31 private IQualifiedNameConverter qualifiedNameConverter;
32
33 private ModelInitializer initializer;
34
35 private IScope nodeScope;
36
37 private IScope relationScope;
38
39 public void setInitializer(ModelInitializer initializer) {
40 if (initializer == null) {
41 throw new IllegalArgumentException("Initializer was already set");
42 }
43 this.initializer = initializer;
44 var problem = initializer.getProblem();
45 nodeScope = scopeProvider.getScope(problem, ProblemPackage.Literals.NODE_ASSERTION_ARGUMENT__NODE);
46 relationScope = scopeProvider.getScope(problem, ProblemPackage.Literals.ASSERTION__RELATION);
47 }
48
49 public static String unnamedNode(int nodeId) {
50 return "::" + nodeId;
51 }
52
53 public List<NodeMetadata> getNodesMetadata() {
54 return getNodesMetadata(initializer.getNodeCount(), true);
55 }
56
57 public List<NodeMetadata> getNodesMetadata(int nodeCount, boolean preserveNewNodes) {
58 var nodes = new NodeMetadata[Math.max(initializer.getNodeCount(), nodeCount)];
59 for (var entry : initializer.getNodeTrace().keyValuesView()) {
60 var node = entry.getOne();
61 var id = entry.getTwo();
62 nodes[id] = getNodeMetadata(id, node, preserveNewNodes);
63 }
64 for (int i = 0; i < nodes.length; i++) {
65 if (nodes[i] == null) {
66 var nodeName = unnamedNode(i);
67 nodes[i] = new NodeMetadata(nodeName, nodeName, NodeKind.IMPLICIT);
68 }
69 }
70 return List.of(nodes);
71 }
72
73 private NodeMetadata getNodeMetadata(int nodeId, Node node, boolean preserveNewNodes) {
74 var kind = getNodeKind(node);
75 if (!preserveNewNodes && kind == NodeKind.NEW) {
76 var nodeName = unnamedNode(nodeId);
77 return new NodeMetadata(nodeName, nodeName, NodeKind.IMPLICIT);
78 }
79 var qualifiedName = getQualifiedName(node);
80 var simpleName = getSimpleName(node, qualifiedName, nodeScope);
81 return new NodeMetadata(qualifiedNameConverter.toString(qualifiedName),
82 qualifiedNameConverter.toString(simpleName), getNodeKind(node));
83 }
84
85 private NodeKind getNodeKind(Node node) {
86 if (ProblemUtil.isImplicitNode(node)) {
87 return NodeKind.IMPLICIT;
88 } else if (ProblemUtil.isIndividualNode(node)) {
89 return NodeKind.INDIVIDUAL;
90 } else if (ProblemUtil.isNewNode(node)) {
91 return NodeKind.NEW;
92 } else {
93 throw new TracedException(node, "Unknown node type");
94 }
95 }
96
97 public List<RelationMetadata> getRelationsMetadata() {
98 var relationTrace = initializer.getRelationTrace();
99 var relations = new ArrayList<RelationMetadata>(relationTrace.size());
100 for (var entry : relationTrace.entrySet()) {
101 var relation = entry.getKey();
102 var partialRelation = entry.getValue();
103 var metadata = getRelationMetadata(relation, partialRelation);
104 relations.add(metadata);
105 }
106 return Collections.unmodifiableList(relations);
107 }
108
109 private RelationMetadata getRelationMetadata(Relation relation, PartialRelation partialRelation) {
110 var qualifiedName = getQualifiedName(relation);
111 var qualifiedNameString = qualifiedNameConverter.toString(qualifiedName);
112 var simpleName = getSimpleName(relation, qualifiedName, relationScope);
113 var simpleNameString = qualifiedNameConverter.toString(simpleName);
114 var arity = partialRelation.arity();
115 var detail = getRelationDetail(relation, partialRelation);
116 return new RelationMetadata(qualifiedNameString, simpleNameString, arity, detail);
117 }
118
119 private RelationDetail getRelationDetail(Relation relation, PartialRelation partialRelation) {
120 if (ProblemUtil.isBuiltIn(relation) && !ProblemUtil.isError(relation)) {
121 return getBuiltInDetail();
122 }
123 if (relation instanceof ClassDeclaration classDeclaration) {
124 return getClassDetail(classDeclaration);
125 } else if (relation instanceof ReferenceDeclaration) {
126 return getReferenceDetail(partialRelation);
127 } else if (relation instanceof EnumDeclaration) {
128 return getEnumDetail();
129 } else if (relation instanceof PredicateDefinition predicateDefinition) {
130 return getPredicateDetail(predicateDefinition);
131 } else {
132 throw new TracedException(relation, "Unknown relation");
133 }
134 }
135
136 private RelationDetail getBuiltInDetail() {
137 return BuiltInDetail.INSTANCE;
138 }
139
140 private RelationDetail getClassDetail(ClassDeclaration classDeclaration) {
141 return ClassDetail.ofAbstractClass(classDeclaration.isAbstract());
142 }
143
144 private RelationDetail getReferenceDetail(PartialRelation partialRelation) {
145 var metamodel = initializer.getMetamodel();
146 var opposite = metamodel.oppositeReferences().get(partialRelation);
147 if (opposite == null) {
148 boolean isContainment = metamodel.containmentHierarchy().containsKey(partialRelation);
149 return ReferenceDetail.ofContainment(isContainment);
150 } else {
151 boolean isContainer = metamodel.containmentHierarchy().containsKey(opposite);
152 return new OppositeReferenceDetail(isContainer, opposite.name());
153 }
154 }
155
156 private RelationDetail getEnumDetail() {
157 return ClassDetail.CONCRETE_CLASS;
158 }
159
160 private RelationDetail getPredicateDetail(PredicateDefinition predicate) {
161 return PredicateDetail.ofError(predicate.isError());
162 }
163
164 private QualifiedName getQualifiedName(EObject eObject) {
165 var qualifiedName = qualifiedNameProvider.getFullyQualifiedName(eObject);
166 if (qualifiedName == null) {
167 throw new TracedException(eObject, "Unknown qualified name");
168 }
169 return qualifiedName;
170 }
171
172 private QualifiedName getSimpleName(EObject eObject, QualifiedName qualifiedName, IScope scope) {
173 var descriptions = scope.getElements(eObject);
174 var names = new HashSet<QualifiedName>();
175 for (var description : descriptions) {
176 // {@code getQualifiedName()} will refer to the full name for objects that are loaded from the global
177 // scope, but {@code getName()} returns the qualified name that we set in
178 // {@code ProblemResourceDescriptionStrategy}.
179 names.add(description.getName());
180 }
181 var iterator = names.stream().sorted(Comparator.comparingInt(QualifiedName::getSegmentCount)).iterator();
182 while (iterator.hasNext()) {
183 var simpleName = iterator.next();
184 if (names.contains(simpleName) && isUnique(scope, simpleName)) {
185 return simpleName;
186 }
187 }
188 throw new TracedException(eObject, "Ambiguous qualified name: " +
189 qualifiedNameConverter.toString(qualifiedName));
190 }
191
192 private boolean isUnique(IScope scope, QualifiedName name) {
193 var iterator = scope.getElements(name).iterator();
194 if (!iterator.hasNext()) {
195 return false;
196 }
197 iterator.next();
198 return !iterator.hasNext();
199 }
200}
diff --git a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/NodeKind.java b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/NodeKind.java
new file mode 100644
index 00000000..01f0cd09
--- /dev/null
+++ b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/NodeKind.java
@@ -0,0 +1,12 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.language.semantics.metadata;
7
8public enum NodeKind {
9 IMPLICIT,
10 INDIVIDUAL,
11 NEW
12}
diff --git a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/NodeMetadata.java b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/NodeMetadata.java
new file mode 100644
index 00000000..812952c0
--- /dev/null
+++ b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/NodeMetadata.java
@@ -0,0 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.language.semantics.metadata;
7
8public record NodeMetadata(String name, String simpleName, NodeKind kind) implements Metadata {
9}
diff --git a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/OppositeReferenceDetail.java b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/OppositeReferenceDetail.java
new file mode 100644
index 00000000..26d7461c
--- /dev/null
+++ b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/OppositeReferenceDetail.java
@@ -0,0 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.language.semantics.metadata;
7
8public record OppositeReferenceDetail(boolean container, String opposite) implements RelationDetail {
9}
diff --git a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/PredicateDetail.java b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/PredicateDetail.java
new file mode 100644
index 00000000..ca397eca
--- /dev/null
+++ b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/PredicateDetail.java
@@ -0,0 +1,16 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.language.semantics.metadata;
7
8public record PredicateDetail(boolean error) implements RelationDetail {
9 public static final PredicateDetail PREDICATE = new PredicateDetail(false);
10
11 public static final PredicateDetail ERROR_PREDICATE = new PredicateDetail(true);
12
13 public static PredicateDetail ofError(boolean error) {
14 return error ? ERROR_PREDICATE : PREDICATE;
15 }
16}
diff --git a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/ReferenceDetail.java b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/ReferenceDetail.java
new file mode 100644
index 00000000..36771566
--- /dev/null
+++ b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/ReferenceDetail.java
@@ -0,0 +1,16 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.language.semantics.metadata;
7
8public record ReferenceDetail(boolean containment) implements RelationDetail {
9 public static final ReferenceDetail CROSS_REFERENCE = new ReferenceDetail(false);
10
11 public static final ReferenceDetail CONTAINMENT_REFERENCE = new ReferenceDetail(true);
12
13 public static ReferenceDetail ofContainment(boolean containment) {
14 return containment ? CONTAINMENT_REFERENCE : CROSS_REFERENCE;
15 }
16}
diff --git a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/RelationDetail.java b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/RelationDetail.java
new file mode 100644
index 00000000..105179fd
--- /dev/null
+++ b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/RelationDetail.java
@@ -0,0 +1,10 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.language.semantics.metadata;
7
8public sealed interface RelationDetail permits ClassDetail, ReferenceDetail, PredicateDetail, OppositeReferenceDetail,
9 BuiltInDetail {
10}
diff --git a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/RelationMetadata.java b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/RelationMetadata.java
new file mode 100644
index 00000000..5abcc253
--- /dev/null
+++ b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/RelationMetadata.java
@@ -0,0 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.language.semantics.metadata;
7
8public record RelationMetadata(String name, String simpleName, int arity, RelationDetail detail) implements Metadata {
9}
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}
diff --git a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/SemanticsUtils.java b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/SemanticsUtils.java
new file mode 100644
index 00000000..47c89e9b
--- /dev/null
+++ b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/SemanticsUtils.java
@@ -0,0 +1,31 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.language.semantics.model;
7
8import com.google.inject.Inject;
9import com.google.inject.Singleton;
10import org.eclipse.emf.ecore.EObject;
11import org.eclipse.xtext.naming.IQualifiedNameConverter;
12import org.eclipse.xtext.naming.IQualifiedNameProvider;
13
14import java.util.Optional;
15
16@Singleton
17public class SemanticsUtils {
18 @Inject
19 private IQualifiedNameProvider qualifiedNameProvider;
20
21 @Inject
22 private IQualifiedNameConverter qualifiedNameConverter;
23
24 public Optional<String> getName(EObject eObject) {
25 var qualifiedName = qualifiedNameProvider.getFullyQualifiedName(eObject);
26 if (qualifiedName == null) {
27 return Optional.empty();
28 }
29 return Optional.of(qualifiedNameConverter.toString(qualifiedName));
30 }
31}
diff --git a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/TracedException.java b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/TracedException.java
new file mode 100644
index 00000000..38fd8a67
--- /dev/null
+++ b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/TracedException.java
@@ -0,0 +1,51 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.language.semantics.model;
7
8import org.eclipse.emf.ecore.EObject;
9
10public class TracedException extends RuntimeException {
11 private final transient EObject sourceElement;
12
13 public TracedException(EObject sourceElement) {
14 this.sourceElement = sourceElement;
15 }
16
17 public TracedException(EObject sourceElement, String message) {
18 super(message);
19 this.sourceElement = sourceElement;
20 }
21
22 public TracedException(EObject sourceElement, String message, Throwable cause) {
23 super(message, cause);
24 this.sourceElement = sourceElement;
25 }
26
27 public TracedException(EObject sourceElement, Throwable cause) {
28 super(cause);
29 this.sourceElement = sourceElement;
30 }
31
32 public EObject getSourceElement() {
33 return sourceElement;
34 }
35
36 @Override
37 public String getMessage() {
38 var message = super.getMessage();
39 if (message == null) {
40 return "Internal error";
41 }
42 return message;
43 }
44
45 public static TracedException addTrace(EObject sourceElement, Throwable cause) {
46 if (cause instanceof TracedException tracedException && tracedException.sourceElement != null) {
47 return tracedException;
48 }
49 return new TracedException(sourceElement, cause);
50 }
51}
diff --git a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/internal/DecisionTree.java b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/internal/DecisionTree.java
index c1afecf9..32112e61 100644
--- a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/internal/DecisionTree.java
+++ b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/internal/DecisionTree.java
@@ -7,10 +7,10 @@ package tools.refinery.language.semantics.model.internal;
7 7
8import org.eclipse.collections.api.factory.primitive.IntObjectMaps; 8import org.eclipse.collections.api.factory.primitive.IntObjectMaps;
9import tools.refinery.store.map.Cursor; 9import tools.refinery.store.map.Cursor;
10import tools.refinery.store.tuple.Tuple;
11import tools.refinery.store.representation.TruthValue; 10import tools.refinery.store.representation.TruthValue;
11import tools.refinery.store.tuple.Tuple;
12 12
13public class DecisionTree { 13class DecisionTree implements MutableSeed<TruthValue> {
14 private final int levels; 14 private final int levels;
15 15
16 private final DecisionTreeNode root; 16 private final DecisionTreeNode root;
@@ -29,30 +29,53 @@ public class DecisionTree {
29 this(levels, null); 29 this(levels, null);
30 } 30 }
31 31
32 @Override
33 public int arity() {
34 return levels;
35 }
36
37 @Override
38 public Class<TruthValue> valueType() {
39 return TruthValue.class;
40 }
41
42 @Override
43 public TruthValue reducedValue() {
44 return root.getOtherwiseReducedValue().getTruthValue();
45 }
46
47 @Override
32 public TruthValue get(Tuple tuple) { 48 public TruthValue get(Tuple tuple) {
33 return root.getValue(levels - 1, tuple).getTruthValue(); 49 return root.getValue(levels - 1, tuple).getTruthValue();
34 } 50 }
35 51
52 @Override
36 public void mergeValue(Tuple tuple, TruthValue truthValue) { 53 public void mergeValue(Tuple tuple, TruthValue truthValue) {
37 if (truthValue != null) { 54 if (truthValue != null) {
38 root.mergeValue(levels - 1, tuple, truthValue); 55 root.mergeValue(levels - 1, tuple, truthValue);
39 } 56 }
40 } 57 }
41 58
59 @Override
42 public void setIfMissing(Tuple tuple, TruthValue truthValue) { 60 public void setIfMissing(Tuple tuple, TruthValue truthValue) {
43 if (truthValue != null) { 61 if (truthValue != null) {
44 root.setIfMissing(levels - 1, tuple, truthValue); 62 root.setIfMissing(levels - 1, tuple, truthValue);
45 } 63 }
46 } 64 }
47 65
66 @Override
48 public void setAllMissing(TruthValue truthValue) { 67 public void setAllMissing(TruthValue truthValue) {
49 if (truthValue != null) { 68 if (truthValue != null) {
50 root.setAllMissing(truthValue); 69 root.setAllMissing(truthValue);
51 } 70 }
52 } 71 }
53 72
54 public void overwriteValues(DecisionTree values) { 73 @Override
55 root.overwriteValues(values.root); 74 public void overwriteValues(MutableSeed<TruthValue> values) {
75 if (!(values instanceof DecisionTree decisionTree)) {
76 throw new IllegalArgumentException("Incompatible overwrite: " + values);
77 }
78 root.overwriteValues(decisionTree.root);
56 } 79 }
57 80
58 public TruthValue getReducedValue() { 81 public TruthValue getReducedValue() {
@@ -60,6 +83,7 @@ public class DecisionTree {
60 return reducedValue == null ? null : reducedValue.getTruthValue(); 83 return reducedValue == null ? null : reducedValue.getTruthValue();
61 } 84 }
62 85
86 @Override
63 public Cursor<Tuple, TruthValue> getCursor(TruthValue defaultValue, int nodeCount) { 87 public Cursor<Tuple, TruthValue> getCursor(TruthValue defaultValue, int nodeCount) {
64 return new DecisionTreeCursor(levels, defaultValue, nodeCount, root); 88 return new DecisionTreeCursor(levels, defaultValue, nodeCount, root);
65 } 89 }
diff --git a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/internal/DecisionTreeCursor.java b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/internal/DecisionTreeCursor.java
index 9a1e15a3..a9fc644a 100644
--- a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/internal/DecisionTreeCursor.java
+++ b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/internal/DecisionTreeCursor.java
@@ -67,6 +67,15 @@ class DecisionTreeCursor implements Cursor<Tuple, TruthValue> {
67 67
68 @Override 68 @Override
69 public boolean move() { 69 public boolean move() {
70 while (moveOne()) {
71 if (!value.equals(defaultValue)) {
72 return true;
73 }
74 }
75 return false;
76 }
77
78 private boolean moveOne() {
70 boolean found = false; 79 boolean found = false;
71 if (path.isEmpty() && !terminated) { 80 if (path.isEmpty() && !terminated) {
72 found = root.moveNext(levels - 1, this); 81 found = root.moveNext(levels - 1, this);
diff --git a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/internal/MutableSeed.java b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/internal/MutableSeed.java
new file mode 100644
index 00000000..99019e2a
--- /dev/null
+++ b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/internal/MutableSeed.java
@@ -0,0 +1,28 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.language.semantics.model.internal;
7
8import tools.refinery.store.reasoning.seed.Seed;
9import tools.refinery.store.representation.TruthValue;
10import tools.refinery.store.tuple.Tuple;
11
12public interface MutableSeed<T> extends Seed<T> {
13 void mergeValue(Tuple tuple, T value);
14
15 void setIfMissing(Tuple tuple, T value);
16
17 void setAllMissing(T value);
18
19 void overwriteValues(MutableSeed<T> other);
20
21 static MutableSeed<TruthValue> of(int levels, TruthValue initialValue) {
22 if (levels == 0) {
23 return new NullaryMutableSeed(initialValue);
24 } else {
25 return new DecisionTree(levels, initialValue);
26 }
27 }
28}
diff --git a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/internal/NullaryMutableSeed.java b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/internal/NullaryMutableSeed.java
new file mode 100644
index 00000000..80644b1f
--- /dev/null
+++ b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/internal/NullaryMutableSeed.java
@@ -0,0 +1,83 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.language.semantics.model.internal;
7
8import tools.refinery.store.map.Cursor;
9import tools.refinery.store.map.Cursors;
10import tools.refinery.store.representation.TruthValue;
11import tools.refinery.store.tuple.Tuple;
12
13class NullaryMutableSeed implements MutableSeed<TruthValue> {
14 private DecisionTreeValue value;
15
16 public NullaryMutableSeed(TruthValue reducedValue) {
17
18 value = DecisionTreeValue.fromTruthValue(reducedValue);
19 }
20
21 @Override
22 public int arity() {
23 return 0;
24 }
25
26 @Override
27 public Class<TruthValue> valueType() {
28 return TruthValue.class;
29 }
30
31 @Override
32 public TruthValue reducedValue() {
33 return value.getTruthValue();
34 }
35
36 @Override
37 public TruthValue get(Tuple key) {
38 validateKey(key);
39 return reducedValue();
40 }
41
42 private static void validateKey(Tuple key) {
43 if (key.getSize() > 0) {
44 throw new IllegalArgumentException("Invalid key: " + key);
45 }
46 }
47
48 @Override
49 public Cursor<Tuple, TruthValue> getCursor(TruthValue defaultValue, int nodeCount) {
50 if (value == DecisionTreeValue.UNSET || value.getTruthValue() == defaultValue) {
51 return Cursors.empty();
52 }
53 return Cursors.singleton(Tuple.of(), value.getTruthValue());
54 }
55
56 @Override
57 public void mergeValue(Tuple tuple, TruthValue value) {
58 this.value = DecisionTreeValue.fromTruthValue(this.value.merge(value));
59 }
60
61 @Override
62 public void setIfMissing(Tuple tuple, TruthValue value) {
63 validateKey(tuple);
64 setAllMissing(value);
65 }
66
67 @Override
68 public void setAllMissing(TruthValue value) {
69 if (this.value == DecisionTreeValue.UNSET) {
70 this.value = DecisionTreeValue.fromTruthValue(value);
71 }
72 }
73
74 @Override
75 public void overwriteValues(MutableSeed<TruthValue> other) {
76 if (!(other instanceof NullaryMutableSeed nullaryMutableSeed)) {
77 throw new IllegalArgumentException("Incompatible overwrite: " + other);
78 }
79 if (nullaryMutableSeed.value != DecisionTreeValue.UNSET) {
80 value = nullaryMutableSeed.value;
81 }
82 }
83}