diff options
Diffstat (limited to 'subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PQueryFlattener.java')
-rw-r--r-- | subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PQueryFlattener.java | 253 |
1 files changed, 253 insertions, 0 deletions
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PQueryFlattener.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PQueryFlattener.java new file mode 100644 index 00000000..76311d8f --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PQueryFlattener.java | |||
@@ -0,0 +1,253 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Marton Bur, Akos Horvath, Zoltan Ujhelyi, Istvan Rath and Daniel Varro | ||
3 | * This program and the accompanying materials are made available under the | ||
4 | * terms of the Eclipse Public License v. 2.0 which is available at | ||
5 | * http://www.eclipse.org/legal/epl-v20.html. | ||
6 | * | ||
7 | * SPDX-License-Identifier: EPL-2.0 | ||
8 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.psystem.rewriters; | ||
10 | |||
11 | import java.util.ArrayDeque; | ||
12 | import java.util.ArrayList; | ||
13 | import java.util.Collections; | ||
14 | import java.util.Deque; | ||
15 | import java.util.HashMap; | ||
16 | import java.util.HashSet; | ||
17 | import java.util.LinkedHashSet; | ||
18 | import java.util.LinkedList; | ||
19 | import java.util.List; | ||
20 | import java.util.Map; | ||
21 | import java.util.Set; | ||
22 | |||
23 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
24 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
25 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall; | ||
26 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PDisjunction; | ||
27 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
28 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery.PQueryStatus; | ||
29 | import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IConstraintFilter.AllowAllFilter; | ||
30 | import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IConstraintFilter.ExportedParameterFilter; | ||
31 | import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IVariableRenamer.HierarchicalName; | ||
32 | import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IVariableRenamer.SameName; | ||
33 | import tools.refinery.viatra.runtime.matchers.util.Preconditions; | ||
34 | import tools.refinery.viatra.runtime.matchers.util.Sets; | ||
35 | |||
36 | /** | ||
37 | * This rewriter class holds the query flattening logic | ||
38 | * | ||
39 | * @author Marton Bur | ||
40 | * | ||
41 | */ | ||
42 | public class PQueryFlattener extends PDisjunctionRewriter { | ||
43 | |||
44 | /** | ||
45 | * Utility function to produce the permutation of every possible mapping of values. | ||
46 | * | ||
47 | * @param values | ||
48 | * @return | ||
49 | */ | ||
50 | private static <K, V> Set<Map<K, V>> permutation(Map<K, Set<V>> values) { | ||
51 | // An ordering of keys is defined here which will help restoring the appropriate values after the execution of | ||
52 | // the cartesian product | ||
53 | List<K> keyList = new ArrayList<>(values.keySet()); | ||
54 | |||
55 | // Produce list of value sets with the ordering defined by keyList | ||
56 | List<Set<V>> valuesList = new ArrayList<Set<V>>(keyList.size()); | ||
57 | for (K key : keyList) { | ||
58 | valuesList.add(values.get(key)); | ||
59 | } | ||
60 | |||
61 | // Cartesian product will obey ordering of the list | ||
62 | Set<List<V>> valueMappings = Sets.cartesianProduct(valuesList); | ||
63 | |||
64 | // Build result | ||
65 | Set<Map<K, V>> result = new LinkedHashSet<>(); | ||
66 | for (List<V> valueList : valueMappings) { | ||
67 | Map<K, V> map = new HashMap<>(); | ||
68 | for (int i = 0; i < keyList.size(); i++) { | ||
69 | map.put(keyList.get(i), valueList.get(i)); | ||
70 | } | ||
71 | result.add(map); | ||
72 | } | ||
73 | |||
74 | return result; | ||
75 | } | ||
76 | |||
77 | private IFlattenCallPredicate flattenCallPredicate; | ||
78 | |||
79 | public PQueryFlattener(IFlattenCallPredicate flattenCallPredicate) { | ||
80 | this.flattenCallPredicate = flattenCallPredicate; | ||
81 | } | ||
82 | |||
83 | @Override | ||
84 | public PDisjunction rewrite(PDisjunction disjunction) { | ||
85 | PQuery query = disjunction.getQuery(); | ||
86 | |||
87 | // Check for recursion | ||
88 | Set<PQuery> allReferredQueries = disjunction.getAllReferredQueries(); | ||
89 | for (PQuery referredQuery : allReferredQueries) { | ||
90 | if (referredQuery.getAllReferredQueries().contains(referredQuery)) { | ||
91 | throw new RewriterException("Recursive queries are not supported, can't flatten query named \"{1}\"", | ||
92 | new String[] { query.getFullyQualifiedName() }, "Unsupported recursive query", query); | ||
93 | } | ||
94 | } | ||
95 | |||
96 | return this.doFlatten(disjunction); | ||
97 | } | ||
98 | |||
99 | /** | ||
100 | * Return the list of dependencies (including the root) in chronological order | ||
101 | * | ||
102 | * @param rootDisjunction | ||
103 | * @return | ||
104 | */ | ||
105 | private List<PDisjunction> disjunctionDependencies(PDisjunction rootDisjunction) { | ||
106 | // Disjunctions are first collected into a list usign a depth-first approach, | ||
107 | // which can be iterated backwards while removing duplicates | ||
108 | Deque<PDisjunction> stack = new ArrayDeque<>(); | ||
109 | LinkedList<PDisjunction> list = new LinkedList<>(); | ||
110 | stack.push(rootDisjunction); | ||
111 | list.add(rootDisjunction); | ||
112 | |||
113 | while (!stack.isEmpty()) { | ||
114 | PDisjunction disjunction = stack.pop(); | ||
115 | // Collect dependencies | ||
116 | for (PBody pBody : disjunction.getBodies()) { | ||
117 | for (PConstraint constraint : pBody.getConstraints()) { | ||
118 | if (constraint instanceof PositivePatternCall) { | ||
119 | PositivePatternCall positivePatternCall = (PositivePatternCall) constraint; | ||
120 | if (flattenCallPredicate.shouldFlatten(positivePatternCall)) { | ||
121 | // If the above preconditions meet, the call should be flattened | ||
122 | PDisjunction calledDisjunction = positivePatternCall.getReferredQuery().getDisjunctBodies(); | ||
123 | stack.push(calledDisjunction); | ||
124 | list.add(calledDisjunction); | ||
125 | } | ||
126 | } | ||
127 | } | ||
128 | } | ||
129 | } | ||
130 | |||
131 | // Remove duplicates (keeping the last instance) and reverse order | ||
132 | Set<PDisjunction> visited = new HashSet<PDisjunction>(); | ||
133 | List<PDisjunction> result = new ArrayList<PDisjunction>(list.size()); | ||
134 | |||
135 | list.descendingIterator().forEachRemaining(item -> { | ||
136 | if (!visited.contains(item)) { | ||
137 | result.add(item); | ||
138 | visited.add(item); | ||
139 | } | ||
140 | |||
141 | }); | ||
142 | |||
143 | return result; | ||
144 | } | ||
145 | |||
146 | /** | ||
147 | * This function holds the actual flattening logic for a PQuery | ||
148 | * | ||
149 | * @param rootDisjunction | ||
150 | * to be flattened | ||
151 | * @return the flattened bodies of the pQuery | ||
152 | */ | ||
153 | private PDisjunction doFlatten(PDisjunction rootDisjunction) { | ||
154 | |||
155 | Map<PDisjunction, Set<PBody>> flatBodyMapping = new HashMap<>(); | ||
156 | |||
157 | List<PDisjunction> dependencies = disjunctionDependencies(rootDisjunction); | ||
158 | |||
159 | for (PDisjunction disjunction : dependencies) { | ||
160 | Set<PBody> flatBodies = new LinkedHashSet<>(); | ||
161 | for (PBody body : disjunction.getBodies()) { | ||
162 | if (isFlatteningNeeded(body)) { | ||
163 | Map<PositivePatternCall, Set<PBody>> flattenedBodies = new HashMap<>(); | ||
164 | for (PConstraint pConstraint : body.getConstraints()) { | ||
165 | |||
166 | if (pConstraint instanceof PositivePatternCall) { | ||
167 | PositivePatternCall positivePatternCall = (PositivePatternCall) pConstraint; | ||
168 | if (flattenCallPredicate.shouldFlatten(positivePatternCall)) { | ||
169 | // If the above preconditions meet, do the flattening and return the disjoint bodies | ||
170 | PDisjunction calledDisjunction = positivePatternCall.getReferredQuery() | ||
171 | .getDisjunctBodies(); | ||
172 | |||
173 | Set<PBody> flattenedBodySet = flatBodyMapping.get(calledDisjunction); | ||
174 | Preconditions.checkArgument(!flattenedBodySet.isEmpty()); | ||
175 | flattenedBodies.put(positivePatternCall, flattenedBodySet); | ||
176 | } | ||
177 | } | ||
178 | } | ||
179 | flatBodies.addAll(createSetOfFlatPBodies(body, flattenedBodies)); | ||
180 | } else { | ||
181 | flatBodies.add(prepareFlatPBody(body)); | ||
182 | } | ||
183 | } | ||
184 | flatBodyMapping.put(disjunction, flatBodies); | ||
185 | } | ||
186 | |||
187 | return new PDisjunction(rootDisjunction.getQuery(), flatBodyMapping.get(rootDisjunction)); | ||
188 | } | ||
189 | |||
190 | /** | ||
191 | * Creates the flattened bodies based on the caller body and the called (and already flattened) disjunctions | ||
192 | * | ||
193 | * @param pBody | ||
194 | * the body to flatten | ||
195 | * @param flattenedDisjunctions | ||
196 | * the | ||
197 | * @param flattenedCalls | ||
198 | * @return | ||
199 | */ | ||
200 | private Set<PBody> createSetOfFlatPBodies(PBody pBody, Map<PositivePatternCall, Set<PBody>> flattenedCalls) { | ||
201 | PQuery pQuery = pBody.getPattern(); | ||
202 | |||
203 | Set<Map<PositivePatternCall, PBody>> conjunctedCalls = permutation(flattenedCalls); | ||
204 | |||
205 | // The result set containing the merged conjuncted bodies | ||
206 | Set<PBody> conjunctedBodies = new HashSet<>(); | ||
207 | |||
208 | for (Map<PositivePatternCall, PBody> calledBodies : conjunctedCalls) { | ||
209 | FlattenerCopier copier = createBodyCopier(pQuery, calledBodies); | ||
210 | |||
211 | int i = 0; | ||
212 | HierarchicalName hierarchicalNamingTool = new HierarchicalName(); | ||
213 | for (PositivePatternCall patternCall : calledBodies.keySet()) { | ||
214 | // Merge each called body | ||
215 | hierarchicalNamingTool.setCallCount(i++); | ||
216 | copier.mergeBody(patternCall, hierarchicalNamingTool, new ExportedParameterFilter()); | ||
217 | } | ||
218 | |||
219 | // Merge the caller's constraints to the conjunct body | ||
220 | copier.mergeBody(pBody); | ||
221 | |||
222 | PBody copiedBody = copier.getCopiedBody(); | ||
223 | copiedBody.setStatus(PQueryStatus.OK); | ||
224 | conjunctedBodies.add(copiedBody); | ||
225 | } | ||
226 | |||
227 | return conjunctedBodies; | ||
228 | } | ||
229 | |||
230 | private FlattenerCopier createBodyCopier(PQuery query, Map<PositivePatternCall, PBody> calledBodies) { | ||
231 | FlattenerCopier flattenerCopier = new FlattenerCopier(query, calledBodies); | ||
232 | flattenerCopier.setTraceCollector(getTraceCollector()); | ||
233 | return flattenerCopier; | ||
234 | } | ||
235 | |||
236 | private PBody prepareFlatPBody(PBody pBody) { | ||
237 | PBodyCopier copier = createBodyCopier(pBody.getPattern(), Collections.<PositivePatternCall, PBody> emptyMap()); | ||
238 | copier.mergeBody(pBody, new SameName(), new AllowAllFilter()); | ||
239 | // the copying of the body here is necessary for only one containing PDisjunction can be assigned to a PBody | ||
240 | return copier.getCopiedBody(); | ||
241 | } | ||
242 | |||
243 | private boolean isFlatteningNeeded(PBody pBody) { | ||
244 | // Check if the body contains positive pattern call AND if it should be flattened | ||
245 | for (PConstraint pConstraint : pBody.getConstraints()) { | ||
246 | if (pConstraint instanceof PositivePatternCall) { | ||
247 | return flattenCallPredicate.shouldFlatten((PositivePatternCall) pConstraint); | ||
248 | } | ||
249 | } | ||
250 | return false; | ||
251 | } | ||
252 | |||
253 | } | ||