aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PQueryFlattener.java
diff options
context:
space:
mode:
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.java253
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 *******************************************************************************/
9package tools.refinery.viatra.runtime.matchers.psystem.rewriters;
10
11import java.util.ArrayDeque;
12import java.util.ArrayList;
13import java.util.Collections;
14import java.util.Deque;
15import java.util.HashMap;
16import java.util.HashSet;
17import java.util.LinkedHashSet;
18import java.util.LinkedList;
19import java.util.List;
20import java.util.Map;
21import java.util.Set;
22
23import tools.refinery.viatra.runtime.matchers.psystem.PBody;
24import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
25import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall;
26import tools.refinery.viatra.runtime.matchers.psystem.queries.PDisjunction;
27import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
28import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery.PQueryStatus;
29import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IConstraintFilter.AllowAllFilter;
30import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IConstraintFilter.ExportedParameterFilter;
31import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IVariableRenamer.HierarchicalName;
32import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IVariableRenamer.SameName;
33import tools.refinery.viatra.runtime.matchers.util.Preconditions;
34import 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 */
42public 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}