aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/typehierarchy/TypeHierarchy.java
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/typehierarchy/TypeHierarchy.java')
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/typehierarchy/TypeHierarchy.java254
1 files changed, 254 insertions, 0 deletions
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/typehierarchy/TypeHierarchy.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/typehierarchy/TypeHierarchy.java
new file mode 100644
index 00000000..3f918c97
--- /dev/null
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/typehierarchy/TypeHierarchy.java
@@ -0,0 +1,254 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.reasoning.translator.typehierarchy;
7
8import tools.refinery.store.reasoning.representation.PartialRelation;
9import tools.refinery.store.reasoning.translator.TranslationException;
10
11import java.util.*;
12
13public class TypeHierarchy {
14 private final Set<PartialRelation> allTypes;
15 private final Map<PartialRelation, ExtendedTypeInfo> extendedTypeInfoMap;
16 private final Map<PartialRelation, PartialRelation> replacements = new LinkedHashMap<>();
17 private final InferredType unknownType;
18 private final Map<PartialRelation, TypeAnalysisResult> preservedTypes;
19
20 TypeHierarchy(Map<PartialRelation, TypeInfo> typeInfoMap) {
21 int size = typeInfoMap.size();
22 allTypes = Collections.unmodifiableSet(new LinkedHashSet<>(typeInfoMap.keySet()));
23 extendedTypeInfoMap = new LinkedHashMap<>(size);
24 var concreteTypes = new LinkedHashSet<PartialRelation>();
25 int index = 0;
26 for (var entry : typeInfoMap.entrySet()) {
27 var type = entry.getKey();
28 var typeInfo = entry.getValue();
29 extendedTypeInfoMap.put(type, new ExtendedTypeInfo(index, type, typeInfo));
30 if (!typeInfo.abstractType()) {
31 concreteTypes.add(type);
32 }
33 index++;
34 }
35 unknownType = new InferredType(Set.of(), concreteTypes, null);
36 computeAllSupertypes();
37 computeAllAndConcreteSubtypes();
38 computeDirectSubtypes();
39 eliminateTrivialSupertypes();
40 preservedTypes = computeAnalysisResults();
41 }
42
43 public boolean isEmpty() {
44 return extendedTypeInfoMap.isEmpty();
45 }
46
47 public InferredType getUnknownType() {
48 return unknownType;
49 }
50
51 public Set<PartialRelation> getAllTypes() {
52 return allTypes;
53 }
54
55 public Map<PartialRelation, TypeAnalysisResult> getPreservedTypes() {
56 return preservedTypes;
57 }
58
59 public Map<PartialRelation, PartialRelation> getEliminatedTypes() {
60 return Collections.unmodifiableMap(replacements);
61 }
62
63 public TypeAnalysisResult getAnalysisResult(PartialRelation type) {
64 var preservedResult = preservedTypes.get(type);
65 if (preservedResult != null) {
66 return preservedResult;
67 }
68 var eliminatedResult = replacements.get(type);
69 if (eliminatedResult != null) {
70 return preservedTypes.get(eliminatedResult);
71 }
72 throw new IllegalArgumentException("Unknown type: " + type);
73 }
74
75 private void computeAllSupertypes() {
76 boolean changed;
77 do {
78 changed = false;
79 for (var extendedTypeInfo : extendedTypeInfoMap.values()) {
80 var found = new HashSet<PartialRelation>();
81 var allSupertypes = extendedTypeInfo.getAllSupertypes();
82 for (var supertype : allSupertypes) {
83 var supertypeInfo = extendedTypeInfoMap.get(supertype);
84 if (supertypeInfo == null) {
85 throw new TranslationException(extendedTypeInfo.getType(),
86 "Supertype %s of %s is missing from the type hierarchy"
87 .formatted(supertype, extendedTypeInfo.getType()));
88 }
89 found.addAll(supertypeInfo.getAllSupertypes());
90 }
91 if (allSupertypes.addAll(found)) {
92 changed = true;
93 }
94 }
95 } while (changed);
96 }
97
98 private void computeAllAndConcreteSubtypes() {
99 for (var extendedTypeInfo : extendedTypeInfoMap.values()) {
100 var type = extendedTypeInfo.getType();
101 if (!extendedTypeInfo.isAbstractType()) {
102 extendedTypeInfo.getConcreteSubtypesAndSelf().add(type);
103 }
104 for (var supertype : extendedTypeInfo.getAllSupertypes()) {
105 if (type.equals(supertype)) {
106 throw new TranslationException(type, "%s cannot be a supertype of itself".formatted(type));
107 }
108 var supertypeInfo = extendedTypeInfoMap.get(supertype);
109 supertypeInfo.getAllSubtypes().add(type);
110 if (!extendedTypeInfo.isAbstractType()) {
111 supertypeInfo.getConcreteSubtypesAndSelf().add(type);
112 }
113 }
114 }
115 }
116
117 private void computeDirectSubtypes() {
118 for (var extendedTypeInfo : extendedTypeInfoMap.values()) {
119 var allSubtypes = extendedTypeInfo.getAllSubtypes();
120 var directSubtypes = new LinkedHashSet<>(allSubtypes);
121 var indirectSubtypes = new LinkedHashSet<PartialRelation>(allSubtypes.size());
122 for (var subtype : allSubtypes) {
123 indirectSubtypes.addAll(extendedTypeInfoMap.get(subtype).getAllSubtypes());
124 }
125 directSubtypes.removeAll(indirectSubtypes);
126 extendedTypeInfo.setDirectSubtypes(directSubtypes);
127 }
128 }
129
130 private void eliminateTrivialSupertypes() {
131 Set<PartialRelation> toInspect = new HashSet<>(extendedTypeInfoMap.keySet());
132 while (!toInspect.isEmpty()) {
133 var toRemove = new ArrayList<PartialRelation>();
134 for (var partialRelation : toInspect) {
135 var extendedTypeInfo = extendedTypeInfoMap.get(partialRelation);
136 if (extendedTypeInfo != null && isTrivialSupertype(extendedTypeInfo)) {
137 toRemove.add(partialRelation);
138 }
139 }
140 toInspect.clear();
141 for (var partialRelation : toRemove) {
142 removeTrivialType(partialRelation, toInspect);
143 }
144 }
145 }
146
147 private boolean isTrivialSupertype(ExtendedTypeInfo extendedTypeInfo) {
148 if (!extendedTypeInfo.isAbstractType()) {
149 return false;
150 }
151 var subtypeIterator = extendedTypeInfo.getDirectSubtypes().iterator();
152 if (!subtypeIterator.hasNext()) {
153 // Do not eliminate abstract types with 0 subtypes, because they can be used para-consistently, i.e.,
154 // an object determined to <b>must</b> have an abstract type with 0 subtypes <b>may not</b> ever exist.
155 return false;
156 }
157 var directSubtype = subtypeIterator.next();
158 return !extendedTypeInfoMap.get(directSubtype).isAbstractType() && !subtypeIterator.hasNext();
159 }
160
161 private void removeTrivialType(PartialRelation trivialType, Set<PartialRelation> toInspect) {
162 var extendedTypeInfo = extendedTypeInfoMap.get(trivialType);
163 var iterator = extendedTypeInfo.getDirectSubtypes().iterator();
164 if (!iterator.hasNext()) {
165 throw new AssertionError("Expected trivial supertype %s to have a direct subtype"
166 .formatted(trivialType));
167 }
168 PartialRelation replacement = setReplacement(trivialType, iterator.next());
169 if (iterator.hasNext()) {
170 throw new AssertionError("Expected trivial supertype %s to have at most 1 direct subtype"
171 .formatted(trivialType));
172 }
173 for (var supertype : extendedTypeInfo.getAllSupertypes()) {
174 var extendedSupertypeInfo = extendedTypeInfoMap.get(supertype);
175 if (!extendedSupertypeInfo.getAllSubtypes().remove(trivialType)) {
176 throw new AssertionError("Expected %s to be subtype of %s".formatted(trivialType, supertype));
177 }
178 var directSubtypes = extendedSupertypeInfo.getDirectSubtypes();
179 if (directSubtypes.remove(trivialType)) {
180 directSubtypes.add(replacement);
181 if (extendedSupertypeInfo.isAbstractType() && directSubtypes.size() == 1) {
182 toInspect.add(supertype);
183 }
184 }
185 }
186 for (var subtype : extendedTypeInfo.getAllSubtypes()) {
187 var extendedSubtypeInfo = extendedTypeInfoMap.get(subtype);
188 if (!extendedSubtypeInfo.getAllSupertypes().remove(trivialType)) {
189 throw new AssertionError("Expected %s to be supertype of %s".formatted(trivialType, subtype));
190 }
191 }
192 extendedTypeInfoMap.remove(trivialType);
193 }
194
195 private PartialRelation setReplacement(PartialRelation trivialRelation, PartialRelation replacement) {
196 if (replacement == null) {
197 return trivialRelation;
198 }
199 var resolved = setReplacement(replacement, replacements.get(replacement));
200 replacements.put(trivialRelation, resolved);
201 return resolved;
202 }
203
204 private Map<PartialRelation, TypeAnalysisResult> computeAnalysisResults() {
205 var allExtendedTypeInfoList = sortTypes();
206 var preservedResults = new LinkedHashMap<PartialRelation, TypeAnalysisResult>(
207 allExtendedTypeInfoList.size());
208 for (var extendedTypeInfo : allExtendedTypeInfoList) {
209 var type = extendedTypeInfo.getType();
210 preservedResults.put(type, new TypeAnalysisResult(extendedTypeInfo, allExtendedTypeInfoList));
211 }
212 return Collections.unmodifiableMap(preservedResults);
213 }
214
215 private List<ExtendedTypeInfo> sortTypes() {
216 // Invert {@code directSubtypes} to keep track of the out-degree of types.
217 for (var extendedTypeInfo : extendedTypeInfoMap.values()) {
218 for (var directSubtype : extendedTypeInfo.getDirectSubtypes()) {
219 var extendedDirectSubtypeInfo = extendedTypeInfoMap.get(directSubtype);
220 extendedDirectSubtypeInfo.getUnsortedDirectSupertypes().add(extendedTypeInfo.getType());
221 }
222 }
223 // Build a <i>inverse</i> topological order ({@code extends} edges always points to earlier nodes in the order,
224 // breaking ties according to the original order ({@link ExtendedTypeInfo#index}) to form a 'stable' sort.
225 // See, e.g., https://stackoverflow.com/a/11236027.
226 var priorityQueue = new PriorityQueue<ExtendedTypeInfo>();
227 for (var extendedTypeInfo : extendedTypeInfoMap.values()) {
228 if (extendedTypeInfo.getUnsortedDirectSupertypes().isEmpty()) {
229 priorityQueue.add(extendedTypeInfo);
230 }
231 }
232 var sorted = new ArrayList<ExtendedTypeInfo>(extendedTypeInfoMap.size());
233 while (!priorityQueue.isEmpty()) {
234 var extendedTypeInfo = priorityQueue.remove();
235 sorted.add(extendedTypeInfo);
236 for (var directSubtype : extendedTypeInfo.getDirectSubtypes()) {
237 var extendedDirectSubtypeInfo = extendedTypeInfoMap.get(directSubtype);
238 var unsortedDirectSupertypes = extendedDirectSubtypeInfo.getUnsortedDirectSupertypes();
239 if (!unsortedDirectSupertypes.remove(extendedTypeInfo.getType())) {
240 throw new AssertionError("Expected %s to be a direct supertype of %s"
241 .formatted(extendedTypeInfo.getType(), directSubtype));
242 }
243 if (unsortedDirectSupertypes.isEmpty()) {
244 priorityQueue.add(extendedDirectSubtypeInfo);
245 }
246 }
247 }
248 return Collections.unmodifiableList(sorted);
249 }
250
251 public static TypeHierarchyBuilder builder() {
252 return new TypeHierarchyBuilder();
253 }
254}