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